Skip to content

Commit

Permalink
Fix CI pipeline
Browse files Browse the repository at this point in the history
  • Loading branch information
DOBEN committed Nov 27, 2023
1 parent af6d71a commit 378a66e
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 101 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- examples/voting/Cargo.toml
- examples/eSealing/Cargo.toml
- examples/auction/Cargo.toml
- examples/cis2-multi/Cargo.toml
- examples/cis2-multi-sponsored-txs/Cargo.toml
- examples/cis2-multi-royalties/Cargo.toml
- examples/cis2-nft/Cargo.toml
- examples/cis3-nft-sponsored-txs/Cargo.toml
Expand Down Expand Up @@ -599,7 +599,7 @@ jobs:
- examples/two-step-transfer/Cargo.toml
- examples/cis2-wccd/Cargo.toml
- examples/cis2-nft/Cargo.toml
- examples/cis2-multi/Cargo.toml
- examples/cis2-multi-sponsored-txs/Cargo.toml
- examples/cis2-multi-royalties/Cargo.toml
- examples/nametoken/Cargo.toml
- examples/account-signature-checks/Cargo.toml
Expand Down Expand Up @@ -677,7 +677,7 @@ jobs:
- examples/voting/Cargo.toml
- examples/eSealing/Cargo.toml
- examples/auction/Cargo.toml
- examples/cis2-multi/Cargo.toml
- examples/cis2-multi-sponsored-txs/Cargo.toml
- examples/cis2-multi-royalties/Cargo.toml
- examples/cis2-nft/Cargo.toml
- examples/cis3-nft-sponsored-txs/Cargo.toml
Expand Down Expand Up @@ -806,7 +806,7 @@ jobs:
- examples/voting
- examples/eSealing
- examples/auction
- examples/cis2-multi
- examples/cis2-multi-sponsored-txs
- examples/cis2-multi-royalties
- examples/cis2-nft
- examples/cis3-nft-sponsored-txs
Expand Down
3 changes: 1 addition & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ The list of contracts is as follows:
mimic the memo feature. Normally a transfer between accounts cannot add any
information other than the amount being transferred. Making transfers to this
intermediate contract instead works around this limitation.
- [cis2-multi](./cis2-multi) An example implementation of the CIS-2 Concordium Token Standard
containing multiple token types.
- [cis2-multi-sponsored-txs](./cis2-multi-sponsored-txs) An example implementation of the CIS-2 Concordium Token Standard and CIS-3 Concordium Sponsored Transaction Standard containing multiple token types.
- [cis2-multi-royalties](./cis2-multi-royalties) An example implementation of the CIS-2 Concordium Token Standard which allows the token minter to be paid royalties
containing multiple token types.
- [cis2-nft](./cis2-nft) An example implementation of the CIS-2 Concordium Token Standard
Expand Down
107 changes: 48 additions & 59 deletions examples/cis2-multi-sponsored-txs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
//! identified by the contract address together with the token ID.
//!
//! In this example the contract is initialized with no tokens, and tokens can
//! be minted through a `mint` contract function, which will only succeed for
//! the contract owner. No functionality to burn token is defined in this
//! example.
//! be minted through a `mint` contract function, which can be called by anyone.
//! The `mint` function airdrops the `MINT_AIRDROP` amount of tokens to a
//! specified `owner` address in the input parameter. No functionality to burn
//! token is defined in this example.
//!
//! Note: The word 'address' refers to either an account address or a
//! contract address.
Expand All @@ -18,16 +19,16 @@
//! implements the CIS3 standard which includes features for sponsored
//! transactions.
//!
//! The use case for this smart contract is for third-party service providers
//! (the owner of this contract) that deal with conventional clients/users that
//! don't want to acquire crypto (such as CCD) from an exchange. The third-party
//! has often traditional fiat channels open (off-chain) with the conventional
//! clients/users and wants to offer to submit transactions on behalf of the
//! user on-chain. The user/client has to sign its intended transaction before
//! communicating it (off-chain) to the third-party service provider
//! (often paying some fees in fiat money). The third-party service provider
//! submits the transaction on behalf of the user and pays the transaction fee
//! to execute the transaction on-chain.
//! The use case for CIS3 in this smart contract is for third-party service
//! providers (the owner of this contract) that deal with conventional
//! clients/users that don't want to acquire crypto (such as CCD) from an
//! exchange. The third-party has often traditional fiat channels open
//! (off-chain) with the conventional clients/users and wants to offer to submit
//! transactions on behalf of the user on-chain. The user/client has to sign its
//! intended transaction before communicating it (off-chain) to the third-party
//! service provider (often paying some fees in fiat money). The third-party
//! service provider submits the transaction on behalf of the user and pays the
//! transaction fee to execute the transaction on-chain.
//!
//! As follows from the CIS2 specification, the contract has a `transfer`
//! function for transferring an amount of a specific token type from one
Expand Down Expand Up @@ -77,6 +78,9 @@ const SUPPORTS_PERMIT_ENTRYPOINTS: [EntrypointName; 2] =
/// Tag for the CIS3 Nonce event.
pub const NONCE_EVENT_TAG: u8 = u8::MAX - 5;

