diff --git a/napi/__test__/index.spec.ts b/napi/__test__/index.spec.ts index 16fb3172..261be6dc 100644 --- a/napi/__test__/index.spec.ts +++ b/napi/__test__/index.spec.ts @@ -270,5 +270,14 @@ test("mint and spend nft", (t) => { simulator.spend(coinSpends, [p2.secretKey]); - t.pass(); + t.true( + compareBytes( + clvm + .nftMetadata( + clvm.parseNftMetadata(clvm.deserialize(result.nfts[0].info.metadata)) + ) + .serialize(), + result.nfts[0].info.metadata + ) + ); }); diff --git a/napi/index.d.ts b/napi/index.d.ts index 49d84293..0cb45f09 100644 --- a/napi/index.d.ts +++ b/napi/index.d.ts @@ -8,6 +8,8 @@ export interface Output { cost: bigint } export declare function curryTreeHash(treeHash: Uint8Array, args: Array): Uint8Array +export declare function intToSignedBytes(bigInt: bigint): Uint8Array +export declare function signedBytesToInt(bytes: Uint8Array): bigint export interface Coin { parentCoinInfo: Uint8Array puzzleHash: Uint8Array @@ -35,7 +37,7 @@ export interface Nft { } export interface NftInfo { launcherId: Uint8Array - metadata: NftMetadata + metadata: Uint8Array metadataUpdaterPuzzleHash: Uint8Array currentOwner?: Uint8Array royaltyPuzzleHash: Uint8Array @@ -92,6 +94,8 @@ export declare class ClvmAllocator { curry(program: Program, args: Array): Program pair(first: ClvmValue, rest: ClvmValue): Program alloc(value: ClvmValue): Program + nftMetadata(value: NftMetadata): Program + parseNftMetadata(value: Program): NftMetadata delegatedSpendForConditions(conditions: Array): Spend spendP2Standard(syntheticKey: Uint8Array, delegatedSpend: Spend): Spend spendP2DelegatedSingleton(launcherId: Uint8Array, coinId: Uint8Array, singletonInnerPuzzleHash: Uint8Array, delegatedSpend: Spend): Spend diff --git a/napi/index.js b/napi/index.js index d0700e45..86a69cad 100644 --- a/napi/index.js +++ b/napi/index.js @@ -310,10 +310,12 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { ClvmAllocator, curryTreeHash, toCoinId, Program, Simulator, compareBytes, sha256, fromHexRaw, fromHex, toHex } = nativeBinding +const { ClvmAllocator, curryTreeHash, intToSignedBytes, signedBytesToInt, toCoinId, Program, Simulator, compareBytes, sha256, fromHexRaw, fromHex, toHex } = nativeBinding module.exports.ClvmAllocator = ClvmAllocator module.exports.curryTreeHash = curryTreeHash +module.exports.intToSignedBytes = intToSignedBytes +module.exports.signedBytesToInt = signedBytesToInt module.exports.toCoinId = toCoinId module.exports.Program = Program module.exports.Simulator = Simulator diff --git a/napi/src/clvm.rs b/napi/src/clvm.rs index 95a6a634..d1f7fef8 100644 --- a/napi/src/clvm.rs +++ b/napi/src/clvm.rs @@ -1,8 +1,8 @@ use chia::{ bls::PublicKey, - clvm_traits::{clvm_quote, ClvmEncoder, ToClvm}, + clvm_traits::{clvm_quote, ClvmEncoder, FromClvm, ToClvm}, clvm_utils::{self, CurriedProgram, TreeHash}, - protocol::Bytes32, + protocol::{self, Bytes32}, puzzles::nft::{self, NFT_METADATA_UPDATER_PUZZLE_HASH}, }; use chia_wallet_sdk::{ @@ -13,8 +13,8 @@ use chia_wallet_sdk::{ AssertHeightAbsolute, AssertHeightRelative, AssertMyAmount, AssertMyBirthHeight, AssertMyBirthSeconds, AssertMyCoinId, AssertMyParentId, AssertMyPuzzleHash, AssertPuzzleAnnouncement, AssertSecondsAbsolute, AssertSecondsRelative, CreateCoin, - CreateCoinAnnouncement, CreatePuzzleAnnouncement, ReceiveMessage, Remark, ReserveFee, - SendMessage, Softfork, SpendContext, + CreateCoinAnnouncement, CreatePuzzleAnnouncement, HashedPtr, ReceiveMessage, Remark, + ReserveFee, SendMessage, Softfork, SpendContext, }; use clvmr::{ run_program, @@ -26,7 +26,7 @@ use napi::bindgen_prelude::*; use crate::{ clvm_value::{Allocate, ClvmValue}, traits::{FromJs, IntoJs, IntoRust}, - Coin, CoinSpend, MintedNfts, Nft, NftMint, ParsedNft, Program, Spend, + Coin, CoinSpend, MintedNfts, Nft, NftMetadata, NftMint, ParsedNft, Program, Spend, }; type Clvm = Reference; @@ -144,6 +144,25 @@ impl ClvmAllocator { Ok(Program::new(this, ptr)) } + #[napi(ts_args_type = "value: NftMetadata")] + pub fn nft_metadata(&mut self, this: This, value: NftMetadata) -> Result { + let metadata = nft::NftMetadata::from_js(value)?; + + let ptr = metadata + .to_clvm(&mut self.0.allocator) + .map_err(|error| Error::from_reason(error.to_string()))?; + + Ok(Program::new(this, ptr)) + } + + #[napi(ts_args_type = "value: Program")] + pub fn parse_nft_metadata(&mut self, value: &Program) -> Result { + let metadata = nft::NftMetadata::from_clvm(&self.0.allocator, value.ptr) + .map_err(|error| Error::from_reason(error.to_string()))?; + + metadata.into_js() + } + #[napi(ts_args_type = "conditions: Array")] pub fn delegated_spend_for_conditions( &mut self, @@ -252,7 +271,14 @@ impl ClvmAllocator { ) .map_err(|error| Error::from_reason(error.to_string()))?; - result.nfts.push(nft.into_js()?); + let serialized_metadata = self + .0 + .serialize(&nft.info.metadata) + .map_err(|error| Error::from_reason(error.to_string()))?; + + result + .nfts + .push(nft.with_metadata(serialized_metadata).into_js()?); for condition in conditions { let condition = condition @@ -286,7 +312,7 @@ impl ClvmAllocator { let puzzle = sdk::Puzzle::parse(&self.0.allocator, puzzle.ptr); let Some((nft_info, inner_puzzle)) = - sdk::NftInfo::::parse(&self.0.allocator, puzzle) + sdk::NftInfo::::parse(&self.0.allocator, puzzle) .map_err(|error| Error::from_reason(error.to_string()))? else { return Ok(None); @@ -307,7 +333,7 @@ impl ClvmAllocator { ) -> Result> { let parent_puzzle = sdk::Puzzle::parse(&self.0.allocator, parent_puzzle.ptr); - let Some(nft) = sdk::Nft::::parse_child( + let Some(nft) = sdk::Nft::::parse_child( &mut self.0.allocator, parent_coin.into_rust()?, parent_puzzle, @@ -318,13 +344,18 @@ impl ClvmAllocator { return Ok(None); }; - Ok(Some(nft.into_js()?)) + let serialized_metadata = self + .0 + .serialize(&nft.info.metadata) + .map_err(|error| Error::from_reason(error.to_string()))?; + + Ok(Some(nft.with_metadata(serialized_metadata).into_js()?)) } #[napi] pub fn spend_nft(&mut self, nft: Nft, inner_spend: Spend) -> Result> { let ctx = &mut self.0; - let nft = sdk::Nft::::from_js(nft)?; + let nft = sdk::Nft::::from_js(nft)?; nft.spend( ctx, @@ -354,6 +385,19 @@ pub fn curry_tree_hash(tree_hash: Uint8Array, args: Vec) -> Result Result { + let number: num_bigint::BigInt = big_int.into_rust()?; + number.to_signed_bytes_be().into_js() +} + +#[napi] +pub fn signed_bytes_to_int(bytes: Uint8Array) -> Result { + let bytes: Vec = bytes.into_rust()?; + let number = num_bigint::BigInt::from_signed_bytes_be(&bytes); + number.into_js() +} + macro_rules! conditions { ( $( $condition:ident { $hint:literal $function:ident( $( $name:ident: $ty:ty $( => $remap:ty )? ),* ) }, )* ) => { $( #[napi] diff --git a/napi/src/nft.rs b/napi/src/nft.rs index 19bb393e..f52af5bc 100644 --- a/napi/src/nft.rs +++ b/napi/src/nft.rs @@ -1,3 +1,4 @@ +use chia::protocol; use chia::puzzles::nft; use chia_wallet_sdk as sdk; use napi::bindgen_prelude::*; @@ -14,7 +15,7 @@ pub struct Nft { pub info: NftInfo, } -impl IntoJs for sdk::Nft { +impl IntoJs for sdk::Nft { fn into_js(self) -> Result { Ok(Nft { coin: self.coin.into_js()?, @@ -24,7 +25,7 @@ impl IntoJs for sdk::Nft { } } -impl FromJs for sdk::Nft { +impl FromJs for sdk::Nft { fn from_js(nft: Nft) -> Result { Ok(sdk::Nft { coin: nft.coin.into_rust()?, @@ -37,7 +38,7 @@ impl FromJs for sdk::Nft { #[napi(object)] pub struct NftInfo { pub launcher_id: Uint8Array, - pub metadata: NftMetadata, + pub metadata: Uint8Array, pub metadata_updater_puzzle_hash: Uint8Array, pub current_owner: Option, pub royalty_puzzle_hash: Uint8Array, @@ -45,7 +46,7 @@ pub struct NftInfo { pub p2_puzzle_hash: Uint8Array, } -impl IntoJs for sdk::NftInfo { +impl IntoJs for sdk::NftInfo { fn into_js(self) -> Result { Ok(NftInfo { launcher_id: self.launcher_id.into_js()?, @@ -59,7 +60,7 @@ impl IntoJs for sdk::NftInfo { } } -impl FromJs for sdk::NftInfo { +impl FromJs for sdk::NftInfo { fn from_js(info: NftInfo) -> Result { Ok(sdk::NftInfo { launcher_id: info.launcher_id.into_rust()?,