From 139aa0d2e1cd16176e1f90c3fa380f117ce90fff Mon Sep 17 00:00:00 2001 From: PierreOssun Date: Fri, 28 Apr 2023 10:09:00 +0200 Subject: [PATCH 1/2] Added tests --- Cargo.toml | 1 + chain-extensions/pallet-assets/Cargo.toml | 8 + chain-extensions/pallet-assets/src/lib.rs | 4 + chain-extensions/pallet-assets/src/mock.rs | 241 ++++ chain-extensions/pallet-assets/src/tests.rs | 310 +++++ .../test-contract/asset_wrapper.json | 1203 +++++++++++++++++ .../test-contract/asset_wrapper.wasm | Bin 0 -> 25247 bytes 7 files changed, 1767 insertions(+) create mode 100644 chain-extensions/pallet-assets/src/mock.rs create mode 100644 chain-extensions/pallet-assets/src/tests.rs create mode 100644 chain-extensions/pallet-assets/test-contract/asset_wrapper.json create mode 100644 chain-extensions/pallet-assets/test-contract/asset_wrapper.wasm diff --git a/Cargo.toml b/Cargo.toml index 3eb8697f..112896e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,6 +97,7 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0 frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39", default-features = false } sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39", default-features = false } +sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39", default-features = false } # (native) sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39" } diff --git a/chain-extensions/pallet-assets/Cargo.toml b/chain-extensions/pallet-assets/Cargo.toml index 94319eef..6456d8f6 100644 --- a/chain-extensions/pallet-assets/Cargo.toml +++ b/chain-extensions/pallet-assets/Cargo.toml @@ -23,6 +23,13 @@ sp-core = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +[dev-dependencies] +env_logger = "0.9" +pallet-balances = { workspace = true } +pallet-timestamp = { workspace = true } +sp-io = { workspace = true } +sp-keystore = { workspace = true } + [features] default = ["std"] std = [ @@ -38,4 +45,5 @@ std = [ "sp-runtime/std", "pallet-assets/std", "assets-chain-extension-types/std", + "pallet-balances/std", ] diff --git a/chain-extensions/pallet-assets/src/lib.rs b/chain-extensions/pallet-assets/src/lib.rs index f4ff4695..7f15a272 100644 --- a/chain-extensions/pallet-assets/src/lib.rs +++ b/chain-extensions/pallet-assets/src/lib.rs @@ -18,6 +18,10 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(test)] +mod mock; +#[cfg(test)] +pub mod tests; pub mod weights; use assets_chain_extension_types::{select_origin, Origin, Outcome}; diff --git a/chain-extensions/pallet-assets/src/mock.rs b/chain-extensions/pallet-assets/src/mock.rs new file mode 100644 index 00000000..2bdf6ac1 --- /dev/null +++ b/chain-extensions/pallet-assets/src/mock.rs @@ -0,0 +1,241 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +use crate::weights::SubstrateWeight; +use crate::{weights, AssetsExtension}; +use frame_support::traits::{AsEnsureOriginWithArg, ConstU128, Currency, Randomness}; +use frame_support::{ + parameter_types, sp_io, + traits::{ConstU32, ConstU64, Nothing}, + weights::Weight, +}; +use pallet_assets::AssetsCallback; +use pallet_contracts::chain_extension::RegisteredChainExtension; +use pallet_contracts::{Config, DefaultAddressGenerator, Frame}; +use parity_scale_codec::Encode; +use sp_core::crypto::AccountId32; +use sp_io::storage; +use sp_keystore::{testing::KeyStore, KeystoreExt}; +use sp_runtime::generic; +use sp_runtime::testing::H256; +use sp_runtime::traits::{BlakeTwo256, Convert, IdentityLookup, Zero}; +use std::sync::Arc; + +pub type BlockNumber = u32; +pub type Balance = u128; +pub type AssetId = u128; + +type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max( + Weight::from_ref_time(2_000_000_000_000).set_proof_size(u64::MAX), + ); +} +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u32; + type BlockNumber = BlockNumber; + type Hash = H256; + type RuntimeCall = RuntimeCall; + type Hashing = BlakeTwo256; + type AccountId = AccountId32; + type Lookup = IdentityLookup; + type Header = generic::Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const DeletionWeightLimit: Weight = Weight::from_ref_time(500_000_000_000); + pub static UnstableInterface: bool = true; + pub Schedule: pallet_contracts::Schedule = Default::default(); + pub static DepositPerByte: BalanceOf = 1; + pub const DepositPerItem: BalanceOf = 1; +} + +pub struct DummyDeprecatedRandomness; +impl Randomness for DummyDeprecatedRandomness { + fn random(_: &[u8]) -> (H256, BlockNumber) { + (Default::default(), Zero::zero()) + } +} + +impl pallet_contracts::Config for Test { + type Time = Timestamp; + type Randomness = DummyDeprecatedRandomness; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type CallFilter = Nothing; + type CallStack = [Frame; 5]; + type WeightPrice = Self; + type WeightInfo = (); + type ChainExtension = AssetsExtension>; + type DeletionQueueDepth = ConstU32<128>; + type DeletionWeightLimit = DeletionWeightLimit; + type Schedule = Schedule; + type DepositPerByte = DepositPerByte; + type DepositPerItem = DepositPerItem; + type AddressGenerator = DefaultAddressGenerator; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; + type MaxStorageKeyLen = ConstU32<128>; + type UnsafeUnstableInterface = UnstableInterface; + type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; +} + +impl RegisteredChainExtension for AssetsExtension { + const ID: u16 = 02; +} + +parameter_types! { + pub static ExistentialDeposit: u64 = 1; +} + +impl pallet_balances::Config for Test { + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<1>; + type WeightInfo = (); +} + +pub struct AssetsCallbackHandle; +impl AssetsCallback for AssetsCallbackHandle { + fn created(_id: &AssetId, _owner: &AccountId32) { + storage::set(b"asset_created", &().encode()); + } + + fn destroyed(_id: &AssetId) { + storage::set(b"asset_destroyed", &().encode()); + } +} + +impl pallet_assets::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetIdParameter = u128; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type AssetDeposit = ConstU128<1>; + type AssetAccountDeposit = ConstU128<10>; + type MetadataDepositBase = ConstU128<1>; + type MetadataDepositPerByte = ConstU128<1>; + type ApprovalDeposit = ConstU128<1>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type WeightInfo = (); + type CallbackHandle = AssetsCallbackHandle; + type Extra = (); + type RemoveItemsLimit = ConstU32<5>; +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Assets: pallet_assets::{Pallet, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Contracts: pallet_contracts::{Pallet, Call, Storage, Event}, + } +); + +pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]); +pub const BOB: AccountId32 = AccountId32::new([2u8; 32]); +pub const GAS_LIMIT: Weight = Weight::from_ref_time(100_000_000_000).set_proof_size(700000u64); +pub const ONE: u128 = 1_000_000_000_000_000_000; + +impl Convert> for Test { + fn convert(w: Weight) -> BalanceOf { + w.ref_time().into() + } +} + +pub struct ExtBuilder { + existential_deposit: u64, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { + existential_deposit: ExistentialDeposit::get(), + } + } +} + +impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + } + pub fn build(self) -> sp_io::TestExternalities { + use env_logger::{Builder, Env}; + let env = Env::new().default_filter_or("runtime=debug"); + let _ = Builder::from_env(env).is_test(true).try_init(); + self.set_associated_consts(); + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![] } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.register_extension(KeystoreExt(Arc::new(KeyStore::new()))); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} diff --git a/chain-extensions/pallet-assets/src/tests.rs b/chain-extensions/pallet-assets/src/tests.rs new file mode 100644 index 00000000..635de956 --- /dev/null +++ b/chain-extensions/pallet-assets/src/tests.rs @@ -0,0 +1,310 @@ +use crate::mock::*; +use frame_support::assert_ok; +use frame_support::traits::fungibles::roles::Inspect; +use frame_support::traits::Currency; +use pallet_contracts::Determinism; +use pallet_contracts_primitives::{Code, ExecReturnValue}; +use parity_scale_codec::{Decode, Encode}; +use sp_core::crypto::AccountId32; +use sp_runtime::DispatchError; +use std::fs; + +#[test] +fn create_works() { + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + let addr = instantiate(); + + assert_ok!(create(addr.clone(), 1, 1)); + + assert_eq!(Assets::owner(1), Some(addr.into())); + }); +} + +#[test] +fn mint_works() { + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + let addr = instantiate(); + + assert_ok!(create(addr.clone(), 1, 1)); + assert_ok!(mint(addr.clone(), 1, ALICE, 1000)); + + assert_eq!(Assets::balance(1, ALICE), 1000); + }); +} + +#[test] +fn burn_works() { + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + let addr = instantiate(); + + assert_ok!(create(addr.clone(), 1, 1)); + assert_ok!(mint(addr.clone(), 1, ALICE, 1000)); + assert_eq!(Assets::balance(1, ALICE), 1000); + + assert_ok!(burn(addr.clone(), 1, ALICE, 1000)); + assert_eq!(Assets::balance(1, ALICE), 0); + }); +} + +#[test] +fn balance_of_and_total_supply() { + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + let addr = instantiate(); + + assert_ok!(create(addr.clone(), 1, 1)); + assert_ok!(mint(addr.clone(), 1, ALICE, 1000)); + + assert_eq!( + balance_of(addr.clone(), 1, ALICE).data[1..], + 1000u128.encode() + ); + assert_eq!(total_supply(addr.clone(), 1).data[1..], 1000u128.encode()); + }); +} + +#[test] +fn approve_transfer_and_check_allowance() { + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + let addr = instantiate(); + + assert_ok!(create(addr.clone(), 1, 1)); + assert_ok!(mint(addr.clone(), 1, addr.clone(), 1000)); + assert_ok!(approve_transfer(addr.clone(), 1, BOB, 100)); + + assert_eq!( + allowance(addr.clone(), 1, addr.clone(), BOB).data[1..], + 100u128.encode() + ); + }); +} + +#[test] +fn approve_transfer_and_transfer_balance() { + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + let addr = instantiate(); + + assert_ok!(Assets::create(RuntimeOrigin::signed(ALICE), 1, ALICE, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(ALICE), 1, ALICE, 1000)); + assert_ok!(Assets::approve_transfer( + RuntimeOrigin::signed(ALICE), + 1, + addr.clone(), + 100 + )); + + // assert_ok!(create(addr.clone(), 1, 1)); + // assert_ok!(mint(addr.clone(), 1, addr.clone(), 1000)); + // assert_ok!(approve_transfer(addr.clone(), 1, ALICE, 100)); + + assert_eq!( + allowance(addr.clone(), 1, ALICE, addr.clone()).data[1..], + 100u128.encode() + ); + + assert_ok!(transfer_approved(addr.clone(), 1, ALICE, BOB, 100)); + + assert_eq!(balance_of(addr.clone(), 1, BOB).data[1..], 100u128.encode()); + assert_eq!( + balance_of(addr.clone(), 1, ALICE).data[1..], + 900u128.encode() + ); + }); +} + +#[test] +fn cancel_approval_works() { + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + let addr = instantiate(); + + assert_ok!(create(addr.clone(), 1, 1)); + assert_ok!(mint(addr.clone(), 1, addr.clone(), 1000)); + assert_ok!(approve_transfer(addr.clone(), 1, BOB, 100)); + + assert_ok!(cancel_approval(addr.clone(), 1, BOB)); + + assert_eq!( + allowance(addr.clone(), 1, addr.clone(), BOB).data[1..], + 0u128.encode() + ); + }); +} + +// ________________________________ +fn instantiate() -> AccountId32 { + let code = fs::read("./test-contract/asset_wrapper.wasm").expect("could not read .wasm file"); + let _ = Balances::deposit_creating(&ALICE, ONE * 1000); + let instance_selector: Vec = [0x9b, 0xae, 0x9d, 0x5e].to_vec(); + Contracts::bare_instantiate( + ALICE, + 0, + GAS_LIMIT, + None, + Code::Upload(code), + instance_selector, + vec![], + false, + ) + .result + .unwrap() + .account_id +} + +fn create( + addr: AccountId32, + asset_id: u128, + min_balance: u128, +) -> Result { + let data = [ + [0xab, 0x70, 0x0a, 0x1b].to_vec(), + (asset_id, min_balance).encode(), + ] + .concat(); + do_bare_call(addr, data, ONE) +} + +fn mint( + addr: AccountId32, + asset_id: u128, + beneficiary: AccountId32, + amount: u128, +) -> Result { + let data = [ + [0xcf, 0xdd, 0x9a, 0xa2].to_vec(), + (asset_id, beneficiary, amount).encode(), + ] + .concat(); + do_bare_call(addr, data, 0) +} + +fn burn( + addr: AccountId32, + asset_id: u128, + who: AccountId32, + amount: u128, +) -> Result { + let data = [ + [0xb1, 0xef, 0xc1, 0x7b].to_vec(), + (asset_id, who, amount).encode(), + ] + .concat(); + do_bare_call(addr, data, 0) +} + +fn transfer( + addr: AccountId32, + asset_id: u128, + target: AccountId32, + amount: u128, +) -> Result { + let data = [ + [0xb1, 0xef, 0xc1, 0x7b].to_vec(), + (asset_id, target, amount).encode(), + ] + .concat(); + do_bare_call(addr, data, 0) +} + +fn transfer_approved( + addr: AccountId32, + asset_id: u128, + owner: AccountId32, + dest: AccountId32, + amount: u128, +) -> Result { + let data = [ + [0x31, 0x05, 0x59, 0x75].to_vec(), + (asset_id, owner, dest, amount).encode(), + ] + .concat(); + do_bare_call(addr, data, 0) +} + +fn approve_transfer( + addr: AccountId32, + asset_id: u128, + delegate: AccountId32, + amount: u128, +) -> Result { + let data = [ + [0x8e, 0x7c, 0x3e, 0xe9].to_vec(), + (asset_id, delegate, amount).encode(), + ] + .concat(); + do_bare_call(addr, data, 0) +} + +fn cancel_approval( + addr: AccountId32, + asset_id: u128, + delegate: AccountId32, +) -> Result { + let data = [ + [0x31, 0x7c, 0x8e, 0x29].to_vec(), + (asset_id, delegate).encode(), + ] + .concat(); + do_bare_call(addr, data, 0) +} + +fn balance_of(addr: AccountId32, asset_id: u128, who: AccountId32) -> ExecReturnValue { + let data = [[0x0f, 0x75, 0x5a, 0x56].to_vec(), (asset_id, who).encode()].concat(); + do_bare_call(addr, data, 0).unwrap() +} + +fn total_supply(addr: AccountId32, asset_id: u128) -> ExecReturnValue { + let data = [[0xdb, 0x63, 0x75, 0xa8].to_vec(), asset_id.encode()].concat(); + do_bare_call(addr, data, 0).unwrap() +} + +fn allowance( + addr: AccountId32, + asset_id: u128, + owner: AccountId32, + delegate: AccountId32, +) -> ExecReturnValue { + let data = [ + [0x6a, 0x00, 0x16, 0x5e].to_vec(), + (asset_id, owner, delegate).encode(), + ] + .concat(); + do_bare_call(addr, data, 0).unwrap() +} + +fn do_bare_call( + addr: AccountId32, + input: Vec, + value: u128, +) -> Result { + Contracts::bare_call( + ALICE, + addr.into(), + value.into(), + GAS_LIMIT, + None, + input, + false, + Determinism::Deterministic, + ) + .result +} diff --git a/chain-extensions/pallet-assets/test-contract/asset_wrapper.json b/chain-extensions/pallet-assets/test-contract/asset_wrapper.json new file mode 100644 index 00000000..f9a6f1b5 --- /dev/null +++ b/chain-extensions/pallet-assets/test-contract/asset_wrapper.json @@ -0,0 +1,1203 @@ +{ + "source": { + "hash": "0xe355795885a65d3d6235679e5f71239c7404471d1573c86e60ee838701490ed1", + "language": "ink! 4.2.0", + "compiler": "rustc 1.69.0-nightly", + "build_info": { + "build_mode": "Debug", + "cargo_contract_version": "2.1.0", + "rust_toolchain": "nightly-x86_64-unknown-linux-gnu", + "wasm_opt_settings": { + "keep_debug_symbols": false, + "optimization_passes": "Z" + } + } + }, + "contract": { + "name": "asset_wrapper", + "version": "0.1.0", + "authors": [ + "Stake Technologies " + ] + }, + "spec": { + "constructors": [ + { + "args": [], + "default": false, + "docs": [], + "label": "new", + "payable": false, + "returnType": { + "displayName": [ + "ink_primitives", + "ConstructorResult" + ], + "type": 0 + }, + "selector": "0x9bae9d5e" + } + ], + "docs": [], + "environment": { + "accountId": { + "displayName": [ + "AccountId" + ], + "type": 7 + }, + "balance": { + "displayName": [ + "Balance" + ], + "type": 3 + }, + "blockNumber": { + "displayName": [ + "BlockNumber" + ], + "type": 16 + }, + "chainExtension": { + "displayName": [ + "ChainExtension" + ], + "type": 17 + }, + "hash": { + "displayName": [ + "Hash" + ], + "type": 14 + }, + "maxEventTopics": 4, + "timestamp": { + "displayName": [ + "Timestamp" + ], + "type": 15 + } + }, + "events": [], + "lang_error": { + "displayName": [ + "ink", + "LangError" + ], + "type": 2 + }, + "messages": [ + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + }, + { + "label": "min_balance", + "type": { + "displayName": [ + "Balance" + ], + "type": 3 + } + } + ], + "default": false, + "docs": [], + "label": "create", + "mutates": true, + "payable": true, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 4 + }, + "selector": "0xab700a1b" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + }, + { + "label": "beneficiary", + "type": { + "displayName": [ + "AccountId" + ], + "type": 7 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "Balance" + ], + "type": 3 + } + } + ], + "default": false, + "docs": [], + "label": "mint", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 4 + }, + "selector": "0xcfdd9aa2" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + }, + { + "label": "who", + "type": { + "displayName": [ + "AccountId" + ], + "type": 7 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "Balance" + ], + "type": 3 + } + } + ], + "default": false, + "docs": [], + "label": "burn", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 4 + }, + "selector": "0xb1efc17b" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + }, + { + "label": "target", + "type": { + "displayName": [ + "AccountId" + ], + "type": 7 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "Balance" + ], + "type": 3 + } + } + ], + "default": false, + "docs": [], + "label": "transfer", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 4 + }, + "selector": "0x84a15da1" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + }, + { + "label": "who", + "type": { + "displayName": [ + "AccountId" + ], + "type": 7 + } + } + ], + "default": false, + "docs": [], + "label": "balance_of", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 10 + }, + "selector": "0x0f755a56" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + } + ], + "default": false, + "docs": [], + "label": "total_supply", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 10 + }, + "selector": "0xdb6375a8" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + }, + { + "label": "owner", + "type": { + "displayName": [ + "AccountId" + ], + "type": 7 + } + }, + { + "label": "delegate", + "type": { + "displayName": [ + "AccountId" + ], + "type": 7 + } + } + ], + "default": false, + "docs": [], + "label": "allowance", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 10 + }, + "selector": "0x6a00165e" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + }, + { + "label": "delegate", + "type": { + "displayName": [ + "AccountId" + ], + "type": 7 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "Balance" + ], + "type": 3 + } + } + ], + "default": false, + "docs": [], + "label": "approve_transfer", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 4 + }, + "selector": "0x8e7c3ee9" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + }, + { + "label": "delegate", + "type": { + "displayName": [ + "AccountId" + ], + "type": 7 + } + } + ], + "default": false, + "docs": [], + "label": "cancel_approval", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 4 + }, + "selector": "0x317c8e29" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + }, + { + "label": "owner", + "type": { + "displayName": [ + "AccountId" + ], + "type": 7 + } + }, + { + "label": "destination", + "type": { + "displayName": [ + "AccountId" + ], + "type": 7 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "Balance" + ], + "type": 3 + } + } + ], + "default": false, + "docs": [], + "label": "transfer_approved", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 4 + }, + "selector": "0x31055975" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + }, + { + "label": "name", + "type": { + "displayName": [ + "Vec" + ], + "type": 11 + } + }, + { + "label": "symbol", + "type": { + "displayName": [ + "Vec" + ], + "type": 11 + } + }, + { + "label": "decimals", + "type": { + "displayName": [ + "u8" + ], + "type": 9 + } + } + ], + "default": false, + "docs": [], + "label": "set_metadata", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 4 + }, + "selector": "0x0b787bb5" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + } + ], + "default": false, + "docs": [], + "label": "metadata_name", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 12 + }, + "selector": "0xf5cddbc1" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + } + ], + "default": false, + "docs": [], + "label": "metadata_symbol", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 12 + }, + "selector": "0x7cdcafc1" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + } + ], + "default": false, + "docs": [], + "label": "metadata_decimals", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 13 + }, + "selector": "0x2554473b" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + }, + { + "label": "owner", + "type": { + "displayName": [ + "AccountId" + ], + "type": 7 + } + } + ], + "default": false, + "docs": [], + "label": "transfer_ownership", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 4 + }, + "selector": "0x107e33ea" + }, + { + "args": [ + { + "label": "asset_id", + "type": { + "displayName": [ + "u128" + ], + "type": 3 + } + }, + { + "label": "min_balance", + "type": { + "displayName": [ + "Balance" + ], + "type": 3 + } + } + ], + "default": false, + "docs": [], + "label": "create_caller", + "mutates": true, + "payable": true, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 4 + }, + "selector": "0x7fb0f9bb" + } + ] + }, + "storage": { + "root": { + "layout": { + "struct": { + "fields": [], + "name": "Mock" + } + }, + "root_key": "0x00000000" + } + }, + "types": [ + { + "id": 0, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 1 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 2 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 1 + }, + { + "name": "E", + "type": 2 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 1, + "type": { + "def": { + "tuple": [] + } + } + }, + { + "id": 2, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 1, + "name": "CouldNotReadInput" + } + ] + } + }, + "path": [ + "ink_primitives", + "LangError" + ] + } + }, + { + "id": 3, + "type": { + "def": { + "primitive": "u128" + } + } + }, + { + "id": 4, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 5 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 2 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 5 + }, + { + "name": "E", + "type": 2 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 5, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 1 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 6 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 1 + }, + { + "name": "E", + "type": 6 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 6, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 1, + "name": "BalanceLow" + }, + { + "index": 2, + "name": "NoAccount" + }, + { + "index": 3, + "name": "NoPermission" + }, + { + "index": 4, + "name": "Unknown" + }, + { + "index": 5, + "name": "Frozen" + }, + { + "index": 6, + "name": "InUse" + }, + { + "index": 7, + "name": "BadWitness" + }, + { + "index": 8, + "name": "MinBalanceZero" + }, + { + "index": 9, + "name": "NoProvider" + }, + { + "index": 10, + "name": "BadMetadata" + }, + { + "index": 11, + "name": "Unapproved" + }, + { + "index": 12, + "name": "WouldDie" + }, + { + "index": 13, + "name": "AlreadyExists" + }, + { + "index": 14, + "name": "NoDeposit" + }, + { + "index": 15, + "name": "WouldBurn" + }, + { + "index": 16, + "name": "LiveAsset" + }, + { + "index": 17, + "name": "AssetNotLive" + }, + { + "index": 18, + "name": "IncorrectStatus" + }, + { + "index": 19, + "name": "NotFrozen" + }, + { + "index": 98, + "name": "OriginCannotBeCaller" + }, + { + "index": 99, + "name": "RuntimeError" + }, + { + "index": 21, + "name": "UnknownStatusCode" + }, + { + "index": 22, + "name": "InvalidScaleEncoding" + } + ] + } + }, + "path": [ + "assets_extension", + "AssetsError" + ] + } + }, + { + "id": 7, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 8, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "AccountId" + ] + } + }, + { + "id": 8, + "type": { + "def": { + "array": { + "len": 32, + "type": 9 + } + } + } + }, + { + "id": 9, + "type": { + "def": { + "primitive": "u8" + } + } + }, + { + "id": 10, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 3 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 2 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 3 + }, + { + "name": "E", + "type": 2 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 11, + "type": { + "def": { + "sequence": { + "type": 9 + } + } + } + }, + { + "id": 12, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 11 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 2 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 11 + }, + { + "name": "E", + "type": 2 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 13, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 9 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 2 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 9 + }, + { + "name": "E", + "type": 2 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 14, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 8, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "Hash" + ] + } + }, + { + "id": 15, + "type": { + "def": { + "primitive": "u64" + } + } + }, + { + "id": 16, + "type": { + "def": { + "primitive": "u32" + } + } + }, + { + "id": 17, + "type": { + "def": { + "variant": {} + }, + "path": [ + "ink_env", + "types", + "NoChainExtension" + ] + } + } + ], + "version": "4" +} \ No newline at end of file diff --git a/chain-extensions/pallet-assets/test-contract/asset_wrapper.wasm b/chain-extensions/pallet-assets/test-contract/asset_wrapper.wasm new file mode 100644 index 0000000000000000000000000000000000000000..555f80d5800e99b2081efbe743205918c9fef26c GIT binary patch literal 25247 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

