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.tsdiffbeforeafterboth1import type {IKeyringPair} from '@polkadot/types/types';2import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '@unique/test-utils/util.js';3import {Event} from '@unique/test-utils';4import {initCouncil, democracyLaunchPeriod, democracyFastTrackVotingPeriod, clearCouncil, clearTechComm, clearFellowship, defaultEnactmentMoment, dummyProposal, dummyProposalCall, fellowshipPropositionOrigin, initFellowship, initTechComm, hardResetFellowshipReferenda, hardResetDemocracy, hardResetGovScheduler} from './util.js';5import type {ITechComms} from './util.js';67describeGov('Governance: Technical Committee tests', () => {8 let sudoer: IKeyringPair;9 let techcomms: ITechComms;10 let donor: IKeyringPair;11 let preImageHash: string;121314 const allTechCommitteeThreshold = 3;1516 before(async function() {17 await usingPlaygrounds(async (helper, privateKey) => {18 requirePalletsOrSkip(this, helper, [Pallets.TechnicalCommittee]);19 sudoer = await privateKey('//Alice');20 donor = await privateKey({url: import.meta.url});2122 techcomms = await initTechComm(donor, sudoer);2324 const proposalCall = await helper.constructApiCall('api.tx.balances.forceSetBalance', [donor.address, 20n * 10n ** 25n]);25 preImageHash = await helper.preimage.notePreimageFromCall(sudoer, proposalCall, true);26 });27 });2829 after(async () => {30 await usingPlaygrounds(async (helper) => {31 await clearTechComm(sudoer);3233 await helper.preimage.unnotePreimage(sudoer, preImageHash);34 await hardResetFellowshipReferenda(sudoer);35 await hardResetDemocracy(sudoer);36 await hardResetGovScheduler(sudoer);37 });38 });3940 function proposalFromAllCommittee(proposal: any) {41 return usingPlaygrounds(async (helper) => {42 expect((await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON().length).to.be.equal(allTechCommitteeThreshold);43 const proposeResult = await helper.technicalCommittee.collective.propose(44 techcomms.andy,45 proposal,46 allTechCommitteeThreshold,47 );4849 const commiteeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult);50 const proposalIndex = commiteeProposedEvent.proposalIndex;51 const proposalHash = commiteeProposedEvent.proposalHash;525354 await helper.technicalCommittee.collective.vote(techcomms.andy, proposalHash, proposalIndex, true);55 await helper.technicalCommittee.collective.vote(techcomms.constantine, proposalHash, proposalIndex, true);56 await helper.technicalCommittee.collective.vote(techcomms.greg, proposalHash, proposalIndex, true);5758 const closeResult = await helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex);59 Event.TechnicalCommittee.Closed.expect(closeResult);60 Event.TechnicalCommittee.Approved.expect(closeResult);61 const {result} = Event.TechnicalCommittee.Executed.expect(closeResult);62 expect(result).to.eq('Ok');6364 return closeResult;65 });66 }6768 itSub('TechComm can fast-track Democracy proposals', async ({helper}) => {69 const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);70 await helper.wait.parachainBlockMultiplesOf(35n);7172 await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);7374 const fastTrackProposal = await proposalFromAllCommittee(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0));75 Event.Democracy.Started.expect(fastTrackProposal);76 });7778 itSub('TechComm can cancel Democracy proposals', async ({helper}) => {79 const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);80 const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;8182 const cancelProposal = await proposalFromAllCommittee(helper.democracy.cancelProposalCall(proposalIndex));83 Event.Democracy.ProposalCanceled.expect(cancelProposal);84 });8586 itSub('TechComm can cancel ongoing Democracy referendums', async ({helper}) => {87 await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));88 const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);89 const referendumIndex = startedEvent.referendumIndex;9091 const emergencyCancelProposal = await proposalFromAllCommittee(helper.democracy.emergencyCancelCall(referendumIndex));92 Event.Democracy.Cancelled.expect(emergencyCancelProposal);93 });9495 itSub('TechComm member can veto Democracy proposals', async ({helper}) => {96 const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);97 await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);9899 const vetoExternalCall = await helper.technicalCommittee.collective.execute(100 techcomms.andy,101 helper.democracy.vetoExternalCall(preimageHash),102 );103 Event.Democracy.Vetoed.expect(vetoExternalCall);104 });105106 itSub('TechComm can cancel Fellowship referendums', async ({helper}) => {107 const fellowship = await initFellowship(donor, sudoer);108 const fellowshipProposer = fellowship[5][0];109 const proposal = dummyProposal(helper);110111 const submitResult = await helper.fellowship.referenda.submit(112 fellowshipProposer,113 fellowshipPropositionOrigin,114 proposal,115 defaultEnactmentMoment,116 );117 const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;118 const cancelProposal = await proposalFromAllCommittee(helper.fellowship.referenda.cancelCall(referendumIndex));119 Event.FellowshipReferenda.Cancelled.expect(cancelProposal);120 });121122 itSub.skip('TechComm member can add a Fellowship member', async ({helper}) => {123 const newFellowshipMember = helper.arrange.createEmptyAccount();124 await expect(helper.technicalCommittee.collective.execute(125 techcomms.andy,126 helper.fellowship.collective.addMemberCall(newFellowshipMember.address),127 )).to.be.fulfilled;128 const fellowshipMembers = (await helper.callRpc('api.query.fellowshipCollective.members')).toJSON();129 expect(fellowshipMembers).to.contains(newFellowshipMember.address);130 await clearFellowship(sudoer);131 });132133 itSub('[Negative] TechComm cannot submit regular democracy proposal', async ({helper}) => {134 const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);135136 await expect(proposalFromAllCommittee(councilProposal)).to.be.rejectedWith('BadOrigin');137 });138139 itSub('[Negative] TechComm cannot externally propose SuperMajorityAgainst', async ({helper}) => {140 const commiteeProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper));141142 await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin');143 });144145 itSub('[Negative] TechComm cannot externally propose SimpleMajority', async ({helper}) => {146 const commiteeProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));147148 await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin');149 });150151 itSub('[Negative] TechComm cannot externally propose SuperMajorityApprove', async ({helper}) => {152 const commiteeProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));153154 await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin');155 });156157 itSub('[Negative] TechComm member cannot submit regular democracy proposal', async ({helper}) => {158 const memberProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);159160 await expect(helper.technicalCommittee.collective.execute(161 techcomms.andy,162 memberProposal,163 )).to.be.rejectedWith('BadOrigin');164 });165166 itSub('[Negative] TechComm member cannot externally propose SuperMajorityAgainst', async ({helper}) => {167 const memberProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper));168169 await expect(helper.technicalCommittee.collective.execute(170 techcomms.andy,171 memberProposal,172 )).to.be.rejectedWith('BadOrigin');173 });174175 itSub('[Negative] TechComm member cannot externally propose SimpleMajority', async ({helper}) => {176 const memberProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));177178 await expect(helper.technicalCommittee.collective.execute(179 techcomms.andy,180 memberProposal,181 )).to.be.rejectedWith('BadOrigin');182 });183184 itSub('[Negative] TechComm member cannot externally propose SuperMajorityApprove', async ({helper}) => {185 const memberProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));186187 await expect(helper.technicalCommittee.collective.execute(188 techcomms.andy,189 memberProposal,190 )).to.be.rejectedWith('BadOrigin');191 });192193194 itSub.skip('[Negative] TechComm cannot promote/demote Fellowship member', async () => {195196 });197198 itSub.skip('[Negative] TechComm member cannot promote/demote Fellowship member', async () => {199200 });201202 itSub('[Negative] TechComm cannot add/remove a Council member', async ({helper}) => {203 const newCouncilMember = helper.arrange.createEmptyAccount();204 const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);205 const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address);206207 await expect(proposalFromAllCommittee(addMemberProposal)).to.be.rejectedWith('BadOrigin');208 await expect(proposalFromAllCommittee(removeMemberProposal)).to.be.rejectedWith('BadOrigin');209 });210211 itSub('[Negative] TechComm member cannot add/remove a Council member', async ({helper}) => {212 const newCouncilMember = helper.arrange.createEmptyAccount();213 const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);214 const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address);215216 await expect(helper.technicalCommittee.collective.execute(217 techcomms.andy,218 addMemberProposal,219 )).to.be.rejectedWith('BadOrigin');220 await expect(helper.technicalCommittee.collective.execute(221 techcomms.andy,222 removeMemberProposal,223 )).to.be.rejectedWith('BadOrigin');224 });225226 itSub('[Negative] TechComm cannot set/clear Council prime member', async ({helper}) => {227 const counselors = await initCouncil(donor, sudoer);228 const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);229 const proposalForClear = await helper.council.membership.clearPrimeCall();230231 await expect(proposalFromAllCommittee(proposalForSet)).to.be.rejectedWith('BadOrigin');232 await expect(proposalFromAllCommittee(proposalForClear)).to.be.rejectedWith('BadOrigin');233 await clearCouncil(sudoer);234 });235236 itSub('[Negative] TechComm member cannot set/clear Council prime member', async ({helper}) => {237 const counselors = await initCouncil(donor, sudoer);238 const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);239 const proposalForClear = await helper.council.membership.clearPrimeCall();240241 await expect(helper.technicalCommittee.collective.execute(242 techcomms.andy,243 proposalForSet,244 )).to.be.rejectedWith('BadOrigin');245 await expect(helper.technicalCommittee.collective.execute(246 techcomms.andy,247 proposalForClear,248 )).to.be.rejectedWith('BadOrigin');249 await clearCouncil(sudoer);250 });251252 itSub('[Negative] TechComm cannot add/remove a TechComm member', async ({helper}) => {253 const newCommMember = helper.arrange.createEmptyAccount();254 const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address);255 const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address);256257 await expect(proposalFromAllCommittee(addMemberProposal)).to.be.rejectedWith('BadOrigin');258 await expect(proposalFromAllCommittee(removeMemberProposal)).to.be.rejectedWith('BadOrigin');259 });260261 itSub('[Negative] TechComm member cannot add/remove a TechComm member', async ({helper}) => {262 const newCommMember = helper.arrange.createEmptyAccount();263 const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address);264 const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address);265266 await expect(helper.technicalCommittee.collective.execute(267 techcomms.andy,268 addMemberProposal,269 )).to.be.rejectedWith('BadOrigin');270 await expect(helper.technicalCommittee.collective.execute(271 techcomms.andy,272 removeMemberProposal,273 )).to.be.rejectedWith('BadOrigin');274 });275276 itSub('[Negative] TechComm cannot remove a Fellowship member', async ({helper}) => {277 const fellowship = await initFellowship(donor, sudoer);278279 await expect(proposalFromAllCommittee(helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5))).to.be.rejectedWith('BadOrigin');280 await clearFellowship(sudoer);281 });282283 itSub('[Negative] TechComm member cannot remove a Fellowship member', async ({helper}) => {284 const fellowship = await initFellowship(donor, sudoer);285286 await expect(helper.technicalCommittee.collective.execute(287 techcomms.andy,288 helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5),289 )).to.be.rejectedWith('BadOrigin');290 await clearFellowship(sudoer);291 });292293 itSub('[Negative] TechComm member cannot fast-track Democracy proposals', async ({helper}) => {294 const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);295 await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);296297 await expect(helper.technicalCommittee.collective.execute(298 techcomms.andy,299 helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0),300 )).to.be.rejectedWith('BadOrigin');301 });302303 itSub('[Negative] TechComm member cannot cancel Democracy proposals', async ({helper}) => {304 const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);305 const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;306307 await expect(helper.technicalCommittee.collective.execute(308 techcomms.andy,309 helper.democracy.cancelProposalCall(proposalIndex),310 ))311 .to.be.rejectedWith('BadOrigin');312 });313314 itSub('[Negative] TechComm member cannot cancel ongoing Democracy referendums', async ({helper}) => {315 await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));316 const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);317 const referendumIndex = startedEvent.referendumIndex;318319 await expect(helper.technicalCommittee.collective.execute(320 techcomms.andy,321 helper.democracy.emergencyCancelCall(referendumIndex),322 )).to.be.rejectedWith('BadOrigin');323 });324325 itSub('[Negative] TechComm cannot blacklist Democracy proposals', async ({helper}) => {326 const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);327 await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);328329 await expect(proposalFromAllCommittee(helper.democracy.blacklistCall(preimageHash))).to.be.rejectedWith('BadOrigin');330 });331332 itSub('[Negative] TechComm member cannot blacklist Democracy proposals', async ({helper}) => {333 const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);334 await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);335336 await expect(helper.technicalCommittee.collective.execute(337 techcomms.andy,338 helper.democracy.blacklistCall(preimageHash),339 )).to.be.rejectedWith('BadOrigin');340 });341342 itSub.skip('[Negative] TechComm member cannot veto external Democracy proposals until the cool-off period pass', async () => {343344 });345346 itSub('[Negative] TechComm member cannot cancel Fellowship referendums', async ({helper}) => {347 const fellowship = await initFellowship(donor, sudoer);348 const fellowshipProposer = fellowship[5][0];349 const proposal = dummyProposal(helper);350351 const submitResult = await helper.fellowship.referenda.submit(352 fellowshipProposer,353 fellowshipPropositionOrigin,354 proposal,355 defaultEnactmentMoment,356 );357358 const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;359360 await expect(helper.technicalCommittee.collective.execute(361 techcomms.andy,362 helper.fellowship.referenda.cancelCall(referendumIndex),363 )).to.be.rejectedWith('BadOrigin');364 });365366 itSub('[Negative] TechComm referendum cannot be closed until the voting threshold is met', async ({helper}) => {367 const committeeSize = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON().length as any as number;368 expect(committeeSize).is.greaterThan(1);369 const proposeResult = await helper.technicalCommittee.collective.propose(370 techcomms.andy,371 dummyProposalCall(helper),372 committeeSize,373 );374375 const committeeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult);376 const proposalIndex = committeeProposedEvent.proposalIndex;377 const proposalHash = committeeProposedEvent.proposalHash;378379 await helper.technicalCommittee.collective.vote(techcomms.constantine, proposalHash, proposalIndex, true);380381 await expect(helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex)).to.be.rejectedWith('TooEarly');382 });383});1import type {IKeyringPair} from '@polkadot/types/types';2import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '@unique/test-utils/util.js';3import {Event} from '@unique/test-utils';4import {initCouncil, democracyLaunchPeriod, democracyFastTrackVotingPeriod, clearCouncil, clearTechComm, clearFellowship, defaultEnactmentMoment, dummyProposal, dummyProposalCall, fellowshipPropositionOrigin, initFellowship, initTechComm, hardResetFellowshipReferenda, hardResetDemocracy, hardResetGovScheduler} from './util.js';5import type {ITechComms} from './util.js';67describeGov('Governance: Technical Committee tests', () => {8 let sudoer: IKeyringPair;9 let techcomms: ITechComms;10 let donor: IKeyringPair;11 let preImageHash: string;121314 const allTechCommitteeThreshold = 3;1516 before(async function() {17 await usingPlaygrounds(async (helper, privateKey) => {18 requirePalletsOrSkip(this, helper, [Pallets.TechnicalCommittee]);19 sudoer = await privateKey('//Alice');20 donor = await privateKey({url: import.meta.url});2122 techcomms = await initTechComm(donor, sudoer);2324 const proposalCall = await helper.constructApiCall('api.tx.balances.forceSetBalance', [donor.address, 20n * 10n ** 25n]);25 preImageHash = await helper.preimage.notePreimageFromCall(sudoer, proposalCall, true);26 });27 });2829 after(async () => {30 await usingPlaygrounds(async (helper) => {31 await clearTechComm(sudoer);3233 await helper.preimage.unnotePreimage(sudoer, preImageHash);34 await hardResetFellowshipReferenda(sudoer);35 await hardResetDemocracy(sudoer);36 await hardResetGovScheduler(sudoer);37 });38 });3940 function proposalFromAllCommittee(proposal: any) {41 return usingPlaygrounds(async (helper) => {42 expect((await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON().length).to.be.equal(allTechCommitteeThreshold);43 const proposeResult = await helper.technicalCommittee.collective.propose(44 techcomms.andy,45 proposal,46 allTechCommitteeThreshold,47 );4849 const commiteeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult);50 const proposalIndex = commiteeProposedEvent.proposalIndex;51 const proposalHash = commiteeProposedEvent.proposalHash;525354 await helper.technicalCommittee.collective.vote(techcomms.andy, proposalHash, proposalIndex, true);55 await helper.technicalCommittee.collective.vote(techcomms.constantine, proposalHash, proposalIndex, true);56 await helper.technicalCommittee.collective.vote(techcomms.greg, proposalHash, proposalIndex, true);5758 const closeResult = await helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex);59 Event.TechnicalCommittee.Closed.expect(closeResult);60 Event.TechnicalCommittee.Approved.expect(closeResult);61 const {result} = Event.TechnicalCommittee.Executed.expect(closeResult);62 expect(result).to.eq('Ok');6364 return closeResult;65 });66 }6768 itSub('TechComm can fast-track Democracy proposals', async ({helper}) => {69 const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);70 await helper.wait.parachainBlockMultiplesOf(35n);7172 await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);7374 const fastTrackProposal = await proposalFromAllCommittee(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0));75 Event.Democracy.Started.expect(fastTrackProposal);76 });7778 itSub('TechComm can cancel Democracy proposals', async ({helper}) => {79 const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);80 const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;8182 const cancelProposal = await proposalFromAllCommittee(helper.democracy.cancelProposalCall(proposalIndex));83 Event.Democracy.ProposalCanceled.expect(cancelProposal);84 });8586 itSub('TechComm can cancel ongoing Democracy referendums', async ({helper}) => {87 await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));88 const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);89 const referendumIndex = startedEvent.referendumIndex;9091 const emergencyCancelProposal = await proposalFromAllCommittee(helper.democracy.emergencyCancelCall(referendumIndex));92 Event.Democracy.Cancelled.expect(emergencyCancelProposal);93 });9495 itSub('TechComm member can veto Democracy proposals', async ({helper}) => {96 const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);97 await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);9899 const vetoExternalCall = await helper.technicalCommittee.collective.execute(100 techcomms.andy,101 helper.democracy.vetoExternalCall(preimageHash),102 );103 Event.Democracy.Vetoed.expect(vetoExternalCall);104 });105106 itSub('TechComm can cancel Fellowship referendums', async ({helper}) => {107 const fellowship = await initFellowship(donor, sudoer);108 const fellowshipProposer = fellowship[5][0];109 const proposal = dummyProposal(helper);110111 const submitResult = await helper.fellowship.referenda.submit(112 fellowshipProposer,113 fellowshipPropositionOrigin,114 proposal,115 defaultEnactmentMoment,116 );117 const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;118 const cancelProposal = await proposalFromAllCommittee(helper.fellowship.referenda.cancelCall(referendumIndex));119 Event.FellowshipReferenda.Cancelled.expect(cancelProposal);120 });121122 itSub.skip('TechComm member can add a Fellowship member', async ({helper}) => {123 const newFellowshipMember = helper.arrange.createEmptyAccount();124 await expect(helper.technicalCommittee.collective.execute(125 techcomms.andy,126 helper.fellowship.collective.addMemberCall(newFellowshipMember.address),127 )).to.be.fulfilled;128 const fellowshipMembers = (await helper.callRpc('api.query.fellowshipCollective.members')).toJSON();129 expect(fellowshipMembers).to.contains(newFellowshipMember.address);130 await clearFellowship(sudoer);131 });132133 itSub('[Negative] TechComm can\'t add FinCouncil member', async ({helper}) => {134 const newFinCouncilMember = helper.arrange.createEmptyAccount();135 const addMemberProposal = helper.finCouncil.membership.addMemberCall(newFinCouncilMember.address);136 await expect(proposalFromAllCommittee(addMemberProposal)).rejectedWith('BadOrigin');137138 const finCouncilMembers = await helper.finCouncil.membership.getMembers();139 expect(finCouncilMembers).to.not.contains(newFinCouncilMember.address);140 });141142143 itSub('[Negative] TechComm member can\'t add FinCouncil member', async ({helper}) => {144 const newFinCouncilMember = helper.arrange.createEmptyAccount();145 await expect(helper.technicalCommittee.collective.execute(146 techcomms.greg,147 helper.finCouncil.membership.addMemberCall(newFinCouncilMember.address),148 )).rejectedWith('BadOrigin');149 });150151 itSub('[Negative] TechComm member cannot register foreign asset', async ({helper}) => {152 const location = {153 parents: 1,154 interior: {X3: [155 {156 Parachain: 1000,157 },158 {159 PalletInstance: 50,160 },161 {162 GeneralIndex: 1985,163 },164 ]},165 };166 const assetId = {Concrete: location};167168 const foreignAssetProposal = helper.constructApiCall(169 'api.tx.foreignAssets.forceRegisterForeignAsset',170 [{V3: assetId}, helper.util.str2vec('New Asset2'), 'NEW', {Fungible: 10}],171 );172173 await expect(helper.technicalCommittee.collective.execute(techcomms.andy, foreignAssetProposal))174 .to.be.rejectedWith('BadOrigin');175 });176177 itSub('[Negative] TechComm cannot submit regular democracy proposal', async ({helper}) => {178 const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);179180 await expect(proposalFromAllCommittee(councilProposal)).to.be.rejectedWith('BadOrigin');181 });182183 itSub('[Negative] TechComm cannot externally propose SuperMajorityAgainst', async ({helper}) => {184 const commiteeProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper));185186 await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin');187 });188189 itSub('[Negative] TechComm cannot externally propose SimpleMajority', async ({helper}) => {190 const commiteeProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));191192 await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin');193 });194195 itSub('[Negative] TechComm cannot externally propose SuperMajorityApprove', async ({helper}) => {196 const commiteeProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));197198 await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin');199 });200201 itSub('[Negative] TechComm member cannot submit regular democracy proposal', async ({helper}) => {202 const memberProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);203204 await expect(helper.technicalCommittee.collective.execute(205 techcomms.andy,206 memberProposal,207 )).to.be.rejectedWith('BadOrigin');208 });209210 itSub('[Negative] TechComm member cannot externally propose SuperMajorityAgainst', async ({helper}) => {211 const memberProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper));212213 await expect(helper.technicalCommittee.collective.execute(214 techcomms.andy,215 memberProposal,216 )).to.be.rejectedWith('BadOrigin');217 });218219 itSub('[Negative] TechComm member cannot externally propose SimpleMajority', async ({helper}) => {220 const memberProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));221222 await expect(helper.technicalCommittee.collective.execute(223 techcomms.andy,224 memberProposal,225 )).to.be.rejectedWith('BadOrigin');226 });227228 itSub('[Negative] TechComm member cannot externally propose SuperMajorityApprove', async ({helper}) => {229 const memberProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));230231 await expect(helper.technicalCommittee.collective.execute(232 techcomms.andy,233 memberProposal,234 )).to.be.rejectedWith('BadOrigin');235 });236237238 itSub.skip('[Negative] TechComm cannot promote/demote Fellowship member', async () => {239240 });241242 itSub.skip('[Negative] TechComm member cannot promote/demote Fellowship member', async () => {243244 });245246 itSub('[Negative] TechComm cannot add/remove a Council member', async ({helper}) => {247 const newCouncilMember = helper.arrange.createEmptyAccount();248 const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);249 const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address);250251 await expect(proposalFromAllCommittee(addMemberProposal)).to.be.rejectedWith('BadOrigin');252 await expect(proposalFromAllCommittee(removeMemberProposal)).to.be.rejectedWith('BadOrigin');253 });254255 itSub('[Negative] TechComm member cannot add/remove a Council member', async ({helper}) => {256 const newCouncilMember = helper.arrange.createEmptyAccount();257 const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);258 const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address);259260 await expect(helper.technicalCommittee.collective.execute(261 techcomms.andy,262 addMemberProposal,263 )).to.be.rejectedWith('BadOrigin');264 await expect(helper.technicalCommittee.collective.execute(265 techcomms.andy,266 removeMemberProposal,267 )).to.be.rejectedWith('BadOrigin');268 });269270 itSub('[Negative] TechComm cannot set/clear Council prime member', async ({helper}) => {271 const counselors = await initCouncil(donor, sudoer);272 const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);273 const proposalForClear = await helper.council.membership.clearPrimeCall();274275 await expect(proposalFromAllCommittee(proposalForSet)).to.be.rejectedWith('BadOrigin');276 await expect(proposalFromAllCommittee(proposalForClear)).to.be.rejectedWith('BadOrigin');277 await clearCouncil(sudoer);278 });279280 itSub('[Negative] TechComm member cannot set/clear Council prime member', async ({helper}) => {281 const counselors = await initCouncil(donor, sudoer);282 const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);283 const proposalForClear = await helper.council.membership.clearPrimeCall();284285 await expect(helper.technicalCommittee.collective.execute(286 techcomms.andy,287 proposalForSet,288 )).to.be.rejectedWith('BadOrigin');289 await expect(helper.technicalCommittee.collective.execute(290 techcomms.andy,291 proposalForClear,292 )).to.be.rejectedWith('BadOrigin');293 await clearCouncil(sudoer);294 });295296 itSub('[Negative] TechComm cannot add/remove a TechComm member', async ({helper}) => {297 const newCommMember = helper.arrange.createEmptyAccount();298 const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address);299 const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address);300301 await expect(proposalFromAllCommittee(addMemberProposal)).to.be.rejectedWith('BadOrigin');302 await expect(proposalFromAllCommittee(removeMemberProposal)).to.be.rejectedWith('BadOrigin');303 });304305 itSub('[Negative] TechComm member cannot add/remove a TechComm member', async ({helper}) => {306 const newCommMember = helper.arrange.createEmptyAccount();307 const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address);308 const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address);309310 await expect(helper.technicalCommittee.collective.execute(311 techcomms.andy,312 addMemberProposal,313 )).to.be.rejectedWith('BadOrigin');314 await expect(helper.technicalCommittee.collective.execute(315 techcomms.andy,316 removeMemberProposal,317 )).to.be.rejectedWith('BadOrigin');318 });319320 itSub('[Negative] TechComm cannot remove a Fellowship member', async ({helper}) => {321 const fellowship = await initFellowship(donor, sudoer);322323 await expect(proposalFromAllCommittee(helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5))).to.be.rejectedWith('BadOrigin');324 await clearFellowship(sudoer);325 });326327 itSub('[Negative] TechComm member cannot remove a Fellowship member', async ({helper}) => {328 const fellowship = await initFellowship(donor, sudoer);329330 await expect(helper.technicalCommittee.collective.execute(331 techcomms.andy,332 helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5),333 )).to.be.rejectedWith('BadOrigin');334 await clearFellowship(sudoer);335 });336337 itSub('[Negative] TechComm member cannot fast-track Democracy proposals', async ({helper}) => {338 const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);339 await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);340341 await expect(helper.technicalCommittee.collective.execute(342 techcomms.andy,343 helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0),344 )).to.be.rejectedWith('BadOrigin');345 });346347 itSub('[Negative] TechComm member cannot cancel Democracy proposals', async ({helper}) => {348 const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);349 const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;350351 await expect(helper.technicalCommittee.collective.execute(352 techcomms.andy,353 helper.democracy.cancelProposalCall(proposalIndex),354 ))355 .to.be.rejectedWith('BadOrigin');356 });357358 itSub('[Negative] TechComm member cannot cancel ongoing Democracy referendums', async ({helper}) => {359 await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));360 const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);361 const referendumIndex = startedEvent.referendumIndex;362363 await expect(helper.technicalCommittee.collective.execute(364 techcomms.andy,365 helper.democracy.emergencyCancelCall(referendumIndex),366 )).to.be.rejectedWith('BadOrigin');367 });368369 itSub('[Negative] TechComm cannot blacklist Democracy proposals', async ({helper}) => {370 const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);371 await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);372373 await expect(proposalFromAllCommittee(helper.democracy.blacklistCall(preimageHash))).to.be.rejectedWith('BadOrigin');374 });375376 itSub('[Negative] TechComm member cannot blacklist Democracy proposals', async ({helper}) => {377 const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);378 await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);379380 await expect(helper.technicalCommittee.collective.execute(381 techcomms.andy,382 helper.democracy.blacklistCall(preimageHash),383 )).to.be.rejectedWith('BadOrigin');384 });385386 itSub.skip('[Negative] TechComm member cannot veto external Democracy proposals until the cool-off period pass', async () => {387388 });389390 itSub('[Negative] TechComm member cannot cancel Fellowship referendums', async ({helper}) => {391 const fellowship = await initFellowship(donor, sudoer);392 const fellowshipProposer = fellowship[5][0];393 const proposal = dummyProposal(helper);394395 const submitResult = await helper.fellowship.referenda.submit(396 fellowshipProposer,397 fellowshipPropositionOrigin,398 proposal,399 defaultEnactmentMoment,400 );401402 const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;403404 await expect(helper.technicalCommittee.collective.execute(405 techcomms.andy,406 helper.fellowship.referenda.cancelCall(referendumIndex),407 )).to.be.rejectedWith('BadOrigin');408 });409410 itSub('[Negative] TechComm referendum cannot be closed until the voting threshold is met', async ({helper}) => {411 const committeeSize = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON().length as any as number;412 expect(committeeSize).is.greaterThan(1);413 const proposeResult = await helper.technicalCommittee.collective.propose(414 techcomms.andy,415 dummyProposalCall(helper),416 committeeSize,417 );418419 const committeeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult);420 const proposalIndex = committeeProposedEvent.proposalIndex;421 const proposalHash = committeeProposedEvent.proposalHash;422423 await helper.technicalCommittee.collective.vote(techcomms.constantine, proposalHash, proposalIndex, true);424425 await expect(helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex)).to.be.rejectedWith('TooEarly');426 });427});js-packages/tests/sub/governance/util.tsdiffbeforeafterboth--- a/js-packages/tests/sub/governance/util.ts
+++ b/js-packages/tests/sub/governance/util.ts
@@ -29,12 +29,62 @@
filip: IKeyringPair;
irina: IKeyringPair;
}
+
+export interface IFinCounselors {
+ greg: IKeyringPair;
+ ildar: IKeyringPair;
+ andy: IKeyringPair;
+}
+
export interface ITechComms {
greg: IKeyringPair;
andy: IKeyringPair;
constantine: IKeyringPair;
}
+export function initFinCouncil(donor: IKeyringPair, superuser: IKeyringPair): Promise<IFinCounselors> {
+ return usingPlaygrounds(async (helper) => {
+ const [greg, ildar, andy] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n], donor);
+ const sudo = helper.getSudo();
+ {
+ const members = (await helper.callRpc('api.query.financialCouncilMembership.members')).toJSON() as [];
+ if(members.length != 0) {
+ await clearFinCouncil(superuser);
+ }
+ }
+ const expectedMembers = [greg, ildar, andy];
+ for(const member of expectedMembers) {
+ await sudo.executeExtrinsic(superuser, 'api.tx.financialCouncilMembership.addMember', [member.address]);
+ }
+ await sudo.executeExtrinsic(superuser, 'api.tx.financialCouncilMembership.setPrime', [greg.address]);
+ {
+ const members = (await helper.callRpc('api.query.financialCouncilMembership.members')).toJSON();
+ expect(members).to.containSubset(expectedMembers.map((x: IKeyringPair) => x.address));
+ expect(members.length).to.be.equal(expectedMembers.length);
+ }
+
+ return {
+ greg,
+ ildar,
+ andy,
+ };
+ });
+}
+
+export async function clearFinCouncil(superuser: IKeyringPair) {
+ await usingPlaygrounds(async (helper) => {
+ let members = (await helper.callRpc('api.query.financialCouncilMembership.members')).toJSON();
+ if(members.length) {
+ const sudo = helper.getSudo();
+ for(const address of members) {
+ await sudo.executeExtrinsic(superuser, 'api.tx.financialCouncilMembership.removeMember', [address]);
+ }
+ members = (await helper.callRpc('api.query.financialCouncilMembership.members')).toJSON();
+ }
+ expect(members).to.be.deep.equal([]);
+ });
+}
+
export async function initCouncil(donor: IKeyringPair, superuser: IKeyringPair) {
let counselors: IKeyringPair[] = [];
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;
+ }
+}