diff --git a/enshrine-esdt-safe/src/custom_builtin_func_proxy.rs b/enshrine-esdt-safe/src/custom_builtin_func_proxy.rs new file mode 100644 index 00000000..55e70fd8 --- /dev/null +++ b/enshrine-esdt-safe/src/custom_builtin_func_proxy.rs @@ -0,0 +1,203 @@ +use multiversx_sc::{codec::Empty, proxy_imports::TopEncode, types::{BigUint, ManagedAddress, ManagedBuffer, ManagedVec, NotPayable, ProxyArg, TokenIdentifier, Tx, TxEnv, TxFrom, TxGas, TxTo, TxTypedCall, TxProxyTrait}}; + +use super::builtin_func_names::{ + CHANGE_OWNER_BUILTIN_FUNC_NAME, CLAIM_DEVELOPER_REWARDS_FUNC_NAME, DELETE_USERNAME_FUNC_NAME, + ESDT_LOCAL_BURN_FUNC_NAME, ESDT_LOCAL_MINT_FUNC_NAME, ESDT_NFT_ADD_QUANTITY_FUNC_NAME, + ESDT_NFT_ADD_URI_FUNC_NAME, ESDT_NFT_BURN_FUNC_NAME, ESDT_NFT_CREATE_FUNC_NAME, + ESDT_NFT_UPDATE_ATTRIBUTES_FUNC_NAME, SET_USERNAME_FUNC_NAME, +}; + +/// Proxy describing the user builtin function signatures. +pub struct UserBuiltinProxy; + +impl TxProxyTrait for UserBuiltinProxy +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + type TxProxyMethods = UserBuiltinProxyMethods; + + fn proxy_methods(self, tx: Tx) -> Self::TxProxyMethods { + UserBuiltinProxyMethods { wrapped_tx: tx } + } +} + +pub struct UserBuiltinProxyMethods +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + wrapped_tx: Tx, +} + +impl UserBuiltinProxyMethods +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + pub fn set_user_name>>( + self, + name: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call(SET_USERNAME_FUNC_NAME) + .argument(&name) + .original_result() + } + + pub fn delete_user_name(self) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call(DELETE_USERNAME_FUNC_NAME) + .original_result() + } + + pub fn claim_developer_rewards(self) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call(CLAIM_DEVELOPER_REWARDS_FUNC_NAME) + .original_result() + } + + pub fn change_owner_address( + self, + new_owner: &ManagedAddress, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call(CHANGE_OWNER_BUILTIN_FUNC_NAME) + .argument(new_owner) + .original_result() + } + + pub fn esdt_local_burn( + self, + token: &TokenIdentifier, + nonce: u64, + amount: &BigUint + ) -> TxTypedCall { + if nonce == 0 { + return self + .wrapped_tx + .payment(NotPayable) + .raw_call(ESDT_LOCAL_BURN_FUNC_NAME) + .argument(token) + .argument(amount) + .original_result(); + } + + self.wrapped_tx + .payment(NotPayable) + .raw_call(ESDT_NFT_BURN_FUNC_NAME) + .argument(token) + .argument(&nonce) + .argument(amount) + .original_result() + } + + pub fn esdt_local_mint( + self, + token: &TokenIdentifier, + nonce: u64, + amount: &BigUint, + ) -> TxTypedCall { + if nonce == 0 { + return self + .wrapped_tx + .payment(NotPayable) + .raw_call(ESDT_LOCAL_MINT_FUNC_NAME) + .argument(token) + .argument(amount) + .original_result(); + } + self.wrapped_tx + .payment(NotPayable) + .raw_call(ESDT_NFT_ADD_QUANTITY_FUNC_NAME) + .argument(token) + .argument(&nonce) + .argument(amount) + .original_result() + } + + pub fn nft_add_multiple_uri( + self, + token_id: &TokenIdentifier, + nft_nonce: u64, + new_uris: &ManagedVec>, + ) -> TxTypedCall { + let mut tx = self + .wrapped_tx + .payment(NotPayable) + .raw_call(ESDT_NFT_ADD_URI_FUNC_NAME) + .argument(token_id) + .argument(&nft_nonce); + + for uri in new_uris { + tx = tx.argument(&uri); + } + + tx.original_result() + } + + pub fn nft_update_attributes( + self, + token_id: &TokenIdentifier, + nft_nonce: u64, + new_attributes: &T, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call(ESDT_NFT_UPDATE_ATTRIBUTES_FUNC_NAME) + .argument(token_id) + .argument(&nft_nonce) + .argument(new_attributes) + .original_result() + } + + #[allow(clippy::too_many_arguments)] + pub fn esdt_nft_create( + self, + token: &TokenIdentifier, + amount: &BigUint, + name: &ManagedBuffer, + royalties: &BigUint, + hash: &ManagedBuffer, + attributes: &T, + uris: &ManagedVec>, + nonce: &u64, + creator: &ManagedAddress + ) -> TxTypedCall { + let mut tx = self + .wrapped_tx + .payment(NotPayable) + .raw_call(ESDT_NFT_CREATE_FUNC_NAME) + .argument(token) + .argument(amount) + .argument(name) + .argument(royalties) + .argument(hash) + .argument(attributes) + .argument(nonce) + .argument(creator); + + if uris.is_empty() { + // at least one URI is required, so we push an empty one + tx = tx.argument(&Empty); + } else { + // The API function has the last argument as variadic, + // so we top-encode each and send as separate argument + for uri in uris { + tx = tx.argument(&uri); + } + } + + tx.original_result() + } +} diff --git a/enshrine-esdt-safe/src/from_sovereign/transfer_tokens.rs b/enshrine-esdt-safe/src/from_sovereign/transfer_tokens.rs index 6daf78f2..f38585c4 100644 --- a/enshrine-esdt-safe/src/from_sovereign/transfer_tokens.rs +++ b/enshrine-esdt-safe/src/from_sovereign/transfer_tokens.rs @@ -4,10 +4,15 @@ use transaction::{ BatchId, GasLimit, Operation, OperationData, OperationEsdtPayment, OperationTuple, }; -use crate::to_sovereign; +use crate::{ + custom_builtin_func_proxy, + to_sovereign::{self, create_tx::ESDT_SYSTEM_SC_ADDRESS}, +}; use super::token_mapping::EsdtTokenInfo; +pub const ESDT_NFT_CREATE_FUNC_NAME: &str = "ESDTNFTCreate"; + multiversx_sc::imports!(); const CALLBACK_GAS: GasLimit = 10_000_000; // Increase if not enough @@ -51,6 +56,25 @@ pub trait TransferTokensModule: self.distribute_payments(hash_of_hashes, operation_tuple, minted_operation_tokens); } + fn call_system_sc_nft_create(&self, operation_token: &OperationEsdtPayment) -> u64 { + self.tx() + .to(ManagedAddress::from(ESDT_SYSTEM_SC_ADDRESS)) + .typed(custom_builtin_func_proxy::UserBuiltinProxy) + .esdt_nft_create( + &operation_token.token_identifier, + &operation_token.token_data.amount, + &operation_token.token_data.name, + &operation_token.token_data.royalties, + &operation_token.token_data.hash, + &operation_token.token_data.attributes, + &operation_token.token_data.uris, + &operation_token.token_nonce, + &operation_token.token_data.creator, + ) + .returns(ReturnsResult) + .sync_call() + } + fn mint_tokens( &self, operation_tokens: &ManagedVec>, @@ -71,15 +95,17 @@ pub trait TransferTokensModule: &operation_token.token_data.amount, ); } else { - nonce = self.send().esdt_nft_create( - &operation_token.token_identifier, - &operation_token.token_data.amount, - &operation_token.token_data.name, - &operation_token.token_data.royalties, - &operation_token.token_data.hash, - &operation_token.token_data.attributes, - &operation_token.token_data.uris, - ); + nonce = self.call_system_sc_nft_create(&operation_token); + + // nonce = self.send().esdt_nft_create( + // &operation_token.token_identifier, + // &operation_token.token_data.amount, + // &operation_token.token_data.name, + // &operation_token.token_data.royalties, + // &operation_token.token_data.hash, + // &operation_token.token_data.attributes, + // &operation_token.token_data.uris, + // ); // let token_data = operation_token.token_data.clone(); // let mut arg_buffer = ManagedArgBuffer::new(); @@ -245,7 +271,8 @@ pub trait TransferTokensModule: let caller = self.blockchain().get_caller(); let header_verifier_address = self.header_verifier_address().get(); - let _ = self.tx() + let _ = self + .tx() .from(caller) .to(header_verifier_address) .typed(header_verifier_proxy::HeaderverifierProxy) diff --git a/enshrine-esdt-safe/src/lib.rs b/enshrine-esdt-safe/src/lib.rs index 2f682f5f..c6f82f17 100644 --- a/enshrine-esdt-safe/src/lib.rs +++ b/enshrine-esdt-safe/src/lib.rs @@ -13,6 +13,7 @@ const DEFAULT_MAX_USER_TX_GAS_LIMIT: GasLimit = 300_000_000; pub mod from_sovereign; pub mod to_sovereign; pub mod enshrine_esdt_safe_proxy; +pub mod custom_builtin_func_proxy; #[multiversx_sc::contract] pub trait EnshrineEsdtSafe: