From 58e7bab882648114d9c8719cce2d79052d6dbd26 Mon Sep 17 00:00:00 2001 From: Amit Yadav Date: Tue, 12 Sep 2023 13:08:53 +0530 Subject: [PATCH] [Astra] rename cleanup : make sure the tests are passing[Rust unit and integration tests] (#3) * update one test * update multi council test * update test bounty test * update test * cleanup * complete general tests * lint fix * update upgrade test * lint * add files * paths * update * update * remove files * path * flow * update * update * update * update * copy * buildg * update * update * update * update * update * update * update * update build * update * update * use 1.69.0 * remove build from job * add more tests * Apply suggestions from code review Co-authored-by: sczembor <43810037+sczembor@users.noreply.github.com> * updates * fix * lint --------- Co-authored-by: sczembor <43810037+sczembor@users.noreply.github.com> --- .github/workflows/build.yml | 156 ----- .github/workflows/tests.yml | 56 +- Cargo.lock | 6 +- Cargo.toml | 4 +- astra-factory/src/lib.rs | 6 +- astra-staking/src/lib.rs | 12 +- astra-staking/src/user.rs | 10 +- astra/Cargo.toml | 2 + astra/src/bounties.rs | 17 +- astra/src/lib.rs | 20 +- astra/src/policy.rs | 7 +- astra/src/proposals.rs | 28 +- astra/src/views.rs | 4 +- astra/tests-ava/__tests__/lib.ava.ts | 2 +- astra/tests-ava/__tests__/utils.ts | 10 +- astra/tests-ava/__tests__/views.ava.ts | 6 +- astra/tests/test_general.rs | 767 +++++++++++++++---------- astra/tests/test_upgrade.rs | 218 ++++--- astra/tests/utils/mod.rs | 263 +++++---- build.sh | 3 + 20 files changed, 804 insertions(+), 793 deletions(-) delete mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 9c736a6b8..000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,156 +0,0 @@ -name: Build -on: - # (you may add filters for when running the workflow) - push: - branches: - - master - pull_request: - -env: - # better visualization - CARGO_TERM_COLOR: always -jobs: - # the first job is used to acquire some tag-naming information - # - # this snippet was based on: - # https://raw.githubusercontent.com/BurntSushi/ripgrep/master/.github/workflows/release.yml - # in case a tag was set, a binary release will be made - create-release-on-tags: - name: Create a new release on tags - runs-on: ubuntu-latest - # env: - # Set to force version number, e.g., when no tag exists. - # RG_VERSION: TEST-0.0.0 - outputs: - rg_version: ${{ env.RG_VERSION }} - steps: - - name: Get the release version from the tag - shell: bash - if: env.RG_VERSION == '' - run: | - # Apparently, this is the right way to get a tag name. Really? - # - # See: https://github.community/t5/GitHub-Actions/How-to-get-just-the-tag-name/m-p/32167/highlight/true#M1027 - echo "RG_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - echo "version is: ${{ env.RG_VERSION }}" - - # this second job builds and tests the contracts - # if it's a tag release, it also creates their release file - build: - # in case this is a binary release, we make sure to wait - # for any requirement to be ready - needs: ["create-release-on-tags"] - runs-on: ubuntu-latest - steps: - # rust compiler for running tests - - uses: actions/checkout@v2 - - name: Install latest stable (for linux-gnu) - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: x86_64-unknown-linux-gnu - components: rustfmt, clippy - # rust compiler for creating binaries - - name: Install latest stable (for wasm) - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: wasm32-unknown-unknown - components: rustfmt, clippy - # caching (cargo registry) - - name: Cache cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: ubuntu-latest-stable-cargo-registry-${{ hashFiles('**/Cargo.toml') }} - # caching (cargo index) - - name: Cache cargo index - uses: actions/cache@v1 - with: - path: ~/.cargo/git - key: ubuntu-latest-stable-cargo-index-${{ hashFiles('**/Cargo.toml') }} - # caching (cargo wasm artifacts) - - name: Cache cargo wasm build (including docs) - uses: actions/cache@v1 - with: - path: target/wasm32-unknown-unknown - key: ubuntu-latest-stable-cargo-release-target-${{ hashFiles('**/Cargo.toml') }} - # caching (cargo testing artifacts) - - name: Cache cargo linux-gnu build (for testing) - uses: actions/cache@v1 - with: - path: target/x86_64-unknown-linux-gnu - key: ubuntu-latest-stable-cargo-release-target-${{ hashFiles('**/Cargo.toml') }} - # downloads/installs any extra requirements - # - # binaryen, which is used to reduce the contract's size. - # based on: - # https://github.com/rustwasm/walrus/blob/9d6c9de432d6a97478dc76ebdf18aed51584c3af/.github/workflows/main.yml#L56 - - name: Install binaryen - run: | - set -e - curl -L https://github.com/WebAssembly/binaryen/releases/download/version_105/binaryen-version_105-x86_64-linux.tar.gz | tar xzf - - echo "`pwd`/binaryen-version_105/bin" >> $GITHUB_PATH - # triggers all build.rs steps - - name: Trigger build.rs steps - run: | - find . \ - -maxdepth 2 \ - -name build.rs \ - -prune \ - -exec touch -c {} \; - # Builds the wasm binaries - - name: Build wasm binaries - uses: actions-rs/cargo@v1 - with: - command: build - args: --release --target wasm32-unknown-unknown - # Copies the wasm binaries to res/ and strips them - # (reducing their's sizes) - - name: Wasm copy and strip - run: | - find target/wasm32-unknown-unknown/release \ - -maxdepth 1 \ - -name \*.wasm \ - -prune \ - -exec cp {} res \; - for f in res/*.wasm - do - wasm-opt -Oz -o "$f" "$f" - done - - name: Show the wasm files and their sizes - run: | - ls -lah res/*.wasm | awk '{print $5 " " $9}' - # run the tests (which depend on the binaries from res/) - - name: Run native tests - uses: actions-rs/cargo@v1 - with: - command: test - # the jobs is optional, to reduce RAM usage - # the --nocapture prints the tests' stdout - args: --target=x86_64-unknown-linux-gnu --jobs=2 -- --nocapture - # for tagged runs, create an archive releaseruns - # - # based on: - # https://raw.githubusercontent.com/BurntSushi/ripgrep/master/.github/workflows/release.yml - - name: Build archive - if: startsWith(github.ref, 'refs/tags') - shell: bash - run: | - staging="contracts-${{ needs.create-release-on-tags.outputs.rg_version }}" - mkdir -p "$staging/info" - # copy all markdown files - find . -name \*.md -not -path "./target/*" -prune -not -path "./$staging/*" -prune -exec cp --parents {} "$staging/info/" \; - # copy all wasm files - cp res/*.wasm "$staging/" - # save the tag name and git sha to the VERSION file - echo ${{ needs.create-release-on-tags.outputs.rg_version }} >> "$staging/info/VERSION" - git rev-parse HEAD >> "$staging/info/VERSION" - tar czf "$staging.tar.gz" "$staging" - echo "ASSET=$staging.tar.gz" >> $GITHUB_ENV - - name: Upload release archive - if: startsWith(github.ref, 'refs/tags') - uses: softprops/action-gh-release@v1 - with: - files: | - ${{ env.ASSET }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 133fc9ffc..1a611cb04 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,31 +1,43 @@ -name: Tests +name: Rust + on: - push: - branches: - - master pull_request: + branches: [master, main] + merge_group: + push: + branches: ["master"] + +concurrency: + group: ci-${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always jobs: - tests: - strategy: - matrix: - platform: [ubuntu-latest, macos-latest] - runs-on: ${{ matrix.platform }} + test: + runs-on: ubuntu-latest + steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v3 + - uses: technote-space/get-diff-action@v6.1.2 + with: + PATTERNS: | + **/**.rs + Cargo.lock + - name: Install latest nightly + if: env.GIT_DIFF + uses: actions-rs/toolchain@v1 with: - toolchain: stable - target: wasm32-unknown-unknown + toolchain: 1.69.0 + override: true + components: rustfmt, clippy + + - name: Install wasm32 toolchain + if: env.GIT_DIFF + run: rustup target add wasm32-unknown-unknown - name: Build - env: - IS_GITHUB_ACTION: true - run: cargo +stable build --workspace --target wasm32-unknown-unknown --release + run: sh build.sh + - name: Run Cargo tests - env: - IS_GITHUB_ACTION: true run: cargo test --workspace -- --nocapture - - name: Run Ava tests - env: - IS_GITHUB_ACTION: true - run: cd astra && sh test.sh diff --git a/Cargo.lock b/Cargo.lock index 2bbdfde17..c51d7d7a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,9 +45,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.56" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arrayref" @@ -71,6 +71,7 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" name = "astra" version = "0.1.0" dependencies = [ + "anyhow", "astra-factory", "astra-staking", "hex 0.4.3", @@ -79,6 +80,7 @@ dependencies = [ "near-units", "serde_with", "test-token", + "tokio", "workspaces", ] diff --git a/Cargo.toml b/Cargo.toml index e5a192208..ad57b4c33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [workspace] members = [ - "astra-staking", "astra", + "test-token", "astra-factory", - "test-token" + "astra-staking", ] [workspace.package] diff --git a/astra-factory/src/lib.rs b/astra-factory/src/lib.rs index f3a3719ae..87e8832bb 100644 --- a/astra-factory/src/lib.rs +++ b/astra-factory/src/lib.rs @@ -17,7 +17,7 @@ const FACTORY_OWNER_KEY: &[u8; 5] = b"OWNER"; const CODE_METADATA_KEY: &[u8; 8] = b"METADATA"; // The values used when writing initial data to the storage. -const DAO_CONTRACT_INITIAL_CODE: &[u8] = include_bytes!("../../res/astra.wasm"); +const DAO_CONTRACT_INITIAL_CODE: &[u8] = include_bytes!("../../target/wasm32-unknown-unknown/release/astra.wasm"); const DAO_CONTRACT_INITIAL_VERSION: Version = [3, 0]; const DAO_CONTRACT_NO_DATA: &str = "no data"; @@ -357,7 +357,7 @@ impl AstraFactory { let storage_metadata = env::storage_read(CODE_METADATA_KEY).expect("INTERNAL_FAIL"); let deserialized_metadata: UnorderedMap = BorshDeserialize::try_from_slice(&storage_metadata).expect("INTERNAL_FAIL"); - return deserialized_metadata.to_vec(); + deserialized_metadata.to_vec() } fn assert_owner(&self) { @@ -371,7 +371,7 @@ impl AstraFactory { pub fn slice_to_hash(hash: &[u8]) -> Base58CryptoHash { let mut result: CryptoHash = [0; 32]; - result.copy_from_slice(&hash); + result.copy_from_slice(hash); Base58CryptoHash::from(result) } diff --git a/astra-staking/src/lib.rs b/astra-staking/src/lib.rs index 8754747f4..d67bd1a58 100644 --- a/astra-staking/src/lib.rs +++ b/astra-staking/src/lib.rs @@ -62,7 +62,7 @@ impl Contract { #[init] pub fn new(owner_id: AccountId, token_id: AccountId, unstake_period: U64) -> Self { Self { - owner_id: owner_id.into(), + owner_id, vote_token_id: token_id, users: LookupMap::new(StorageKeys::Users), total_amount: 0, @@ -89,11 +89,11 @@ impl Contract { /// If enough tokens and storage, forwards this to owner account. pub fn delegate(&mut self, account_id: AccountId, amount: U128) -> Promise { let sender_id = env::predecessor_account_id(); - self.internal_delegate(sender_id, account_id.clone().into(), amount.0); + self.internal_delegate(sender_id, account_id.clone(), amount.0); ext_astra::ext(self.owner_id.clone()) .with_static_gas(GAS_FOR_DELEGATE) .delegate( - account_id.into(), + account_id, amount ) } @@ -101,11 +101,11 @@ impl Contract { /// Remove given amount of delegation. pub fn undelegate(&mut self, account_id: AccountId, amount: U128) -> Promise { let sender_id = env::predecessor_account_id(); - self.internal_undelegate(sender_id, account_id.clone().into(), amount.0); + self.internal_undelegate(sender_id, account_id.clone(), amount.0); ext_astra::ext(self.owner_id.clone()) .with_static_gas(GAS_FOR_UNDELEGATE) .undelegate( - account_id.into(), + account_id, amount ) } @@ -198,7 +198,7 @@ mod tests { testing_env!(context.attached_deposit(parse_near!("1 N")).build()); contract.storage_deposit(Some(delegate_from_user.clone()), None); - testing_env!(context.predecessor_account_id(voting_token.clone()).build()); + testing_env!(context.predecessor_account_id(voting_token).build()); contract.ft_on_transfer( delegate_from_user.clone(), U128(parse_near!("100")), diff --git a/astra-staking/src/user.rs b/astra-staking/src/user.rs index 6c75334f8..2e04c4b95 100644 --- a/astra-staking/src/user.rs +++ b/astra-staking/src/user.rs @@ -161,17 +161,17 @@ impl Contract { /// Deposit voting token. pub fn internal_deposit(&mut self, sender_id: &AccountId, amount: Balance) { - let mut sender = self.internal_get_user(&sender_id); + let mut sender = self.internal_get_user(sender_id); sender.deposit(amount); - self.save_user(&sender_id, sender); + self.save_user(sender_id, sender); self.total_amount += amount; } /// Withdraw voting token. pub fn internal_withdraw(&mut self, sender_id: &AccountId, amount: Balance) { - let mut sender = self.internal_get_user(&sender_id); + let mut sender = self.internal_get_user(sender_id); sender.withdraw(amount); - self.save_user(&sender_id, sender); + self.save_user(sender_id, sender); assert!(self.total_amount >= amount, "ERR_INTERNAL"); self.total_amount -= amount; } @@ -186,7 +186,7 @@ impl Contract { ) { let mut sender = self.internal_get_user(&sender_id); assert!(self.users.contains_key(&delegate_id), "ERR_NOT_REGISTERED"); - sender.delegate(delegate_id.clone(), amount); + sender.delegate(delegate_id, amount); self.save_user(&sender_id, sender); } diff --git a/astra/Cargo.toml b/astra/Cargo.toml index 7534d65d0..ccb134261 100644 --- a/astra/Cargo.toml +++ b/astra/Cargo.toml @@ -22,6 +22,8 @@ version = "1.4.0" [dev-dependencies] workspaces.workspace = true near-units.workspace = true +tokio.workspace = true +anyhow.workspace = true test-token = { path = "../test-token" } astra-staking = { path = "../astra-staking" } astra-factory = { path = "../astra-factory" } diff --git a/astra/src/bounties.rs b/astra/src/bounties.rs index ef48bd905..f1b876707 100644 --- a/astra/src/bounties.rs +++ b/astra/src/bounties.rs @@ -87,12 +87,7 @@ impl Contract { } fn internal_find_claim(&self, bounty_id: u64, claims: &[BountyClaim]) -> Option { - for i in 0..claims.len() { - if claims[i].bounty_id == bounty_id { - return Some(i); - } - } - None + (0..claims.len()).find(|&i| claims[i].bounty_id == bounty_id) } } @@ -136,7 +131,7 @@ impl Contract { fn internal_remove_claim(&mut self, bounty_id: u64, claimer_id: &AccountId) { let (mut claims, claim_idx) = self.internal_get_claims(bounty_id, claimer_id); claims.remove(claim_idx); - if claims.len() == 0 { + if claims.is_empty() { self.bounty_claimers.remove(claimer_id); } else { self.bounty_claimers.insert(claimer_id, &claims); @@ -148,7 +143,7 @@ impl Contract { fn internal_get_claims(&mut self, id: u64, sender_id: &AccountId) -> (Vec, usize) { let claims = self .bounty_claimers - .get(&sender_id) + .get(sender_id) .expect("ERR_NO_BOUNTY_CLAIMS"); let claim_idx = self .internal_find_claim(id, &claims) @@ -161,7 +156,7 @@ impl Contract { /// On expired, anyone can call it to free up the claim slot. #[payable] pub fn bounty_done(&mut self, id: u64, account_id: Option, description: String) { - let sender_id = account_id.unwrap_or_else(|| env::predecessor_account_id()); + let sender_id = account_id.unwrap_or_else(env::predecessor_account_id); let (mut claims, claim_idx) = self.internal_get_claims(id, &sender_id); assert!(!claims[claim_idx].completed, "ERR_BOUNTY_CLAIM_COMPLETED"); if env::block_timestamp() > claims[claim_idx].start_time.0 + claims[claim_idx].deadline.0 { @@ -244,7 +239,7 @@ mod tests { testing_env!(context.predecessor_account_id(accounts(1)).build()); let mut contract = Contract::new( Config::test_config(), - VersionedPolicy::Default(vec![accounts(1).into()]), + VersionedPolicy::Default(vec![accounts(1)]), ); add_bounty(&mut context, &mut contract, 2); @@ -307,7 +302,7 @@ mod tests { testing_env!(context.predecessor_account_id(accounts(1)).build()); let mut contract = Contract::new( Config::test_config(), - VersionedPolicy::Default(vec![accounts(1).into()]), + VersionedPolicy::Default(vec![accounts(1)]), ); let id = add_bounty(&mut context, &mut contract, 1); contract.bounty_claim(id, U64::from(500)); diff --git a/astra/src/lib.rs b/astra/src/lib.rs index c6fc75750..50da8a8bf 100644 --- a/astra/src/lib.rs +++ b/astra/src/lib.rs @@ -187,7 +187,7 @@ mod tests { description: "test".to_string(), kind: ProposalKind::Transfer { token_id: String::from(OLD_BASE_TOKEN), - receiver_id: accounts(2).into(), + receiver_id: accounts(2), amount: U128(parse_near!("100 N")), msg: None, }, @@ -200,7 +200,7 @@ mod tests { testing_env!(context.predecessor_account_id(accounts(1)).build()); let mut contract = Contract::new( Config::test_config(), - VersionedPolicy::Default(vec![accounts(1).into()]), + VersionedPolicy::Default(vec![accounts(1)]), ); let id = create_proposal(&mut context, &mut contract); assert_eq!(contract.get_proposal(id).proposal.description, "test"); @@ -232,7 +232,7 @@ mod tests { let _id = contract.add_proposal(ProposalInput { description: "test".to_string(), kind: ProposalKind::AddMemberToRole { - member_id: accounts(2).into(), + member_id: accounts(2), role: "council".to_string(), }, }); @@ -245,7 +245,7 @@ mod tests { testing_env!(context.predecessor_account_id(accounts(1)).build()); let mut contract = Contract::new( Config::test_config(), - VersionedPolicy::Default(vec![accounts(1).into()]), + VersionedPolicy::Default(vec![accounts(1)]), ); let id = create_proposal(&mut context, &mut contract); assert_eq!(contract.get_proposal(id).proposal.description, "test"); @@ -256,7 +256,7 @@ mod tests { fn test_remove_proposal_allowed() { let mut context = VMContextBuilder::new(); testing_env!(context.predecessor_account_id(accounts(1)).build()); - let mut policy = VersionedPolicy::Default(vec![accounts(1).into()]).upgrade(); + let mut policy = VersionedPolicy::Default(vec![accounts(1)]).upgrade(); policy.to_policy_mut().roles[1] .permissions .insert("*:RemoveProposal".to_string()); @@ -273,7 +273,7 @@ mod tests { testing_env!(context.predecessor_account_id(accounts(1)).build()); let mut contract = Contract::new( Config::test_config(), - VersionedPolicy::Default(vec![accounts(1).into()]), + VersionedPolicy::Default(vec![accounts(1)]), ); let id = create_proposal(&mut context, &mut contract); testing_env!(context @@ -289,7 +289,7 @@ mod tests { testing_env!(context.predecessor_account_id(accounts(1)).build()); let mut contract = Contract::new( Config::test_config(), - VersionedPolicy::Default(vec![accounts(1).into(), accounts(2).into()]), + VersionedPolicy::Default(vec![accounts(1), accounts(2)]), ); let id = create_proposal(&mut context, &mut contract); contract.act_proposal(id, Action::VoteApprove, None); @@ -302,13 +302,13 @@ mod tests { testing_env!(context.predecessor_account_id(accounts(1)).build()); let mut contract = Contract::new( Config::test_config(), - VersionedPolicy::Default(vec![accounts(1).into()]), + VersionedPolicy::Default(vec![accounts(1)]), ); testing_env!(context.attached_deposit(parse_near!("1 N")).build()); let id = contract.add_proposal(ProposalInput { description: "test".to_string(), kind: ProposalKind::AddMemberToRole { - member_id: accounts(2).into(), + member_id: accounts(2), role: "missing".to_string(), }, }); @@ -325,7 +325,7 @@ mod tests { testing_env!(context.predecessor_account_id(accounts(1)).build()); let mut contract = Contract::new( Config::test_config(), - VersionedPolicy::Default(vec![accounts(1).into()]), + VersionedPolicy::Default(vec![accounts(1)]), ); testing_env!(context.attached_deposit(parse_near!("1 N")).build()); let _id = contract.add_proposal(ProposalInput { diff --git a/astra/src/policy.rs b/astra/src/policy.rs index 396c1d66d..555665372 100644 --- a/astra/src/policy.rs +++ b/astra/src/policy.rs @@ -371,12 +371,7 @@ impl Policy { } fn internal_get_role(&self, name: &String) -> Option<&RolePermission> { - for role in self.roles.iter() { - if role.name == *name { - return Some(role); - } - } - None + self.roles.iter().find(|&role| role.name == *name) } /// Get proposal status for given proposal. diff --git a/astra/src/proposals.rs b/astra/src/proposals.rs index 2c416c76e..45a240d9b 100644 --- a/astra/src/proposals.rs +++ b/astra/src/proposals.rs @@ -316,13 +316,13 @@ impl Contract { } ProposalKind::AddMemberToRole { member_id, role } => { let mut new_policy = policy.clone(); - new_policy.add_member_to_role(role, &member_id.clone().into()); + new_policy.add_member_to_role(role, &member_id.clone()); self.policy.set(&VersionedPolicy::Current(new_policy)); PromiseOrValue::Value(()) } ProposalKind::RemoveMemberFromRole { member_id, role } => { let mut new_policy = policy.clone(); - new_policy.remove_member_from_role(role, &member_id.clone().into()); + new_policy.remove_member_from_role(role, &member_id.clone()); self.policy.set(&VersionedPolicy::Current(new_policy)); PromiseOrValue::Value(()) } @@ -330,10 +330,10 @@ impl Contract { receiver_id, actions, } => { - let mut promise = Promise::new(receiver_id.clone().into()); + let mut promise = Promise::new(receiver_id.clone()); for action in actions { promise = promise.function_call( - action.method_name.clone().into(), + action.method_name.clone(), action.args.clone().into(), action.deposit.0, Gas(action.gas.0), @@ -342,7 +342,7 @@ impl Contract { promise.into() } ProposalKind::UpgradeSelf { hash } => { - upgrade_using_factory(hash.clone()); + upgrade_using_factory(*hash); PromiseOrValue::Value(()) } ProposalKind::UpgradeRemote { @@ -350,7 +350,7 @@ impl Contract { method_name, hash, } => { - upgrade_remote(&receiver_id, method_name, &CryptoHash::from(hash.clone())); + upgrade_remote(receiver_id, method_name, &CryptoHash::from(*hash)); PromiseOrValue::Value(()) } ProposalKind::Transfer { @@ -360,14 +360,14 @@ impl Contract { msg, } => self.internal_payout( &convert_old_to_new_token(token_id), - &receiver_id, + receiver_id, amount.0, proposal.description.clone(), msg.clone(), ), ProposalKind::SetStakingContract { staking_id } => { assert!(self.staking_id.is_none(), "ERR_INVALID_STAKING_CHANGE"); - self.staking_id = Some(staking_id.clone().into()); + self.staking_id = Some(staking_id.clone()); PromiseOrValue::Value(()) } ProposalKind::AddBounty { bounty } => { @@ -377,7 +377,7 @@ impl Contract { ProposalKind::BountyDone { bounty_id, receiver_id, - } => self.internal_execute_bounty_payout(*bounty_id, &receiver_id.clone().into(), true), + } => self.internal_execute_bounty_payout(*bounty_id, &receiver_id.clone(), true), ProposalKind::Vote => PromiseOrValue::Value(()), ProposalKind::FactoryInfoUpdate { factory_info } => { internal_set_factory_info(factory_info); @@ -417,7 +417,7 @@ impl Contract { proposal_id )) .into(), - PromiseOrValue::Value(()) => self.internal_return_bonds(&policy, &proposal).into(), + PromiseOrValue::Value(()) => self.internal_return_bonds(policy, proposal).into(), } } @@ -437,7 +437,7 @@ impl Contract { } } proposal.status = ProposalStatus::Approved; - self.internal_return_bonds(&policy, &proposal).into() + self.internal_return_bonds(&policy, proposal).into() } pub(crate) fn internal_callback_proposal_fail( @@ -464,7 +464,7 @@ impl Contract { bounty_id, receiver_id, } => { - self.internal_execute_bounty_payout(*bounty_id, &receiver_id.clone().into(), false) + self.internal_execute_bounty_payout(*bounty_id, &receiver_id.clone(), false) } _ => PromiseOrValue::Value(()), } @@ -502,7 +502,7 @@ impl Contract { }, ProposalKind::Transfer { token_id, msg, .. } => { assert!( - !(token_id == OLD_BASE_TOKEN) || msg.is_none(), + token_id != OLD_BASE_TOKEN || msg.is_none(), "ERR_BASE_TOKEN_NO_MSG" ); } @@ -641,7 +641,7 @@ impl Contract { PromiseResult::Failed => self.internal_callback_proposal_fail(&mut proposal), }; self.proposals - .insert(&proposal_id, &VersionedProposal::Default(proposal.into())); + .insert(&proposal_id, &VersionedProposal::Default(proposal)); result } } diff --git a/astra/src/views.rs b/astra/src/views.rs index ee7ebb759..bdf5c925f 100644 --- a/astra/src/views.rs +++ b/astra/src/views.rs @@ -33,12 +33,12 @@ impl Contract { /// Returns config of this contract. pub fn get_config(&self) -> Config { - self.config.get().unwrap().clone() + self.config.get().unwrap() } /// Returns policy of this contract. pub fn get_policy(&self) -> Policy { - self.policy.get().unwrap().to_policy().clone() + self.policy.get().unwrap().to_policy() } /// Returns staking contract if available. Otherwise returns empty. diff --git a/astra/tests-ava/__tests__/lib.ava.ts b/astra/tests-ava/__tests__/lib.ava.ts index 5d9a4c7a3..1baeb3984 100644 --- a/astra/tests-ava/__tests__/lib.ava.ts +++ b/astra/tests-ava/__tests__/lib.ava.ts @@ -28,7 +28,7 @@ import { } from './utils'; import * as fs from 'fs'; -const DAO_WASM_BYTES: Uint8Array = fs.readFileSync('../res/astra.wasm'); +const DAO_WASM_BYTES: Uint8Array = fs.readFileSync('../../res/astra.wasm'); workspaceWithFactory.test( 'Upgrade self using factory', diff --git a/astra/tests-ava/__tests__/utils.ts b/astra/tests-ava/__tests__/utils.ts index 37d5b5056..2c8225348 100644 --- a/astra/tests-ava/__tests__/utils.ts +++ b/astra/tests-ava/__tests__/utils.ts @@ -8,7 +8,7 @@ async function initWorkspace(root: NearAccount) { const policy = [root.accountId]; //for short let's call it just dao - const dao = await root.createAndDeploy('dao', '../res/astra.wasm', { + const dao = await root.createAndDeploy('dao', '../../res/astra.wasm', { method: 'new', args: { config, policy }, initialBalance: toYocto('200'), @@ -29,7 +29,7 @@ export const workspaceWithoutInit = Workspace.init(async ({ root }) => { const alice = await root.createAccount('alice'); //for short let's call it just dao - const dao = await root.createAndDeploy('dao', '../res/astra.wasm', { + const dao = await root.createAndDeploy('dao', '../../res/astra.wasm', { initialBalance: toYocto('200'), }); return { alice, dao }; @@ -38,7 +38,7 @@ export const workspaceWithoutInit = Workspace.init(async ({ root }) => { export const workspaceWithFactory = Workspace.init(async ({ root }) => { const factory = await root.createAndDeploy( 'factory', - '../../astra-factory/res/astra_factory.wasm', + '../../res/astra_factory.wasm', { initialBalance: toYocto('500'), }, @@ -50,7 +50,7 @@ export const workspaceWithFactory = Workspace.init(async ({ root }) => { export async function initTestToken(root: NearAccount) { const testToken = await root.createAndDeploy( 'test-token', - '../../test-token/res/test_token.wasm', + '../../res/test_token.wasm', { method: 'new', initialBalance: toYocto('200'), @@ -66,7 +66,7 @@ export async function initStaking( ) { const staking = await root.createAndDeploy( 'staking', - '../../astra-staking/res/astra_staking.wasm', + '../../res/astra_staking.wasm', { method: 'new', args: { diff --git a/astra/tests-ava/__tests__/views.ava.ts b/astra/tests-ava/__tests__/views.ava.ts index 3c895be1f..2c74b730e 100644 --- a/astra/tests-ava/__tests__/views.ava.ts +++ b/astra/tests-ava/__tests__/views.ava.ts @@ -38,7 +38,7 @@ workspace.test('View method get_config', async (test, { root }) => { }; const policy = [root.accountId]; - const bob = await root.createAndDeploy('bob', '../res/astra.wasm', { + const bob = await root.createAndDeploy('bob', '../../res/astra.wasm', { method: 'new', args: { config, policy }, initialBalance: toYocto('200'), @@ -54,7 +54,7 @@ workspace.test('View method get_policy', async (test, { root }) => { }; const versionedPolicy = [root.accountId]; - const bob = await root.createAndDeploy('bob', '../res/astra.wasm', { + const bob = await root.createAndDeploy('bob', '../../res/astra.wasm', { method: 'new', args: { config, policy: versionedPolicy }, initialBalance: toYocto('200'), @@ -111,7 +111,7 @@ workspace.test( workspace.test('View has_blob', async (test, { alice, root, dao }) => { const DAO_WASM_BYTES: Uint8Array = fs.readFileSync( - '../res/astra.wasm', + '../../res/astra.wasm', ); const hash: String = await root.call(dao, 'store_blob', DAO_WASM_BYTES, { attachedDeposit: toYocto('200'), diff --git a/astra/tests/test_general.rs b/astra/tests/test_general.rs index c78e2f517..645fae6bb 100644 --- a/astra/tests/test_general.rs +++ b/astra/tests/test_general.rs @@ -1,9 +1,8 @@ use std::collections::HashMap; -use near_sdk::json_types::U128; +use near_sdk::json_types::{U128, Base64VecU8, U64}; use near_sdk::serde_json::json; -use near_sdk::{env, AccountId}; -use near_sdk_sim::{call, init_simulator, to_yocto, view}; +use near_sdk::{env, AccountId, ONE_NEAR}; use crate::utils::*; use astra_staking::User; @@ -16,30 +15,29 @@ use astra::{ mod utils; fn user(id: u32) -> AccountId { - format!("user{}", id).parse().unwrap() + format!("user{}.test.near", id).parse().unwrap() } -#[test] -fn test_large_policy() { - let root = init_simulator(None); - let factory = setup_factory(&root); - factory - .user_account - .call( - factory.user_account.account_id.clone(), - "new", - &[], - near_sdk_sim::DEFAULT_GAS, - 0, - ) - .assert_success(); +#[tokio::test] +async fn test_large_policy() -> anyhow::Result<()> { + let worker = workspaces::sandbox().await?; + let factory_contract = worker.dev_deploy(include_bytes!("../../res/astra_factory.wasm")).await?; + let root = worker.dev_create_account().await?; + // initialize contract + let res1 = factory_contract + .call("new") + .max_gas() + .transact(); + + assert!(res1.await?.is_success()); let config = Config { name: "testdao".to_string(), purpose: "to test".to_string(), metadata: Base64VecU8(vec![]), }; - let mut policy = default_policy(vec![root.account_id()]); + let root_near_account: AccountId = root.id().parse().unwrap(); + let mut policy = default_policy(vec![root_near_account]); const NO_OF_COUNCILS: u32 = 10; const USERS_PER_COUNCIL: u32 = 100; for council_no in 0..NO_OF_COUNCILS { @@ -71,30 +69,38 @@ fn test_large_policy() { .to_string() .into_bytes(); - call!( - root, - factory.create( - AccountId::new_unchecked("testdao".to_string()), - Base64VecU8(params) - ), - deposit = to_yocto("10") - ) - .assert_success(); - - let dao_account_id = AccountId::new_unchecked("testdao.factory".to_string()); - let dao_list = factory - .user_account - .view(factory.user_account.account_id.clone(), "get_dao_list", &[]) - .unwrap_json::>(); + let res2 = factory_contract + .call("create") + .args_json((AccountId::new_unchecked("testdao".to_string()), Base64VecU8(params))) + .max_gas() + .deposit(ONE_NEAR * 10) + .transact() + .await?; + assert!(res2.is_success()); + + let dao_account_id = AccountId::new_unchecked("testdao.".to_string() + factory_contract.id()); + + let dao_list: Vec= factory_contract + .call("get_dao_list") + .view() + .await? + .json()?; assert_eq!(dao_list, vec![dao_account_id.clone()]); + + Ok(()) } -#[test] -fn test_multi_council() { - let (root, dao) = setup_dao(); - let user1 = root.create_user(user(1), to_yocto("1000")); - let user2 = root.create_user(user(2), to_yocto("1000")); - let user3 = root.create_user(user(3), to_yocto("1000")); +#[tokio::test] +async fn test_multi_council() -> anyhow::Result<()> { + let (root, dao_contract, worker) = setup_dao().await?; + + let user1 = gen_user_account(&worker, user(1).as_str()).await?; + let _ = transfer_near(&worker, user1.id(), ONE_NEAR * 50).await?; + let user2 = gen_user_account(&worker, user(2).as_str()).await?; + let _ = transfer_near(&worker, user2.id(), ONE_NEAR * 50).await?; + let user3 = gen_user_account(&worker, user(3).as_str()).await?; + let _ = transfer_near(&worker, user3.id(), ONE_NEAR * 50).await?; + let new_policy = Policy { roles: vec![ RolePermission { @@ -122,385 +128,542 @@ fn test_multi_council() { bounty_bond: U128(10u128.pow(24)), bounty_forgiveness_period: U64::from(1_000_000_000 * 60 * 60 * 24), }; - add_proposal( - &root, - &dao, - ProposalInput { - description: "new policy".to_string(), - kind: ProposalKind::ChangePolicy { - policy: VersionedPolicy::Current(new_policy.clone()), - }, + + let proposal = ProposalInput { + description: "new policy".to_string(), + kind: ProposalKind::ChangePolicy { + policy: VersionedPolicy::Current(new_policy.clone()), }, - ) - .assert_success(); - vote(vec![&root], &dao, 0); - assert_eq!(view!(dao.get_policy()).unwrap_json::(), new_policy); - add_transfer_proposal(&root, &dao, base_token(), user(1), 1_000_000, None).assert_success(); - vote(vec![&user2], &dao, 1); - vote(vec![&user3], &dao, 1); - let proposal = view!(dao.get_proposal(1)).unwrap_json::(); + }; + let res2 = root.call(dao_contract.id(), "add_proposal") + .args_json(json!({"proposal": proposal})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res2.is_success(), "{:?}", res2); + + vote(vec![root.clone()], &dao_contract, 0).await?; + + let policy: Policy = dao_contract.call("get_policy").view().await?.json()?; + assert_eq!(policy, new_policy); + add_transfer_proposal(root.clone(), &dao_contract, base_token(), user(1), 1_000_000, None).await?; + + vote(vec![user2], &dao_contract, 1).await?; + vote(vec![user3], &dao_contract, 1).await?; + let proposal: Proposal = dao_contract.call("get_proposal").args_json(json!({"id":1})).view().await?.json()?; // Votes from members in different councils. assert_eq!(proposal.status, ProposalStatus::InProgress); // Finish with vote that is in both councils, which approves the proposal. - vote(vec![&user1], &dao, 1); - let proposal = view!(dao.get_proposal(1)).unwrap_json::(); + vote(vec![user1], &dao_contract, 1).await?; + let proposal: Proposal = dao_contract.call("get_proposal").args_json(json!({"id":1})).view().await?.json()?; assert_eq!(proposal.status, ProposalStatus::Approved); + + Ok(()) } -#[test] -fn test_bounty_workflow() { - let (root, dao) = setup_dao(); - let user1 = root.create_user(user(1), to_yocto("1000")); - let user2 = root.create_user(user(2), to_yocto("1000")); +#[tokio::test] +async fn test_bounty_workflow() -> anyhow::Result<()> { + let (root, dao_contract, worker) = setup_dao().await?; + + let user1 = gen_user_account(&worker, user(1).as_str()).await?; + let _ = transfer_near(&worker, user1.id(), ONE_NEAR * 900).await?; + let user2 = gen_user_account(&worker, user(2).as_str()).await?; + let _ = transfer_near(&worker, user2.id(), ONE_NEAR * 900).await?; - let mut proposal_id = add_bounty_proposal(&root, &dao).unwrap_json::(); + let mut proposal_id = add_bounty_proposal(root.clone(), &dao_contract).await?; assert_eq!(proposal_id, 0); - call!( - root, - dao.act_proposal(proposal_id, Action::VoteApprove, None) - ) - .assert_success(); - let bounty_id = view!(dao.get_last_bounty_id()).unwrap_json::() - 1; + vote(vec![root.clone()], &dao_contract, proposal_id).await?; + + let mut bounty_id: u64 = dao_contract.call("get_last_bounty_id").view().await?.json()?; + bounty_id -= 1u64; + assert_eq!(bounty_id, 0); + let bounty: BountyOutput = dao_contract.call("get_bounty") + .args_json(json!({"id": bounty_id})).view().await?.json()?; assert_eq!( - view!(dao.get_bounty(bounty_id)) - .unwrap_json::() - .bounty - .times, + bounty.bounty.times, 3 ); - assert_eq!(to_yocto("1000"), user1.account().unwrap().amount); - call!( - user1, - dao.bounty_claim(bounty_id, U64::from(0)), - deposit = to_yocto("1") - ) - .assert_success(); - assert!(user1.account().unwrap().amount < to_yocto("999")); + assert_eq!(ONE_NEAR * 1000, user1.view_account().await?.balance); + + let res = user1 + .call(dao_contract.id(), "bounty_claim") + .args_json(json!({"id": bounty_id, "deadline": U64::from(0)})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + assert!(user1.view_account().await?.balance < ONE_NEAR * 999); + + let bounty_claim: Vec = dao_contract.call("get_bounty_claims") + .args_json(json!({"account_id": user1.id()})).view().await?.json()?; assert_eq!( - view!(dao.get_bounty_claims(user1.account_id())) - .unwrap_json::>() - .len(), + bounty_claim.len(), 1 ); + + let bounty_claim: u64 = dao_contract.call("get_bounty_number_of_claims") + .args_json(json!({"id": bounty_id})).view().await?.json()?; assert_eq!( - view!(dao.get_bounty_number_of_claims(bounty_id)).unwrap_json::(), + bounty_claim, 1 ); - call!(user1, dao.bounty_giveup(bounty_id)).assert_success(); - assert!(user1.account().unwrap().amount > to_yocto("999")); + let res = user1 + .call(dao_contract.id(), "bounty_giveup") + .args_json(json!({"id": bounty_id})) + .max_gas() + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + assert!(user1.view_account().await?.balance > ONE_NEAR * 999); + + let bounty_claim: Vec = dao_contract.call("get_bounty_claims") + .args_json(json!({"account_id": user1.id()})).view().await?.json()?; assert_eq!( - view!(dao.get_bounty_claims(user1.account_id())) - .unwrap_json::>() - .len(), + bounty_claim.len(), 0 ); + let bounty_claim_number: u64 = dao_contract.call("get_bounty_number_of_claims") + .args_json(json!({"id": bounty_id})).view().await?.json()?; assert_eq!( - view!(dao.get_bounty_number_of_claims(bounty_id)).unwrap_json::(), + bounty_claim_number, 0 ); - assert_eq!(to_yocto("1000"), user2.account().unwrap().amount); - call!( - user2, - dao.bounty_claim(bounty_id, U64(env::block_timestamp() + 5_000_000_000)), - deposit = to_yocto("1") - ) - .assert_success(); - assert!(user2.account().unwrap().amount < to_yocto("999")); + assert_eq!(ONE_NEAR * 1000, user2.view_account().await?.balance); + + let res = user2 + .call(dao_contract.id(), "bounty_claim") + .args_json(json!({"id": bounty_id, "deadline": U64(env::block_timestamp() + 5_000_000_000)})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + assert!(user2.view_account().await?.balance < ONE_NEAR * 999); + + let bounty_claim: Vec = dao_contract.call("get_bounty_claims") + .args_json(json!({"account_id": user2.id()})).view().await?.json()?; assert_eq!( - view!(dao.get_bounty_claims(user2.account_id())) - .unwrap_json::>() - .len(), + bounty_claim.len(), 1 ); + let bounty_claim_number: u64 = dao_contract.call("get_bounty_number_of_claims") + .args_json(json!({"id": bounty_id})).view().await?.json()?; assert_eq!( - view!(dao.get_bounty_number_of_claims(bounty_id)).unwrap_json::(), + bounty_claim_number, 1 ); - call!( - user2, - dao.bounty_done(bounty_id, None, "Bounty is done".to_string()), - deposit = to_yocto("1") - ) - .assert_success(); - assert!(user2.account().unwrap().amount < to_yocto("998")); - proposal_id = view!(dao.get_last_proposal_id()).unwrap_json::() - 1; + let res = user2 + .call(dao_contract.id(), "bounty_done") + .args_json(json!({"id": bounty_id, "description": "Bounty is done".to_string()})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + assert!(user2.view_account().await?.balance < ONE_NEAR * 998); + + let latest_prop_id: u64 = dao_contract.call("get_last_proposal_id").view().await?.json()?; + proposal_id = latest_prop_id - 1u64; assert_eq!(proposal_id, 1); + + let prop_out: ProposalOutput = dao_contract.call("get_proposal") + .args_json(json!({"id": proposal_id})).view().await?.json()?; assert_eq!( - view!(dao.get_proposal(proposal_id)) - .unwrap_json::() + prop_out .proposal .kind .to_policy_label(), "bounty_done" ); - call!( - root, - dao.act_proposal(proposal_id, Action::VoteApprove, None) - ) - .assert_success(); - assert!(user2.account().unwrap().amount > to_yocto("999")); + vote(vec![root.clone()], &dao_contract, proposal_id).await?; + + assert!(user2.view_account().await?.balance > ONE_NEAR * 999); + + let bounty_claim: Vec = dao_contract.call("get_bounty_claims") + .args_json(json!({"account_id": user2.id()})).view().await?.json()?; assert_eq!( - view!(dao.get_bounty_claims(user2.account_id())) - .unwrap_json::>() - .len(), + bounty_claim.len(), 0 ); + let bounty_claim_number: u64 = dao_contract.call("get_bounty_number_of_claims") + .args_json(json!({"id": bounty_id})).view().await?.json()?; assert_eq!( - view!(dao.get_bounty_number_of_claims(bounty_id)).unwrap_json::(), + bounty_claim_number, 0 ); + let bounty: BountyOutput = dao_contract.call("get_bounty") + .args_json(json!({"id": bounty_id})).view().await?.json()?; assert_eq!( - view!(dao.get_bounty(bounty_id)) - .unwrap_json::() - .bounty - .times, + bounty.bounty.times, 2 ); + + Ok(()) } -#[test] -fn test_create_dao_and_use_token() { - let (root, dao) = setup_dao(); - let user2 = root.create_user(user(2), to_yocto("1000")); - let user3 = root.create_user(user(3), to_yocto("1000")); - let test_token = setup_test_token(&root); - let staking = setup_staking(&root); - - assert!(view!(dao.get_staking_contract()) - .unwrap_json::() - .is_empty()); - add_member_proposal(&root, &dao, user2.account_id.clone()).assert_success(); - assert_eq!(view!(dao.get_last_proposal_id()).unwrap_json::(), 1); - // Voting by user who is not member should fail. - should_fail(call!(user2, dao.act_proposal(0, Action::VoteApprove, None))); - call!(root, dao.act_proposal(0, Action::VoteApprove, None)).assert_success(); - // voting second time should fail. - should_fail(call!(root, dao.act_proposal(0, Action::VoteApprove, None))); +#[tokio::test] +async fn proposal_tests() -> anyhow::Result<()> { + let (root, dao, worker) = setup_dao().await?; + let user2 = gen_user_account(&worker, user(2).as_str()).await?; + let _ = transfer_near(&worker, user2.id(), ONE_NEAR * 900).await?; + let user3 = gen_user_account(&worker, user(3).as_str()).await?; + let _ = transfer_near(&worker, user3.id(), ONE_NEAR * 900).await?; + let user2_near_account: AccountId = user2.id().parse().unwrap(); + add_member_proposal(root.clone(), &dao, user2_near_account).await?; + vote(vec![root.clone()], &dao.clone(), 0).await?; + + let config = Config { name: "astra".to_string(), purpose: "testing".to_string(), metadata: Base64VecU8("".to_string().into()) }; + let proposal = ProposalInput { + description: "rename the dao".to_string(), + kind: ProposalKind::ChangeConfig { config } + }; + let res = root + .call(dao.id(), "add_proposal") + .args_json(json!({"proposal": proposal})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + vote(vec![root.clone(), user2.clone()], &dao.clone(), 1).await?; + + let last_prop: u64 = dao.call("get_last_proposal_id").view().await?.json()?; + assert_eq!(last_prop, 2); + + let prop: Proposal = dao.call("get_proposal").args_json(json!({"id": 1})).view().await?.json()?; + assert_eq!( + prop.status, + ProposalStatus::Approved + ); + + Ok(()) +} + +#[tokio::test] +async fn test_create_dao_and_use_token() -> anyhow::Result<()> { + let (root, dao, worker) = setup_dao().await?; + let user2 = gen_user_account(&worker, user(2).as_str()).await?; + let _ = transfer_near(&worker, user2.id(), ONE_NEAR * 900).await?; + let user3 = gen_user_account(&worker, user(3).as_str()).await?; + let _ = transfer_near(&worker, user3.id(), ONE_NEAR * 900).await?; + let (test_token, worker) = setup_test_token(worker).await?; + let (staking, _) = setup_staking(test_token.id().clone(), dao.id().clone(), worker).await?; + + let staking_contract: String = dao.call("get_staking_contract").view().await?.json()?; + assert!(staking_contract.is_empty()); + + let user2_near_account: AccountId = user2.id().parse().unwrap(); + add_member_proposal(root.clone(), &dao, user2_near_account).await?; + + let last_prop: u64 = dao.call("get_last_proposal_id").view().await?.json()?; + assert_eq!(last_prop, 1); + + // Voting by user who is not a member should fail. + let res = user2.clone() + .call(dao.id(), "act_proposal") + .args_json(json!({"id": 0, "action": Action::VoteApprove})) + .max_gas() + .transact() + .await?; + assert!(res.is_failure(), "{:?}", res); + let res = root.clone() + .call(dao.id(), "act_proposal") + .args_json(json!({"id": 0, "action": Action::VoteApprove})) + .max_gas() + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + // Voting second time on the same proposal should fail. + let res = root.clone() + .call(dao.id(), "act_proposal") + .args_json(json!({"id": 0, "action": Action::VoteApprove})) + .max_gas() + .transact() + .await?; + assert!(res.is_failure(), "{:?}", res); + // Add 3rd member. - add_member_proposal(&user2, &dao, user3.account_id.clone()).assert_success(); - vote(vec![&root, &user2], &dao, 1); - let policy = view!(dao.get_policy()).unwrap_json::(); + let user3_near_account: AccountId = user3.id().parse().unwrap(); + + add_member_proposal(user2.clone(), &dao.clone(), user3_near_account).await?; + vote(vec![root.clone(), user2.clone()], &dao.clone(), 1).await?; + let policy: Policy = dao.call("get_policy").view().await?.json()?; assert_eq!(policy.roles.len(), 2); assert_eq!( policy.roles[1].kind, RoleKind::Group( vec![ - root.account_id.clone(), - user2.account_id.clone(), - user3.account_id.clone() + root.id().parse().unwrap(), + user2.id().parse().unwrap(), + user3.id().parse().unwrap() ] .into_iter() .collect() ) ); - add_proposal( - &user2, - &dao, - ProposalInput { - description: "test".to_string(), - kind: ProposalKind::SetStakingContract { - staking_id: "staking".parse().unwrap(), - }, + + let proposal = ProposalInput { + description: "test".to_string(), + kind: ProposalKind::SetStakingContract { + staking_id: staking.id().parse().unwrap(), }, - ) - .assert_success(); - vote(vec![&user3, &user2], &dao, 2); - assert!(!view!(dao.get_staking_contract()) - .unwrap_json::() - .is_empty()); + }; + let res = user2 + .call(dao.id(), "add_proposal") + .args_json(json!({"proposal": proposal})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + vote(vec![user3.clone(), user2.clone()], &dao, 2).await?; + + let staking_contract: String = dao.call("get_staking_contract").view().await?.json()?; + assert!(!staking_contract.is_empty()); + + let prop: Proposal = dao.call("get_proposal").args_json(json!({"id": 2})).view().await?.json()?; assert_eq!( - view!(dao.get_proposal(2)).unwrap_json::().status, + prop.status, ProposalStatus::Approved ); - staking - .user_account - .view_method_call(staking.contract.ft_total_supply()); + let supply: U128 = staking.call("ft_total_supply").view().await?.json()?; assert_eq!( - view!(staking.ft_total_supply()).unwrap_json::().0, - to_yocto("0") - ); - call!( - user2, - test_token.mint(user2.account_id.clone(), U128(to_yocto("100"))) - ) - .assert_success(); - call!( - user2, - test_token.storage_deposit(Some(staking.account_id()), None), - deposit = to_yocto("1") - ) - .assert_success(); - call!( - user2, - staking.storage_deposit(None, None), - deposit = to_yocto("1") + supply.0, + 0 ); - call!( - user2, - test_token.ft_transfer_call( - staking.account_id(), - U128(to_yocto("10")), - None, - "".to_string() - ), - deposit = 1 - ) - .assert_success(); + + let res = user2 + .call(test_token.id(), "mint") + .args_json(json!({"account_id": user2.id(), "amount": U128(100 * ONE_NEAR)})) + .max_gas() + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + let res = user2 + .call(test_token.id(), "storage_deposit") + .args_json(json!({"account_id": staking.id()})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + let res = user2 + .call(staking.id(), "storage_deposit") + .args_json(json!({})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + let res = user2 + .call(test_token.id(), "ft_transfer_call") + .args_json(json!({"receiver_id": staking.id(), "amount": U128(10 * ONE_NEAR), "msg": "".to_string()})) + .max_gas() + .deposit(1) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + let supply: U128 = staking.call("ft_total_supply").view().await?.json()?; assert_eq!( - view!(staking.ft_total_supply()).unwrap_json::().0, - to_yocto("10") + supply.0, + 10 * ONE_NEAR ); - let user2_id = user2.account_id.clone(); + + let bal: U128 = staking.call("ft_balance_of").args_json(json!({"account_id": user2.id()})).view().await?.json()?; assert_eq!( - view!(staking.ft_balance_of(user2_id.clone())) - .unwrap_json::() - .0, - to_yocto("10") + bal.0, + 10 * ONE_NEAR ); + + let bal: U128 = test_token.call("ft_balance_of").args_json(json!({"account_id": user2.id()})).view().await?.json()?; assert_eq!( - view!(test_token.ft_balance_of(user2_id.clone())) - .unwrap_json::() - .0, - to_yocto("90") + bal.0, + 90 * ONE_NEAR ); - call!(user2, staking.withdraw(U128(to_yocto("5")))).assert_success(); + let res = user2 + .call(staking.id(), "withdraw") + .args_json(json!({"amount": U128(5 * ONE_NEAR)})) + .max_gas() + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + let supply: U128 = staking.call("ft_total_supply").view().await?.json()?; assert_eq!( - view!(staking.ft_total_supply()).unwrap_json::().0, - to_yocto("5") + supply.0, + 5 * ONE_NEAR ); + let bal: U128 = test_token.call("ft_balance_of").args_json(json!({"account_id": user2.id()})).view().await?.json()?; assert_eq!( - view!(test_token.ft_balance_of(user2_id.clone())) - .unwrap_json::() - .0, - to_yocto("95") + bal.0, + 95 * ONE_NEAR ); - call!( - user2, - staking.delegate(user2_id.clone(), U128(to_yocto("5"))) - ) - .assert_success(); - call!( - user2, - staking.undelegate(user2_id.clone(), U128(to_yocto("1"))) - ) - .assert_success(); - // should fail right after undelegation as need to wait for voting period before can delegate again. - should_fail(call!( - user2, - staking.delegate(user2_id.clone(), U128(to_yocto("1"))) - )); - let user = view!(staking.get_user(user2_id.clone())).unwrap_json::(); + + let res = user2 + .call(staking.id(), "delegate") + .args_json(json!({"account_id": user2.id(), "amount": U128(5 * ONE_NEAR)})) + .max_gas() + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + let res = user2 + .call(staking.id(), "undelegate") + .args_json(json!({"account_id": user2.id(), "amount": U128(ONE_NEAR)})) + .max_gas() + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + // should fail right after undelegation as user needs to wait for voting period before they can delegate again. + let res = user2 + .call(staking.id(), "delegate") + .args_json(json!({"account_id": user2.id(), "amount": U128(ONE_NEAR)})) + .max_gas() + .transact() + .await?; + assert!(res.is_failure(), "{:?}", res); + let user: User = staking.call("get_user").args_json(json!({"account_id": user2.id()})).view().await?.json()?; assert_eq!( user.delegated_amounts, - vec![(user2_id.clone(), U128(to_yocto("4")))] + vec![(user2.id().parse().unwrap(), U128(4 * ONE_NEAR))] ); + + let supply: U128 = dao.call("delegation_total_supply").view().await?.json()?; assert_eq!( - view!(dao.delegation_total_supply()).unwrap_json::().0, - to_yocto("4") + supply.0, + 4 * ONE_NEAR ); + let bal: U128 = dao.call("delegation_balance_of").args_json(json!({"account_id": user2.id()})).view().await?.json()?; assert_eq!( - view!(dao.delegation_balance_of(user2_id.clone())) - .unwrap_json::() - .0, - to_yocto("4") + bal.0, + 4 * ONE_NEAR ); + + Ok(()) } /// Test various cases that must fail. -#[test] -fn test_failures() { - let (root, dao) = setup_dao(); - should_fail(add_transfer_proposal( - &root, - &dao, - base_token(), - user(1), - 1_000_000, - Some("some".to_string()), - )); +#[tokio::test] +async fn test_failurestest_create_dao_and_use_token() -> anyhow::Result<()> { + let (root, dao, _) = setup_dao().await?; + + let proposal = ProposalInput { + description: "test".to_string(), + kind: ProposalKind::Transfer { + token_id: convert_new_to_old_token(base_token()), + receiver_id: user(1), + amount: U128(1_000_000), + msg: Some("some".to_string()), + }, + }; + let res = root + .call(dao.id(), "add_proposal") + .args_json(json!({"proposal": proposal})) + .max_gas() + .transact() + .await?; + assert!(res.is_failure(), "{:?}", res); + + Ok(()) } -/// Test payments that fail -#[test] -fn test_payment_failures() { - let (root, dao) = setup_dao(); - let user1 = root.create_user(user(1), to_yocto("1000")); - let whale = root.create_user(user(2), to_yocto("1000")); +// Test payments that fail +#[tokio::test] +async fn test_payment_failures() -> anyhow::Result<()> { + let (root, dao, worker) = setup_dao().await?; + let user1 = gen_user_account(&worker, user(1).as_str()).await?; + let _ = transfer_near(&worker, user1.id(), ONE_NEAR * 900).await?; + let whale = gen_user_account(&worker, user(2).as_str()).await?; + let _ = transfer_near(&worker, whale.id(), ONE_NEAR * 900).await?; // Add user1 - add_member_proposal(&root, &dao, user1.account_id.clone()).assert_success(); - vote(vec![&root], &dao, 0); + add_member_proposal(root.clone(), &dao, user1.id().parse().unwrap()).await?; + vote(vec![root.clone()], &dao, 0).await?; // Set up fungible tokens and give 5 to the dao - let test_token = setup_test_token(&root); - call!( - dao.user_account, - test_token.mint(dao.user_account.account_id.clone(), U128(5)) - ) - .assert_success(); - call!( - user1, - test_token.storage_deposit(Some(user1.account_id.clone()), Some(true)), - deposit = to_yocto("125") - ) - .assert_success(); + let (test_token, _) = setup_test_token(worker).await?; + let res = dao.as_account() + .call(test_token.id(), "mint") + .args_json(json!({"account_id": dao.id(), "amount": U128(5)})) + .max_gas() + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + let res = user1 + .call(test_token.id(), "storage_deposit") + .args_json(json!({"account_id": user1.id(), "registration_only": true})) + .max_gas() + .deposit(ONE_NEAR * 125) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); // Attempt to transfer more than it has add_transfer_proposal( - &root, + root.clone(), &dao, - Some(test_token.account_id()), + Some(test_token.id().parse().unwrap()), user(1), 10, None, - ) - .assert_success(); + ).await?; // Vote in the transfer - vote(vec![&root, &user1], &dao, 1); - let mut proposal = view!(dao.get_proposal(1)).unwrap_json::(); + vote(vec![root.clone(), user1.clone()], &dao, 1).await?; + let mut proposal: Proposal = dao.call("get_proposal").args_json(json!({"id": 1})).view().await?.json()?; + assert_eq!(proposal.status, ProposalStatus::Failed); // Set up benefactor whale who will donate the needed tokens - call!( - whale, - test_token.mint(whale.account_id.clone(), U128(6_000_000_000)) - ) - .assert_success(); - call!( - whale, - test_token.ft_transfer( - dao.account_id(), - U128::from(1000), - Some("Heard you're in a pinch, let me help.".to_string()) - ), - deposit = 1 - ) - .assert_success(); + let res = whale + .call(test_token.id(), "mint") + .args_json(json!({"account_id": whale.id(), "amount": U128(6_000_000_000)})) + .max_gas() + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + let res = whale + .call(test_token.id(), "ft_transfer") + .args_json(json!({"receiver_id": dao.id(), "amount": U128::from(1000), "msg": "Heard you're in a pinch, let me help.".to_string()})) + .max_gas() + .deposit(1) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); // Council member retries payment via an action - call!( - root, - dao.act_proposal( - 1, - Action::Finalize, - Some("Sorry! We topped up our tokens. Thanks.".to_string()) - ) - ) - .assert_success(); + let res = root + .call(dao.id(), "act_proposal") + .args_json(json!({"id": 1, "action": Action::Finalize, "msg": "Sorry! We topped up our tokens. Thanks.".to_string()})) + .max_gas() + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); - proposal = view!(dao.get_proposal(1)).unwrap_json::(); + proposal = dao.call("get_proposal").args_json(json!({"id": 1})).view().await?.json()?; assert_eq!( proposal.status, ProposalStatus::Approved, "Did not return to approved status." ); + + Ok(()) } diff --git a/astra/tests/test_upgrade.rs b/astra/tests/test_upgrade.rs index 48250fff0..87df60bff 100644 --- a/astra/tests/test_upgrade.rs +++ b/astra/tests/test_upgrade.rs @@ -1,96 +1,85 @@ use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; -use near_sdk::json_types::Base58CryptoHash; -use near_sdk::serde_json::json; -use near_sdk::AccountId; +use std::str::FromStr; -use near_sdk_sim::{call, init_simulator, to_yocto, DEFAULT_GAS}; -use astra::{Action, Config, ProposalInput, ProposalKind, VersionedPolicy}; - -mod utils; use crate::utils::*; +mod utils; +use workspaces::{AccountId as WorkAccountId}; +use astra::{Config, VersionedPolicy, ProposalInput, ProposalKind, Action}; +use near_sdk::{serde_json::json, json_types::{Base64VecU8, Base58CryptoHash}, AccountId, ONE_NEAR}; -near_sdk_sim::lazy_static_include::lazy_static_include_bytes! { - DAO_WASM_BYTES => "res/astra.wasm", - OTHER_WASM_BYTES => "res/ref_exchange_release.wasm" -} +#[tokio::test] +async fn test_upgrade_using_factory() -> anyhow::Result<()> { + let worker = workspaces::sandbox().await?; + let factory_contract = worker.dev_deploy(include_bytes!("../../res/astra_factory.wasm")).await?; + let root = worker.dev_create_account().await?; + // initialize contract + let res1 = factory_contract + .call("new") + .args_json(json!({})) + .max_gas() + .transact(); -#[test] -fn test_upgrade_using_factory() { - let root = init_simulator(None); - let factory = setup_factory(&root); - factory - .user_account - .call( - factory.user_account.account_id.clone(), - "new", - &[], - near_sdk_sim::DEFAULT_GAS, - 0, - ) - .assert_success(); + assert!(res1.await?.is_success()); let config = Config { name: "testdao".to_string(), purpose: "to test".to_string(), metadata: Base64VecU8(vec![]), }; - let policy = VersionedPolicy::Default(vec![root.account_id()]); + let root_near_account: AccountId = root.id().parse().unwrap(); + + let policy = VersionedPolicy::Default(vec![root_near_account]); let params = json!({ "config": config, "policy": policy }) .to_string() .into_bytes(); - call!( - root, - factory.create( - AccountId::new_unchecked("testdao".to_string()), - Base64VecU8(params) - ), - deposit = to_yocto("10") - ) - .assert_success(); - - let dao_account_id = AccountId::new_unchecked("testdao.factory".to_string()); - let dao_list = factory - .user_account - .view(factory.user_account.account_id.clone(), "get_dao_list", &[]) - .unwrap_json::>(); + let res2 = root + .call(factory_contract.id(), "create") + .args_json((AccountId::new_unchecked("testdao".to_string()), Base64VecU8(params))) + .gas(300_000_000_000_000) + .deposit(ONE_NEAR * 10) + .transact() + .await?; + assert!(res2.is_success()); + + let dao_account_id = AccountId::new_unchecked("testdao.".to_string() + factory_contract.id()); + let dao_list: Vec= factory_contract + .call("get_dao_list") + .view() + .await? + .json()?; assert_eq!(dao_list, vec![dao_account_id.clone()]); - let hash = factory - .user_account - .view( - factory.user_account.account_id.clone(), - "get_default_code_hash", - &[], - ) - .unwrap_json::(); - - let proposal_id = root - .call( - dao_account_id.clone(), - "add_proposal", - &json!({ "proposal": ProposalInput { - description: "proposal to test".to_string(), - kind: ProposalKind::UpgradeSelf { hash } - }}) - .to_string() - .into_bytes(), - near_sdk_sim::DEFAULT_GAS, - to_yocto("1"), - ) - .unwrap_json::(); - assert_eq!(0, proposal_id); - - root.call( - dao_account_id.clone(), - "act_proposal", - &json!({ "id": 0, "action": Action::VoteApprove}) - .to_string() - .into_bytes(), - near_sdk_sim::DEFAULT_GAS, - 0, - ) - .assert_success(); + let dao = WorkAccountId::from_str(dao_account_id.as_ref())?; + + let hash: Base58CryptoHash= factory_contract + .call("get_default_code_hash") + .view() + .await? + .json()?; + + let proposal = ProposalInput { + description: "proposal to test".to_string(), + kind: ProposalKind::UpgradeSelf { hash } + }; + let res = root + .call(&dao, "add_proposal") + .args_json(json!({"proposal": proposal})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + let res = root + .call(&dao, "act_proposal") + .args_json(json!({"id": 0, "action": Action::VoteApprove})) + .max_gas() + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + Ok(()) } #[derive(BorshSerialize, BorshDeserialize)] @@ -100,46 +89,41 @@ struct NewArgs { referral_fee: u32, } -/// Test that astra can upgrade another contract. -#[test] -fn test_upgrade_other() { - let (root, dao) = setup_dao(); - let ref_account_id: AccountId = "ref-finance".parse().unwrap(); - let _ = root.deploy_and_init( - &OTHER_WASM_BYTES, - ref_account_id.clone(), - "new", - &json!({ - "owner_id": dao.account_id(), - "exchange_fee": 1, - "referral_fee": 1, - }) - .to_string() - .into_bytes(), - to_yocto("1000"), - DEFAULT_GAS, - ); - let hash = root - .call( - dao.user_account.account_id.clone(), - "store_blob", - &OTHER_WASM_BYTES, - near_sdk_sim::DEFAULT_GAS, - to_yocto("200"), - ) - .unwrap_json::(); - add_proposal( - &root, - &dao, - ProposalInput { - description: "test".to_string(), - kind: ProposalKind::UpgradeRemote { - receiver_id: ref_account_id.clone(), - method_name: "upgrade".to_string(), - hash, - }, +// /// Test that astra can upgrade another contract. +#[tokio::test] +async fn test_upgrade_other() -> anyhow::Result<()> { + let (root, dao, worker) = setup_dao().await?; + let _ = transfer_near(&worker, root.id(), ONE_NEAR * 1000).await?; + let (other_contract, _) = setup_test_token(worker).await?; + + let res = root + .call(dao.id(), "store_blob") + .args(include_bytes!("../../res/test_token.wasm").to_vec()) + .max_gas() + .deposit(ONE_NEAR * 200) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + let hash: Base58CryptoHash = res.json()?; + + let proposal = ProposalInput { + description: "test".to_string(), + kind: ProposalKind::UpgradeRemote { + receiver_id: other_contract.id().clone().parse().unwrap(), + method_name: "upgrade".to_string(), + hash, }, - ) - .assert_success(); - call!(root, dao.act_proposal(0, Action::VoteApprove, None)).assert_success(); + }; + let res = root + .call(dao.id(), "add_proposal") + .args_json(json!({"proposal": proposal})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + vote(vec![root], &dao, 0).await?; + + Ok(()) } diff --git a/astra/tests/utils/mod.rs b/astra/tests/utils/mod.rs index 02b089e99..564221a04 100644 --- a/astra/tests/utils/mod.rs +++ b/astra/tests/utils/mod.rs @@ -1,166 +1,151 @@ -#![allow(dead_code)] -pub use near_sdk::json_types::{Base64VecU8, U64}; -use near_sdk::{env, AccountId, Balance}; -use near_sdk_sim::transaction::ExecutionStatus; -use near_sdk_sim::{ - call, deploy, init_simulator, to_yocto, ContractAccount, ExecutionResult, UserAccount, -}; - -use near_sdk::json_types::U128; -use astra_staking::ContractContract as StakingContract; -use astra::{ - Action, Bounty, Config, ContractContract as DAOContract, OldAccountId, ProposalInput, - ProposalKind, VersionedPolicy, OLD_BASE_TOKEN, -}; -use astra_factory::AstraFactoryContract as FactoryContract; -use test_token::ContractContract as TestTokenContract; - -near_sdk_sim::lazy_static_include::lazy_static_include_bytes! { - FACTORY_WASM_BYTES => "../astra-factory/res/astra_factory.wasm", - DAO_WASM_BYTES => "res/astra.wasm", - TEST_TOKEN_WASM_BYTES => "../test-token/res/test_token.wasm", - STAKING_WASM_BYTES => "../astra-staking/res/astra_staking.wasm", -} +use std::str::FromStr; -type Contract = ContractAccount; +use anyhow::Ok; +use astra::{Action, ProposalInput, ProposalKind, OldAccountId, OLD_BASE_TOKEN, Bounty, Config, VersionedPolicy}; +use near_sdk::{serde_json::json, Balance, AccountId, json_types::{U128, U64, Base64VecU8}, ONE_NEAR, env}; +use workspaces::{AccountId as WorkAccountId, Contract, Account, Worker, DevNetwork, types::{SecretKey, KeyType}, network::Sandbox, result::ExecutionSuccess}; pub fn base_token() -> Option { None } -pub fn should_fail(r: ExecutionResult) { - match r.status() { - ExecutionStatus::Failure(_) => {} - _ => panic!("Should fail"), - } -} - -pub fn setup_factory(root: &UserAccount) -> ContractAccount { - deploy!( - contract: FactoryContract, - contract_id: "factory".to_string(), - bytes: &FACTORY_WASM_BYTES, - signer_account: root, - deposit: to_yocto("500"), - ) -} - -pub fn setup_dao() -> (UserAccount, Contract) { - let root = init_simulator(None); +pub async fn setup_dao() -> anyhow::Result<(Account, Contract, Worker)> { + let worker = workspaces::sandbox().await?; + let dao_contract = worker.dev_deploy(include_bytes!("../../../res/astra.wasm")).await?; + let root = worker.dev_create_account().await?; let config = Config { name: "test".to_string(), purpose: "to test".to_string(), metadata: Base64VecU8(vec![]), }; - let dao = deploy!( - contract: DAOContract, - contract_id: "dao".to_string(), - bytes: &DAO_WASM_BYTES, - signer_account: root, - deposit: to_yocto("200"), - init_method: new(config, VersionedPolicy::Default(vec![root.account_id.clone()])) - ); - (root, dao) + // initialize contract + let root_near_account: AccountId = root.id().parse().unwrap(); + let res1 = dao_contract + .call("new") + .args_json(json!({ + "config": config, "policy": VersionedPolicy::Default(vec![root_near_account]) + })) + .max_gas() + .transact().await?; + assert!(res1.is_success(), "{:?}", res1); + Ok((root, dao_contract, worker)) } -pub fn setup_test_token(root: &UserAccount) -> ContractAccount { - deploy!( - contract: TestTokenContract, - contract_id: "test_token".to_string(), - bytes: &TEST_TOKEN_WASM_BYTES, - signer_account: root, - deposit: to_yocto("200"), - init_method: new() - ) -} +pub async fn setup_test_token(worker: Worker) -> anyhow::Result<(Contract, Worker)> { + let test_token = worker.dev_deploy(include_bytes!("../../../res/test_token.wasm")).await?; + let res1 = test_token + .call("new") + .max_gas() + .transact().await?; + assert!(res1.is_success(), "{:?}", res1); -pub fn setup_staking(root: &UserAccount) -> ContractAccount { - deploy!( - contract: StakingContract, - contract_id: "staking".to_string(), - bytes: &STAKING_WASM_BYTES, - signer_account: root, - deposit: to_yocto("100"), - init_method: new("dao".parse().unwrap(), "test_token".parse::().unwrap(), U64(100_000_000_000)) - ) + Ok((test_token, worker)) } -pub fn add_proposal( - root: &UserAccount, - dao: &Contract, - proposal: ProposalInput, -) -> ExecutionResult { - call!(root, dao.add_proposal(proposal), deposit = to_yocto("1")) +pub async fn setup_staking(token_id: WorkAccountId, dao: WorkAccountId, worker: Worker) -> anyhow::Result<(Contract, Worker)> { + let staking = worker.dev_deploy(include_bytes!("../../../res/astra_staking.wasm")).await?; + let res1 = staking + .call("new") + .args_json(json!({ + "owner_id": dao, "token_id": token_id, + "unstake_period": U64(100_000_000_000) + })) + .max_gas() + .transact().await?; + assert!(res1.is_success(), "{:?}", res1); + + Ok((staking, worker)) } -pub fn add_member_proposal( - root: &UserAccount, +pub async fn add_member_proposal( + root: Account, dao: &Contract, member_id: AccountId, -) -> ExecutionResult { - add_proposal( - root, - dao, - ProposalInput { - description: "test".to_string(), - kind: ProposalKind::AddMemberToRole { - member_id: member_id, - role: "council".to_string(), - }, +) -> anyhow::Result<()> { + let proposal = ProposalInput { + description: "test".to_string(), + kind: ProposalKind::AddMemberToRole { + member_id, + role: "council".to_string(), }, - ) + }; + let res = root + .call(dao.id(), "add_proposal") + .args_json(json!({"proposal": proposal})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + Ok(()) } -pub fn add_transfer_proposal( - root: &UserAccount, +pub async fn add_transfer_proposal( + root: Account, dao: &Contract, token_id: Option, receiver_id: AccountId, amount: Balance, msg: Option, -) -> ExecutionResult { - add_proposal( - root, - dao, - ProposalInput { - description: "test".to_string(), - kind: ProposalKind::Transfer { - token_id: convert_new_to_old_token(token_id), - receiver_id, - amount: U128(amount), - msg, - }, +) -> anyhow::Result<()> { + let proposal = ProposalInput { + description: "test".to_string(), + kind: ProposalKind::Transfer { + token_id: convert_new_to_old_token(token_id), + receiver_id, + amount: U128(amount), + msg, }, - ) + }; + let res = root + .call(dao.id(), "add_proposal") + .args_json(json!({"proposal": proposal})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + Ok(()) } -pub fn add_bounty_proposal(root: &UserAccount, dao: &Contract) -> ExecutionResult { - add_proposal( - root, - dao, - ProposalInput { - description: "test".to_string(), - kind: ProposalKind::AddBounty { - bounty: Bounty { - description: "test bounty".to_string(), - token: String::from(OLD_BASE_TOKEN), - amount: U128(to_yocto("10")), - times: 3, - max_deadline: U64(env::block_timestamp() + 10_000_000_000), - }, +pub async fn add_bounty_proposal(root: Account, dao: &Contract) -> anyhow::Result { + let proposal = ProposalInput { + description: "test".to_string(), + kind: ProposalKind::AddBounty { + bounty: Bounty { + description: "test bounty".to_string(), + token: String::from(OLD_BASE_TOKEN), + amount: U128(ONE_NEAR * 10), + times: 3, + max_deadline: U64(env::block_timestamp() + 10_000_000_000), }, }, - ) + }; + let res = root + .call(dao.id(), "add_proposal") + .args_json(json!({"proposal": proposal})) + .max_gas() + .deposit(ONE_NEAR) + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); + + Ok(res.json()?) } -pub fn vote(users: Vec<&UserAccount>, dao: &Contract, proposal_id: u64) { +pub async fn vote(users: Vec, dao: &Contract, proposal_id: u64) -> anyhow::Result<()> { for user in users.into_iter() { - call!( - user, - dao.act_proposal(proposal_id, Action::VoteApprove, None) - ) - .assert_success(); + let res = user + .call(dao.id(), "act_proposal") + .args_json(json!({"id": proposal_id, "action": Action::VoteApprove})) + .max_gas() + .transact() + .await?; + assert!(res.is_success(), "{:?}", res); } + Ok(()) } pub fn convert_new_to_old_token(new_account_id: Option) -> OldAccountId { @@ -169,3 +154,29 @@ pub fn convert_new_to_old_token(new_account_id: Option) -> OldAccount } new_account_id.unwrap().to_string() } + + +// Generate user sub-account +pub async fn gen_user_account(worker: &Worker, account_id: &str) -> anyhow::Result +where + T: DevNetwork + Send + Sync, +{ + let id = workspaces::AccountId::from_str(account_id)?; + let sk = SecretKey::from_random(KeyType::ED25519); + + let account = worker.create_tla(id, sk).await?.into_result()?; + + Ok(account) +} + +pub async fn transfer_near( + worker: &Worker, + account_id: &workspaces::AccountId, + deposit: Balance, +) -> anyhow::Result { + Ok(worker + .root_account()? + .transfer_near(account_id, deposit) + .await? + .into_result()?) +} \ No newline at end of file diff --git a/build.sh b/build.sh index cc919a4be..1418a93f3 100755 --- a/build.sh +++ b/build.sh @@ -1,6 +1,9 @@ #!/bin/bash set -e +cd astra +cargo build --target wasm32-unknown-unknown --release +cd .. cargo build --target wasm32-unknown-unknown --release mkdir -p res cp target/wasm32-unknown-unknown/release/*.wasm ./res/