From dff6c7b384d2ad66f249dd589bb1b908a8832007 Mon Sep 17 00:00:00 2001 From: Boris Oncev Date: Fri, 20 Sep 2024 21:13:54 +0200 Subject: [PATCH 1/2] add data deposit fee to wasm functions - normalise parameter types for nft and fungible token functions --- wasm-wrappers/WASM-API.md | 4 ++ wasm-wrappers/js-bindings/wasm_test.js | 75 +++++++++++++++++++++++--- wasm-wrappers/src/lib.rs | 41 +++++++------- 3 files changed, 93 insertions(+), 27 deletions(-) diff --git a/wasm-wrappers/WASM-API.md b/wasm-wrappers/WASM-API.md index abb2c228e..cf4190c0f 100644 --- a/wasm-wrappers/WASM-API.md +++ b/wasm-wrappers/WASM-API.md @@ -186,6 +186,10 @@ this function creates an output that issues that NFT. Given data to be deposited in the blockchain, this function provides the output that deposits this data +### Function: `data_deposit_fee` + +Returns the fee that needs to be paid by a transaction for issuing a data deposit + ### Function: `encode_output_htlc` Given the parameters needed to create hash timelock contract, and a network type (mainnet, testnet, etc), diff --git a/wasm-wrappers/js-bindings/wasm_test.js b/wasm-wrappers/js-bindings/wasm_test.js index 6d55abfb9..a00cbc4ad 100644 --- a/wasm-wrappers/js-bindings/wasm_test.js +++ b/wasm-wrappers/js-bindings/wasm_test.js @@ -40,7 +40,10 @@ import { get_transaction_id, effective_pool_balance, Amount, + TotalSupply, + FreezableToken, encode_output_issue_nft, + encode_output_issue_fungible_token, sign_challenge, verify_challenge, } from "../pkg/wasm_wrappers.js"; @@ -83,7 +86,7 @@ export async function run_test() { // Attempt to use a bad private key to get a public key (test returned Result<> object, which will become a string error) const bad_priv_key = "bad"; try { - const bad_pub_key = public_key_from_private_key(bad_priv_key); + public_key_from_private_key(bad_priv_key); throw new Error("Invalid private key worked somehow!"); } catch (e) { if (!e.includes("Invalid private key encoding")) { @@ -502,6 +505,68 @@ export async function run_test() { assert_eq_arrays(pool_data, expected_pool_data); + let encoded_fungible_token = encode_output_issue_fungible_token( + address, + "XXX", + "http://uri.com", + 2, + TotalSupply.Unlimited, + null, + FreezableToken.Yes, + BigInt(1), + Network.Testnet + ); + + const expected_fungible_token = [ + 7, 1, 12, 88, 88, 88, 2, 56, 104, 116, + 116, 112, 58, 47, 47, 117, 114, 105, 46, 99, + 111, 109, 2, 1, 91, 58, 110, 176, 100, 207, + 6, 194, 41, 193, 30, 91, 4, 195, 202, 103, + 207, 80, 217, 178, 1 + ]; + + assert_eq_arrays(encoded_fungible_token, expected_fungible_token); + + + const account_pubkey = make_default_account_privkey( + mnemonic, + Network.Testnet + ); + const receiving_privkey = make_receiving_address(account_pubkey, 0); + const receiving_pubkey = public_key_from_private_key(receiving_privkey); + + let encoded_nft = encode_output_issue_nft( + token_id, + address, + "nft", + "XXX", + "desc", + "1234", + receiving_pubkey, + "http://uri", + "http://icon", + "http://foo", + BigInt(1), + Network.Testnet + ); + + const expected_nft_encoding = [ + 8, 162, 208, 145, 194, 165, 27, 14, 118, 31, 139, 199, + 254, 11, 190, 108, 15, 64, 180, 50, 106, 211, 26, 107, + 242, 121, 29, 55, 172, 185, 5, 196, 119, 0, 1, 0, + 2, 227, 252, 33, 195, 223, 44, 38, 35, 73, 145, 212, + 180, 49, 115, 4, 150, 204, 250, 205, 123, 131, 201, 114, + 130, 186, 209, 98, 181, 118, 233, 133, 89, 12, 110, 102, + 116, 16, 100, 101, 115, 99, 12, 88, 88, 88, 44, 104, + 116, 116, 112, 58, 47, 47, 105, 99, 111, 110, 40, 104, + 116, 116, 112, 58, 47, 47, 102, 111, 111, 40, 104, 116, + 116, 112, 58, 47, 47, 117, 114, 105, 16, 1, 2, 3, + 4, 1, 91, 58, 110, 176, 100, 207, 6, 194, 41, 193, + 30, 91, 4, 195, 202, 103, 207, 80, 217, 178 + ]; + + assert_eq_arrays(encoded_nft, expected_nft_encoding); + try { const invalid_token_id = "asd"; encode_output_issue_nft( @@ -510,7 +575,7 @@ export async function run_test() { "nft", "XXX", "desc", - "123", + "12345", undefined, undefined, undefined, @@ -732,12 +797,6 @@ export async function run_test() { assert_eq_arrays(witness, expected_no_signature_witness); console.log("empty witness encoding ok"); - const account_pubkey = make_default_account_privkey( - mnemonic, - Network.Testnet - ); - const receiving_privkey = make_receiving_address(account_pubkey, 0); - const opt_utxos = [1, ...output, 1, ...stake_pool_output]; try { diff --git a/wasm-wrappers/src/lib.rs b/wasm-wrappers/src/lib.rs index d023d6e76..893b6b850 100644 --- a/wasm-wrappers/src/lib.rs +++ b/wasm-wrappers/src/lib.rs @@ -676,8 +676,8 @@ pub fn token_change_authority_fee(current_block_height: u64, network: Network) - #[wasm_bindgen] pub fn encode_output_issue_fungible_token( authority: &str, - token_ticker: &[u8], - metadata_uri: &[u8], + token_ticker: &str, + metadata_uri: &str, number_of_decimals: u8, total_supply: TotalSupply, supply_amount: Option, @@ -718,10 +718,10 @@ pub fn encode_output_issue_nft( ticker: &str, description: &str, media_hash: &[u8], - creator: Option, - media_uri: Option>, - icon_uri: Option>, - additional_metadata_uri: Option>, + creator: Option>, + media_uri: Option, + icon_uri: Option, + additional_metadata_uri: Option, _current_block_height: u64, network: Network, ) -> Result, Error> { @@ -730,21 +730,15 @@ pub fn encode_output_issue_nft( let authority = parse_addressable(&chain_config, authority)?; let name = name.into(); let ticker = ticker.into(); - let media_uri = media_uri.into(); - let icon_uri = icon_uri.into(); + let media_uri = media_uri.map(Into::into).into(); + let icon_uri = icon_uri.map(Into::into).into(); let media_hash = media_hash.into(); - let additional_metadata_uri = additional_metadata_uri.into(); + let additional_metadata_uri = additional_metadata_uri.map(Into::into).into(); let creator = creator - .map(|addr| parse_addressable::(&chain_config, &addr)) - .transpose()? - .map(|dest| match dest { - Destination::PublicKey(public_key) => Ok(TokenCreator { public_key }), - Destination::AnyoneCanSpend - | Destination::ScriptHash(_) - | Destination::PublicKeyHash(_) - | Destination::ClassicMultisig(_) => Err(Error::InvalidCreatorPublicKey), - }) - .transpose()?; + .map(|pk| PublicKey::decode_all(&mut pk.as_slice())) + .transpose() + .map_err(|_| Error::InvalidCreatorPublicKey)? + .map(|public_key| TokenCreator { public_key }); let nft_issuance = NftIssuanceV0 { metadata: Metadata { @@ -772,6 +766,15 @@ pub fn encode_output_data_deposit(data: &[u8]) -> Result, Error> { Ok(output.encode()) } +/// Returns the fee that needs to be paid by a transaction for issuing a data deposit +#[wasm_bindgen] +pub fn data_deposit_fee(current_block_height: u64, network: Network) -> Amount { + let chain_config = Builder::new(network.into()).build(); + Amount::from_internal_amount( + chain_config.data_deposit_fee(BlockHeight::new(current_block_height)), + ) +} + /// Given the parameters needed to create hash timelock contract, and a network type (mainnet, testnet, etc), /// this function creates an output. #[wasm_bindgen] From 70c00ab08bb2f07acbbd5775e0f51363dbee2091 Mon Sep 17 00:00:00 2001 From: Boris Oncev Date: Tue, 24 Sep 2024 00:40:51 +0200 Subject: [PATCH 2/2] Add utility function for get_token_id to wasm --- wasm-wrappers/WASM-API.md | 4 ++++ wasm-wrappers/js-bindings/wasm_test.js | 23 +++++++++++++++++++++++ wasm-wrappers/src/error.rs | 2 ++ wasm-wrappers/src/lib.rs | 22 ++++++++++++++++++++-- 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/wasm-wrappers/WASM-API.md b/wasm-wrappers/WASM-API.md index cf4190c0f..e4dc91d80 100644 --- a/wasm-wrappers/WASM-API.md +++ b/wasm-wrappers/WASM-API.md @@ -177,6 +177,10 @@ The current block height information is used in case a network upgrade changed t Given the parameters needed to issue a fungible token, and a network type (mainnet, testnet, etc), this function creates an output that issues that token. +### Function: `get_token_id` + +Returns the Fungible/NFT Token ID for the given inputs of a transaction + ### Function: `encode_output_issue_nft` Given the parameters needed to issue an NFT, and a network type (mainnet, testnet, etc), diff --git a/wasm-wrappers/js-bindings/wasm_test.js b/wasm-wrappers/js-bindings/wasm_test.js index a00cbc4ad..5512a6466 100644 --- a/wasm-wrappers/js-bindings/wasm_test.js +++ b/wasm-wrappers/js-bindings/wasm_test.js @@ -46,6 +46,7 @@ import { encode_output_issue_fungible_token, sign_challenge, verify_challenge, + get_token_id, } from "../pkg/wasm_wrappers.js"; function assert_eq_arrays(arr1, arr2) { @@ -284,6 +285,28 @@ export async function run_test() { assert_eq_arrays(inputs, expected_inputs); + try { + get_token_id([], Network.Testnet); + throw "Token Id generated without a UTXO input somehow!"; + } catch (e) { + if (!e.includes("No UTXO input found")) { + throw e; + } + console.log("Tested no UTXO inputs for token ID successfully"); + } + + { + const expected_token_id = + "tmltk13cncdptay55g9ajhrkaw0fp46r0tspq9kptul8vj2q7yvd69n4zsl24gea"; + const token_id = get_token_id(inputs, Network.Testnet); + console.log(token_id); + + if (token_id != expected_token_id) { + throw new Error("Different token id"); + } + + } + const token_id = "tmltk15tgfrs49rv88v8utcllqh0nvpaqtgvn26vdxhuner5m6ewg9c3msn9fxns"; try { diff --git a/wasm-wrappers/src/error.rs b/wasm-wrappers/src/error.rs index 52e863ac2..7ebfabc88 100644 --- a/wasm-wrappers/src/error.rs +++ b/wasm-wrappers/src/error.rs @@ -76,6 +76,8 @@ pub enum Error { TransactionCreationError(#[from] TransactionCreationError), #[error("Produce signature error: {0}")] ProduceSignatureError(#[from] DestinationSigError), + #[error("No UTXO input found in the provided inputs")] + NoUtxoInInputs, } // This is required to make an error readable in JavaScript diff --git a/wasm-wrappers/src/lib.rs b/wasm-wrappers/src/lib.rs index 893b6b850..892b5a2ee 100644 --- a/wasm-wrappers/src/lib.rs +++ b/wasm-wrappers/src/lib.rs @@ -42,8 +42,8 @@ use common::{ stakelock::StakePoolData, timelock::OutputTimeLock, tokens::{ - IsTokenFreezable, Metadata, NftIssuance, NftIssuanceV0, TokenCreator, TokenId, - TokenIssuance, TokenIssuanceV1, TokenTotalSupply, + make_token_id, IsTokenFreezable, Metadata, NftIssuance, NftIssuanceV0, TokenCreator, + TokenId, TokenIssuance, TokenIssuanceV1, TokenTotalSupply, }, AccountNonce, AccountOutPoint, AccountSpending, ChainConfig, Destination, OutPointSourceId, SignedTransaction, Transaction, TxInput, TxOutput, UtxoOutPoint, @@ -707,6 +707,24 @@ pub fn encode_output_issue_fungible_token( Ok(output.encode()) } +/// Returns the Fungible/NFT Token ID for the given inputs of a transaction +#[wasm_bindgen] +pub fn get_token_id(mut inputs: &[u8], network: Network) -> Result { + let chain_config = Builder::new(network.into()).build(); + + let mut tx_inputs = vec![]; + while !inputs.is_empty() { + let input = TxInput::decode(&mut inputs).map_err(|_| Error::InvalidInput)?; + tx_inputs.push(input); + } + + let token_id = make_token_id(&tx_inputs).ok_or(Error::NoUtxoInInputs)?; + + Ok(Address::new(&chain_config, token_id) + .expect("Should not fail to create address") + .to_string()) +} + /// Given the parameters needed to issue an NFT, and a network type (mainnet, testnet, etc), /// this function creates an output that issues that NFT. #[allow(clippy::too_many_arguments)]