Skip to content

Commit

Permalink
feat: 🎸 allow multiSig signers to submit proposals
Browse files Browse the repository at this point in the history
When a multiSig signer submits a transaction it will be wrapped and a
`proposal` field will be present in the response (or `multiSig` field
present will be in offline payloads)
  • Loading branch information
polymath-eric committed Aug 9, 2024
1 parent 50d1e0d commit aac249b
Show file tree
Hide file tree
Showing 60 changed files with 463 additions and 182 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ Process modes include:
- `offline` This creates an unsigned transaction and returns a serialized JSON payload. The information can be signed, and then submitted to the chain.
- `AMQP` This creates an transaction to be processed by worker processes using an AMQP broker to ensure reliable processing

### MultiSig

If the signer of a transaction is a MultiSig signer the transaction will be implicitly wrapped as a MultiSigProposal. This mostly behaves as a normal transaction with a few exceptions. The transaction may need additional approvals depending on the MultiSig configuration, and will be scheduled to run in a later block. The filed `proposal` will be set and additional details will not be set, e.g. `POST /portfolios/create` will not return portfolio information for a MultiSig signer. For offline payloads the field `multiSig` will be set to the acting MultiSig address when a proposal is being made.

### Signing Managers

A signing manager is required for `submit` and `submitWithCallback` processing modes.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"@polymeshassociation/fireblocks-signing-manager": "^2.5.0",
"@polymeshassociation/hashicorp-vault-signing-manager": "^3.4.0",
"@polymeshassociation/local-signing-manager": "^3.3.0",
"@polymeshassociation/polymesh-sdk": "24.7.0-alpha.2",
"@polymeshassociation/polymesh-sdk": "24.7.0-alpha.11",
"@polymeshassociation/signing-manager-types": "^3.2.0",
"class-transformer": "0.5.1",
"class-validator": "^0.14.0",
Expand Down
12 changes: 6 additions & 6 deletions src/accounts/accounts.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { IdentityModel } from '~/identities/models/identity.model';
import { IdentitySignerModel } from '~/identities/models/identity-signer.model';
import { NetworkService } from '~/network/network.service';
import { SubsidyService } from '~/subsidy/subsidy.service';
import { extrinsic, testValues } from '~/test-utils/consts';
import { extrinsic, processedTxResult, testValues } from '~/test-utils/consts';
import {
createMockResponseObject,
createMockSubsidy,
Expand Down Expand Up @@ -111,7 +111,7 @@ describe('AccountsController', () => {

const result = await controller.transferPolyx(body);

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
});
});

Expand Down Expand Up @@ -229,7 +229,7 @@ describe('AccountsController', () => {

const result = await controller.freezeSecondaryAccounts(body);

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
});
});

Expand All @@ -242,7 +242,7 @@ describe('AccountsController', () => {

const result = await controller.unfreezeSecondaryAccounts(body);

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
});
});

Expand All @@ -257,7 +257,7 @@ describe('AccountsController', () => {

const result = await controller.revokePermissions(body);

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
});
});

