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
+ );
});