POG>{+kTNzxK zy$_V{W&XD@pZHSZlmF^q%9P8e(g&ti($>*q?NjcT8K-q;W>=0)wWp7WZ4kDR=06sg z-aj?F(3-g=Wz26gw!u?;kMmEJXN^yt&s4juNz?XH%C8f8$}OE}E#JSe^Z?!6{YO_9 z+Ox+NfJEdi1{KKPyo|i89vf^gEiD-0D}#%(hmW)uPVF2S8X4U=G_rGOV&}=p@q^=I zJ69Ly7ndGb+_^BjxO#Hu;l))^U{EvYDIro>jmuL=6~8OEH0_(G9yoZSHSOi&2gAUf z1jY};JMm?D>e$ruZ2MFPHrk$J42I1WW6PnZ?W+s6App?+cNtTwPPO^9$8_obG`;Us zyS0)t?LoGat^wYk<|+PEc%I<-&%n|6mEOO!d=##U>FDh&>L^i?-1!;(4?!ZcQ;S|~ zUtLu1KiU>|{t~#`z&!)KQ=YFb5Qxa&(WMyxIT?Ey+12CeSJZ!jx^zMx{Rer?#&qEP zTN^f0g0K+)&+k)@ebRxR~xl_g$n0c%*Aj^9!xTban;Zpi6Z>kd~h+2PROz!gCYAkd?Sw6S2>^-=j^X;@QpI z4YVJdTAZDpmuj@r8*jWRt@Zfvb)FIZvY5qODQ(EEOZo4nzJb1h4u!vqhHiRkxn*_^ zEgD>0JqqE6hK7cRMutX*#)ftcjSo!>O%ClI9vU7V9vL1T9vj{@JU%=zJUP4@BRf1Y zGBP?cHnMAEd}Lx|a%A`D(CF~!$mrXbc zCdYQ~8rn6yYh>5xuCZOac8%|v*fqIp_xRBG@c791==j+9uJQ5liSfzt-4jC-!xJMD zqZ4BjyC%jbCMG5)c25pX4o{9uj!uqE?wTB*oS2-P+`StRceDI%rrynKyJ>pW$>;Uo zy|)rLd9qbJstjmRFO{8}r2eYHwX!ff-5SITOdnZ>w_ADx_g-Llr{LgI)PI!n>$>XG zlqJhW{}IYJUQ&Mxb$*Zda4S{pfmbliDpbq&;5t$RLE46f9mbDe1P1BR|K!>1%+Xq$ z$!FN!7r3OW|9e2M*>+3Ra_%-@`G>&rZ{g+Zd43OI$)(Db4maG)TN8BI&Qfnovrzx;Nv-5Mg1-e z{?6|OTo%artY@I^W&F2*WApa{7E%iIZBY{NB(VHAFnkK9Dj6v1Pf|}WsT=?E@lVnJ z2KrxB{))GQo{)oNs^&a_;aGp*9^&~kd;rV68NV)n@Kq|9k&eGVHl+-Ak)-d~)Tyca z2+Q(p3Vm3ZF%@2JP01?q1MjPgHiD$dVmE~+w06gLd4`j&v=+!9mzFPkqv&{5Y2*{B zg{f0STrl{t4c`9@LW`}?;uXYmmnDnJWS1?aDYDO_5cJkJivsuGjI$Zo(O1M)r-|3Z ziw^F8>tOrRp!L4h`}tvh$?#d$lK+%VQj*wRmyPPK4|mmDl)L*sO1;77_UW0Gse6dn zNs>?Pvqbv`AnQFC&Do%xXN0t_5?r5DVrqHyl z);%piVza{6nC95A*350nEm|EyzZ$Wt4{|L2fC1m+nuTo0!!Ue52W!6=SAQeu60|EP z1|5K3p=2DJT4n=a=gRcdLW}(tv~%a^&13milhq{??hBg&_YVL{y9e4?x?S5lJYUO4 ZLUJviEqVRNQIdmv4)EE Date: Fri, 28 Apr 2023 16:27:16 +0200 Subject: [PATCH 2/2] Added tests --- chain-extensions/pallet-assets/src/tests.rs | 229 ++++++++++++++++++-- 1 file changed, 209 insertions(+), 20 deletions(-) diff --git a/chain-extensions/pallet-assets/src/tests.rs b/chain-extensions/pallet-assets/src/tests.rs index 635de956..255e64e2 100644 --- a/chain-extensions/pallet-assets/src/tests.rs +++ b/chain-extensions/pallet-assets/src/tests.rs @@ -1,14 +1,42 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + use crate::mock::*; use frame_support::assert_ok; use frame_support::traits::fungibles::roles::Inspect; use frame_support::traits::Currency; use pallet_contracts::Determinism; -use pallet_contracts_primitives::{Code, ExecReturnValue}; -use parity_scale_codec::{Decode, Encode}; +use pallet_contracts_primitives::{Code, ExecReturnValue, ReturnFlags}; +use parity_scale_codec::Encode; use sp_core::crypto::AccountId32; use sp_runtime::DispatchError; use std::fs; +// Those tests use the contract scheduler_example avilable here: +// https://github.com/AstarNetwork/chain-extension-contracts/blob/main/examples/assets +// It maps chain extension functions to ink! callable messages +// ex: +// #[ink(message)] +// pub fn burn(&mut self, asset_id: u128, who: AccountId, amount: Balance) -> Result<(), AssetsError> { +// AssetsExtension::burn(Origin::Caller, asset_id, who, amount)?; +// Ok(()) +// } + #[test] fn create_works() { ExtBuilder::default() @@ -17,8 +45,10 @@ fn create_works() { .execute_with(|| { let addr = instantiate(); + // Act - Create asset assert_ok!(create(addr.clone(), 1, 1)); + // Assert - Contract is the owner assert_eq!(Assets::owner(1), Some(addr.into())); }); } @@ -31,9 +61,13 @@ fn mint_works() { .execute_with(|| { let addr = instantiate(); + // Arrange - create asset assert_ok!(create(addr.clone(), 1, 1)); + + // Act - Mint 1000 assets to Alice assert_ok!(mint(addr.clone(), 1, ALICE, 1000)); + // Assert - Alice balance is 1000 assert_eq!(Assets::balance(1, ALICE), 1000); }); } @@ -46,15 +80,39 @@ fn burn_works() { .execute_with(|| { let addr = instantiate(); + // Arrange - Create & mint 1000 to Alice assert_ok!(create(addr.clone(), 1, 1)); assert_ok!(mint(addr.clone(), 1, ALICE, 1000)); - assert_eq!(Assets::balance(1, ALICE), 1000); + // Act - Burn 1000 of Alice tokens assert_ok!(burn(addr.clone(), 1, ALICE, 1000)); + + // Assert - Balance of Alice is then 0 assert_eq!(Assets::balance(1, ALICE), 0); }); } +#[test] +fn transfer_works() { + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + let addr = instantiate(); + + // Assert - Create & mint 1000 to contract + assert_ok!(create(addr.clone(), 1, 1)); + assert_ok!(mint(addr.clone(), 1, addr.clone(), 1000)); + + // Act - Tranfer 1000 from contract to Alice + assert_ok!(transfer(addr.clone(), 1, ALICE, 1000)); + + // Assert - Alice balance is 1000 and contract is zero + assert_eq!(Assets::balance(1, ALICE), 1000); + assert_eq!(Assets::balance(1, addr.clone()), 0); + }); +} + #[test] fn balance_of_and_total_supply() { ExtBuilder::default() @@ -63,9 +121,11 @@ fn balance_of_and_total_supply() { .execute_with(|| { let addr = instantiate(); + // Arrange - create & mint 1000 to Alice assert_ok!(create(addr.clone(), 1, 1)); assert_ok!(mint(addr.clone(), 1, ALICE, 1000)); + // Assert - Balance and total supply is 1000 assert_eq!( balance_of(addr.clone(), 1, ALICE).data[1..], 1000u128.encode() @@ -82,10 +142,14 @@ fn approve_transfer_and_check_allowance() { .execute_with(|| { let addr = instantiate(); + // Arrange - Create and mint 1000 to contract assert_ok!(create(addr.clone(), 1, 1)); assert_ok!(mint(addr.clone(), 1, addr.clone(), 1000)); + + // Act - approve transfer To BOB for 100 assert_ok!(approve_transfer(addr.clone(), 1, BOB, 100)); + // Assert - Bob has 100 allowance assert_eq!( allowance(addr.clone(), 1, addr.clone(), BOB).data[1..], 100u128.encode() @@ -101,30 +165,27 @@ fn approve_transfer_and_transfer_balance() { .execute_with(|| { let addr = instantiate(); - assert_ok!(Assets::create(RuntimeOrigin::signed(ALICE), 1, ALICE, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(ALICE), 1, ALICE, 1000)); + // Arrange + // As transfer_approved() can only be called on behalf of the contract + // Bob creates & mint token to himself + // and approve the contract to spend his assets + assert_ok!(Assets::create(RuntimeOrigin::signed(BOB), 1, BOB, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(BOB), 1, BOB, 1000)); assert_ok!(Assets::approve_transfer( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(BOB), 1, addr.clone(), 100 )); - // assert_ok!(create(addr.clone(), 1, 1)); - // assert_ok!(mint(addr.clone(), 1, addr.clone(), 1000)); - // assert_ok!(approve_transfer(addr.clone(), 1, ALICE, 100)); - - assert_eq!( - allowance(addr.clone(), 1, ALICE, addr.clone()).data[1..], - 100u128.encode() - ); - - assert_ok!(transfer_approved(addr.clone(), 1, ALICE, BOB, 100)); + // Act - The contract transfer 100 from Alice to Bob + assert_ok!(transfer_approved(addr.clone(), 1, BOB, ALICE, 100)); - assert_eq!(balance_of(addr.clone(), 1, BOB).data[1..], 100u128.encode()); + // Assert - Bob has 900 and Alice 100 + assert_eq!(balance_of(addr.clone(), 1, BOB).data[1..], 900u128.encode()); assert_eq!( balance_of(addr.clone(), 1, ALICE).data[1..], - 900u128.encode() + 100u128.encode() ); }); } @@ -137,12 +198,16 @@ fn cancel_approval_works() { .execute_with(|| { let addr = instantiate(); + // Arrange - Create and mint 1000 to contract + // and approve Bob to spend 100 assert_ok!(create(addr.clone(), 1, 1)); assert_ok!(mint(addr.clone(), 1, addr.clone(), 1000)); assert_ok!(approve_transfer(addr.clone(), 1, BOB, 100)); + // Act - cancel approval assert_ok!(cancel_approval(addr.clone(), 1, BOB)); + // Assert - Bob allowance is 0 assert_eq!( allowance(addr.clone(), 1, addr.clone(), BOB).data[1..], 0u128.encode() @@ -150,10 +215,78 @@ fn cancel_approval_works() { }); } -// ________________________________ +#[test] +fn set_metadata_and_checks() { + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + let addr = instantiate(); + + // Arrange - create + assert_ok!(create(addr.clone(), 1, 1)); + + // Act - set metadata + assert_ok!(set_metadata( + addr.clone(), + 1, + "Name".as_bytes().to_vec(), + "SYMB".as_bytes().to_vec(), + 18 + )); + + // Assert - metadata Name, Symbol & decimal is correct + assert_eq!(metadata_name(addr.clone(), 1).data[1..], "Name".encode()); + assert_eq!(metadata_symbol(addr.clone(), 1).data[1..], "SYMB".encode()); + assert_eq!(metadata_decimals(addr.clone(), 1).data[1..], 18u8.encode()); + }); +} + +#[test] +fn transfer_ownership_works() { + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + let addr = instantiate(); + + // Arrange - create token - owner is contract + assert_ok!(create(addr.clone(), 1, 1)); + assert_eq!(Assets::owner(1), Some(addr.clone())); + + // Act - transfer ownership to Alice + assert_ok!(transfer_ownership(addr.clone(), 1, ALICE)); + + // Assert - Alice is the owner + assert_eq!(Assets::owner(1), Some(ALICE)); + }); +} + +#[test] +fn cannot_make_tx_on_behalf_of_caller() { + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + let addr = instantiate(); + + // Assert + // When calling chan extensio with Orgin::Caller + // it reverts + assert_eq!( + create_caller(addr.clone(), 1, 1).unwrap(), + ExecReturnValue { + flags: ReturnFlags::REVERT, + data: [0, 1, 98].into() + } + ); + }); +} + fn instantiate() -> AccountId32 { let code = fs::read("./test-contract/asset_wrapper.wasm").expect("could not read .wasm file"); let _ = Balances::deposit_creating(&ALICE, ONE * 1000); + let _ = Balances::deposit_creating(&BOB, ONE * 1000); let instance_selector: Vec = [0x9b, 0xae, 0x9d, 0x5e].to_vec(); Contracts::bare_instantiate( ALICE, @@ -183,6 +316,19 @@ fn create( do_bare_call(addr, data, ONE) } +fn create_caller( + addr: AccountId32, + asset_id: u128, + min_balance: u128, +) -> Result { + let data = [ + [0x7f, 0xb0, 0xf9, 0xbb].to_vec(), + (asset_id, min_balance).encode(), + ] + .concat(); + do_bare_call(addr, data, ONE) +} + fn mint( addr: AccountId32, asset_id: u128, @@ -218,7 +364,7 @@ fn transfer( amount: u128, ) -> Result { let data = [ - [0xb1, 0xef, 0xc1, 0x7b].to_vec(), + [0x84, 0xa1, 0x5d, 0xa1].to_vec(), (asset_id, target, amount).encode(), ] .concat(); @@ -267,6 +413,34 @@ fn cancel_approval( do_bare_call(addr, data, 0) } +fn transfer_ownership( + addr: AccountId32, + asset_id: u128, + owner: AccountId32, +) -> Result { + let data = [ + [0x10, 0x7e, 0x33, 0xea].to_vec(), + (asset_id, owner).encode(), + ] + .concat(); + do_bare_call(addr, data, 0) +} + +fn set_metadata( + addr: AccountId32, + asset_id: u128, + name: Vec, + symbol: Vec, + decimals: u8, +) -> Result { + let data = [ + [0x0b, 0x78, 0x7b, 0xb5].to_vec(), + (asset_id, name, symbol, decimals).encode(), + ] + .concat(); + do_bare_call(addr, data, 0) +} + fn balance_of(addr: AccountId32, asset_id: u128, who: AccountId32) -> ExecReturnValue { let data = [[0x0f, 0x75, 0x5a, 0x56].to_vec(), (asset_id, who).encode()].concat(); do_bare_call(addr, data, 0).unwrap() @@ -291,6 +465,21 @@ fn allowance( do_bare_call(addr, data, 0).unwrap() } +fn metadata_name(addr: AccountId32, asset_id: u128) -> ExecReturnValue { + let data = [[0xf5, 0xcd, 0xdb, 0xc1].to_vec(), asset_id.encode()].concat(); + do_bare_call(addr, data, 0).unwrap() +} + +fn metadata_symbol(addr: AccountId32, asset_id: u128) -> ExecReturnValue { + let data = [[0x7c, 0xdc, 0xaf, 0xc1].to_vec(), asset_id.encode()].concat(); + do_bare_call(addr, data, 0).unwrap() +} + +fn metadata_decimals(addr: AccountId32, asset_id: u128) -> ExecReturnValue { + let data = [[0x25, 0x54, 0x47, 0x3b].to_vec(), asset_id.encode()].concat(); + do_bare_call(addr, data, 0).unwrap() +} + fn do_bare_call( addr: AccountId32, input: Vec,