/// The amount of tokens to airdrop when the mint function is invoked.
pub const MINT_AIRDROP: TokenAmountU64 = TokenAmountU64(100);

/// Tagged events to be serialized for the event log.
#[derive(Debug, Serial, Deserial, PartialEq, Eq)]
#[concordium(repr(u8))]
Expand Down Expand Up @@ -183,20 +187,17 @@ pub type ContractTokenId = TokenIdU8;
/// Contract token amount type.
pub type ContractTokenAmount = TokenAmountU64;

#[derive(Serialize, SchemaType)]
pub struct MintParam {
pub token_amount: ContractTokenAmount,
pub metadata_url: MetadataUrl,
}

/// The parameter for the contract function `mint` which mints a number of
/// token types and/or amounts of tokens to a given address.
/// The parameter for the contract function `mint` which mints/airdrops a number
/// of tokens to the owner address.
#[derive(Serialize, SchemaType)]
pub struct MintParams {
/// Owner of the newly minted tokens.
pub owner: Address,
/// A collection of tokens to mint.
pub tokens: collections::BTreeMap<ContractTokenId, MintParam>,
pub owner: Address,
/// The metadata_url of the token (needs to be present for the first time
/// this token_id is minted).
pub metadata_url: MetadataUrl,
/// The token_id to mint/create additional tokens.
pub token_id: ContractTokenId,
}

/// The state for each address.
Expand Down Expand Up @@ -389,15 +390,15 @@ impl State {
fn mint(
&mut self,
token_id: &ContractTokenId,
mint_param: &MintParam,
mint_param: &MintParams,
owner: &Address,
state_builder: &mut StateBuilder,
) {
self.tokens.insert(*token_id, mint_param.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_param.token_amount;
*owner_balance += MINT_AIRDROP;
}

/// Check that the token ID currently exists in this contract.
Expand Down Expand Up @@ -570,21 +571,18 @@ fn contract_view(_ctx: &ReceiveContext, host: &Host<State>) -> ReceiveResult<Vie
})
}

/// Mint new tokens with a given address as the owner of these tokens.
/// Can only be called by the contract owner.
/// Mint/Airdrops the fixed amount of `MINT_AIRDROP` of new tokens to the
/// `owner` address. ATTENTION: Can be called by anyone. You should add your
/// custom access control to this function.
///
/// Logs a `Mint` and a `TokenMetadata` event for each token.
/// The url for the token metadata is the token ID encoded in hex, appended on
/// the `TOKEN_METADATA_BASE_URL`.
///
/// It rejects if:
/// - The sender is not the contract instance owner.
/// - Fails to parse parameter.
/// - Any of the tokens fails to be minted, which could be if:
/// - Fails to log Mint event.
/// - Fails to log TokenMetadata event.
///
/// Note: Can at most mint 32 token types in one call due to the limit on the
/// number of logs a smart contract can produce on each function call.
/// - Fails to log Mint event.
/// - Fails to log TokenMetadata event.
#[receive(
contract = "cis2_multi_sponsored_txs",
name = "mint",
Expand All @@ -598,34 +596,25 @@ fn contract_mint(
host: &mut Host<State>,
logger: &mut impl HasLogger,
) -> ContractResult<()> {
// Get the contract owner
let owner = ctx.owner();
// Get the sender of the transaction
let sender = ctx.sender();

ensure!(sender.matches_account(&owner), ContractError::Unauthorized);

// Parse the parameter.
let params: MintParams = ctx.parameter_cursor().get()?;

let (state, builder) = host.state_and_builder();
for (token_id, mint_param) in params.tokens {
// Mint the token in the state.
state.mint(&token_id, &mint_param, &params.owner, builder);

// Event for minted token.
logger.log(&Cis2Event::Mint(MintEvent {
token_id,
amount: mint_param.token_amount,
owner: params.owner,
}))?;

// Metadata URL for the token.
logger.log(&Cis2Event::TokenMetadata::<_, ContractTokenAmount>(TokenMetadataEvent {
token_id,
metadata_url: mint_param.metadata_url,
}))?;
}
// Mint the token in the state.
state.mint(&params.token_id, &params, &params.owner, builder);

// Event for minted token.
logger.log(&Cis2Event::Mint(MintEvent {
token_id: params.token_id,
amount: MINT_AIRDROP,
owner: params.owner,
}))?;

// Metadata URL for the token.
logger.log(&Cis2Event::TokenMetadata::<_, ContractTokenAmount>(TokenMetadataEvent {
token_id: params.token_id,
metadata_url: params.metadata_url,
}))?;
Ok(())
}