Expand All @@ -281,7 +281,7 @@ describe('AccountsController', () => {

const result = await controller.modifyPermissions(body);

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/accounts/accounts.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { AccountDetailsModel } from '~/accounts/models/account-details.model';
import { MultiSigDetailsModel } from '~/accounts/models/multi-sig-details.model';
import { PermissionsModel } from '~/accounts/models/permissions.model';
import { BalanceModel } from '~/assets/models/balance.model';
import { ApiArrayResponse, ApiTransactionResponse } from '~/common/decorators/swagger';
import { ApiArrayResponse, ApiTransactionResponse } from '~/common/decorators/';
import { TransactionBaseDto } from '~/common/dto/transaction-base-dto';
import { ExtrinsicModel } from '~/common/models/extrinsic.model';
import { PaginatedResultsModel } from '~/common/models/paginated-results.model';
Expand Down
26 changes: 13 additions & 13 deletions src/assets/assets.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { createAuthorizationRequestModel } from '~/authorizations/authorizations
import { PaginatedResultsModel } from '~/common/models/paginated-results.model';
import { MetadataService } from '~/metadata/metadata.service';
import { PortfolioDto } from '~/portfolios/dto/portfolio.dto';
import { testValues } from '~/test-utils/consts';
import { processedTxResult, testValues } from '~/test-utils/consts';
import { MockAsset, MockAuthorizationRequest } from '~/test-utils/mocks';
import { MockAssetService, mockMetadataServiceProvider } from '~/test-utils/service-mocks';

Expand Down Expand Up @@ -223,7 +223,7 @@ describe('AssetsController', () => {
mockAssetsService.setDocuments.mockResolvedValue(txResult);

const result = await controller.setDocuments({ ticker }, body);
expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.setDocuments).toHaveBeenCalledWith(ticker, body);
});
});
Expand All @@ -240,7 +240,7 @@ describe('AssetsController', () => {
mockAssetsService.createAsset.mockResolvedValue(txResult);

const result = await controller.createAsset(input);
expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.createAsset).toHaveBeenCalledWith(input);
});
});
Expand All @@ -252,7 +252,7 @@ describe('AssetsController', () => {
mockAssetsService.issue.mockResolvedValue(txResult);

const result = await controller.issue({ ticker }, { signer, amount });
expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.issue).toHaveBeenCalledWith(ticker, { signer, amount });
});
});
Expand All @@ -272,7 +272,7 @@ describe('AssetsController', () => {
const result = await controller.transferOwnership({ ticker }, body);

expect(result).toEqual({
...txResult,
...processedTxResult,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
authorizationRequest: createAuthorizationRequestModel(mockAuthorization as any),
});
Expand All @@ -288,7 +288,7 @@ describe('AssetsController', () => {
mockAssetsService.redeem.mockResolvedValue(txResult);

const result = await controller.redeem({ ticker }, { signer, amount, from });
expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.redeem).toHaveBeenCalledWith(ticker, { signer, amount, from });
});
});
Expand All @@ -299,7 +299,7 @@ describe('AssetsController', () => {
mockAssetsService.freeze.mockResolvedValue(txResult);

const result = await controller.freeze({ ticker }, { signer });
expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.freeze).toHaveBeenCalledWith(ticker, { signer });
});
});
Expand All @@ -310,7 +310,7 @@ describe('AssetsController', () => {
mockAssetsService.unfreeze.mockResolvedValue(txResult);

const result = await controller.unfreeze({ ticker }, { signer });
expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.unfreeze).toHaveBeenCalledWith(ticker, { signer });
});
});
Expand All @@ -325,7 +325,7 @@ describe('AssetsController', () => {

const result = await controller.controllerTransfer({ ticker }, { signer, origin, amount });

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.controllerTransfer).toHaveBeenCalledWith(ticker, {
signer,
origin,
Expand Down Expand Up @@ -391,7 +391,7 @@ describe('AssetsController', () => {

const result = await controller.addRequiredMediators({ ticker }, { signer, mediators });

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.addRequiredMediators).toHaveBeenCalledWith(ticker, {
signer,
mediators,
Expand All @@ -408,7 +408,7 @@ describe('AssetsController', () => {

const result = await controller.removeRequiredMediators({ ticker }, { signer, mediators });

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.removeRequiredMediators).toHaveBeenCalledWith(ticker, {
signer,
mediators,
Expand All @@ -424,7 +424,7 @@ describe('AssetsController', () => {

const result = await controller.preApprove({ ticker }, { signer });

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.preApprove).toHaveBeenCalledWith(ticker, {
signer,
});
Expand All @@ -439,7 +439,7 @@ describe('AssetsController', () => {

const result = await controller.removePreApproval({ ticker }, { signer });

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.removePreApproval).toHaveBeenCalledWith(ticker, {
signer,
});
Expand Down
2 changes: 1 addition & 1 deletion src/assets/assets.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { IdentityBalanceModel } from '~/assets/models/identity-balance.model';
import { RequiredMediatorsModel } from '~/assets/models/required-mediators.model';
import { authorizationRequestResolver } from '~/authorizations/authorizations.util';
import { CreatedAuthorizationRequestModel } from '~/authorizations/models/created-authorization-request.model';
import { ApiArrayResponse, ApiTransactionResponse } from '~/common/decorators/swagger';
import { ApiArrayResponse, ApiTransactionResponse } from '~/common/decorators/';
import { PaginatedParamsDto } from '~/common/dto/paginated-params.dto';
import { TransactionBaseDto } from '~/common/dto/transaction-base-dto';
import { TransferOwnershipDto } from '~/common/dto/transfer-ownership.dto';
Expand Down
6 changes: 3 additions & 3 deletions src/authorizations/authorizations.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BigNumber } from '@polymeshassociation/polymesh-sdk';

import { AuthorizationsController } from '~/authorizations/authorizations.controller';
import { AuthorizationsService } from '~/authorizations/authorizations.service';
import { testValues } from '~/test-utils/consts';
import { processedTxResult, testValues } from '~/test-utils/consts';
import { MockAuthorizationsService } from '~/test-utils/service-mocks';

describe('AuthorizationsController', () => {
Expand Down Expand Up @@ -34,7 +34,7 @@ describe('AuthorizationsController', () => {
const authId = new BigNumber(1);
const result = await controller.accept({ id: authId }, { signer });

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAuthorizationsService.accept).toHaveBeenCalledWith(authId, { signer });
});
});
Expand All @@ -46,7 +46,7 @@ describe('AuthorizationsController', () => {
const authId = new BigNumber(1);
const result = await controller.remove({ id: authId }, { signer });

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
expect(mockAuthorizationsService.remove).toHaveBeenCalledWith(authId, { signer });
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/* istanbul ignore file */

import { ApiProperty } from '@nestjs/swagger';
import { ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';

import { AuthorizationRequestModel } from '~/authorizations/models/authorization-request.model';
import { TransactionQueueModel } from '~/common/models/transaction-queue.model';

export class CreatedAuthorizationRequestModel extends TransactionQueueModel {
@ApiProperty({
@ApiPropertyOptional({
description: 'Details of the newly created Authorization Request',
type: AuthorizationRequestModel,
})
Expand Down
8 changes: 4 additions & 4 deletions src/checkpoints/checkpoints.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { PeriodComplexityModel } from '~/checkpoints/models/period-complexity.mo
import { ScheduleComplexityModel } from '~/checkpoints/models/schedule-complexity.model';
import { PaginatedResultsModel } from '~/common/models/paginated-results.model';
import { ResultsModel } from '~/common/models/results.model';
import { testValues } from '~/test-utils/consts';
import { processedTxResult, testValues } from '~/test-utils/consts';
import { MockCheckpoint, MockCheckpointSchedule } from '~/test-utils/mocks';
import { MockCheckpointsService } from '~/test-utils/service-mocks';

Expand Down Expand Up @@ -115,7 +115,7 @@ describe('CheckpointsController', () => {
const result = await controller.createCheckpoint({ ticker }, body);

expect(result).toEqual({
...txResult,
...processedTxResult,
checkpoint: mockCheckpoint,
});
});
Expand Down Expand Up @@ -222,7 +222,7 @@ describe('CheckpointsController', () => {
pendingPoints: [mockDate],
});
expect(result).toEqual({
...txResult,
...processedTxResult,
schedule: mockCreatedSchedule,
});
});
Expand Down Expand Up @@ -299,7 +299,7 @@ describe('CheckpointsController', () => {

const result = await controller.deleteSchedule({ id: new BigNumber(1), ticker }, { signer });

expect(result).toEqual(txResult);
expect(result).toEqual(processedTxResult);
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/checkpoints/checkpoints.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { CreatedCheckpointModel } from '~/checkpoints/models/created-checkpoint.
import { CreatedCheckpointScheduleModel } from '~/checkpoints/models/created-checkpoint-schedule.model';
import { PeriodComplexityModel } from '~/checkpoints/models/period-complexity.model';
import { ScheduleComplexityModel } from '~/checkpoints/models/schedule-complexity.model';
import { ApiArrayResponse, ApiTransactionResponse } from '~/common/decorators/swagger';
import { ApiArrayResponse, ApiTransactionResponse } from '~/common/decorators/';
import { IsTicker } from '~/common/decorators/validation';
import { IdParamsDto } from '~/common/dto/id-params.dto';
import { PaginatedParamsDto } from '~/common/dto/paginated-params.dto';
Expand Down
4 changes: 2 additions & 2 deletions src/checkpoints/models/created-checkpoint-schedule.model.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/* istanbul ignore file */

import { ApiProperty } from '@nestjs/swagger';
import { ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';

import { CheckpointScheduleModel } from '~/checkpoints/models/checkpoint-schedule.model';
import { TransactionQueueModel } from '~/common/models/transaction-queue.model';

export class CreatedCheckpointScheduleModel extends TransactionQueueModel {
@ApiProperty({
@ApiPropertyOptional({
description: 'Static data (and identifiers) of the newly created Schedule',
type: CheckpointScheduleModel,
})
Expand Down
4 changes: 2 additions & 2 deletions src/checkpoints/models/created-checkpoint.model.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/* istanbul ignore file */

import { ApiProperty } from '@nestjs/swagger';
import { ApiPropertyOptional } from '@nestjs/swagger';
import { Checkpoint } from '@polymeshassociation/polymesh-sdk/types';

import { FromEntity } from '~/common/decorators/transformation';
import { TransactionQueueModel } from '~/common/models/transaction-queue.model';

export class CreatedCheckpointModel extends TransactionQueueModel {
@ApiProperty({
@ApiPropertyOptional({
description: 'Identifiers of the newly created Checkpoint',
example: {
id: '1',
Expand Down
10 changes: 5 additions & 5 deletions src/claims/claims.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { CustomClaimTypeModel } from '~/claims/models/custom-claim-type.model';
import { CustomClaimTypeWithDid } from '~/claims/models/custom-claim-type-did.model';
import { PaginatedResultsModel } from '~/common/models/paginated-results.model';
import { mockPolymeshLoggerProvider } from '~/logger/mock-polymesh-logger';
import { testValues } from '~/test-utils/consts';
import { processedTxResult, testValues } from '~/test-utils/consts';
import { mockClaimsServiceProvider } from '~/test-utils/service-mocks';

const { did, txResult, signer } = testValues;
Expand Down Expand Up @@ -55,7 +55,7 @@ describe('ClaimsController', () => {

expect(mockClaimsService.addClaimsOnDid).toHaveBeenCalledWith(mockPayload);

expect(result).toEqual({ ...txResult, results: undefined });
expect(result).toEqual({ ...processedTxResult, results: undefined });
});
});

Expand All @@ -67,7 +67,7 @@ describe('ClaimsController', () => {

expect(mockClaimsService.editClaimsOnDid).toHaveBeenCalledWith(mockPayload);

expect(result).toEqual({ ...txResult, results: undefined });
expect(result).toEqual({ ...processedTxResult, results: undefined });
});
});

Expand All @@ -79,7 +79,7 @@ describe('ClaimsController', () => {

expect(mockClaimsService.revokeClaimsFromDid).toHaveBeenCalledWith(mockPayload);

expect(result).toEqual({ ...txResult, results: undefined });
expect(result).toEqual({ ...processedTxResult, results: undefined });
});
});

Expand All @@ -101,7 +101,7 @@ describe('ClaimsController', () => {
expect(mockClaimsService.registerCustomClaimType).toHaveBeenCalledWith(
mockRegisterCustomClaimTypeDto
);
expect(result).toEqual({ ...txResult, results: undefined });
expect(result).toEqual({ ...processedTxResult, results: undefined });
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/claims/claims.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { ModifyClaimsDto } from '~/claims/dto/modify-claims.dto';
import { RegisterCustomClaimTypeDto } from '~/claims/dto/register-custom-claim-type.dto';
import { CustomClaimTypeModel } from '~/claims/models/custom-claim-type.model';
import { CustomClaimTypeWithDid } from '~/claims/models/custom-claim-type-did.model';
import { ApiTransactionFailedResponse, ApiTransactionResponse } from '~/common/decorators/swagger';
import { ApiTransactionFailedResponse, ApiTransactionResponse } from '~/common/decorators/';
import { PaginatedResultsModel } from '~/common/models/paginated-results.model';
import { TransactionQueueModel } from '~/common/models/transaction-queue.model';
import { handleServiceResult, TransactionResponseModel } from '~/common/utils';
Expand Down
Loading

0 comments on commit aac249b

Please sign in to comment.