Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add smart contract wallet #413

Merged
merged 28 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
- examples/cis2-wccd/Cargo.toml
- examples/cis3-nft-sponsored-txs/Cargo.toml
- examples/counter-notify/Cargo.toml
- examples/cis5-smart-contract-wallet/Cargo.toml
- examples/credential-registry/Cargo.toml
- examples/eSealing/Cargo.toml
- examples/factory/Cargo.toml
Expand Down Expand Up @@ -714,6 +715,7 @@ jobs:
- examples/cis2-nft/Cargo.toml
- examples/cis3-nft-sponsored-txs/Cargo.toml
- examples/cis2-wccd/Cargo.toml
- examples/cis5-smart-contract-wallet/Cargo.toml
- examples/credential-registry/Cargo.toml
- examples/factory/Cargo.toml
- examples/fib/Cargo.toml
Expand Down Expand Up @@ -847,6 +849,7 @@ jobs:
- examples/cis2-nft
- examples/cis3-nft-sponsored-txs
- examples/cis2-wccd
- examples/cis5-smart-contract-wallet
- examples/credential-registry
- examples/factory
- examples/fib
Expand Down Expand Up @@ -894,9 +897,9 @@ jobs:
if: ${{ matrix.crates == 'examples/smart-contract-upgrade/contract-version1' }}
run: cargo concordium build --out "examples/smart-contract-upgrade/contract-version2/concordium-out/module.wasm.v1" -- --manifest-path "examples/smart-contract-upgrade/contract-version2/Cargo.toml"

# The 'sponsored-tx-enabled-auction' example needs the wasm module `cis2-multi` for its tests.
# The 'sponsored-tx-enabled-auction' and 'cis5-smart-contract-wallet' examples need the wasm module `cis2-multi` for its tests.
- name: Build cis2-multi module if needed
if: ${{ matrix.crates == 'examples/sponsored-tx-enabled-auction' }}
if: ${{ matrix.crates == 'examples/sponsored-tx-enabled-auction' || matrix.crates == 'examples/cis5-smart-contract-wallet'}}
run: cargo concordium build --out "examples/cis2-multi/concordium-out/module.wasm.v1" -- --manifest-path "examples/cis2-multi/Cargo.toml"

- name: Run cargo concordium test
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ The list of contracts is as follows:
- [proxy](./proxy) A proxy contract that can be put in front of another contract. It works with V0 as well as V1 smart contracts.
- [recorder](./recorder) A contract that records account addresses, and has an entry point to invoke transfers to all those addresses.
- [signature-verifier](./signature-verifier) An example of how to use `crypto_primitives`. The contract verifies an Ed25519 signature.
- [cis5-smart-contract-wallet](./cis5-smart-contract-wallet) An example of how to implement a CIS5 compatible smart contract wallet.
- [nametoken](./nametoken) An example of how to register and manage names as tokens in a smart contract.
- [voting](./voting) An example of how to conduct an election using a smart contract.
- [transfer-policy-check](./transfer-policy-check) A contract that showcases how to use policies.
Expand Down
31 changes: 23 additions & 8 deletions examples/cis2-multi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,14 +320,16 @@ pub type ContractTokenAmount = TokenAmountU64;

/// The parameter for the contract function `mint` which mints/airdrops a number
/// of tokens to the owner's address.
#[derive(Serialize, SchemaType)]
#[derive(Serialize, SchemaType, Clone)]
pub struct MintParams {
/// Owner of the newly minted tokens.
pub owner: Address,
pub to: Receiver,
/// The metadata_url of the token.
pub metadata_url: MetadataUrl,
/// The token_id to mint/create additional tokens.
pub token_id: ContractTokenId,
/// Additional data that can be sent to the receiving contract.
pub data: AdditionalData,
}