Expand Down
70 changes: 34 additions & 36 deletions examples/cis2-multi-sponsored-txs/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ fn test_minting() {
let rv: ViewState = invoke.parse_return_value().expect("ViewState return value");
assert_eq!(rv.tokens[..], [TOKEN_0, TOKEN_1]);
assert_eq!(rv.state, vec![(ALICE_ADDR, ViewAddressState {
balances: vec![(TOKEN_0, 400.into()), (TOKEN_1, 1.into())],
balances: vec![(TOKEN_0, 100.into()), (TOKEN_1, 100.into())],
operators: Vec::new(),
})]);

Expand All @@ -62,21 +62,9 @@ fn test_minting() {
events.map(|e| e.parse().expect("Deserialize event")).collect();

assert_eq!(events, [
Cis2Event::Mint(MintEvent {
token_id: TokenIdU8(2),
amount: TokenAmountU64(400),
owner: ALICE_ADDR,
}),
Cis2Event::TokenMetadata(TokenMetadataEvent {
token_id: TokenIdU8(2),
metadata_url: MetadataUrl {
url: "https://some.example/token/02".to_string(),
hash: None,
},
}),
Cis2Event::Mint(MintEvent {
token_id: TokenIdU8(42),
amount: TokenAmountU64(1),
amount: TokenAmountU64(100),
owner: ALICE_ADDR,
}),
Cis2Event::TokenMetadata(TokenMetadataEvent {
Expand Down Expand Up @@ -129,7 +117,7 @@ fn test_account_transfer() {
let rv: ViewState = invoke.parse_return_value().expect("ViewState return value");
assert_eq!(rv.state, vec![
(ALICE_ADDR, ViewAddressState {
balances: vec![(TOKEN_0, 399.into()), (TOKEN_1, 1.into())],
balances: vec![(TOKEN_0, 99.into()), (TOKEN_1, 100.into())],
operators: Vec::new(),
}),
(BOB_ADDR, ViewAddressState {
Expand Down Expand Up @@ -302,7 +290,7 @@ fn test_operator_can_transfer() {
let rv: ViewState = invoke.parse_return_value().expect("ViewState return value");
assert_eq!(rv.state, vec![
(ALICE_ADDR, ViewAddressState {
balances: vec![(TOKEN_0, 399.into()), (TOKEN_1, 1.into())],
balances: vec![(TOKEN_0, 99.into()), (TOKEN_1, 100.into())],
operators: vec![BOB_ADDR],
}),
(BOB_ADDR, ViewAddressState {
Expand Down Expand Up @@ -423,7 +411,7 @@ fn test_inside_signature_permit_transfer() {
// Check balances in state.
let balance_of_alice_and_bob = get_balances(&chain, contract_address);

assert_eq!(balance_of_alice_and_bob.0, [TokenAmountU64(1), TokenAmountU64(0)]);
assert_eq!(balance_of_alice_and_bob.0, [TokenAmountU64(100), TokenAmountU64(0)]);

// Create input parameters for the `permit` transfer function.
let transfer = concordium_cis2::Transfer {
Expand Down Expand Up @@ -517,7 +505,7 @@ fn test_inside_signature_permit_transfer() {
// Check balances in state.
let balance_of_alice_and_bob = get_balances(&chain, contract_address);

assert_eq!(balance_of_alice_and_bob.0, [TokenAmountU64(0), TokenAmountU64(1)]);
assert_eq!(balance_of_alice_and_bob.0, [TokenAmountU64(99), TokenAmountU64(1)]);
}

// Test `nonceOf` query. We check that the nonce of `ALICE` is 1 when
Expand Down Expand Up @@ -746,26 +734,36 @@ fn initialize_contract_with_alice_tokens(
let (mut chain, keypairs, contract_address) = initialize_chain_and_contract();

let mint_params = MintParams {
owner: ALICE_ADDR,
tokens: BTreeMap::from_iter(vec![
(TOKEN_0, MintParam {
token_amount: 400.into(),
metadata_url: MetadataUrl {
url: "https://some.example/token/02".to_string(),
hash: None,
},
}),
(TOKEN_1, MintParam {
token_amount: 1.into(),
metadata_url: MetadataUrl {
url: "https://some.example/token/2A".to_string(),
hash: None,
},
}),
]),
owner: ALICE_ADDR,
token_id: TOKEN_0,
metadata_url: MetadataUrl {
url: "https://some.example/token/02".to_string(),
hash: None,
},
};

// Mint/airdrop TOKEN_0 to Alice as the owner.
let _update = chain
.contract_update(SIGNER, ALICE, ALICE_ADDR, Energy::from(10000), UpdateContractPayload {
amount: Amount::zero(),
receive_name: OwnedReceiveName::new_unchecked(
"cis2_multi_sponsored_txs.mint".to_string(),
),
address: contract_address,
message: OwnedParameter::from_serial(&mint_params).expect("Mint params"),
})
.expect("Mint tokens");

let mint_params = MintParams {
owner: ALICE_ADDR,
token_id: TOKEN_1,
metadata_url: MetadataUrl {
url: "https://some.example/token/2A".to_string(),
hash: None,
},
};

// Mint two tokens for which Alice is the owner.
// Mint/airdrop TOKEN_1 to Alice as the owner.
let update = chain
.contract_update(SIGNER, ALICE, ALICE_ADDR, Energy::from(10000), UpdateContractPayload {
amount: Amount::zero(),
Expand Down

0 comments on commit 378a66e

Please sign in to comment.