difftreelog
Merge pull request #1059 from UniqueNetwork/feature/financial-council
in: master
15 files changed
js-packages/.vscode/settings.jsondiffbeforeafterboth--- a/js-packages/.vscode/settings.json
+++ b/js-packages/.vscode/settings.json
@@ -4,7 +4,8 @@
"RUN_XCM_TESTS": "1"
},
"mochaExplorer.files": "tests/**/*.test.ts",
- "mochaExplorer.require": "ts-node/register",
+ "mochaExplorer.esmLoader": true,
+ "mochaExplorer.nodeArgv": ["--loader", "ts-node/esm"],
"eslint.format.enable": true,
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
js-packages/test-utils/index.tsdiffbeforeafterboth--- a/js-packages/test-utils/index.ts
+++ b/js-packages/test-utils/index.ts
@@ -214,6 +214,23 @@
}));
};
+ static FinCouncil = class extends EventSection('financialCouncil') {
+ static Proposed = this.Method('Proposed', data => ({
+ account: eventHumanData(data, 0),
+ proposalIndex: eventJsonData<number>(data, 1),
+ proposalHash: eventHumanData(data, 2),
+ threshold: eventJsonData<number>(data, 3),
+ }));
+ static Closed = this.Method('Closed', data => ({
+ proposalHash: eventHumanData(data, 0),
+ yes: eventJsonData<number>(data, 1),
+ no: eventJsonData<number>(data, 2),
+ }));
+ static Executed = this.Method('Executed', data => ({
+ proposalHash: eventHumanData(data, 0),
+ }));
+ };
+
static TechnicalCommittee = class extends EventSection('technicalCommittee') {
static Proposed = this.Method('Proposed', data => ({
account: eventHumanData(data, 0),
@@ -475,6 +492,7 @@
scheduler: SchedulerGroup;
collatorSelection: CollatorSelectionGroup;
council: ICollectiveGroup;
+ finCouncil: ICollectiveGroup;
technicalCommittee: ICollectiveGroup;
fellowship: IFellowshipGroup;
democracy: DemocracyGroup;
@@ -498,6 +516,10 @@
collective: new CollectiveGroup(this, 'council'),
membership: new CollectiveMembershipGroup(this, 'councilMembership'),
};
+ this.finCouncil = {
+ collective: new CollectiveGroup(this, 'financialCouncil'),
+ membership: new CollectiveMembershipGroup(this, 'financialCouncilMembership'),
+ };
this.technicalCommittee = {
collective: new CollectiveGroup(this, 'technicalCommittee'),
membership: new CollectiveMembershipGroup(this, 'technicalCommitteeMembership'),
js-packages/tests/pallet-presence.test.tsdiffbeforeafterboth--- a/js-packages/tests/pallet-presence.test.ts
+++ b/js-packages/tests/pallet-presence.test.ts
@@ -80,6 +80,8 @@
'scheduler',
'technicalcommittee',
'technicalcommitteemembership',
+ 'financialcouncil',
+ 'financialcouncilmembership',
'identity',
];
const testUtils = 'testutils';
js-packages/tests/sub/governance/council.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/governance/council.test.ts
+++ b/js-packages/tests/sub/governance/council.test.ts
@@ -2,7 +2,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '@unique/test-utils/util.js';
import {Event} from '@unique/test-utils';
-import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, councilMotionDuration, democracyFastTrackVotingPeriod, fellowshipRankLimit, clearCouncil, clearTechComm, initTechComm, clearFellowship, dummyProposal, dummyProposalCall, initFellowship, defaultEnactmentMoment, fellowshipPropositionOrigin} from './util.js';
+import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, councilMotionDuration, democracyFastTrackVotingPeriod, fellowshipRankLimit, clearCouncil, clearTechComm, initTechComm, clearFellowship, dummyProposal, dummyProposalCall, initFellowship, defaultEnactmentMoment, fellowshipPropositionOrigin, initFinCouncil} from './util.js';
import type {ICounselors} from './util.js';
describeGov('Governance: Council tests', () => {
@@ -192,6 +192,25 @@
expect(techCommMembers).to.not.contains(techComm.andy.address);
});
+ itSub('Council can remove FinCouncil member', async ({helper}) => {
+ const finCouncil = await initFinCouncil(donor, sudoer);
+ const removeMemberPrpoposal = helper.finCouncil.membership.removeMemberCall(finCouncil.andy.address);
+ await proposalFromMoreThanHalfCouncil(removeMemberPrpoposal);
+
+ const finCouncilMembers = await helper.finCouncil.membership.getMembers();
+ expect(finCouncilMembers).to.not.contains(finCouncil.andy.address);
+ });
+
+ itSub('Council can add FinCouncil member', async ({helper}) => {
+ await initFinCouncil(donor, sudoer);
+ const newFinCouncilMember = helper.arrange.createEmptyAccount();
+ const addMemberPrpoposal = helper.finCouncil.membership.addMemberCall(newFinCouncilMember.address);
+ await proposalFromMoreThanHalfCouncil(addMemberPrpoposal);
+
+ const finCouncilMembers = await helper.finCouncil.membership.getMembers();
+ expect(finCouncilMembers).to.contains(newFinCouncilMember.address);
+ });
+
itSub.skip('Council member can add Fellowship member', async ({helper}) => {
const newFellowshipMember = helper.arrange.createEmptyAccount();
await expect(helper.council.collective.execute(
@@ -328,6 +347,22 @@
)).to.be.rejectedWith('BadOrigin');
});
+ itSub('[Negative] Council member can\'t add FinCouncil member', async ({helper}) => {
+ const newFinCouncilMember = helper.arrange.createEmptyAccount();
+ await expect(helper.council.collective.execute(
+ counselors.alex,
+ helper.finCouncil.membership.addMemberCall(newFinCouncilMember.address),
+ )).rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] Council member can\'t remove FinCouncil member', async ({helper}) => {
+ const finCouncil = await initFinCouncil(donor, sudoer);
+ await expect(helper.council.collective.execute(
+ counselors.alex,
+ helper.finCouncil.membership.removeMemberCall(finCouncil.ildar.address),
+ )).rejectedWith('BadOrigin');
+ });
+
itSub('[Negative] Council member cannot promote/demote a Fellowship member', async ({helper}) => {
const fellowship = await initFellowship(donor, sudoer);
const memberWithRankOne = fellowship[1][0];
@@ -454,4 +489,21 @@
await expect(helper.council.collective.close(counselors.filip, proposalHash, proposalIndex)).to.be.rejectedWith('TooEarly');
});
+ itSub('[Negative] Council can\'t veto Democracy proposals', async ({helper}) => {
+ const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+ await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+ await expect(proposalFromAllCouncil(helper.democracy.vetoExternalCall(preimageHash)))
+ .rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] Council member can\'t veto Democracy proposals', async ({helper}) => {
+ const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+ await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+ await expect(helper.council.collective.execute(
+ counselors.charu,
+ helper.democracy.vetoExternalCall(preimageHash),
+ )).rejectedWith('BadOrigin');
+ });
});
js-packages/tests/sub/governance/financialCouncil.test.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/tests/sub/governance/financialCouncil.test.ts
@@ -0,0 +1,440 @@
+import type {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, itSub, expect, describeGov} from '@unique/test-utils/util.js';
+import {Event} from '@unique/test-utils';
+import {democracyFastTrackVotingPeriod, IFinCounselors, clearTechComm, dummyProposalCall, initFinCouncil, clearFinCouncil, democracyLaunchPeriod, initFellowship, dummyProposal, fellowshipPropositionOrigin, defaultEnactmentMoment, initCouncil, clearCouncil, clearFellowship} from './util.js';
+
+
+describeGov('Governance: Financial Council tests', () => {
+ let donor: IKeyringPair;
+ let finCounselors: IFinCounselors;
+ let sudoer: IKeyringPair;
+
+ const moreThanHalfCouncilThreshold = 2;
+
+ before(async function() {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ sudoer = await privateKey('//Alice');
+ donor = await privateKey({url: import.meta.url});
+ });
+ });
+
+ beforeEach(async () => {
+ finCounselors = await initFinCouncil(donor, sudoer);
+ });
+
+ afterEach(async () => {
+ await clearFinCouncil(sudoer);
+ await clearTechComm(sudoer);
+ });
+
+ async function proposalFromMoreThanHalfFinCouncil(proposal: any) {
+ return await usingPlaygrounds(async (helper) => {
+ expect((await helper.finCouncil.membership.getMembers()).length).to.be.equal(3);
+ const proposeResult = await helper.finCouncil.collective.propose(
+ finCounselors.ildar,
+ proposal,
+ moreThanHalfCouncilThreshold,
+ );
+
+ const councilProposedEvent = Event.FinCouncil.Proposed.expect(proposeResult);
+ const proposalIndex = councilProposedEvent.proposalIndex;
+ const proposalHash = councilProposedEvent.proposalHash;
+
+ await helper.finCouncil.collective.vote(finCounselors.greg, proposalHash, proposalIndex, true);
+ await helper.finCouncil.collective.vote(finCounselors.ildar, proposalHash, proposalIndex, true);
+
+ return await helper.finCouncil.collective.close(finCounselors.ildar, proposalHash, proposalIndex);
+ });
+ }
+
+ async function proposalFromAllFinCouncil(proposal: any) {
+ return await usingPlaygrounds(async (helper) => {
+ expect((await helper.finCouncil.membership.getMembers()).length).to.be.equal(3);
+ const proposeResult = await helper.finCouncil.collective.propose(
+ finCounselors.ildar,
+ proposal,
+ moreThanHalfCouncilThreshold,
+ );
+
+ const councilProposedEvent = Event.FinCouncil.Proposed.expect(proposeResult);
+ const proposalIndex = councilProposedEvent.proposalIndex;
+ const proposalHash = councilProposedEvent.proposalHash;
+
+ await helper.finCouncil.collective.vote(finCounselors.greg, proposalHash, proposalIndex, true);
+ await helper.finCouncil.collective.vote(finCounselors.ildar, proposalHash, proposalIndex, true);
+ await helper.finCouncil.collective.vote(finCounselors.andy, proposalHash, proposalIndex, true);
+
+ return await helper.finCouncil.collective.close(finCounselors.andy, proposalHash, proposalIndex);
+ });
+ }
+
+ itSub('FinCouncil member can register foreign asset', async ({helper}) => {
+ const location = {
+ parents: 1,
+ interior: {X3: [
+ {
+ Parachain: 1000,
+ },
+ {
+ PalletInstance: 50,
+ },
+ {
+ GeneralIndex: 1984,
+ },
+ ]},
+ };
+ const assetId = {Concrete: location};
+
+ const registerForeignAssetCall = helper.constructApiCall(
+ 'api.tx.foreignAssets.forceRegisterForeignAsset',
+ [{V3: assetId}, helper.util.str2vec('New Asset'), 'NEW', {Fungible: 10}],
+ );
+
+ await helper.finCouncil.collective.execute(finCounselors.andy, registerForeignAssetCall);
+
+ const asset = await helper.foreignAssets.foreignCollectionId(location);
+ expect(asset).not.null;
+ });
+
+ itSub('[Negative] FinCouncil can\'t fast-track Democracy proposals', async ({helper}) => {
+ const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+ await helper.wait.parachainBlockMultiplesOf(35n);
+
+ await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+ await expect(proposalFromAllFinCouncil(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0)))
+ .rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot fast-track Democracy proposals', async ({helper}) => {
+ const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+ await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0),
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil can\'t cancel Democracy proposals', async ({helper}) => {
+ const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
+ const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+
+ await expect(proposalFromAllFinCouncil(helper.democracy.cancelProposalCall(proposalIndex)))
+ .rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot cancel Democracy proposals', async ({helper}) => {
+ const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
+ const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ helper.democracy.cancelProposalCall(proposalIndex),
+ ))
+ .to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil can\'t cancel ongoing Democracy referendums', async ({helper}) => {
+ await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));
+ const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
+ const referendumIndex = startedEvent.referendumIndex;
+
+ await expect(proposalFromAllFinCouncil(helper.democracy.emergencyCancelCall(referendumIndex)))
+ .rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot cancel ongoing Democracy referendums', async ({helper}) => {
+ await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));
+ const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
+ const referendumIndex = startedEvent.referendumIndex;
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ helper.democracy.emergencyCancelCall(referendumIndex),
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil can\'t veto Democracy proposals', async ({helper}) => {
+ const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+ await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+ await expect(proposalFromAllFinCouncil(helper.democracy.vetoExternalCall(preimageHash)))
+ .rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member can\'t veto Democracy proposals', async ({helper}) => {
+ const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+ await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ helper.democracy.vetoExternalCall(preimageHash),
+ )).rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil cannot blacklist Democracy proposals', async ({helper}) => {
+ const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+ await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+ await expect(proposalFromAllFinCouncil(helper.democracy.blacklistCall(preimageHash))).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot blacklist Democracy proposals', async ({helper}) => {
+ const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+ await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ helper.democracy.blacklistCall(preimageHash),
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil can\'t cancel Fellowship referendums', async ({helper}) => {
+ const fellowship = await initFellowship(donor, sudoer);
+ const fellowshipProposer = fellowship[5][0];
+ const proposal = dummyProposal(helper);
+
+ const submitResult = await helper.fellowship.referenda.submit(
+ fellowshipProposer,
+ fellowshipPropositionOrigin,
+ proposal,
+ defaultEnactmentMoment,
+ );
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ await expect(proposalFromAllFinCouncil(helper.fellowship.referenda.cancelCall(referendumIndex)))
+ .rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot cancel Fellowship referendums', async ({helper}) => {
+ const fellowship = await initFellowship(donor, sudoer);
+ const fellowshipProposer = fellowship[5][0];
+ const proposal = dummyProposal(helper);
+
+ const submitResult = await helper.fellowship.referenda.submit(
+ fellowshipProposer,
+ fellowshipPropositionOrigin,
+ proposal,
+ defaultEnactmentMoment,
+ );
+
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ helper.fellowship.referenda.cancelCall(referendumIndex),
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil cannot add a Fellowship member', async ({helper}) => {
+ const newFellowshipMember = helper.arrange.createEmptyAccount();
+ const addMemberProposal = helper.fellowship.collective.addMemberCall(newFellowshipMember.address);
+
+ await expect(proposalFromAllFinCouncil(addMemberProposal)).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot add a Fellowship member', async ({helper}) => {
+ const newFellowshipMember = helper.arrange.createEmptyAccount();
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ helper.fellowship.collective.addMemberCall(newFellowshipMember.address),
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil cannot submit regular democracy proposal', async ({helper}) => {
+ const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);
+
+ await expect(proposalFromAllFinCouncil(councilProposal)).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil cannot externally propose SuperMajorityAgainst', async ({helper}) => {
+ const commiteeProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper));
+
+ await expect(proposalFromAllFinCouncil(commiteeProposal)).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot submit regular democracy proposal', async ({helper}) => {
+ const memberProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ memberProposal,
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil cannot externally propose SimpleMajority', async ({helper}) => {
+ const commiteeProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));
+
+ await expect(proposalFromAllFinCouncil(commiteeProposal)).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil cannot externally propose SuperMajorityApprove', async ({helper}) => {
+ const commiteeProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));
+
+ await expect(proposalFromAllFinCouncil(commiteeProposal)).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot externally propose SuperMajorityAgainst', async ({helper}) => {
+ const memberProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper));
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ memberProposal,
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot externally propose SimpleMajority', async ({helper}) => {
+ const memberProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ memberProposal,
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot externally propose SuperMajorityApprove', async ({helper}) => {
+ const memberProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ memberProposal,
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil cannot add/remove a Council member', async ({helper}) => {
+ const newCouncilMember = helper.arrange.createEmptyAccount();
+ const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);
+ const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address);
+
+ await expect(proposalFromAllFinCouncil(addMemberProposal)).to.be.rejectedWith('BadOrigin');
+ await expect(proposalFromAllFinCouncil(removeMemberProposal)).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot add/remove a Council member', async ({helper}) => {
+ const newCouncilMember = helper.arrange.createEmptyAccount();
+ const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);
+ const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address);
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ addMemberProposal,
+ )).to.be.rejectedWith('BadOrigin');
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ removeMemberProposal,
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil cannot add/remove a FinCouncil member', async ({helper}) => {
+ const newCouncilMember = helper.arrange.createEmptyAccount();
+ const addMemberProposal = helper.finCouncil.membership.addMemberCall(newCouncilMember.address);
+ const removeMemberProposal = helper.finCouncil.membership.removeMemberCall(finCounselors.ildar.address);
+
+ await expect(proposalFromAllFinCouncil(addMemberProposal)).to.be.rejectedWith('BadOrigin');
+ await expect(proposalFromAllFinCouncil(removeMemberProposal)).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil cannot add/remove a FinCouncil prime member', async ({helper}) => {
+ const setPrimeCall = helper.finCouncil.membership.setPrimeCall(finCounselors.andy.address);
+ const clearPrimeCall = helper.finCouncil.membership.clearPrimeCall();
+
+ await expect(proposalFromAllFinCouncil(setPrimeCall)).to.be.rejectedWith('BadOrigin');
+ await expect(proposalFromAllFinCouncil(clearPrimeCall)).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot add/remove a FinCouncil prime member', async ({helper}) => {
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ helper.finCouncil.membership.setPrimeCall(finCounselors.andy.address),
+ )).to.be.rejectedWith('BadOrigin');
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ helper.finCouncil.membership.clearPrimeCall(),
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot add/remove a FinCouncil member', async ({helper}) => {
+ const newCouncilMember = helper.arrange.createEmptyAccount();
+ const addMemberProposal = helper.finCouncil.membership.addMemberCall(newCouncilMember.address);
+ const removeMemberProposal = helper.finCouncil.membership.removeMemberCall(finCounselors.ildar.address);
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ addMemberProposal,
+ )).to.be.rejectedWith('BadOrigin');
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ removeMemberProposal,
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil cannot set/clear Council prime member', async ({helper}) => {
+ const counselors = await initCouncil(donor, sudoer);
+ const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);
+ const proposalForClear = await helper.council.membership.clearPrimeCall();
+
+ await expect(proposalFromAllFinCouncil(proposalForSet)).to.be.rejectedWith('BadOrigin');
+ await expect(proposalFromAllFinCouncil(proposalForClear)).to.be.rejectedWith('BadOrigin');
+ await clearCouncil(sudoer);
+ });
+
+ itSub('[Negative] FinCouncil member cannot set/clear Council prime member', async ({helper}) => {
+ const counselors = await initCouncil(donor, sudoer);
+ const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);
+ const proposalForClear = await helper.council.membership.clearPrimeCall();
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ proposalForSet,
+ )).to.be.rejectedWith('BadOrigin');
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ proposalForClear,
+ )).to.be.rejectedWith('BadOrigin');
+ await clearCouncil(sudoer);
+ });
+
+ itSub('[Negative] FinCouncil cannot add/remove a TechComm member', async ({helper}) => {
+ const newCommMember = helper.arrange.createEmptyAccount();
+ const addMemberProposal = helper.technicalCommittee.membership.addMemberCall(newCommMember.address);
+ const removeMemberProposal = helper.technicalCommittee.membership.removeMemberCall(newCommMember.address);
+
+ await expect(proposalFromAllFinCouncil(addMemberProposal)).to.be.rejectedWith('BadOrigin');
+ await expect(proposalFromAllFinCouncil(removeMemberProposal)).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil member cannot add/remove a TechComm member', async ({helper}) => {
+ const newCommMember = helper.arrange.createEmptyAccount();
+ const addMemberProposal = helper.technicalCommittee.membership.addMemberCall(newCommMember.address);
+ const removeMemberProposal = helper.technicalCommittee.membership.removeMemberCall(newCommMember.address);
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ addMemberProposal,
+ )).to.be.rejectedWith('BadOrigin');
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ removeMemberProposal,
+ )).to.be.rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] FinCouncil cannot remove a Fellowship member', async ({helper}) => {
+ const fellowship = await initFellowship(donor, sudoer);
+
+ await expect(proposalFromAllFinCouncil(helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5))).to.be.rejectedWith('BadOrigin');
+ await clearFellowship(sudoer);
+ });
+
+ itSub('[Negative] FinCouncil member cannot remove a Fellowship member', async ({helper}) => {
+ const fellowship = await initFellowship(donor, sudoer);
+
+ await expect(helper.finCouncil.collective.execute(
+ finCounselors.andy,
+ helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5),
+ )).to.be.rejectedWith('BadOrigin');
+ await clearFellowship(sudoer);
+ });
+
+
+});
js-packages/tests/sub/governance/technicalCommittee.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/governance/technicalCommittee.test.ts
+++ b/js-packages/tests/sub/governance/technicalCommittee.test.ts
@@ -130,6 +130,50 @@
await clearFellowship(sudoer);
});
+ itSub('[Negative] TechComm can\'t add FinCouncil member', async ({helper}) => {
+ const newFinCouncilMember = helper.arrange.createEmptyAccount();
+ const addMemberProposal = helper.finCouncil.membership.addMemberCall(newFinCouncilMember.address);
+ await expect(proposalFromAllCommittee(addMemberProposal)).rejectedWith('BadOrigin');
+
+ const finCouncilMembers = await helper.finCouncil.membership.getMembers();
+ expect(finCouncilMembers).to.not.contains(newFinCouncilMember.address);
+ });
+
+
+ itSub('[Negative] TechComm member can\'t add FinCouncil member', async ({helper}) => {
+ const newFinCouncilMember = helper.arrange.createEmptyAccount();
+ await expect(helper.technicalCommittee.collective.execute(
+ techcomms.greg,
+ helper.finCouncil.membership.addMemberCall(newFinCouncilMember.address),
+ )).rejectedWith('BadOrigin');
+ });
+
+ itSub('[Negative] TechComm member cannot register foreign asset', async ({helper}) => {
+ const location = {
+ parents: 1,
+ interior: {X3: [
+ {
+ Parachain: 1000,
+ },
+ {
+ PalletInstance: 50,
+ },
+ {
+ GeneralIndex: 1985,
+ },
+ ]},
+ };
+ const assetId = {Concrete: location};
+
+ const foreignAssetProposal = helper.constructApiCall(
+ 'api.tx.foreignAssets.forceRegisterForeignAsset',
+ [{V3: assetId}, helper.util.str2vec('New Asset2'), 'NEW', {Fungible: 10}],
+ );
+
+ await expect(helper.technicalCommittee.collective.execute(techcomms.andy, foreignAssetProposal))
+ .to.be.rejectedWith('BadOrigin');
+ });
+
itSub('[Negative] TechComm cannot submit regular democracy proposal', async ({helper}) => {
const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);
js-packages/tests/sub/governance/util.tsdiffbeforeafterboth1import type {IKeyringPair} from '@polkadot/types/types';2import {xxhashAsHex} from '@polkadot/util-crypto';3import type {u32} from '@polkadot/types-codec';4import {usingPlaygrounds, expect} from '@unique/test-utils/util.js';5import {UniqueHelper} from '@unique-nft/playgrounds/unique.js';6import {DevUniqueHelper} from '@unique/test-utils';78export const democracyLaunchPeriod = 35;9export const democracyVotingPeriod = 35;10export const councilMotionDuration = 35;11export const democracyEnactmentPeriod = 40;12export const democracyFastTrackVotingPeriod = 5;1314export const fellowshipRankLimit = 7;15export const fellowshipPropositionOrigin = 'FellowshipProposition';16export const fellowshipPreparePeriod = 3;17export const fellowshipConfirmPeriod = 3;18export const fellowshipMinEnactPeriod = 1;1920export const defaultEnactmentMoment = {After: 0};2122export const democracyTrackId = 10;23export const democracyTrackMinRank = 3;24const twox128 = (data: any) => xxhashAsHex(data, 128);25export interface ICounselors {26 alex: IKeyringPair;27 ildar: IKeyringPair;28 charu: IKeyringPair;29 filip: IKeyringPair;30 irina: IKeyringPair;31}32export interface ITechComms {33 greg: IKeyringPair;34 andy: IKeyringPair;35 constantine: IKeyringPair;36}3738export async function initCouncil(donor: IKeyringPair, superuser: IKeyringPair) {39 let counselors: IKeyringPair[] = [];4041 await usingPlaygrounds(async (helper) => {42 const [alex, ildar, charu, filip, irina] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n, 10_000n, 10_000n], donor);43 const sudo = helper.getSudo();44 {45 const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON() as [];46 if(members.length != 0) {47 await clearCouncil(superuser);48 }49 }50 const expectedMembers = [alex, ildar, charu, filip, irina];51 for(const member of expectedMembers) {52 await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.addMember', [member.address]);53 }54 await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.setPrime', [alex.address]);55 {56 const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();57 expect(members).to.containSubset(expectedMembers.map((x: IKeyringPair) => x.address));58 expect(members.length).to.be.equal(expectedMembers.length);59 }6061 counselors = [alex, ildar, charu, filip, irina];62 });63 return {64 alex: counselors[0],65 ildar: counselors[1],66 charu: counselors[2],67 filip: counselors[3],68 irina: counselors[4],69 };70}7172export async function clearCouncil(superuser: IKeyringPair) {73 await usingPlaygrounds(async (helper) => {74 let members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();75 if(members.length) {76 const sudo = helper.getSudo();77 for(const address of members) {78 await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.removeMember', [address]);79 }80 members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();81 }82 expect(members).to.be.deep.equal([]);83 });84}858687export async function initTechComm(donor: IKeyringPair, superuser: IKeyringPair) {88 let techcomms: IKeyringPair[] = [];8990 await usingPlaygrounds(async (helper) => {91 const [greg, andy, constantine] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n], donor);92 const sudo = helper.getSudo();93 {94 const members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON() as [];95 if(members.length != 0) {96 await clearTechComm(superuser);97 }98 }99 await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [greg.address]);100 await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [andy.address]);101 await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [constantine.address]);102 await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.setPrime', [greg.address]);103 {104 const members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();105 expect(members).to.containSubset([greg.address, andy.address, constantine.address]);106 expect(members.length).to.be.equal(3);107 }108109 techcomms = [greg, andy, constantine];110 });111112 return {113 greg: techcomms[0],114 andy: techcomms[1],115 constantine: techcomms[2],116 };117}118119export async function clearTechComm(superuser: IKeyringPair) {120 await usingPlaygrounds(async (helper) => {121 let members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();122 if(members.length) {123 const sudo = helper.getSudo();124 for(const address of members) {125 await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.removeMember', [address]);126 }127 members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();128 }129 expect(members).to.be.deep.equal([]);130 });131}132133export async function initFellowship(donor: IKeyringPair, sudoer: IKeyringPair) {134 const numMembersInRank = 3;135 const memberBalance = 5000n;136 const members: IKeyringPair[][] = [];137138 await usingPlaygrounds(async (helper) => {139 const currentFellows = await helper.getApi().query.fellowshipCollective.members.keys();140141 if(currentFellows.length != 0) {142 await clearFellowship(sudoer);143 }144 for(let i = 0; i < fellowshipRankLimit; i++) {145 const rankMembers = await helper.arrange.createAccounts(146 Array(numMembersInRank).fill(memberBalance),147 donor,148 );149150 for(const member of rankMembers) {151 await helper.getSudo().fellowship.collective.addMember(sudoer, member.address);152153 for(let rank = 0; rank < i; rank++) {154 await helper.getSudo().fellowship.collective.promote(sudoer, member.address);155 }156 }157158 members.push(rankMembers);159 }160 });161162 return members;163}164165export async function clearFellowship(sudoer: IKeyringPair) {166 await usingPlaygrounds(async (helper) => {167 const fellowship = (await helper.getApi().query.fellowshipCollective.members.keys())168 .map((key) => key.args[0].toString());169 for(const member of fellowship) {170 await helper.getSudo().fellowship.collective.removeMember(sudoer, member, fellowshipRankLimit);171 }172 });173}174175export async function clearFellowshipReferenda(sudoer: IKeyringPair) {176 await usingPlaygrounds(async (helper) => {177 const proposalsCount = (await helper.getApi().query.fellowshipReferenda.referendumCount()) as u32;178 for(let i = 0; i < proposalsCount.toNumber(); i++) {179 await helper.getSudo().fellowship.referenda.cancel(sudoer, i);180 }181 });182}183184export async function hardResetFellowshipReferenda(sudoer: IKeyringPair) {185 await usingPlaygrounds(async (helper) => {186 const api = helper.getApi();187 const prefix = twox128('FellowshipReferenda');188 await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 100)));189 });190}191192export async function hardResetDemocracy(sudoer: IKeyringPair) {193 await usingPlaygrounds(async (helper) => {194 const api = helper.getApi();195 const prefix = twox128('Democracy');196 await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 100)));197 });198}199200export async function hardResetGovScheduler(sudoer: IKeyringPair) {201 await usingPlaygrounds(async (helper) => {202 const api = helper.getApi();203 const prefix = twox128('GovScheduler');204 await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 500)));205 });206}207208export async function voteUnanimouslyInFellowship(helper: DevUniqueHelper, fellows: IKeyringPair[][], minRank: number, referendumIndex: number) {209 for(let rank = minRank; rank < fellowshipRankLimit; rank++) {210 for(const member of fellows[rank]) {211 await helper.fellowship.collective.vote(member, referendumIndex, true);212 }213 }214}215216export function dummyProposalCall(helper: UniqueHelper) {217 return helper.constructApiCall('api.tx.system.remark', ['dummy proposal' + (new Date()).getTime()]);218}219220export function dummyProposal(helper: UniqueHelper) {221 return {222 Inline: dummyProposalCall(helper).method.toHex(),223 };224}1import type {IKeyringPair} from '@polkadot/types/types';2import {xxhashAsHex} from '@polkadot/util-crypto';3import type {u32} from '@polkadot/types-codec';4import {usingPlaygrounds, expect} from '@unique/test-utils/util.js';5import {UniqueHelper} from '@unique-nft/playgrounds/unique.js';6import {DevUniqueHelper} from '@unique/test-utils';78export const democracyLaunchPeriod = 35;9export const democracyVotingPeriod = 35;10export const councilMotionDuration = 35;11export const democracyEnactmentPeriod = 40;12export const democracyFastTrackVotingPeriod = 5;1314export const fellowshipRankLimit = 7;15export const fellowshipPropositionOrigin = 'FellowshipProposition';16export const fellowshipPreparePeriod = 3;17export const fellowshipConfirmPeriod = 3;18export const fellowshipMinEnactPeriod = 1;1920export const defaultEnactmentMoment = {After: 0};2122export const democracyTrackId = 10;23export const democracyTrackMinRank = 3;24const twox128 = (data: any) => xxhashAsHex(data, 128);25export interface ICounselors {26 alex: IKeyringPair;27 ildar: IKeyringPair;28 charu: IKeyringPair;29 filip: IKeyringPair;30 irina: IKeyringPair;31}3233export interface IFinCounselors {34 greg: IKeyringPair;35 ildar: IKeyringPair;36 andy: IKeyringPair;37}3839export interface ITechComms {40 greg: IKeyringPair;41 andy: IKeyringPair;42 constantine: IKeyringPair;43}4445export function initFinCouncil(donor: IKeyringPair, superuser: IKeyringPair): Promise<IFinCounselors> {46 return usingPlaygrounds(async (helper) => {47 const [greg, ildar, andy] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n], donor);48 const sudo = helper.getSudo();49 {50 const members = (await helper.callRpc('api.query.financialCouncilMembership.members')).toJSON() as [];51 if(members.length != 0) {52 await clearFinCouncil(superuser);53 }54 }55 const expectedMembers = [greg, ildar, andy];56 for(const member of expectedMembers) {57 await sudo.executeExtrinsic(superuser, 'api.tx.financialCouncilMembership.addMember', [member.address]);58 }59 await sudo.executeExtrinsic(superuser, 'api.tx.financialCouncilMembership.setPrime', [greg.address]);60 {61 const members = (await helper.callRpc('api.query.financialCouncilMembership.members')).toJSON();62 expect(members).to.containSubset(expectedMembers.map((x: IKeyringPair) => x.address));63 expect(members.length).to.be.equal(expectedMembers.length);64 }6566 return {67 greg,68 ildar,69 andy,70 };71 });72}7374export async function clearFinCouncil(superuser: IKeyringPair) {75 await usingPlaygrounds(async (helper) => {76 let members = (await helper.callRpc('api.query.financialCouncilMembership.members')).toJSON();77 if(members.length) {78 const sudo = helper.getSudo();79 for(const address of members) {80 await sudo.executeExtrinsic(superuser, 'api.tx.financialCouncilMembership.removeMember', [address]);81 }82 members = (await helper.callRpc('api.query.financialCouncilMembership.members')).toJSON();83 }84 expect(members).to.be.deep.equal([]);85 });86}8788export async function initCouncil(donor: IKeyringPair, superuser: IKeyringPair) {89 let counselors: IKeyringPair[] = [];9091 await usingPlaygrounds(async (helper) => {92 const [alex, ildar, charu, filip, irina] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n, 10_000n, 10_000n], donor);93 const sudo = helper.getSudo();94 {95 const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON() as [];96 if(members.length != 0) {97 await clearCouncil(superuser);98 }99 }100 const expectedMembers = [alex, ildar, charu, filip, irina];101 for(const member of expectedMembers) {102 await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.addMember', [member.address]);103 }104 await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.setPrime', [alex.address]);105 {106 const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();107 expect(members).to.containSubset(expectedMembers.map((x: IKeyringPair) => x.address));108 expect(members.length).to.be.equal(expectedMembers.length);109 }110111 counselors = [alex, ildar, charu, filip, irina];112 });113 return {114 alex: counselors[0],115 ildar: counselors[1],116 charu: counselors[2],117 filip: counselors[3],118 irina: counselors[4],119 };120}121122export async function clearCouncil(superuser: IKeyringPair) {123 await usingPlaygrounds(async (helper) => {124 let members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();125 if(members.length) {126 const sudo = helper.getSudo();127 for(const address of members) {128 await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.removeMember', [address]);129 }130 members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();131 }132 expect(members).to.be.deep.equal([]);133 });134}135136137export async function initTechComm(donor: IKeyringPair, superuser: IKeyringPair) {138 let techcomms: IKeyringPair[] = [];139140 await usingPlaygrounds(async (helper) => {141 const [greg, andy, constantine] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n], donor);142 const sudo = helper.getSudo();143 {144 const members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON() as [];145 if(members.length != 0) {146 await clearTechComm(superuser);147 }148 }149 await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [greg.address]);150 await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [andy.address]);151 await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [constantine.address]);152 await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.setPrime', [greg.address]);153 {154 const members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();155 expect(members).to.containSubset([greg.address, andy.address, constantine.address]);156 expect(members.length).to.be.equal(3);157 }158159 techcomms = [greg, andy, constantine];160 });161162 return {163 greg: techcomms[0],164 andy: techcomms[1],165 constantine: techcomms[2],166 };167}168169export async function clearTechComm(superuser: IKeyringPair) {170 await usingPlaygrounds(async (helper) => {171 let members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();172 if(members.length) {173 const sudo = helper.getSudo();174 for(const address of members) {175 await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.removeMember', [address]);176 }177 members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();178 }179 expect(members).to.be.deep.equal([]);180 });181}182183export async function initFellowship(donor: IKeyringPair, sudoer: IKeyringPair) {184 const numMembersInRank = 3;185 const memberBalance = 5000n;186 const members: IKeyringPair[][] = [];187188 await usingPlaygrounds(async (helper) => {189 const currentFellows = await helper.getApi().query.fellowshipCollective.members.keys();190191 if(currentFellows.length != 0) {192 await clearFellowship(sudoer);193 }194 for(let i = 0; i < fellowshipRankLimit; i++) {195 const rankMembers = await helper.arrange.createAccounts(196 Array(numMembersInRank).fill(memberBalance),197 donor,198 );199200 for(const member of rankMembers) {201 await helper.getSudo().fellowship.collective.addMember(sudoer, member.address);202203 for(let rank = 0; rank < i; rank++) {204 await helper.getSudo().fellowship.collective.promote(sudoer, member.address);205 }206 }207208 members.push(rankMembers);209 }210 });211212 return members;213}214215export async function clearFellowship(sudoer: IKeyringPair) {216 await usingPlaygrounds(async (helper) => {217 const fellowship = (await helper.getApi().query.fellowshipCollective.members.keys())218 .map((key) => key.args[0].toString());219 for(const member of fellowship) {220 await helper.getSudo().fellowship.collective.removeMember(sudoer, member, fellowshipRankLimit);221 }222 });223}224225export async function clearFellowshipReferenda(sudoer: IKeyringPair) {226 await usingPlaygrounds(async (helper) => {227 const proposalsCount = (await helper.getApi().query.fellowshipReferenda.referendumCount()) as u32;228 for(let i = 0; i < proposalsCount.toNumber(); i++) {229 await helper.getSudo().fellowship.referenda.cancel(sudoer, i);230 }231 });232}233234export async function hardResetFellowshipReferenda(sudoer: IKeyringPair) {235 await usingPlaygrounds(async (helper) => {236 const api = helper.getApi();237 const prefix = twox128('FellowshipReferenda');238 await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 100)));239 });240}241242export async function hardResetDemocracy(sudoer: IKeyringPair) {243 await usingPlaygrounds(async (helper) => {244 const api = helper.getApi();245 const prefix = twox128('Democracy');246 await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 100)));247 });248}249250export async function hardResetGovScheduler(sudoer: IKeyringPair) {251 await usingPlaygrounds(async (helper) => {252 const api = helper.getApi();253 const prefix = twox128('GovScheduler');254 await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 500)));255 });256}257258export async function voteUnanimouslyInFellowship(helper: DevUniqueHelper, fellows: IKeyringPair[][], minRank: number, referendumIndex: number) {259 for(let rank = minRank; rank < fellowshipRankLimit; rank++) {260 for(const member of fellows[rank]) {261 await helper.fellowship.collective.vote(member, referendumIndex, true);262 }263 }264}265266export function dummyProposalCall(helper: UniqueHelper) {267 return helper.constructApiCall('api.tx.system.remark', ['dummy proposal' + (new Date()).getTime()]);268}269270export function dummyProposal(helper: UniqueHelper) {271 return {272 Inline: dummyProposalCall(helper).method.toHex(),273 };274}pallets/foreign-assets/src/lib.rsdiffbeforeafterboth--- a/pallets/foreign-assets/src/lib.rs
+++ b/pallets/foreign-assets/src/lib.rs
@@ -107,7 +107,7 @@
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// Origin for force registering of a foreign asset.
- type ForceRegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;
+ type ManagerOrigin: EnsureOrigin<Self::RuntimeOrigin>;
/// The ID of the foreign assets pallet.
type PalletId: Get<PalletId>;
@@ -197,7 +197,7 @@
token_prefix: CollectionTokenPrefix,
mode: ForeignCollectionMode,
) -> DispatchResult {
- T::ForceRegisterOrigin::ensure_origin(origin.clone())?;
+ T::ManagerOrigin::ensure_origin(origin.clone())?;
let asset_id: AssetId = versioned_asset_id
.as_ref()
runtime/common/config/governance/financial_council.rsdiffbeforeafterboth--- /dev/null
+++ b/runtime/common/config/governance/financial_council.rs
@@ -0,0 +1,56 @@
+use super::*;
+
+parameter_types! {
+ pub FinancialCouncilMaxProposals: u32 = 100;
+ pub FinancialCouncilMaxMembers: u32 = 100;
+}
+
+#[cfg(not(feature = "gov-test-timings"))]
+use crate::governance_timings::financial_council as financial_council_timings;
+
+#[cfg(feature = "gov-test-timings")]
+pub mod financial_council_timings {
+ use super::*;
+
+ parameter_types! {
+ pub FinancialCouncilMotionDuration: BlockNumber = 35;
+ }
+}
+
+pub type FinancialCollective = pallet_collective::Instance3;
+impl pallet_collective::Config<FinancialCollective> for Runtime {
+ type RuntimeOrigin = RuntimeOrigin;
+ type Proposal = RuntimeCall;
+ type RuntimeEvent = RuntimeEvent;
+ type MotionDuration = financial_council_timings::FinancialCouncilMotionDuration;
+ type MaxProposals = FinancialCouncilMaxProposals;
+ type MaxMembers = FinancialCouncilMaxMembers;
+ type DefaultVote = pallet_collective::PrimeDefaultVote;
+ type WeightInfo = pallet_collective::weights::SubstrateWeight<Runtime>;
+ type SetMembersOrigin = EnsureRoot<AccountId>;
+ type MaxProposalWeight = MaxCollectivesProposalWeight;
+}
+
+pub type FinancialCollectiveMembership = pallet_membership::Instance3;
+impl pallet_membership::Config<FinancialCollectiveMembership> for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type AddOrigin = RootOrMoreThanHalfCouncil;
+ type RemoveOrigin = RootOrMoreThanHalfCouncil;
+ type SwapOrigin = RootOrMoreThanHalfCouncil;
+ type ResetOrigin = EnsureRoot<AccountId>;
+ type PrimeOrigin = RootOrMoreThanHalfCouncil;
+ type MembershipInitialized = FinancialCouncil;
+ type MembershipChanged = FinancialCouncil;
+ type MaxMembers = FinancialCouncilMaxMembers;
+ type WeightInfo = pallet_membership::weights::SubstrateWeight<Runtime>;
+}
+
+pub type FinancialCouncilMember = pallet_collective::EnsureMember<AccountId, FinancialCollective>;
+
+pub type RootOrFinancialCouncilMember =
+ EitherOfDiverse<EnsureRoot<AccountId>, FinancialCouncilMember>;
+
+pub type AllFinancialCouncil =
+ pallet_collective::EnsureProportionAtLeast<AccountId, FinancialCollective, 1, 1>;
+
+pub type RootOrAllFinancialCouncil = EitherOfDiverse<EnsureRoot<AccountId>, AllFinancialCouncil>;
runtime/common/config/governance/mod.rsdiffbeforeafterboth--- a/runtime/common/config/governance/mod.rs
+++ b/runtime/common/config/governance/mod.rs
@@ -36,8 +36,8 @@
};
use crate::{
- Balances, Council, OriginCaller, Preimage, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin,
- Scheduler, TechnicalCommittee, Treasury,
+ Balances, Council, FinancialCouncil, OriginCaller, Preimage, Runtime, RuntimeCall,
+ RuntimeEvent, RuntimeOrigin, Scheduler, TechnicalCommittee, Treasury,
};
pub mod council;
@@ -49,6 +49,9 @@
pub mod technical_committee;
pub use technical_committee::*;
+pub mod financial_council;
+pub use financial_council::*;
+
pub mod fellowship;
pub use fellowship::*;
runtime/common/config/pallets/foreign_asset.rsdiffbeforeafterboth--- a/runtime/common/config/pallets/foreign_asset.rs
+++ b/runtime/common/config/pallets/foreign_asset.rs
@@ -40,10 +40,10 @@
type RuntimeEvent = RuntimeEvent;
#[cfg(feature = "governance")]
- type ForceRegisterOrigin = governance::RootOrTechnicalCommitteeMember;
+ type ManagerOrigin = governance::RootOrFinancialCouncilMember;
#[cfg(not(feature = "governance"))]
- type ForceRegisterOrigin = EnsureRoot<Self::AccountId>;
+ type ManagerOrigin = EnsureRoot<Self::AccountId>;
type PalletId = ForeignAssetPalletId;
type SelfLocation = SelfLocation;
runtime/common/construct_runtime.rsdiffbeforeafterboth--- a/runtime/common/construct_runtime.rs
+++ b/runtime/common/construct_runtime.rs
@@ -81,6 +81,12 @@
Scheduler: pallet_scheduler = 49,
#[cfg(feature = "governance")]
+ FinancialCouncil: pallet_collective::<Instance3> = 97,
+
+ #[cfg(feature = "governance")]
+ FinancialCouncilMembership: pallet_membership::<Instance3> = 98,
+
+ #[cfg(feature = "governance")]
Origins: pallet_gov_origins = 99,
// XCM helpers.
runtime/opal/src/governance_timings.rsdiffbeforeafterboth--- a/runtime/opal/src/governance_timings.rs
+++ b/runtime/opal/src/governance_timings.rs
@@ -52,3 +52,11 @@
pub TechnicalMotionDuration: BlockNumber = 15 * MINUTES;
}
}
+
+pub mod financial_council {
+ use super::*;
+
+ parameter_types! {
+ pub FinancialCouncilMotionDuration: BlockNumber = 15 * MINUTES;
+ }
+}
runtime/quartz/src/governance_timings.rsdiffbeforeafterboth--- a/runtime/quartz/src/governance_timings.rs
+++ b/runtime/quartz/src/governance_timings.rs
@@ -52,3 +52,11 @@
pub TechnicalMotionDuration: BlockNumber = 3 * DAYS;
}
}
+
+pub mod financial_council {
+ use super::*;
+
+ parameter_types! {
+ pub FinancialCouncilMotionDuration: BlockNumber = 3 * DAYS;
+ }
+}
runtime/unique/src/governance_timings.rsdiffbeforeafterboth--- a/runtime/unique/src/governance_timings.rs
+++ b/runtime/unique/src/governance_timings.rs
@@ -52,3 +52,11 @@
pub TechnicalMotionDuration: BlockNumber = 3 * DAYS;
}
}
+
+pub mod financial_council {
+ use super::*;
+
+ parameter_types! {
+ pub FinancialCouncilMotionDuration: BlockNumber = 3 * DAYS;
+ }
+}