1import {blake2AsHex} from '@polkadot/util-crypto';2import type {PalletDemocracyConviction} from '@polkadot/types/lookup';3import type {IPhasicEvent, TSigner} from './types.js';4import {HelperGroup, UniqueHelper} from './unique.js';56export class CollectiveGroup extends HelperGroup<UniqueHelper> {7 8910 private collective: string;1112 constructor(helper: UniqueHelper, collective: string) {13 super(helper);14 this.collective = collective;15 }1617 1819202122 private checkExecutedEvent(events: IPhasicEvent[]): string {23 const executionEvents = events.filter(x =>24 x.event.section === this.collective && (x.event.method === 'Executed' || x.event.method === 'MemberExecuted'));2526 if(executionEvents.length != 1) {27 if(events.filter(x => x.event.section === this.collective && x.event.method === 'Disapproved').length > 0)28 throw new Error(`Disapproved by ${this.collective}`);29 else30 throw new Error(`Expected one 'Executed' or 'MemberExecuted' event for ${this.collective}`);31 }3233 const result = (executionEvents[0].event.data as any).result;3435 if(result.isErr) {36 if(result.asErr.isModule) {37 const error = result.asErr.asModule;38 const metaError = this.helper.getApi()?.registry.findMetaError(error);39 throw new Error(`Proposal execution failed with ${metaError.section}.${metaError.name}`);40 } else {41 throw new Error('Proposal execution failed with ' + result.asErr.toHuman());42 }43 }4445 return (executionEvents[0].event.data as any).proposalHash;46 }4748 495051 async getMembers() {52 return (await this.helper.callRpc(`api.query.${this.collective}.members`, [])).toHuman();53 }5455 565758 async getPrimeMember() {59 return (await this.helper.callRpc(`api.query.${this.collective}.prime`, [])).toHuman();60 }6162 636465 async getProposals() {66 return (await this.helper.callRpc(`api.query.${this.collective}.proposals`, [])).toHuman();67 }6869 7071727374 async getProposalCallOf(hash: string) {75 return (await this.helper.callRpc(`api.query.${this.collective}.proposalOf`, [hash])).toHuman();76 }7778 798081 async getTotalProposalsCount() {82 return (await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, [])).toNumber();83 }8485 8687888990919293 async propose(signer: TSigner, proposal: any, voteThreshold: number, lengthBound = 10000) {94 return await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [voteThreshold, proposal, lengthBound]);95 }9697 9899100101102103104105 vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {106 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve]);107 }108109 110111112113114115116 async execute(signer: TSigner, proposal: any, lengthBound = 10000) {117 const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.execute`, [proposal, lengthBound]);118 this.checkExecutedEvent(result.result.events);119 return result;120 }121122 123124125126127128129130131 async close(132 signer: TSigner,133 proposalHash: string,134 proposalIndex: number,135 weightBound: [number, number] | any = [20_000_000_000, 1000_000],136 lengthBound = 10_000,137 ) {138 const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [139 proposalHash,140 proposalIndex,141 weightBound,142 lengthBound,143 ]);144 this.checkExecutedEvent(result.result.events);145 return result;146 }147148 149150151152153154 disapproveProposal(signer: TSigner, proposalHash: string) {155 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.disapproveProposal`, [proposalHash]);156 }157}158159export class CollectiveMembershipGroup extends HelperGroup<UniqueHelper> {160 161162163 private membership: string;164165 constructor(helper: UniqueHelper, membership: string) {166 super(helper);167 this.membership = membership;168 }169170 171172173174 async getMembers() {175 return (await this.helper.callRpc(`api.query.${this.membership}.members`, [])).toHuman();176 }177178 179180181 async getPrimeMember() {182 return (await this.helper.callRpc(`api.query.${this.membership}.prime`, [])).toHuman();183 }184185 186187188189190191 addMember(signer: TSigner, member: string) {192 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.addMember`, [member]);193 }194195 addMemberCall(member: string) {196 return this.helper.constructApiCall(`api.tx.${this.membership}.addMember`, [member]);197 }198199 200201202203204205 removeMember(signer: TSigner, member: string) {206 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.removeMember`, [member]);207 }208209 removeMemberCall(member: string) {210 return this.helper.constructApiCall(`api.tx.${this.membership}.removeMember`, [member]);211 }212213 214215216217218219 resetMembers(signer: TSigner, members: string[]) {220 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.resetMembers`, [members]);221 }222223 224225226227228229 setPrime(signer: TSigner, prime: string) {230 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.setPrime`, [prime]);231 }232233 setPrimeCall(member: string) {234 return this.helper.constructApiCall(`api.tx.${this.membership}.setPrime`, [member]);235 }236237 238239240241242 clearPrime(signer: TSigner) {243 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.clearPrime`, []);244 }245246 clearPrimeCall() {247 return this.helper.constructApiCall(`api.tx.${this.membership}.clearPrime`, []);248 }249}250251export class RankedCollectiveGroup extends HelperGroup<UniqueHelper> {252 253254255 private collective: string;256257 constructor(helper: UniqueHelper, collective: string) {258 super(helper);259 this.collective = collective;260 }261262 addMember(signer: TSigner, newMember: string) {263 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.addMember`, [newMember]);264 }265266 addMemberCall(newMember: string) {267 return this.helper.constructApiCall(`api.tx.${this.collective}.addMember`, [newMember]);268 }269270 removeMember(signer: TSigner, member: string, minRank: number) {271 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.removeMember`, [member, minRank]);272 }273274 removeMemberCall(newMember: string, minRank: number) {275 return this.helper.constructApiCall(`api.tx.${this.collective}.removeMember`, [newMember, minRank]);276 }277278 promote(signer: TSigner, member: string) {279 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.promoteMember`, [member]);280 }281282 promoteCall(member: string) {283 return this.helper.constructApiCall(`api.tx.${this.collective}.promoteMember`, [member]);284 }285286 demote(signer: TSigner, member: string) {287 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.demoteMember`, [member]);288 }289290 demoteCall(newMember: string) {291 return this.helper.constructApiCall(`api.tx.${this.collective}.demoteMember`, [newMember]);292 }293294 vote(signer: TSigner, pollIndex: number, aye: boolean) {295 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [pollIndex, aye]);296 }297298 async getMembers() {299 return (await this.helper.getApi().query.fellowshipCollective.members.keys())300 .map((key) => key.args[0].toString());301 }302303 async getMemberRank(member: string) {304 return (await this.helper.callRpc('api.query.fellowshipCollective.members', [member])).toJSON().rank;305 }306}307308export class ReferendaGroup extends HelperGroup<UniqueHelper> {309 310311312 private referenda: string;313314 constructor(helper: UniqueHelper, referenda: string) {315 super(helper);316 this.referenda = referenda;317 }318319 submit(320 signer: TSigner,321 proposalOrigin: string,322 proposal: any,323 enactmentMoment: any,324 ) {325 return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.submit`, [326 {Origins: proposalOrigin},327 proposal,328 enactmentMoment,329 ]);330 }331332 placeDecisionDeposit(signer: TSigner, referendumIndex: number) {333 return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.placeDecisionDeposit`, [referendumIndex]);334 }335336 cancel(signer: TSigner, referendumIndex: number) {337 return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.cancel`, [referendumIndex]);338 }339340 cancelCall(referendumIndex: number) {341 return this.helper.constructApiCall(`api.tx.${this.referenda}.cancel`, [referendumIndex]);342 }343344 async referendumInfo(referendumIndex: number) {345 return (await this.helper.callRpc(`api.query.${this.referenda}.referendumInfoFor`, [referendumIndex])).toJSON();346 }347348 async enactmentEventId(referendumIndex: number) {349 const api = await this.helper.getApi();350351 const bytes = api.createType('([u8;8], Text, u32)', ['assembly', 'enactment', referendumIndex]).toU8a();352 return blake2AsHex(bytes, 256);353 }354}355356export interface IFellowshipGroup {357 collective: RankedCollectiveGroup;358 referenda: ReferendaGroup;359}360361export interface ICollectiveGroup {362 collective: CollectiveGroup;363 membership: CollectiveMembershipGroup;364}365366export class DemocracyGroup extends HelperGroup<UniqueHelper> {367 368 propose(signer: TSigner, call: any, deposit: bigint) {369 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);370 }371372 proposeWithPreimage(signer: TSigner, preimage: string, deposit: bigint) {373 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Legacy: preimage}, deposit]);374 }375376 proposeCall(call: any, deposit: bigint) {377 return this.helper.constructApiCall('api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);378 }379380 second(signer: TSigner, proposalIndex: number) {381 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.second', [proposalIndex]);382 }383384 externalPropose(signer: TSigner, proposalCall: any) {385 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);386 }387388 externalProposeMajority(signer: TSigner, proposalCall: any) {389 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);390 }391392 externalProposeDefault(signer: TSigner, proposalCall: any) {393 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);394 }395396 externalProposeDefaultWithPreimage(signer: TSigner, preimage: string) {397 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);398 }399400 externalProposeCall(proposalCall: any) {401 return this.helper.constructApiCall('api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);402 }403404 externalProposeMajorityCall(proposalCall: any) {405 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);406 }407408 externalProposeDefaultCall(proposalCall: any) {409 return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);410 }411412 externalProposeDefaultWithPreimageCall(preimage: string) {413 return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);414 }415416 417 vetoExternal(signer: TSigner, proposalHash: string) {418 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vetoExternal', [proposalHash]);419 }420421 vetoExternalCall(proposalHash: string) {422 return this.helper.constructApiCall('api.tx.democracy.vetoExternal', [proposalHash]);423 }424425 blacklist(signer: TSigner, proposalHash: string, referendumIndex: number | null = null) {426 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.blacklist', [proposalHash, referendumIndex]);427 }428429 blacklistCall(proposalHash: string, referendumIndex: number | null = null) {430 return this.helper.constructApiCall('api.tx.democracy.blacklist', [proposalHash, referendumIndex]);431 }432433 434 cancelProposal(signer: TSigner, proposalIndex: number) {435 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.cancelProposal', [proposalIndex]);436 }437438 cancelProposalCall(proposalIndex: number) {439 return this.helper.constructApiCall('api.tx.democracy.cancelProposal', [proposalIndex]);440 }441442 clearPublicProposals(signer: TSigner) {443 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.clearPublicProposals', []);444 }445446 fastTrack(signer: TSigner, proposalHash: string, votingPeriod: number, delayPeriod: number) {447 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);448 }449450 fastTrackCall(proposalHash: string, votingPeriod: number, delayPeriod: number) {451 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);452 }453454 455 emergencyCancel(signer: TSigner, referendumIndex: number) {456 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.emergencyCancel', [referendumIndex]);457 }458459 emergencyCancelCall(referendumIndex: number) {460 return this.helper.constructApiCall('api.tx.democracy.emergencyCancel', [referendumIndex]);461 }462463 vote(signer: TSigner, referendumIndex: number, vote: any) {464 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, vote]);465 }466467 removeVote(signer: TSigner, referendumIndex: number, targetAccount?: string) {468 if(targetAccount) {469 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeOtherVote', [targetAccount, referendumIndex]);470 } else {471 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeVote', [referendumIndex]);472 }473 }474475 unlock(signer: TSigner, targetAccount: string) {476 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.unlock', [targetAccount]);477 }478479 delegate(signer: TSigner, toAccount: string, conviction: PalletDemocracyConviction, balance: bigint) {480 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.delegate', [toAccount, conviction, balance]);481 }482483 undelegate(signer: TSigner) {484 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.undelegate', []);485 }486487 async referendumInfo(referendumIndex: number) {488 return (await this.helper.callRpc('api.query.democracy.referendumInfoOf', [referendumIndex])).toJSON();489 }490491 async publicProposals() {492 return (await this.helper.callRpc('api.query.democracy.publicProps', [])).toJSON();493 }494495 async findPublicProposal(proposalIndex: number) {496 const proposalInfo = (await this.publicProposals()).find((proposalInfo: any[]) => proposalInfo[0] == proposalIndex);497498 return proposalInfo ? proposalInfo[1] : null;499 }500501 async expectPublicProposal(proposalIndex: number) {502 const proposal = await this.findPublicProposal(proposalIndex);503504 if(proposal) {505 return proposal;506 } else {507 throw Error(`Proposal #${proposalIndex} is expected to exist`);508 }509 }510511 async getExternalProposal() {512 return (await this.helper.callRpc('api.query.democracy.nextExternal', []));513 }514515 async expectExternalProposal() {516 const proposal = await this.getExternalProposal();517518 if(proposal) {519 return proposal;520 } else {521 throw Error('An external proposal is expected to exist');522 }523 }524525 526527 528529530531}