diff --git a/packages/util/package.json b/packages/util/package.json index 1219d1734..a495c9394 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@sora-substrate/util", - "version": "1.33.15", + "version": "1.33.16-beta.1", "license": "Apache-2.0", "main": "./build/index.js", "typings": "./build/index.d.ts", diff --git a/packages/util/src/BaseApi.ts b/packages/util/src/BaseApi.ts index 72fa7bdce..4f815db98 100644 --- a/packages/util/src/BaseApi.ts +++ b/packages/util/src/BaseApi.ts @@ -832,12 +832,46 @@ export enum Operation { XorlessTransfer = 'XorlessTransfer', Mint = 'Mint', Burn = 'Burn', + UpdateAssetInfo = 'UpdateAssetInfo', /** Kensetsu */ CreateVault = 'CreateVault', CloseVault = 'CloseVault', RepayVaultDebt = 'RepayVaultDebt', DepositCollateral = 'DepositCollateral', BorrowVaultDebt = 'BorrowVaultDebt', + /** Governance */ + GovernanceVoteOnReferendum = 'GovernanceVoteOnReferendum', + GovernanceNotePreimage = 'GovernanceNotePreimage', + GovernanceSubmitProposal = 'GovernanceSubmitProposal', + GovernanceVoteOnCandidate = 'GovernanceVoteOnCandidate', + GovernanceSubmitCandidacy = 'GovernanceSubmitCandidacy', +} + +export interface History { + txId?: string; + type: Operation; + amount?: string; + symbol?: string; + assetAddress?: string; + id?: string; + blockId?: string; + blockHeight?: number; + to?: string; + receivers?: Array; + amount2?: string; + symbol2?: string; + asset2Address?: string; + decimals?: number; + decimals2?: number; + startTime?: number; + endTime?: number; + from?: string; + status?: string; + errorMessage?: ErrorMessageFields | string; + liquiditySource?: string; + liquidityProviderFee?: CodecString; + soraNetworkFee?: CodecString; + payload?: any; // can be used to integrate with third-party services } export interface OnChainIdentity { diff --git a/packages/util/src/governance/index.ts b/packages/util/src/governance/index.ts new file mode 100644 index 000000000..215661c4a --- /dev/null +++ b/packages/util/src/governance/index.ts @@ -0,0 +1,103 @@ +import type { Observable } from '@polkadot/types/types'; + +import { assert } from '@polkadot/util'; +import { Operation } from '../BaseApi'; +import type { Api } from '../api'; +import { CollectiveProposal, ElectionsInfo, Proposal, Referendum, ReferendumExt } from './types'; +import { AccountId } from '@sora-substrate/types'; +import { Messages } from '../logger'; + +export class GovernanceModule { + constructor(private readonly root: Api) {} + + /** + * Get observable referendums + * @returns The list of referendums + */ + public getReferendumsObservable(): Observable { + return this.root.apiRx.derive.democracy.referendums(); + } + + /** + * Get observable active referendums + * @returns The list of active referendums + */ + public getReferendumsActiveObservable(): Observable { + return this.root.apiRx.derive.democracy.referendumsActive(); + } + + public voteOnReferendum(referendumIndex: number, vote: string): Promise { + assert(this.root.account, Messages.connectWallet); + return this.root.submitExtrinsic(this.root.api.tx.democracy.vote(referendumIndex, vote), this.root.account.pair, { + type: Operation.GovernanceVoteOnReferendum, + }); + } + + /** + * Get observable proposals + * @returns The list of proposals + */ + public getProposalsObservable(): Observable { + return this.root.apiRx.derive.democracy.proposals(); + } + + public notePreimage(proposal: any): Promise { + assert(this.root.account, Messages.connectWallet); + return this.root.submitExtrinsic(this.root.api.tx.preimage.notePreimage(proposal), this.root.account.pair, { + type: Operation.GovernanceNotePreimage, + }); + } + + public submitProposal(proposal: any, value: number): Promise { + assert(this.root.account, Messages.connectWallet); + return this.root.submitExtrinsic(this.root.api.tx.democracy.propose(proposal, value), this.root.account.pair, { + type: Operation.GovernanceSubmitProposal, + }); + } + + /** + * Get observable elections info + * @returns The structure with the list of council members, candidates, and runners-up + */ + public getElectionsInfoObservable(): Observable { + return this.root.apiRx.derive.elections.info(); + } + + public voteOnCandidate(candidates: AccountId[], value: number): Promise { + assert(this.root.account, Messages.connectWallet); + return this.root.submitExtrinsic( + this.root.api.tx.electionsPhragmen.vote(candidates, value), + this.root.account.pair, + { + type: Operation.GovernanceVoteOnCandidate, + } + ); + } + + public submitCandidacy(value: number): Promise { + assert(this.root.account, Messages.connectWallet); + return this.root.submitExtrinsic( + this.root.api.tx.electionsPhragmen.submitCandidacy(value), + this.root.account.pair, + { + type: Operation.GovernanceSubmitCandidacy, + } + ); + } + + /** + * Get observable technical committee members + * @returns The list of technical committee members + */ + public getTechnicalCommitteeMembersObservable(): Observable { + return this.root.apiRx.derive.technicalCommittee.members(); + } + + /** + * Get observable proposals + * @returns The list of proposals + */ + public getTechnicalCommitteeProposalsObservable(): Observable { + return this.root.apiRx.derive.technicalCommittee.proposals() as any as Observable; + } +} diff --git a/packages/util/src/governance/types.ts b/packages/util/src/governance/types.ts new file mode 100644 index 000000000..7986c75a7 --- /dev/null +++ b/packages/util/src/governance/types.ts @@ -0,0 +1,104 @@ +import type { Vec, u32 } from '@polkadot/types'; +import type { + AccountId, + Balance, + Call, + Hash, + PropIndex, + ReferendumIndex, + ReferendumInfoTo239, + Vote, + SetIndex, + VoteIndex, + Votes, +} from '@polkadot/types/interfaces'; +import type { PalletDemocracyReferendumStatus, PalletDemocracyVoteThreshold } from '@polkadot/types/lookup'; +import type { BN } from '@polkadot/util'; +import type { HexString } from '@polkadot/util/types'; +import { BlockNumber } from '@sora-substrate/types'; + +export interface AtBlock { + at: BN; +} +export interface DemocracyLock { + balance: Balance; + isDelegated: boolean; + isFinished: boolean; + referendumEnd: BN; + referendumId: ReferendumIndex; + unlockAt: BN; + vote: Vote; +} +export interface ProposalImage extends AtBlock { + balance: Balance; + proposal?: Call; + proposalHash?: HexString; + proposalLen?: number; + proposer: AccountId; +} +export interface Dispatch extends AtBlock { + index: ReferendumIndex; + imageHash: HexString; + image?: ProposalImage; +} +export interface Proposal { + balance?: Balance; + index: PropIndex; + image?: ProposalImage; + imageHash: Hash; + proposer: AccountId; + seconds: Vec; +} +export interface ProposalExternal { + image?: ProposalImage; + imageHash: HexString; + threshold: PalletDemocracyVoteThreshold; +} +export interface Referendum { + index: ReferendumIndex; + image?: ProposalImage; + imageHash: HexString; + status: PalletDemocracyReferendumStatus | ReferendumInfoTo239; +} +export interface ReferendumVote { + accountId: AccountId; + balance: Balance; + isDelegating: boolean; + vote: Vote; +} +export interface ReferendumVoteState { + allAye: ReferendumVote[]; + allNay: ReferendumVote[]; + voteCount: number; + voteCountAye: number; + voteCountNay: number; + votedAye: BN; + votedNay: BN; + votedTotal: BN; +} +export interface ReferendumVotes extends ReferendumVoteState { + isPassing: boolean; + votes: ReferendumVote[]; +} +export interface ReferendumExt extends Referendum, ReferendumVotes {} + +export interface ElectionsInfo { + candidates: AccountId[]; + candidateCount: u32; + candidacyBond?: Balance; + desiredRunnersUp?: u32; + desiredSeats?: u32; + members: [AccountId, Balance][]; + nextVoterSet?: SetIndex; + runnersUp: [AccountId, Balance][]; + termDuration?: BlockNumber; + voteCount?: VoteIndex; + voterCount?: SetIndex; + votingBond?: Balance; +} + +export interface CollectiveProposal { + hash: Hash; + proposal: Proposal | null; + votes: Votes | null; +}