diff --git a/examples/auction/tests/tests.rs b/examples/auction/tests/tests.rs index 1c716eb7..fb4d3dfa 100644 --- a/examples/auction/tests/tests.rs +++ b/examples/auction/tests/tests.rs @@ -252,7 +252,7 @@ fn initialize_chain_and_auction() -> (Chain, ContractAddress) { .build() .expect("Exchange rate is in valid range"); - // Create some accounts accounts on the chain. + // Create some accounts on the chain. chain.create_account(Account::new(ALICE, ACC_INITIAL_BALANCE)); chain.create_account(Account::new(BOB, ACC_INITIAL_BALANCE)); chain.create_account(Account::new(CAROL, ACC_INITIAL_BALANCE)); diff --git a/examples/cis2-multi-royalties/src/lib.rs b/examples/cis2-multi-royalties/src/lib.rs index 7e8069ac..025515c1 100644 --- a/examples/cis2-multi-royalties/src/lib.rs +++ b/examples/cis2-multi-royalties/src/lib.rs @@ -165,8 +165,6 @@ pub enum CustomContractError { LogFull, /// Failed logging: Log is malformed. LogMalformed, - /// Invalid contract name. - InvalidContractName, /// Only a smart contract can call this function. ContractOnly, /// Failed to invoke a contract. @@ -209,10 +207,6 @@ impl From for ContractError { fn from(c: CustomContractError) -> Self { Cis2Error::Custom(c) } } -impl From for CustomContractError { - fn from(_: NewReceiveNameError) -> Self { Self::InvalidContractName } -} - impl State { /// Construct a state with no tokens fn empty(state_builder: &mut StateBuilder, pay_royalty: bool) -> Self { @@ -291,7 +285,7 @@ impl State { } // Get the `from` state and balance, if not present it will fail since the // balance is interpreted as 0 and the transfer amount must be more than - // 0 as this point.; + // 0 at this point. { let mut from_address_state = self.state.entry(*from).occupied_or(ContractError::InsufficientFunds)?; @@ -813,7 +807,7 @@ fn contract_on_cis2_received(ctx: &ReceiveContext, host: &Host) -> Contra host.invoke_contract_read_only( &sender, ¶meter, - EntrypointName::new("transfer")?, + EntrypointName::new_unchecked("transfer"), Amount::zero(), )?; Ok(()) diff --git a/examples/cis2-multi-sponsored-txs/src/lib.rs b/examples/cis2-multi-sponsored-txs/src/lib.rs index e3a357d4..d0d2c598 100644 --- a/examples/cis2-multi-sponsored-txs/src/lib.rs +++ b/examples/cis2-multi-sponsored-txs/src/lib.rs @@ -89,6 +89,7 @@ pub enum Event { /// whenever the `permit` function is invoked. #[concordium(tag = 250)] Nonce(NonceEvent), + /// Cis2 token events. #[concordium(forward = cis2_events)] Cis2Event(Cis2Event), } @@ -188,13 +189,13 @@ pub type ContractTokenId = TokenIdU8; pub type ContractTokenAmount = TokenAmountU64; /// The parameter for the contract function `mint` which mints/airdrops a number -/// of tokens to the owner address. +/// of tokens to the owner's address. #[derive(Serialize, SchemaType)] pub struct MintParams { /// Owner of the newly minted tokens. pub owner: Address, - /// The metadata_url of the token (needs to be present for the first time - /// this token_id is minted). + /// The metadata_url of the token (is ignored except for the first time + /// a token_id is minted). pub metadata_url: MetadataUrl, /// The token_id to mint/create additional tokens. pub token_id: ContractTokenId, @@ -206,7 +207,7 @@ pub struct MintParams { struct AddressState { /// The amount of tokens owned by this address. balances: StateMap, - /// The address which are currently enabled as operators for this address. + /// The addresses which are currently enabled as operators for this address. operators: StateSet, } @@ -222,13 +223,13 @@ impl AddressState { /// The contract state, /// /// Note: The specification does not specify how to structure the contract state -/// and this could be structured in a more space efficient way. +/// and this could be structured in a more space-efficient way. #[derive(Serial, DeserialWithState)] #[concordium(state_parameter = "S")] struct State { /// The state of addresses. state: StateMap, S>, - /// All of the token IDs + /// All of the token IDs. tokens: StateMap, /// Map with contract addresses providing implementations of additional /// standards. @@ -347,7 +348,7 @@ impl From for CustomContractError { } } -/// Mapping the logging errors to ContractError. +/// Mapping the logging errors to CustomContractError. impl From for CustomContractError { fn from(le: LogError) -> Self { match le { @@ -367,14 +368,6 @@ impl From for ContractError { fn from(c: CustomContractError) -> Self { Cis2Error::Custom(c) } } -impl From for CustomContractError { - fn from(_: NewReceiveNameError) -> Self { Self::InvalidContractName } -} - -impl From for CustomContractError { - fn from(_: NewContractNameError) -> Self { Self::InvalidContractName } -} - impl State { /// Construct a state with no tokens fn empty(state_builder: &mut StateBuilder) -> Self { @@ -387,28 +380,37 @@ impl State { } /// Mints an amount of tokens with a given address as the owner. + /// The metadataURL for the given token_id is added to the state only + /// if the token_id is being minted or created for the first time. + /// Otherwise, the metadataURL provided in the input parameter is ignored. fn mint( &mut self, token_id: &ContractTokenId, - mint_param: &MintParams, + metadata_url: &MetadataUrl, owner: &Address, state_builder: &mut StateBuilder, - ) { - self.tokens.insert(*token_id, mint_param.metadata_url.to_owned()); + ) -> MetadataUrl { + let token_metadata = self.tokens.get(token_id).map(|x| x.to_owned()); + if token_metadata.is_none() { + self.tokens.insert(*token_id, metadata_url.to_owned()); + } + let mut owner_state = self.state.entry(*owner).or_insert_with(|| AddressState::empty(state_builder)); let mut owner_balance = owner_state.balances.entry(*token_id).or_insert(0.into()); *owner_balance += MINT_AIRDROP; + + if let Some(token_metadata) = token_metadata { + token_metadata + } else { + metadata_url.clone() + } } /// Check that the token ID currently exists in this contract. #[inline(always)] fn contains_token(&self, token_id: &ContractTokenId) -> bool { - self.get_token(token_id).is_some() - } - - fn get_token(&self, token_id: &ContractTokenId) -> Option { - self.tokens.get(token_id).map(|x| x.to_owned()) + self.tokens.get(token_id).map(|x| x.to_owned()).is_some() } /// Get the current balance of a given token id for a given address. @@ -452,7 +454,7 @@ impl State { // Get the `from` state and balance, if not present it will fail since the // balance is interpreted as 0 and the transfer amount must be more than - // 0 as this point.; + // 0 at this point. { let mut from_address_state = self.state.entry(*from).occupied_or(ContractError::InsufficientFunds)?; @@ -515,7 +517,7 @@ impl State { // Contract functions -/// Initialize contract instance with a no token types. +/// Initialize contract instance with no token types. #[init( contract = "cis2_multi_sponsored_txs", event = "Cis2Event" @@ -538,8 +540,7 @@ pub struct ViewState { } /// View function for testing. This reports on the entire state of the contract -/// for testing purposes. In a realistic example there `balance_of` and similar -/// functions with a smaller response. +/// for testing purposes. #[receive(contract = "cis2_multi_sponsored_txs", name = "view", return_value = "ViewState")] fn contract_view(_ctx: &ReceiveContext, host: &Host) -> ReceiveResult { let state = host.state(); @@ -576,8 +577,8 @@ fn contract_view(_ctx: &ReceiveContext, host: &Host) -> ReceiveResult(TokenMetadataEvent { token_id: params.token_id, - metadata_url: params.metadata_url, + metadata_url: token_metadata, }))?; Ok(()) } type TransferParameter = TransferParams; -/// Execute a list of token transfers, in the order of the list. -/// -/// Logs a `Transfer` event and invokes a receive hook function for every -/// transfer in the list. -/// -/// It rejects if: -/// - It fails to parse the parameter. -/// - Any of the transfers fail to be executed, which could be if: -/// - The `token_id` does not exist. -/// - The sender is not the owner of the token, or an operator for this -/// specific `token_id` and `from` address. -/// - The token is not owned by the `from`. -/// - Fails to log event. -/// - Any of the receive hook function calls rejects. -// #[receive( -// contract = "cis2_multi_sponsored_txs", -// name = "transfer", -// parameter = "TransferParameter", -// error = "ContractError", -// enable_logger, -// mutable -// )] -// fn contract_transfer( -// ctx: &ReceiveContext, -// host: &mut Host, -// logger: &mut impl HasLogger, -// ) -> ContractResult<()> { -// // Parse the parameter. -// let TransferParams(transfers): TransferParameter = ctx.parameter_cursor().get()?; -// // Get the sender who invoked this contract function. -// let sender = ctx.sender(); - -// for Transfer { -// token_id, -// amount, -// from, -// to, -// data, -// } in transfers -// { -// let (state, builder) = host.state_and_builder(); -// // Authenticate the sender for this transfer -// ensure!(from == sender || state.is_operator(&sender, &from), -// ContractError::Unauthorized); let to_address = to.address(); -// // Update the contract state -// state.transfer(&token_id, amount, &from, &to_address, builder)?; - -// // Log transfer event -// logger.log(&Cis2Event::Transfer(TransferEvent { -// token_id, -// amount, -// from, -// to: to_address, -// }))?; - -// // If the receiver is a contract we invoke it. -// if let Receiver::Contract(address, entrypoint_name) = to { -// let parameter = OnReceivingCis2Params { -// token_id, -// amount, -// from, -// data, -// }; -// host.invoke_contract( -// &address, -// ¶meter, -// entrypoint_name.as_entrypoint_name(), -// Amount::zero(), -// )?; -// } -// } -// Ok(()) -// } - /// Internal `transfer/permit` helper function. Invokes the `transfer` /// function of the state. Logs a `Transfer` event and invokes a receive hook /// function. The function assumes that the transfer is authorized. @@ -1008,50 +935,6 @@ fn contract_update_operator( Ok(()) } -/// Enable or disable addresses as operators of the sender address. -/// Logs an `UpdateOperator` event. -/// -/// It rejects if: -/// - It fails to parse the parameter. -/// - Fails to log event. -// #[receive( -// contract = "cis2_multi_sponsored_txs", -// name = "updateOperator", -// parameter = "UpdateOperatorParams", -// error = "ContractError", -// enable_logger, -// mutable -// )] -// fn contract_update_operator( -// ctx: &ReceiveContext, -// host: &mut Host, -// logger: &mut impl HasLogger, -// ) -> ContractResult<()> { -// // Parse the parameter. -// let UpdateOperatorParams(params) = ctx.parameter_cursor().get()?; -// // Get the sender who invoked this contract function. -// let sender = ctx.sender(); - -// let (state, builder) = host.state_and_builder(); -// for param in params { -// // Update the operator in the state. -// match param.update { -// OperatorUpdate::Add => state.add_operator(&sender, ¶m.operator, builder), -// OperatorUpdate::Remove => state.remove_operator(&sender, ¶m.operator), -// } - -// // Log the appropriate event -// logger.log(&Cis2Event::::UpdateOperator( -// UpdateOperatorEvent { -// owner: sender, -// operator: param.operator, -// update: param.update, -// }, -// ))?; -// } -// Ok(()) -// } - /// Parameter type for the CIS-2 function `balanceOf` specialized to the subset /// of TokenIDs used by this contract. pub type ContractBalanceOfQueryParams = BalanceOfQueryParams; @@ -1255,8 +1138,8 @@ fn contract_token_metadata( /// it, and define the function to forward any transfers to the owner of the /// contract instance. /// -/// Note: The name of this function is not part the CIS2, and a contract can -/// have multiple functions for receiving tokens. +/// Note: The name of this function is not part the CIS2 standard, and a +/// contract can have multiple functions for receiving tokens. /// /// It rejects if: /// - Sender is not a contract. @@ -1291,7 +1174,7 @@ fn contract_on_cis2_received(ctx: &ReceiveContext, host: &Host) -> Contra host.invoke_contract_read_only( &sender, ¶meter, - EntrypointName::new("transfer")?, + EntrypointName::new_unchecked("transfer"), Amount::zero(), )?; Ok(()) diff --git a/examples/cis2-multi-sponsored-txs/tests/tests.rs b/examples/cis2-multi-sponsored-txs/tests/tests.rs index 67359770..68e57a4b 100644 --- a/examples/cis2-multi-sponsored-txs/tests/tests.rs +++ b/examples/cis2-multi-sponsored-txs/tests/tests.rs @@ -1,4 +1,4 @@ -//! Tests for the `cis2_multi` contract. +//! Tests for the `cis2_multi_sponsored_txs` contract. use cis2_multi_sponsored_txs::{ContractBalanceOfQueryParams, ContractBalanceOfQueryResponse, *}; use concordium_cis2::*; use concordium_smart_contract_testing::*; @@ -21,9 +21,10 @@ const TOKEN_1: ContractTokenId = TokenIdU8(42); /// Initial balance of the accounts. const ACC_INITIAL_BALANCE: Amount = Amount::from_ccd(10000); -/// A signer for all the transactions. +/// A signer with one key. const SIGNER: Signer = Signer::with_one_key(); +/// Dummy signature used as placeholder. const DUMMY_SIGNATURE: SignatureEd25519 = SignatureEd25519([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -102,8 +103,8 @@ fn test_account_transfer() { }) .expect("Transfer tokens"); - // Check that Bob has 1 `TOKEN_0` and Alice has 399. Also check that Alice still - // has 1 `TOKEN_1`. + // Check that Bob has 1 `TOKEN_0` and Alice has 99. Also check that Alice still + // has 100 `TOKEN_1`. let invoke = chain .contract_invoke(ALICE, ALICE_ADDR, Energy::from(10000), UpdateContractPayload { amount: Amount::zero(), @@ -275,8 +276,8 @@ fn test_operator_can_transfer() { }) .expect("Transfer tokens"); - // Check that Bob now has 1 of `TOKEN_0` and Alice has 399. Also check that - // Alice still has 1 `TOKEN_1`. + // Check that Bob now has 1 of `TOKEN_0` and Alice has 99. Also check that + // Alice still has 100 `TOKEN_1`. let invoke = chain .contract_invoke(ALICE, ALICE_ADDR, Energy::from(10000), UpdateContractPayload { amount: Amount::zero(), @@ -728,7 +729,9 @@ fn get_balances( } /// Helper function that sets up the contract with two types of tokens minted to -/// Alice. She has 400 of `TOKEN_0` and 1 of `TOKEN_1`. +/// Alice. She has 100 of `TOKEN_0` and 100 of `TOKEN_1`. +/// Alice's account is created with keys. +/// Hence, Alice's account signature can be checked in the test cases. fn initialize_contract_with_alice_tokens( ) -> (Chain, AccountKeys, ContractAddress, ContractInvokeSuccess) { let (mut chain, keypairs, contract_address) = initialize_chain_and_contract(); @@ -796,7 +799,7 @@ fn initialize_chain_and_contract() -> (Chain, AccountKeys, ContractAddress) { locked: Amount::zero(), }; - // Create some accounts accounts on the chain. + // Create some accounts on the chain. chain.create_account(Account::new_with_keys(ALICE, balance, (&keypairs).into())); chain.create_account(Account::new(BOB, ACC_INITIAL_BALANCE)); diff --git a/examples/cis2-nft/tests/tests.rs b/examples/cis2-nft/tests/tests.rs index 78b68cd9..258e75dc 100644 --- a/examples/cis2-nft/tests/tests.rs +++ b/examples/cis2-nft/tests/tests.rs @@ -314,7 +314,7 @@ fn initialize_contract_with_alice_tokens() -> (Chain, ContractAddress, ContractI fn initialize_chain_and_contract() -> (Chain, ContractAddress) { let mut chain = Chain::new(); - // Create some accounts accounts on the chain. + // Create some accounts on the chain. chain.create_account(Account::new(ALICE, ACC_INITIAL_BALANCE)); chain.create_account(Account::new(BOB, ACC_INITIAL_BALANCE)); diff --git a/examples/cis2-wccd/tests/tests.rs b/examples/cis2-wccd/tests/tests.rs index 1d73a26b..8bccf31d 100644 --- a/examples/cis2-wccd/tests/tests.rs +++ b/examples/cis2-wccd/tests/tests.rs @@ -566,7 +566,7 @@ fn initialize_contract_with_alice_tokens() -> (Chain, ContractAddress, ContractI fn initialize_chain_and_contract() -> (Chain, ContractInitSuccess) { let mut chain = Chain::new(); - // Create some accounts accounts on the chain. + // Create some accounts on the chain. chain.create_account(Account::new(ALICE, ACC_INITIAL_BALANCE)); chain.create_account(Account::new(BOB, ACC_INITIAL_BALANCE)); diff --git a/examples/cis3-nft-sponsored-txs/tests/tests.rs b/examples/cis3-nft-sponsored-txs/tests/tests.rs index 7e51244a..a79ac660 100644 --- a/examples/cis3-nft-sponsored-txs/tests/tests.rs +++ b/examples/cis3-nft-sponsored-txs/tests/tests.rs @@ -970,7 +970,7 @@ fn initialize_chain_and_contract( locked: Amount::zero(), }; - // Create some accounts accounts on the chain. + // Create some accounts on the chain. chain.create_account(Account::new_with_keys(ALICE, balance, account_access_structure)); chain.create_account(Account::new(CHARLIE, ACC_INITIAL_BALANCE)); chain.create_account(Account::new(BOB, ACC_INITIAL_BALANCE)); diff --git a/examples/eSealing/tests/tests.rs b/examples/eSealing/tests/tests.rs index df8449c7..5c18ede8 100644 --- a/examples/eSealing/tests/tests.rs +++ b/examples/eSealing/tests/tests.rs @@ -127,7 +127,7 @@ fn deserialize_update_events(update: &ContractInvokeSuccess) -> Vec { fn init() -> (Chain, ContractAddress) { let mut chain = Chain::new(); - // Create some accounts accounts on the chain. + // Create some accounts on the chain. chain.create_account(Account::new(ALICE, Amount::from_ccd(1000))); // Load and deploy the module.