From db2c542e46ecc90e3dce6b5b4c19bb70e9f59fc4 Mon Sep 17 00:00:00 2001 From: Eric Richardson Date: Tue, 10 Sep 2024 17:40:04 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20set=20admin=20meth?= =?UTF-8?q?od=20to=20multi=20instance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit allow for mangement of a multiSig admin for v7 chains. Signers can create a proposal to set an admin and an admin can relinquish the privilege --- src/api/client/__tests__/Network.ts | 1 + src/api/entities/Account/MultiSig/index.ts | 12 + .../entities/Account/__tests__/MultiSig.ts | 20 ++ .../__tests__/evaluateMultiSigProposal.ts | 34 ++- .../procedures/__tests__/setMultiSigAdmin.ts | 228 ++++++++++++++++++ .../procedures/evaluateMultiSigProposal.ts | 52 +++- src/api/procedures/setMultiSigAdmin.ts | 127 ++++++++++ src/api/procedures/types.ts | 9 +- src/internal.ts | 1 + src/testUtils/mocks/dataSources.ts | 57 ++--- 10 files changed, 499 insertions(+), 42 deletions(-) create mode 100644 src/api/procedures/__tests__/setMultiSigAdmin.ts create mode 100644 src/api/procedures/setMultiSigAdmin.ts diff --git a/src/api/client/__tests__/Network.ts b/src/api/client/__tests__/Network.ts index a5f7e375d8..78f5a615c0 100644 --- a/src/api/client/__tests__/Network.ts +++ b/src/api/client/__tests__/Network.ts @@ -414,6 +414,7 @@ describe('Network Class', () => { .mockResolvedValue( dsMockUtils.createMockRuntimeDispatchInfo({ partialFee: rawGasFees, + weight: dsMockUtils.createMockWeight(), }) ); diff --git a/src/api/entities/Account/MultiSig/index.ts b/src/api/entities/Account/MultiSig/index.ts index 0e25972794..965fd99010 100644 --- a/src/api/entities/Account/MultiSig/index.ts +++ b/src/api/entities/Account/MultiSig/index.ts @@ -2,6 +2,7 @@ import BigNumber from 'bignumber.js'; import { UniqueIdentifiers } from '~/api/entities/Account'; import { MultiSigProposal } from '~/api/entities/MultiSigProposal'; +import { setMultiSigAdmin } from '~/api/procedures/setMultiSigAdmin'; import { Account, Context, Identity, modifyMultiSig, PolymeshError } from '~/internal'; import { multiSigProposalsQuery } from '~/middleware/queries/multisigs'; import { Query } from '~/middleware/types'; @@ -12,6 +13,7 @@ import { ProcedureMethod, ProposalStatus, ResultSet, + SetMultiSigAdminParams, } from '~/types'; import { Ensured } from '~/types/utils'; import { @@ -43,6 +45,10 @@ export class MultiSig extends Account { }, context ); + this.setAdmin = createProcedureMethod( + { getProcedureAndArgs: adminArgs => [setMultiSigAdmin, { multiSig: this, ...adminArgs }] }, + context + ); } /** @@ -324,4 +330,10 @@ export class MultiSig extends Account { Pick, void >; + + /** + * Set an admin for the MultiSig. When setting an admin it must be signed by one of the MultiSig signers and ran + * as a proposal. When removing an admin it must be called by account belonging to the admin's identity + */ + public setAdmin: ProcedureMethod; } diff --git a/src/api/entities/Account/__tests__/MultiSig.ts b/src/api/entities/Account/__tests__/MultiSig.ts index f34c5c2ed4..eb38766210 100644 --- a/src/api/entities/Account/__tests__/MultiSig.ts +++ b/src/api/entities/Account/__tests__/MultiSig.ts @@ -349,4 +349,24 @@ describe('MultiSig class', () => { expect(procedure).toBe(expectedTransaction); }); }); + + describe('method: setAdmin', () => { + it('should prepare the procedure and return the resulting procedure', async () => { + const did = 'someDid'; + const admin = entityMockUtils.getIdentityInstance({ did }); + + const expectedTransaction = 'someQueue' as unknown as PolymeshTransaction; + const args = { + admin, + }; + + when(procedureMockUtils.getPrepareMock()) + .calledWith({ args: { multiSig, ...args }, transformer: undefined }, context, {}) + .mockResolvedValue(expectedTransaction); + + const procedure = await multiSig.setAdmin(args); + + expect(procedure).toBe(expectedTransaction); + }); + }); }); diff --git a/src/api/procedures/__tests__/evaluateMultiSigProposal.ts b/src/api/procedures/__tests__/evaluateMultiSigProposal.ts index ee9e3bb6d8..cadc5001d6 100644 --- a/src/api/procedures/__tests__/evaluateMultiSigProposal.ts +++ b/src/api/procedures/__tests__/evaluateMultiSigProposal.ts @@ -1,5 +1,5 @@ import { u64 } from '@polkadot/types'; -import { AccountId } from '@polkadot/types/interfaces'; +import { AccountId, RuntimeDispatchInfo } from '@polkadot/types/interfaces'; import { PolymeshPrimitivesSecondaryKeySignatory } from '@polkadot/types/lookup'; import BigNumber from 'bignumber.js'; import { when } from 'jest-when'; @@ -43,11 +43,14 @@ describe('evaluateMultiSigProposal', () => { let rawSignerAccount: AccountId; let rawOtherAccount: AccountId; let rawProposalId: u64; + let rawDispatchInfo: RuntimeDispatchInfo; let proposal: MultiSigProposal; let rawSigner: PolymeshPrimitivesSecondaryKeySignatory; let creator: Identity; let proposalDetails: MultiSigProposalDetails; let votesQuery: jest.Mock; + let proposalQuery: jest.Mock; + let callInfoCall: jest.Mock; beforeAll(() => { dsMockUtils.initMocks(); @@ -57,6 +60,10 @@ describe('evaluateMultiSigProposal', () => { stringToAccountId = jest.spyOn(utilsConversionModule, 'stringToAccountId'); bigNumberToU64Spy = jest.spyOn(utilsConversionModule, 'bigNumberToU64'); signerToSignatorySpy = jest.spyOn(utilsConversionModule, 'signerToSignatory'); + rawDispatchInfo = dsMockUtils.createMockRuntimeDispatchInfo({ + weight: dsMockUtils.createMockWeight(), + partialFee: dsMockUtils.createMockBalance(), + }); multiSigAddress = 'multiSigAddress'; signerAddress = 'someAddress'; @@ -70,6 +77,8 @@ describe('evaluateMultiSigProposal', () => { }); votesQuery = dsMockUtils.createQueryMock('multiSig', 'votes'); + proposalQuery = dsMockUtils.createQueryMock('multiSig', 'proposals'); + callInfoCall = dsMockUtils.createCallMock('transactionPaymentCallApi', 'queryCallInfo'); rawMultiSigAccount = dsMockUtils.createMockAccountId(multiSigAddress); rawSignerAccount = dsMockUtils.createMockAccountId(signerAddress); @@ -118,6 +127,8 @@ describe('evaluateMultiSigProposal', () => { }); votesQuery.mockResolvedValue(dsMockUtils.createMockBool(false)); + proposalQuery.mockResolvedValue(dsMockUtils.createMockOption(dsMockUtils.createMockCall())); + callInfoCall.mockResolvedValue(rawDispatchInfo); }); afterEach(() => { @@ -271,7 +282,24 @@ describe('evaluateMultiSigProposal', () => { } }); - it('should return a approveAsKey transaction spec', async () => { + it('should throw an error if proposal information is not found', () => { + const proc = procedureMockUtils.getInstance(mockContext); + proposalQuery.mockResolvedValue(dsMockUtils.createMockOption()); + + const expectedError = new PolymeshError({ + code: ErrorCode.DataUnavailable, + message: 'The proposal data was not found on chain', + }); + + return expect( + prepareMultiSigProposalEvaluation.call(proc, { + proposal, + action: MultiSigProposalAction.Approve, + }) + ).rejects.toThrow(expectedError); + }); + + it('should return a approve transaction spec', async () => { const proc = procedureMockUtils.getInstance(mockContext); const transaction = dsMockUtils.createTxMock('multiSig', 'approve'); @@ -284,7 +312,7 @@ describe('evaluateMultiSigProposal', () => { expect(result).toEqual({ transaction, paidForBy: creator, - args: [rawMultiSigAccount, rawProposalId], + args: [rawMultiSigAccount, rawProposalId, rawDispatchInfo.weight], }); }); diff --git a/src/api/procedures/__tests__/setMultiSigAdmin.ts b/src/api/procedures/__tests__/setMultiSigAdmin.ts new file mode 100644 index 0000000000..64833636e0 --- /dev/null +++ b/src/api/procedures/__tests__/setMultiSigAdmin.ts @@ -0,0 +1,228 @@ +import { AccountId } from '@polkadot/types/interfaces'; +import { PolymeshPrimitivesIdentityId } from '@polkadot/types/lookup'; +import BigNumber from 'bignumber.js'; +import { when } from 'jest-when'; + +import { + getAuthorization, + Params, + prepareSetMultiSigAdmin, +} from '~/api/procedures/setMultiSigAdmin'; +import { Context, Identity, MultiSig, PolymeshError } from '~/internal'; +import { dsMockUtils, entityMockUtils, procedureMockUtils } from '~/testUtils/mocks'; +import { Mocked } from '~/testUtils/types'; +import { ErrorCode, TxTags } from '~/types'; +import { PolymeshTx } from '~/types/internal'; +import * as utilsConversionModule from '~/utils/conversion'; + +jest.mock( + '~/api/entities/Asset/Base', + require('~/testUtils/mocks/entities').mockBaseAssetModule('~/api/entities/Asset/Base') +); + +describe('setMultiSigAdmin procedure', () => { + const adminDid = 'adminDid'; + const multiSigAddress = 'multiSigAddress'; + + let mockContext: Mocked; + let multiSig: MultiSig; + let adminIdentity: Identity; + let rawAdminDid: PolymeshPrimitivesIdentityId; + let rawMultiSigAddress: AccountId; + let stringToAccountIdSpy: jest.SpyInstance; + let stringToIdentityIdSpy: jest.SpyInstance; + + beforeAll(() => { + dsMockUtils.initMocks(); + procedureMockUtils.initMocks(); + entityMockUtils.initMocks(); + }); + + let addAdminTransaction: PolymeshTx<[PolymeshPrimitivesIdentityId]>; + let removeAdminTransaction: PolymeshTx<[AccountId]>; + + beforeEach(() => { + mockContext = dsMockUtils.getContextInstance(); + + adminIdentity = entityMockUtils.getIdentityInstance({ did: adminDid }); + rawAdminDid = dsMockUtils.createMockIdentityId(adminDid); + multiSig = entityMockUtils.getMultiSigInstance({ address: multiSigAddress }); + rawMultiSigAddress = dsMockUtils.createMockAccountId(multiSigAddress); + stringToAccountIdSpy = jest.spyOn(utilsConversionModule, 'stringToAccountId'); + stringToIdentityIdSpy = jest.spyOn(utilsConversionModule, 'stringToIdentityId'); + + when(stringToAccountIdSpy) + .calledWith(multiSigAddress, mockContext) + .mockReturnValue(rawMultiSigAddress); + when(stringToIdentityIdSpy).calledWith(adminDid, mockContext).mockReturnValue(rawAdminDid); + + addAdminTransaction = dsMockUtils.createTxMock('multiSig', 'addAdmin'); + removeAdminTransaction = dsMockUtils.createTxMock('multiSig', 'removeAdminViaAdmin'); + }); + + afterEach(() => { + entityMockUtils.reset(); + procedureMockUtils.reset(); + dsMockUtils.reset(); + }); + + afterAll(() => { + procedureMockUtils.cleanup(); + dsMockUtils.cleanup(); + }); + + it('should throw an error if the chain is on v6', () => { + mockContext.isV6 = true; + const proc = procedureMockUtils.getInstance(mockContext); + + const expectedError = new PolymeshError({ + code: ErrorCode.General, + message: 'MultiSig admins are not supported on v6 chains', + }); + + return expect( + prepareSetMultiSigAdmin.call(proc, { multiSig, admin: adminIdentity }) + ).rejects.toThrow(expectedError); + }); + + it('should throw an error if the supplied identity is already an admin for the MultiSig', () => { + const proc = procedureMockUtils.getInstance(mockContext); + + const expectedError = new PolymeshError({ + code: ErrorCode.ValidationError, + message: 'The identity is already the admin of the MultiSig', + }); + + return expect( + prepareSetMultiSigAdmin.call(proc, { multiSig, admin: adminIdentity }) + ).rejects.toThrow(expectedError); + }); + + it('should return an add admin transaction when given an admin', async () => { + adminIdentity = entityMockUtils.getIdentityInstance({ did: adminDid, isEqual: false }); + const proc = procedureMockUtils.getInstance(mockContext); + + const expectedError = new PolymeshError({ + code: ErrorCode.ValidationError, + message: 'The signing account is not part of the MultiSig', + }); + + return expect( + prepareSetMultiSigAdmin.call(proc, { + multiSig, + admin: adminIdentity, + }) + ).rejects.toThrow(expectedError); + }); + + it('should return an add admin transaction when given an admin', async () => { + adminIdentity = entityMockUtils.getIdentityInstance({ did: adminDid, isEqual: false }); + multiSig = entityMockUtils.getMultiSigInstance({ + details: { requiredSignatures: new BigNumber(1), signers: [mockContext.getSigningAccount()] }, + }); + const proc = procedureMockUtils.getInstance(mockContext); + + const result = await prepareSetMultiSigAdmin.call(proc, { + multiSig, + admin: adminIdentity, + }); + + expect(result).toEqual({ + transaction: addAdminTransaction, + args: [rawAdminDid], + resolver: undefined, + }); + }); + + it('should throw an error if trying to remove an admin from a MultiSig without one', () => { + const proc = procedureMockUtils.getInstance(mockContext); + + multiSig = entityMockUtils.getMultiSigInstance({ + getAdmin: null, + }); + + const expectedError = new PolymeshError({ + code: ErrorCode.NoDataChange, + message: 'The multiSig does not have an admin set currently', + data: { multiSig: multiSig.address }, + }); + + return expect( + prepareSetMultiSigAdmin.call(proc, { + multiSig, + admin: null, + }) + ).rejects.toThrow(expectedError); + }); + + it('should throw an error if an identity that is not the admin is calling for remove', () => { + const proc = procedureMockUtils.getInstance(mockContext); + + mockContext = dsMockUtils.getContextInstance({ + signingIdentityIsEqual: false, + }); + + multiSig = entityMockUtils.getMultiSigInstance({ + getAdmin: entityMockUtils.getIdentityInstance({ did: 'someOtherDid' }), + }); + + const expectedError = new PolymeshError({ + code: ErrorCode.NoDataChange, + message: "Only the current admin's identity can remove themselves", + }); + + return expect( + prepareSetMultiSigAdmin.call(proc, { + multiSig, + admin: null, + }) + ).rejects.toThrow(expectedError); + }); + + it('should return an remove admin transaction when given null for an admin', async () => { + const proc = procedureMockUtils.getInstance(mockContext); + + const result = await prepareSetMultiSigAdmin.call(proc, { + multiSig, + admin: null, + }); + + expect(result).toEqual({ + transaction: removeAdminTransaction, + args: [rawMultiSigAddress], + resolver: undefined, + }); + }); + + describe('getAuthorization', () => { + it('should return AddAdmin transaction when adding an admin', () => { + const proc = procedureMockUtils.getInstance(mockContext); + const boundFunc = getAuthorization.bind(proc); + const params = { + admin: adminIdentity, + multiSig, + }; + + expect(boundFunc(params)).toEqual({ + permissions: { + transactions: [TxTags.multiSig.AddAdmin], + }, + }); + }); + + it('should return RemoveAdminViaAdmin transaction when removing an admin', () => { + const proc = procedureMockUtils.getInstance(mockContext); + const boundFunc = getAuthorization.bind(proc); + const params = { + admin: null, + multiSig, + }; + + expect(boundFunc(params)).toEqual({ + permissions: { + transactions: [TxTags.multiSig.RemoveAdminViaAdmin], + }, + }); + }); + }); +}); diff --git a/src/api/procedures/evaluateMultiSigProposal.ts b/src/api/procedures/evaluateMultiSigProposal.ts index 5f9afd5d5a..6a50765d97 100644 --- a/src/api/procedures/evaluateMultiSigProposal.ts +++ b/src/api/procedures/evaluateMultiSigProposal.ts @@ -31,9 +31,10 @@ export async function prepareMultiSigProposalEvaluation( context: { isV6, polymeshApi: { + call, tx: { multiSig }, query: { - multiSig: { votes }, + multiSig: { votes, proposals }, }, }, }, @@ -117,25 +118,54 @@ export async function prepareMultiSigProposalEvaluation( }); } - let transaction; /* istanbul ignore if: this will be removed after dual version support for v6-v7 */ if (isV6) { - transaction = + const transaction = action === MultiSigProposalAction.Approve ? // eslint-disable-next-line @typescript-eslint/no-explicit-any (multiSig as any).approveAsKey // NOSONAR : // eslint-disable-next-line @typescript-eslint/no-explicit-any (multiSig as any).rejectAsKey; // NOSONAR + + return { + transaction, + paidForBy: payer ?? undefined, + args: [rawMultiSigAddress, rawProposalId], + resolver: undefined, + }; } else { - transaction = action === MultiSigProposalAction.Approve ? multiSig.approve : multiSig.reject; - } + const paidForBy = payer ?? undefined; + + if (action === MultiSigProposalAction.Approve) { + const rawProposal = await proposals(rawMultiSigAddress, rawProposalId); + if (!rawProposal || rawProposal.isNone) { + throw new PolymeshError({ + code: ErrorCode.DataUnavailable, + message: 'The proposal data was not found on chain', + data: { multiSig: multiSigAddress, proposalId: proposal.id.toString() }, + }); + } - return { - transaction, - paidForBy: payer ?? undefined, - args: [rawMultiSigAddress, rawProposalId], - resolver: undefined, - }; + const runtimeInfo = await call.transactionPaymentCallApi.queryCallInfo( + rawProposal.unwrap(), + rawProposal.encodedLength + ); + + return { + transaction: multiSig.approve, + paidForBy, + args: [rawMultiSigAddress, rawProposalId, runtimeInfo.weight], + resolver: undefined, + }; + } else { + return { + transaction: multiSig.reject, + paidForBy, + args: [rawMultiSigAddress, rawProposalId], + resolver: undefined, + }; + } + } } /** diff --git a/src/api/procedures/setMultiSigAdmin.ts b/src/api/procedures/setMultiSigAdmin.ts new file mode 100644 index 0000000000..11f4546833 --- /dev/null +++ b/src/api/procedures/setMultiSigAdmin.ts @@ -0,0 +1,127 @@ +import { PolymeshError, Procedure } from '~/internal'; +import { ErrorCode, MultiSig, SetMultiSigAdminParams, TxTags } from '~/types'; +import { ExtrinsicParams, ProcedureAuthorization, TransactionSpec } from '~/types/internal'; +import { stringToAccountId, stringToIdentityId } from '~/utils/conversion'; +import { asIdentity } from '~/utils/internal'; + +/** + * @hidden + */ +export type Params = { multiSig: MultiSig } & SetMultiSigAdminParams; + +/** + * @hidden + */ +export async function prepareSetMultiSigAdmin( + this: Procedure, + args: Params +): Promise< + | TransactionSpec> + | TransactionSpec> +> { + const { + context: { + isV6, + polymeshApi: { tx }, + }, + context, + } = this; + + if (isV6) { + throw new PolymeshError({ + code: ErrorCode.General, + message: 'MultiSig admins are not supported on v6 chains', + }); + } + + const { admin, multiSig } = args; + if (admin) { + const identity = asIdentity(admin, context); + const [currentAdmin, { signers }] = await Promise.all([ + multiSig.getAdmin(), + multiSig.details(), + ]); + + if (currentAdmin && identity.isEqual(currentAdmin)) { + throw new PolymeshError({ + code: ErrorCode.NoDataChange, + message: 'The identity is already the admin of the MultiSig', + data: { multiSig: multiSig.address, admin: currentAdmin.did }, + }); + } + + const signingAccount = context.getSigningAccount(); + + if (!signers.some(signer => signer.isEqual(signingAccount))) { + throw new PolymeshError({ + code: ErrorCode.ValidationError, + message: 'The signing account is not part of the MultiSig', + data: { signingAccount: signingAccount.address, multiSig: multiSig.address }, + }); + } + + const rawAdminDid = stringToIdentityId(identity.did, context); + + return { + transaction: tx.multiSig.addAdmin, + args: [rawAdminDid], + resolver: undefined, + }; + } else { + const [currentAdmin, signingIdentity] = await Promise.all([ + multiSig.getAdmin(), + context.getSigningIdentity(), + ]); + + if (!currentAdmin) { + throw new PolymeshError({ + code: ErrorCode.NoDataChange, + message: 'The multiSig does not have an admin set currently', + data: { multiSig: multiSig.address }, + }); + } + + if (!signingIdentity.isEqual(currentAdmin)) { + throw new PolymeshError({ + code: ErrorCode.General, + message: "Only the current admin's identity can remove themselves", + data: { + multiSig: multiSig.address, + currentAdmin: currentAdmin.did, + signingIdentity: signingIdentity.did, + }, + }); + } + + const rawMultiSigAddress = stringToAccountId(multiSig.address, context); + return { + transaction: tx.multiSig.removeAdminViaAdmin, + args: [rawMultiSigAddress], + resolver: undefined, + }; + } +} + +/** + * @hidden + */ +export function getAuthorization(this: Procedure, args: Params): ProcedureAuthorization { + const transactions = []; + if (args.admin) { + transactions.push(TxTags.multiSig.AddAdmin); + } else { + transactions.push(TxTags.multiSig.RemoveAdminViaAdmin); + } + + return { + permissions: { + transactions, + }, + }; +} + +/** + * @hidden + */ +export const setMultiSigAdmin = (): Procedure => + new Procedure(prepareSetMultiSigAdmin, getAuthorization); diff --git a/src/api/procedures/types.ts b/src/api/procedures/types.ts index f54538a520..b7654cb670 100644 --- a/src/api/procedures/types.ts +++ b/src/api/procedures/types.ts @@ -1639,7 +1639,14 @@ export interface ModifyMultiSigParams { requiredSignatures?: BigNumber; } -export interface JoinCreatorAsPrimary { +export interface SetMultiSigAdminParams { + /** + * The identity to become an admin for the MultiSig. `null` will remove the current admin + */ + admin: Identity | string | null; +} + +interface JoinCreatorAsPrimary { asPrimary: true; cddAuthId?: BigNumber; } diff --git a/src/internal.ts b/src/internal.ts index 36c8ed799a..53931ef8b0 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -158,6 +158,7 @@ export { acceptPrimaryKeyRotation } from '~/api/procedures/acceptPrimaryKeyRotat export { addAssetMediators } from '~/api/procedures/addAssetMediators'; export { removeAssetMediators } from '~/api/procedures/removeAssetMediators'; export { Storage as ModifyMultiSigStorage, modifyMultiSig } from '~/api/procedures/modifyMultiSig'; +export { setMultiSigAdmin } from '~/api/procedures/setMultiSigAdmin'; export { SetCountTransferRestrictionsParams, SetPercentageTransferRestrictionsParams, diff --git a/src/testUtils/mocks/dataSources.ts b/src/testUtils/mocks/dataSources.ts index f30bdca8a3..2264557f15 100644 --- a/src/testUtils/mocks/dataSources.ts +++ b/src/testUtils/mocks/dataSources.ts @@ -4061,6 +4061,32 @@ export const createMockBlockHash = (value?: string | BlockHash): MockCodec; }; +/** + * @hidden + * NOTE: `isEmpty` will be set to true if no value is passed + */ +export const createMockWeight = ( + weight?: + | Weight + | { + refTime: Compact; + proofSize: Compact; + } +): MockCodec => { + const { refTime, proofSize } = weight ?? { + refTime: createMockCompact(), + proofSize: createMockCompact(), + }; + + return createMockCodec( + { + refTime: createMockCompact(refTime.unwrap()), + proofSize: createMockCompact(proofSize.unwrap()), + }, + !weight + ); +}; + /** * NOTE: `isEmpty` will be set to true if no value is passed */ @@ -4068,16 +4094,19 @@ export const createMockRuntimeDispatchInfo = ( runtimeDispatchInfo?: | RuntimeDispatchInfo | { + weight: Weight | Parameters[0]; partialFee: Balance | Parameters[0]; } ): MockCodec => { - const { partialFee } = runtimeDispatchInfo ?? { + const { partialFee, weight } = runtimeDispatchInfo ?? { partialFee: createMockBalance(), + weight: createMockWeight(), }; return createMockCodec( { partialFee: createMockBalance(partialFee), + weight: createMockWeight(weight), }, !runtimeDispatchInfo ); @@ -4685,32 +4714,6 @@ export const createMockMediatorAffirmationStatus = ( return createMockEnum(status); }; -/** - * @hidden - * NOTE: `isEmpty` will be set to true if no value is passed - */ -export const createMockWeight = ( - weight?: - | Weight - | { - refTime: Compact; - proofSize: Compact; - } -): MockCodec => { - const { refTime, proofSize } = weight ?? { - refTime: createMockCompact(), - proofSize: createMockCompact(), - }; - - return createMockCodec( - { - refTime: createMockCompact(refTime.unwrap()), - proofSize: createMockCompact(proofSize.unwrap()), - }, - !weight - ); -}; - /** * @hidden * NOTE: `isEmpty` will be set to true if no value is passed