From fdbacc68c98205cba9f42c130d464ab3114257b6 Mon Sep 17 00:00:00 2001 From: dongcool <95206510+dongcool@users.noreply.github.com> Date: Wed, 23 Mar 2022 06:29:58 +0800 Subject: [PATCH] feat: contract operator (#68) * operator * set operator command * operator set * refactor: operator -> manager (#69) * rename owner to admin * rename to manager * revert: owner -> admin * fix: method names Co-authored-by: Linguists <95207870+linguists@users.noreply.github.com> * style: code format * refactor: manager commands Co-authored-by: Linguists <95207870+linguists@users.noreply.github.com> --- bin/commands/add-manager.js | 42 ++ bin/commands/del-manager.js | 42 ++ bin/commands/list-managers.js | 27 + contracts/linear/src/errors.rs | 3 + contracts/linear/src/internal.rs | 24 +- contracts/linear/src/lib.rs | 7 +- contracts/linear/src/owner.rs | 10 + contracts/linear/src/validator_pool.rs | 10 +- contracts/linear/src/view.rs | 4 + tests/__tests__/linear/beneficiaries.ava.ts | 190 +++--- tests/__tests__/linear/manager.ava.ts | 63 ++ tests/__tests__/linear/validator-pool.ava.ts | 622 ++++++++++--------- 12 files changed, 635 insertions(+), 409 deletions(-) create mode 100644 bin/commands/add-manager.js create mode 100644 bin/commands/del-manager.js create mode 100644 bin/commands/list-managers.js create mode 100644 tests/__tests__/linear/manager.ava.ts diff --git a/bin/commands/add-manager.js b/bin/commands/add-manager.js new file mode 100644 index 00000000..1b2d0f46 --- /dev/null +++ b/bin/commands/add-manager.js @@ -0,0 +1,42 @@ +const { init } = require("../near"); + +exports.command = 'add-manager
'; +exports.desc = 'Add manager'; +exports.builder = yargs => { + yargs + .positional('address', { + describe: 'Contract address', + type: 'string' + }) + .option('network', { + describe: 'network ID', + default: 'testnet', + choices: ['testnet', 'mainnet'] + }) + .option('signer', { + describe: 'signer account Id to call contract' + }) + .option('manager', { + describe: 'new manager ID' + }) + .demandOption(['signer', 'manager']) +} + +exports.handler = async function (argv) { + const { address, manager } = argv; + + const near = await init(argv.network); + const signer = await near.account(argv.signer); + + console.log(`Adding manager ${manager}`); + + await signer.functionCall({ + contractId: address, + methodName: 'add_manager', + args: { + new_manager_id: manager + } + }); + + console.log('done'); +} diff --git a/bin/commands/del-manager.js b/bin/commands/del-manager.js new file mode 100644 index 00000000..f67c8f08 --- /dev/null +++ b/bin/commands/del-manager.js @@ -0,0 +1,42 @@ +const { init } = require("../near"); + +exports.command = 'del-manager
'; +exports.desc = 'Remove manager'; +exports.builder = yargs => { + yargs + .positional('address', { + describe: 'Contract address', + type: 'string' + }) + .option('network', { + describe: 'network ID', + default: 'testnet', + choices: ['testnet', 'mainnet'] + }) + .option('signer', { + describe: 'signer account Id to call contract' + }) + .option('manager', { + describe: 'manager ID to remove' + }) + .demandOption(['signer', 'manager']) +} + +exports.handler = async function (argv) { + const { address, manager } = argv; + + const near = await init(argv.network); + const signer = await near.account(argv.signer); + + console.log(`Removing manager ${manager}`); + + await signer.functionCall({ + contractId: address, + methodName: 'remove_manager', + args: { + manager_id: manager + } + }); + + console.log('done'); +} diff --git a/bin/commands/list-managers.js b/bin/commands/list-managers.js new file mode 100644 index 00000000..1509168c --- /dev/null +++ b/bin/commands/list-managers.js @@ -0,0 +1,27 @@ +const { init } = require("../near"); + +exports.command = 'list-managers
'; +exports.desc = 'List manager'; +exports.builder = yargs => { + yargs + .positional('address', { + describe: 'Contract address', + type: 'string' + }) + .option('network', { + describe: 'network ID', + default: 'testnet', + choices: ['testnet', 'mainnet'] + }) +} + +exports.handler = async function (argv) { + const { address } = argv; + + const near = await init(argv.network); + const contract = await near.account(address); + + const managers = await contract.viewFunction(address, 'get_managers'); + + console.log(managers); +} diff --git a/contracts/linear/src/errors.rs b/contracts/linear/src/errors.rs index 39531133..3bd9680b 100644 --- a/contracts/linear/src/errors.rs +++ b/contracts/linear/src/errors.rs @@ -8,6 +8,9 @@ pub const ERR_NO_ENOUGH_INIT_DEPOSIT: &str = // owner pub const ERR_NOT_OWNER: &str = "Only owner can perform this action"; +// manager +pub const ERR_NOT_MANAGER: &str = "Only manager can perform this action"; + // account pub const ERR_NO_ACCOUNT: &str = "Account not found"; pub const ERR_UNREGISTER_POSITIVE_UNSTAKED: &str = "Cannot delete the account because the unstaked amount is not empty. Withdraw your balance first."; diff --git a/contracts/linear/src/internal.rs b/contracts/linear/src/internal.rs index 280fddc2..d06249a7 100644 --- a/contracts/linear/src/internal.rs +++ b/contracts/linear/src/internal.rs @@ -198,7 +198,7 @@ impl LiquidStakingContract { } /// When there are rewards, a part of them will be - /// given to operator/treasury by minting new LiNEAR tokens. + /// given to executor, manager or treasury by minting new LiNEAR tokens. pub(crate) fn internal_distribute_staking_rewards(&mut self, rewards: Balance) { let hashmap: HashMap = self.internal_get_beneficiaries(); for (account_id, fraction) in hashmap.iter() { @@ -313,3 +313,25 @@ impl LiquidStakingContract { } } } + +// -- manager related methods +impl LiquidStakingContract { + pub(crate) fn internal_add_manager(&mut self, manager_id: &AccountId) { + self.managers.insert(manager_id); + } + + pub(crate) fn internal_remove_manager(&mut self, manager_id: &AccountId) -> bool { + self.managers.remove(manager_id) + } + + pub(crate) fn internal_get_managers(&self) -> Vec { + self.managers.to_vec() + } + + pub(crate) fn assert_manager(&self) { + require!( + self.managers.contains(&env::predecessor_account_id()), + ERR_NOT_MANAGER + ); + } +} diff --git a/contracts/linear/src/lib.rs b/contracts/linear/src/lib.rs index aa25ea99..ae565d7c 100644 --- a/contracts/linear/src/lib.rs +++ b/contracts/linear/src/lib.rs @@ -42,13 +42,16 @@ pub(crate) enum StorageKey { Farms, // AuthorizedUsers, AuthorizedFarmTokens, + Managers, } #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)] pub struct LiquidStakingContract { - /// The account ID of the owner who's running the liquid staking contract. + /// The account ID of the owner owner_id: AccountId, + /// The accounts that are able to change key parameters and settings in the contract such as validator pool membership + managers: UnorderedSet, /// The account ID of the treasury that manages portion of the received fees and rewards. treasury_id: AccountId, /// Total amount of LiNEAR that was minted (minus burned). @@ -134,6 +137,7 @@ impl LiquidStakingContract { ); let mut this = Self { owner_id: owner_id.clone(), + managers: UnorderedSet::new(StorageKey::Managers), treasury_id: owner_id.clone(), total_share_amount: 10 * ONE_NEAR, total_staked_near_amount: 10 * ONE_NEAR, @@ -155,6 +159,7 @@ impl LiquidStakingContract { // authorized_users: UnorderedSet::new(StorageKey::AuthorizedUsers), authorized_farm_tokens: UnorderedSet::new(StorageKey::AuthorizedFarmTokens), }; + this.internal_add_manager(&owner_id); this.measure_account_storage_usage(); this } diff --git a/contracts/linear/src/owner.rs b/contracts/linear/src/owner.rs index 31603884..681499c3 100644 --- a/contracts/linear/src/owner.rs +++ b/contracts/linear/src/owner.rs @@ -8,6 +8,16 @@ impl LiquidStakingContract { self.owner_id = new_owner_id; } + pub fn add_manager(&mut self, new_manager_id: AccountId) { + self.assert_owner(); + self.internal_add_manager(&new_manager_id); + } + + pub fn remove_manager(&mut self, manager_id: AccountId) -> bool { + self.assert_owner(); + self.internal_remove_manager(&manager_id) + } + pub fn set_beneficiary(&mut self, account_id: AccountId, fraction: Fraction) { self.assert_owner(); fraction.assert_valid(); diff --git a/contracts/linear/src/validator_pool.rs b/contracts/linear/src/validator_pool.rs index 8125b076..c742b3ca 100644 --- a/contracts/linear/src/validator_pool.rs +++ b/contracts/linear/src/validator_pool.rs @@ -209,7 +209,7 @@ fn min3(x: u128, y: u128, z: u128) -> u128 { #[near_bindgen] impl LiquidStakingContract { pub fn add_validator(&mut self, validator_id: AccountId, weight: u16) -> Validator { - self.assert_owner(); + self.assert_manager(); self.validator_pool.add_validator(&validator_id, weight) } @@ -218,7 +218,7 @@ impl LiquidStakingContract { validator_ids: Vec, weights: Vec, ) -> Vec { - self.assert_owner(); + self.assert_manager(); require!(validator_ids.len() == weights.len(), ERR_BAD_VALIDATOR_LIST); let mut results: Vec = vec![]; for i in 0..validator_ids.len() { @@ -232,12 +232,12 @@ impl LiquidStakingContract { } pub fn remove_validator(&mut self, validator_id: AccountId) -> Validator { - self.assert_owner(); + self.assert_manager(); self.validator_pool.remove_validator(&validator_id) } pub fn update_weight(&mut self, validator_id: AccountId, weight: u16) { - self.assert_owner(); + self.assert_manager(); self.validator_pool.update_weight(&validator_id, weight); } @@ -247,7 +247,6 @@ impl LiquidStakingContract { } pub fn get_validator(&self, validator_id: AccountId) -> ValidatorInfo { - self.assert_owner(); self.validator_pool .get_validator(&validator_id) .expect(ERR_VALIDATOR_NOT_EXIST) @@ -255,7 +254,6 @@ impl LiquidStakingContract { } pub fn get_validators(&self, offset: u64, limit: u64) -> Vec { - self.assert_owner(); self.validator_pool .get_validators(offset, limit) .iter() diff --git a/contracts/linear/src/view.rs b/contracts/linear/src/view.rs index ee7994e3..3e51c488 100644 --- a/contracts/linear/src/view.rs +++ b/contracts/linear/src/view.rs @@ -79,6 +79,10 @@ impl LiquidStakingContract { self.internal_get_beneficiaries() } + pub fn get_managers(&self) -> Vec { + self.internal_get_managers() + } + pub fn get_summary(&self) -> Summary { Summary { total_share_amount: self.total_share_amount.into(), diff --git a/tests/__tests__/linear/beneficiaries.ava.ts b/tests/__tests__/linear/beneficiaries.ava.ts index bc563399..aac30d96 100644 --- a/tests/__tests__/linear/beneficiaries.ava.ts +++ b/tests/__tests__/linear/beneficiaries.ava.ts @@ -2,108 +2,108 @@ import { assertFailure, initWorkSpace } from "./helper"; const workspace = initWorkSpace(); -workspace.test('non-owner call beneficiaries', async (test, {contract, alice}) => { - await assertFailure( - test, - alice.call( - contract, - 'set_beneficiary', - { - account_id: alice.accountId, - fraction: { - numerator: 1, - denominator: 10 - } - } - ), - 'Only owner can perform this action' - ); +workspace.test('non-owner call beneficiaries', async (test, { contract, alice }) => { + await assertFailure( + test, + alice.call( + contract, + 'set_beneficiary', + { + account_id: alice.accountId, + fraction: { + numerator: 1, + denominator: 10 + } + } + ), + 'Only owner can perform this action' + ); - await assertFailure( - test, - alice.call( - contract, - 'remove_beneficiary', - { - account_id: alice.accountId - } - ), - 'Only owner can perform this action' - ); + await assertFailure( + test, + alice.call( + contract, + 'remove_beneficiary', + { + account_id: alice.accountId + } + ), + 'Only owner can perform this action' + ); }); -workspace.test('set beneficiaries', async (test, {contract, owner}) => { - const initValues: object = await owner.call( - contract, - 'get_beneficiaries', - {} - ); - test.deepEqual(initValues, {}); +workspace.test('set beneficiaries', async (test, { contract, owner }) => { + const initValues: object = await owner.call( + contract, + 'get_beneficiaries', + {} + ); + test.deepEqual(initValues, {}); - await owner.call( - contract, - 'set_beneficiary', - { - account_id: 'foo', - fraction: { - numerator: 1, - denominator: 10 - } - } - ); - await owner.call( - contract, - 'set_beneficiary', - { - account_id: 'bar', - fraction: { - numerator: 5, - denominator: 10 - } - } - ); + await owner.call( + contract, + 'set_beneficiary', + { + account_id: 'foo', + fraction: { + numerator: 1, + denominator: 10 + } + } + ); + await owner.call( + contract, + 'set_beneficiary', + { + account_id: 'bar', + fraction: { + numerator: 5, + denominator: 10 + } + } + ); - const twoValues = await owner.call( - contract, - 'get_beneficiaries', - {} - ); + const twoValues = await owner.call( + contract, + 'get_beneficiaries', + {} + ); - test.deepEqual( - twoValues, - { - foo: { - numerator: 1, - denominator: 10 - }, - bar: { - numerator: 5, - denominator: 10 - } - } - ); + test.deepEqual( + twoValues, + { + foo: { + numerator: 1, + denominator: 10 + }, + bar: { + numerator: 5, + denominator: 10 + } + } + ); - await owner.call( - contract, - 'remove_beneficiary', - { - account_id: 'foo' - } - ); + await owner.call( + contract, + 'remove_beneficiary', + { + account_id: 'foo' + } + ); - const oneValue = await owner.call( - contract, - 'get_beneficiaries', - {} - ); + const oneValue = await owner.call( + contract, + 'get_beneficiaries', + {} + ); - test.deepEqual( - oneValue, - { - bar: { - numerator: 5, - denominator: 10 - } - } - ); + test.deepEqual( + oneValue, + { + bar: { + numerator: 5, + denominator: 10 + } + } + ); }); diff --git a/tests/__tests__/linear/manager.ava.ts b/tests/__tests__/linear/manager.ava.ts new file mode 100644 index 00000000..90956c9b --- /dev/null +++ b/tests/__tests__/linear/manager.ava.ts @@ -0,0 +1,63 @@ +import { assertFailure, initWorkSpace } from "./helper"; + +const workspace = initWorkSpace(); + +workspace.test('non-owner call manager methods', async (test, { contract, alice }) => { + await assertFailure( + test, + alice.call( + contract, + 'add_manager', + { + new_manager_id: alice.accountId + } + ), + 'Only owner can perform this action' + ); + + await assertFailure( + test, + alice.call( + contract, + 'remove_manager', + { + manager_id: alice.accountId + }, + ), + 'Only owner can perform this action' + ); +}); + +workspace.test('set manager', async (test, {contract, owner, alice}) => { + await owner.call( + contract, + 'add_manager', + { + new_manager_id: alice.accountId + } + ); + + const managers: string[] = await contract.view('get_managers'); + test.assert(managers.includes(alice.accountId)); +}); + +workspace.test('remove manager', async (test, {contract, owner, alice}) => { + await owner.call( + contract, + 'add_manager', + { + new_manager_id: alice.accountId + } + ); + + await owner.call( + contract, + 'remove_manager', + { + manager_id: alice.accountId + } + ); + + const managers: string[] = await contract.view('get_managers'); + test.assert(!managers.includes(alice.accountId)); +}); diff --git a/tests/__tests__/linear/validator-pool.ava.ts b/tests/__tests__/linear/validator-pool.ava.ts index a17d3648..518c28f7 100644 --- a/tests/__tests__/linear/validator-pool.ava.ts +++ b/tests/__tests__/linear/validator-pool.ava.ts @@ -1,327 +1,337 @@ -import { Gas } from "near-units"; +import { Gas, NEAR } from "near-units"; +import { NearAccount } from "near-workspaces-ava"; import { assertFailure, initWorkSpace } from "./helper"; const workspace = initWorkSpace(); -workspace.test('not owner', async (test, {contract, alice}) => { - let errMsg = "Only owner can perform this action"; - await assertFailure( - test, - alice.call( - contract, - 'add_validator', - { - validator_id: 'foo', - weight: 10 - } - ), - errMsg - ); - - await assertFailure( - test, - alice.call( - contract, - 'add_validators', - { - validator_ids: ['foo'], - weights: [10] - } - ), - errMsg - ); - - await assertFailure( - test, - alice.call( - contract, - 'remove_validator', - { - validator_id: 'foo', - } - ), - errMsg - ); +async function setManager(root: NearAccount, contract: NearAccount, owner: NearAccount) { + const manager = await root.createAccount('linear_manager', { initialBalance: NEAR.parse("1000000").toString() }); - await assertFailure( - test, - alice.call( - contract, - 'update_weight', - { - validator_id: 'foo', - weight: 10 - } - ), - errMsg - ); - - await assertFailure( - test, - alice.call( - contract, - 'get_validators', - { - offset: 0, - limit: 1 - } - ), - errMsg - ); + // set manager + await owner.call( + contract, + 'add_manager', + { + new_manager_id: manager.accountId + } + ); + + return manager; +} + +workspace.test('not manager', async (test, { contract, alice, root, owner }) => { + await setManager(root, contract, owner); + + let errMsg = "Only manager can perform this action"; + await assertFailure( + test, + alice.call( + contract, + 'add_validator', + { + validator_id: 'foo', + weight: 10 + } + ), + errMsg + ); + + await assertFailure( + test, + alice.call( + contract, + 'add_validators', + { + validator_ids: ['foo'], + weights: [10] + } + ), + errMsg + ); + + await assertFailure( + test, + alice.call( + contract, + 'remove_validator', + { + validator_id: 'foo', + } + ), + errMsg + ); + + await assertFailure( + test, + alice.call( + contract, + 'update_weight', + { + validator_id: 'foo', + weight: 10 + } + ), + errMsg + ); }); workspace.test('add validator', async (test, context) => { - const { owner, contract } = context; - - await owner.call( - contract, - 'add_validator', - { - validator_id: 'foo', - weight: 10 - } - ); - test.is( - await contract.view('get_total_weight'), - 10 - ); - - await owner.call( - contract, - 'add_validator', - { - validator_id: 'bar', - weight: 20 - } - ); - test.is( - await contract.view('get_total_weight'), - 30 - ); - - const validators: [any] = await owner.call( - contract, - 'get_validators', - { - offset: 0, - limit: 10 - } - ); - - test.is( - validators.filter(v => v.account_id === 'foo')[0].weight, - 10 - ); - test.is( - validators.filter(v => v.account_id === 'bar')[0].weight, - 20 - ); + const { root, owner, contract } = context; + const manager = await setManager(root, contract, owner); + + await manager.call( + contract, + 'add_validator', + { + validator_id: 'foo', + weight: 10 + } + ); + test.is( + await contract.view('get_total_weight'), + 10 + ); + + await manager.call( + contract, + 'add_validator', + { + validator_id: 'bar', + weight: 20 + } + ); + test.is( + await contract.view('get_total_weight'), + 30 + ); + + const validators: [any] = await contract.view( + 'get_validators', + { + offset: 0, + limit: 10 + } + ); + + test.is( + validators.filter(v => v.account_id === 'foo')[0].weight, + 10 + ); + test.is( + validators.filter(v => v.account_id === 'bar')[0].weight, + 20 + ); }); workspace.test('bulk add a few validators', async (test, context) => { - const { owner, contract } = context; - - await owner.call( - contract, - 'add_validators', - { - validator_ids: ['foo', 'bar'], - weights: [10, 20] - } - ); - - test.is( - await contract.view('get_total_weight'), - 30 - ); - - const validators: [any] = await owner.call( - contract, - 'get_validators', - { - offset: 0, - limit: 10 - } - ); - - test.is( - validators.filter(v => v.account_id === 'foo')[0].weight, - 10 - ); - test.is( - validators.filter(v => v.account_id === 'bar')[0].weight, - 20 - ); -}); - -workspace.test('bulk add a lot validators', async (test, { owner, contract }) => { - for (let i = 0; i < 2; i++) { - const validators = Array.from({ length: 50 }, (_, j) => `validator-${i}-${j}`); - const weights = validators.map(_ => 1); - - await owner.call( - contract, - 'add_validators', - { - validator_ids: validators, - weights - }, - { - gas: Gas.parse('200 Tgas') - } - ); + const { root, owner, contract } = context; + const manager = await setManager(root, contract, owner); + + await manager.call( + contract, + 'add_validators', + { + validator_ids: ['foo', 'bar'], + weights: [10, 20] } - - test.is( - await contract.view('get_total_weight'), - 100 - ); - - // read all validators - for (let i = 0; i < 5; i++) { - const limit = 20; - const offset = i * limit; - - await owner.call( - contract, - 'get_validators', - { - offset, - limit - }, - { - gas: Gas.parse('200 Tgas') - } - ); + ); + + test.is( + await contract.view('get_total_weight'), + 30 + ); + + const validators: [any] = await contract.view( + 'get_validators', + { + offset: 0, + limit: 10 } + ); + + test.is( + validators.filter(v => v.account_id === 'foo')[0].weight, + 10 + ); + test.is( + validators.filter(v => v.account_id === 'bar')[0].weight, + 20 + ); }); -workspace.test('remove validator', async (test, context) => { - const { owner, contract } = context; - - // add foo, bar - await owner.call( - contract, - 'add_validator', - { - validator_id: 'foo', - weight: 10 - } - ); - await owner.call( - contract, - 'add_validator', - { - validator_id: 'bar', - weight: 20 - } - ); - - // remove foo - await owner.call( - contract, - 'remove_validator', - { - validator_id: 'foo' - } - ); - - test.is( - await contract.view('get_total_weight'), - 20 - ); - let validators: [any] = await owner.call( - contract, - 'get_validators', - { - offset: 0, - limit: 10 - } - ); - - test.is( - validators.length, - 1 - ); - test.is( - validators[0].account_id, - 'bar' - ); - - // remove bar - await owner.call( - contract, - 'remove_validator', - { - validator_id: 'bar' - } - ); - test.is( - await contract.view('get_total_weight'), - 0 - ); +workspace.test('bulk add a lot validators', async (test, context) => { + const { root, owner, contract } = context; + const manager = await setManager(root, contract, owner); + + for (let i = 0; i < 2; i++) { + const validators = Array.from({ length: 50 }, (_, j) => `validator-${i}-${j}`); + const weights = validators.map(_ => 1); + + await manager.call( + contract, + 'add_validators', + { + validator_ids: validators, + weights + }, + { + gas: Gas.parse('200 Tgas') + } + ); + } + + test.is( + await contract.view('get_total_weight'), + 100 + ); + + // read all validators + for (let i = 0; i < 5; i++) { + const limit = 20; + const offset = i * limit; + + await manager.call( + contract, + 'get_validators', + { + offset, + limit + }, + { + gas: Gas.parse('200 Tgas') + } + ); + } +}); - validators = await owner.call( - contract, - 'get_validators', - { - offset: 0, - limit: 10 - } - ); +workspace.test('remove validator', async (test, context) => { + const { root, owner, contract } = context; + const manager = await setManager(root, contract, owner); + + // add foo, bar + await manager.call( + contract, + 'add_validator', + { + validator_id: 'foo', + weight: 10 + } + ); + await manager.call( + contract, + 'add_validator', + { + validator_id: 'bar', + weight: 20 + } + ); + + // remove foo + await manager.call( + contract, + 'remove_validator', + { + validator_id: 'foo' + } + ); + + test.is( + await contract.view('get_total_weight'), + 20 + ); + + let validators: [any] = await contract.view( + 'get_validators', + { + offset: 0, + limit: 10 + } + ); + + test.is( + validators.length, + 1 + ); + test.is( + validators[0].account_id, + 'bar' + ); + + // remove bar + await manager.call( + contract, + 'remove_validator', + { + validator_id: 'bar' + } + ); + test.is( + await contract.view('get_total_weight'), + 0 + ); + + validators = await manager.call( + contract, + 'get_validators', + { + offset: 0, + limit: 10 + } + ); - test.is( - validators.length, - 0 - ); + test.is( + validators.length, + 0 + ); }); workspace.test('update weight', async (test, context) => { - const { owner, contract } = context; - - // add foo, bar - await owner.call( - contract, - 'add_validator', - { - validator_id: 'foo', - weight: 10 - } - ); - await owner.call( - contract, - 'add_validator', - { - validator_id: 'bar', - weight: 20 - } - ); - - // update foo - await owner.call( - contract, - 'update_weight', - { - validator_id: 'foo', - weight: 30 - } - ); - test.is( - await contract.view('get_total_weight'), - 50 - ); - - // update bar - await owner.call( - contract, - 'update_weight', - { - validator_id: 'bar', - weight: 5 - } - ); - test.is( - await contract.view('get_total_weight'), - 35 - ); + const { root, owner, contract } = context; + const manager = await setManager(root, contract, owner); + + // add foo, bar + await manager.call( + contract, + 'add_validator', + { + validator_id: 'foo', + weight: 10 + } + ); + await manager.call( + contract, + 'add_validator', + { + validator_id: 'bar', + weight: 20 + } + ); + + // update foo + await manager.call( + contract, + 'update_weight', + { + validator_id: 'foo', + weight: 30 + } + ); + test.is( + await contract.view('get_total_weight'), + 50 + ); + + // update bar + await manager.call( + contract, + 'update_weight', + { + validator_id: 'bar', + weight: 5 + } + ); + test.is( + await contract.view('get_total_weight'), + 35 + ); });