/// The parameter for the contract function `burn` which burns a number
Expand Down Expand Up @@ -952,11 +954,13 @@ fn contract_view(_ctx: &ReceiveContext, host: &Host<State>) -> ReceiveResult<Vie
/// function of the state. Logs a `Mint` event.
/// The function assumes that the mint is authorized.
fn mint(
params: MintParams,
params: &MintParams,
host: &mut Host<State>,
logger: &mut impl HasLogger,
) -> ContractResult<()> {
let is_blacklisted = host.state().blacklist.contains(&get_canonical_address(params.owner)?);
let to_address = params.to.address();

let is_blacklisted = host.state().blacklist.contains(&get_canonical_address(to_address)?);

// Check token owner is not blacklisted.
ensure!(!is_blacklisted, CustomContractError::Blacklisted.into());
Expand All @@ -970,7 +974,7 @@ fn mint(
let token_metadata = state.mint(
&params.token_id,
&params.metadata_url,
&params.owner,
&to_address,
state.mint_airdrop,
builder,
);
Expand All @@ -979,7 +983,7 @@ fn mint(
logger.log(&Cis2Event::Mint(MintEvent {
token_id: params.token_id,
amount: state.mint_airdrop,
owner: params.owner,
owner: to_address,
}))?;

// Metadata URL for the token.
Expand Down Expand Up @@ -1030,7 +1034,18 @@ fn contract_mint(
// ensure!(host.state().has_role(&sender, Roles::MINTER),
// ContractError::Unauthorized);

mint(params, host, logger)?;
mint(&params, host, logger)?;

// If the receiver is a contract: invoke the receive hook function.
if let Receiver::Contract(address, function) = params.to {
let parameter = OnReceivingCis2Params {
token_id: params.token_id,
amount: host.state.mint_airdrop,
from: Address::from(address),
data: params.data,
};
host.invoke_contract(&address, &parameter, function.as_entrypoint_name(), Amount::zero())?;
}

Ok(())
}
Expand Down Expand Up @@ -1383,7 +1398,7 @@ fn contract_permit(
// ContractError::Unauthorized
// );

mint(params, host, logger)?;
mint(&params, host, logger)?;
}
BURN_ENTRYPOINT => {
// Burn tokens.
Expand Down
15 changes: 10 additions & 5 deletions examples/cis2-multi/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,12 +313,13 @@ fn test_permit_mint() {

// Create input parameters for the `mint` function.
let payload = MintParams {
owner: ALICE_ADDR,
to: Receiver::from_account(ALICE),
metadata_url: MetadataUrl {
url: "https://some.example/token/2A".to_string(),
hash: None,
},
token_id: TOKEN_1,
data: AdditionalData::empty(),
};

let update =
Expand Down Expand Up @@ -893,12 +894,13 @@ fn test_token_balance_of_blacklisted_address_can_not_change() {

// Bob cannot mint tokens to its address.
let mint_params = MintParams {
owner: BOB_ADDR,
to: Receiver::from_account(BOB),
token_id: TOKEN_0,
metadata_url: MetadataUrl {
url: "https://some.example/token/02".to_string(),
hash: None,
},
data: AdditionalData::empty(),
};

let update = chain
Expand Down Expand Up @@ -1101,12 +1103,13 @@ fn test_no_execution_of_state_mutative_functions_when_paused() {

// Try to mint tokens.
let params = MintParams {
owner: ALICE_ADDR,
to: Receiver::from_account(ALICE),
metadata_url: MetadataUrl {
url: "https://some.example/token/02".to_string(),
hash: None,
},
token_id: TOKEN_0,
data: AdditionalData::empty(),
};

let update_operator = chain
Expand Down Expand Up @@ -1306,12 +1309,13 @@ fn initialize_contract_with_alice_tokens(
let (mut chain, keypairs, contract_address, module_reference) = initialize_chain_and_contract();

let mint_params = MintParams {
owner: ALICE_ADDR,
to: Receiver::from_account(ALICE),
token_id: TOKEN_0,
metadata_url: MetadataUrl {
url: "https://some.example/token/02".to_string(),
hash: None,
},
data: AdditionalData::empty(),
};

// Mint/airdrop TOKEN_0 to Alice as the owner.
Expand All @@ -1325,12 +1329,13 @@ fn initialize_contract_with_alice_tokens(
.expect("Mint tokens");

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

// Mint/airdrop TOKEN_1 to Alice as the owner.
Expand Down
29 changes: 29 additions & 0 deletions examples/cis5-smart-contract-wallet/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "smart-contract-wallet"
version = "0.1.0"
authors = ["Concordium <developers@concordium.com>"]
edition = "2021"
license = "MPL-2.0"

[features]
default = ["std", "bump_alloc"]
std = ["concordium-std/std", "concordium-cis2/std"]
bump_alloc = ["concordium-std/bump_alloc"]

[dependencies]
concordium-std = {path = "../../concordium-std", default-features = false}
concordium-cis2 = {path = "../../concordium-cis2", default-features = false, features=[
"u256_amount"]}

[dev-dependencies]
concordium-smart-contract-testing = {path = "../../contract-testing"}
cis2-multi = {path = "../cis2-multi"}
ed25519-dalek = { version = "2.0", features = ["rand_core"] }
rand = "0.8"

[lib]
crate-type=["cdylib", "rlib"]

[profile.release]
codegen-units = 1
opt-level = "s"
Loading
Loading