Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions modules/sdk-coin-canton/src/canton.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import {
AuditDecryptedKeyParams,
BaseCoin,
BaseTransaction,
BitGoBase,
CantonCommand,
CantonCommandParams,
CantonCreateCommand,
CantonExerciseCommand,
EDDSAMethods,
extractCommonKeychain,
InvalidAddressError,
KeyPair,
MPCAlgorithm,
MultisigType,
multisigTypes,
ParsedTransaction,
ParseTransactionOptions,
PopulatedIntent,
PrebuildTransactionWithIntentOptions,
SignedTransaction,
SignTransactionOptions,
TokenEnablementConfig,
TransactionExplanation as BaseTransactionExplanation,
TransactionParams,
TransactionType,
VerifyTransactionOptions,
TransactionExplanation as BaseTransactionExplanation,
BaseTransaction,
PopulatedIntent,
PrebuildTransactionWithIntentOptions,
TssVerifyAddressOptions,
InvalidAddressError,
extractCommonKeychain,
EDDSAMethods,
TokenEnablementConfig,
VerifyTransactionOptions,
} from '@bitgo/sdk-core';
import { auditEddsaPrivateKey } from '@bitgo/sdk-lib-mpc';
import { BaseCoin as StaticsBaseCoin, coins } from '@bitgo/statics';
Expand Down Expand Up @@ -126,7 +126,9 @@ export class Canton extends BaseCoin {
case TransactionType.TransferOfferWithdrawn:
case TransactionType.CosignDelegationAccept:
case TransactionType.CosignDelegationProposal:
// There is no input for these type of transactions, so always return true.
case TransactionType.AllocationAllocate:
case TransactionType.AllocationRequest:
// There is no recipient info to verify for these transaction types, so always return true.
return true;
case TransactionType.CantonCommand:
return this.verifyCantonCommandTransaction(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ export class Transaction extends BaseTransaction {
inputAmount = txData.amount;
break;
}
case TransactionType.AllocationAllocate:
case TransactionType.Send: {
const txData = this.toJson();
const output: ITransactionRecipient = {
Expand Down
43 changes: 43 additions & 0 deletions modules/sdk-coin-canton/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,49 @@ export class Utils implements BaseUtils {
break;
}

case TransactionType.AllocationAllocate: {
// DvpLegAllocation create node → allocation.transferLeg contains the full settlement transfer details:
// sender, receiver (the actual settlement counterparty), amount, and instrumentId
const dvpFields = findCreateNodeFields('DvpLegAllocation');
if (dvpFields) {
const allocationField = getField(dvpFields, 'allocation');
if (allocationField?.oneofKind === 'record') {
const allocationFields = allocationField.record?.fields ?? [];
const transferLegField = getField(allocationFields, 'transferLeg');
if (transferLegField?.oneofKind === 'record') {
const transferLegFields = transferLegField.record?.fields ?? [];
const senderData = getField(transferLegFields, 'sender');
if (senderData?.oneofKind === 'party') sender = senderData.party ?? '';
const receiverData = getField(transferLegFields, 'receiver');
if (receiverData?.oneofKind === 'party') receiver = receiverData.party ?? '';
const amountData = getField(transferLegFields, 'amount');
if (amountData?.oneofKind === 'numeric') amount = amountData.numeric ?? '';
const instrumentIdField = getField(transferLegFields, 'instrumentId');
if (instrumentIdField?.oneofKind === 'record') {
const instrumentIdFields = instrumentIdField.record?.fields ?? [];
const adminData = getField(instrumentIdFields, 'admin');
if (adminData?.oneofKind === 'party') instrumentAdmin = adminData.party ?? '';
const idData = getField(instrumentIdFields, 'id');
if (idData?.oneofKind === 'text') instrumentId = idData.text ?? '';
}
}
}
}
// Fallback: if DvpLegAllocation is absent, use the operator as receiver
if (!receiver) {
const allocateFields = findExerciseNodeFields('AllocationFactory_Allocate');
if (allocateFields) {
const adminData = getField(allocateFields, 'expectedAdmin');
if (adminData?.oneofKind === 'party') receiver = adminData.party ?? '';
}
}
if (!sender) {
const senderParty = findExerciseActingParty('AllocationFactory_Allocate');
if (senderParty) sender = senderParty;
}
break;
}

case TransactionType.CosignDelegationAccept: {
// exercise CosignDelegationProposal_Accept → actingParties[0] = signer (sender)
const signerParty = findExerciseActingParty('CosignDelegationProposal_Accept');
Expand Down
28 changes: 28 additions & 0 deletions modules/sdk-coin-canton/test/resources.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { coins } from '@bitgo/statics';

import { AllocationAllocateBuilder, Transaction } from '../../../../src';
import { CantonAllocationAllocateRequest } from '../../../../src/lib/iface';
import { CantonAllocationAllocatePrepareResponse } from '../../../resources';

const commandId = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
const amount = 100;
Expand Down Expand Up @@ -330,4 +331,44 @@ describe('AllocationAllocate Builder', () => {
txBuilder.setTransaction(prepareCommandResponse);
assert.deepEqual(tx.prepareCommand, prepareCommandResponse);
});

describe('Prepared transaction parsing', () => {
it('should parse the allocation allocate prepared transaction', function () {
const txBuilder = new AllocationAllocateBuilder(coins.get('tcanton'));
const tx = new Transaction(coins.get('tcanton'));
txBuilder.initBuilder(tx);
txBuilder.commandId(commandId);
txBuilder.setTransaction(CantonAllocationAllocatePrepareResponse);
const txData = txBuilder.transaction.toJson();
should.exist(txData);
assert.equal(
txData.sender,
'ravi-2-step-party::122092e7d33ac10c0f3d55976342f37555df05da5b742956d56a62ae2367769079d2'
);
// receiver = transferLeg.receiver from DvpLegAllocation — the actual settlement counterparty
assert.equal(
txData.receiver,
'ravi-2-step-party-new::122092e7d33ac10c0f3d55976342f37555df05da5b742956d56a62ae2367769079d2'
);
assert.equal(txData.amount, '50000000000');
});

it('should validate the raw allocation allocate transaction', async function () {
const txBuilder = new AllocationAllocateBuilder(coins.get('tcanton'));
const tx = new Transaction(coins.get('tcanton'));
txBuilder.initBuilder(tx);
txBuilder.commandId(commandId);
txBuilder.setTransaction(CantonAllocationAllocatePrepareResponse);
await txBuilder.validateRawTransaction(CantonAllocationAllocatePrepareResponse.preparedTransaction);
});

it('should validate the allocation allocate transaction', async function () {
const txBuilder = new AllocationAllocateBuilder(coins.get('tcanton'));
const tx = new Transaction(coins.get('tcanton'));
tx.prepareCommand = CantonAllocationAllocatePrepareResponse;
txBuilder.initBuilder(tx);
txBuilder.setTransaction(CantonAllocationAllocatePrepareResponse);
await txBuilder.validateTransaction(tx);
});
});
});
19 changes: 19 additions & 0 deletions modules/sdk-coin-canton/test/unit/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Value } from '../../src/lib/resourcesInterface';
import {
CANTON_ADDRESSES,
CANTON_BLOCK_HEIGHT,
CantonAllocationAllocatePrepareResponse,
CantonExerciseCommandPrepareResponse,
GenerateTopologyResponse,
OneStepPreApprovalPrepareResponse,
Expand Down Expand Up @@ -99,6 +100,24 @@ describe('Canton Util', function () {
);
assert.equal(parsedData.amount, '0');
});

it('should parse the allocation allocate prepared transaction', () => {
const parsedData = utils.parseRawCantonTransactionData(
CantonAllocationAllocatePrepareResponse.preparedTransaction,
TransactionType.AllocationAllocate
);
should.exist(parsedData);
assert.equal(
parsedData.sender,
'ravi-2-step-party::122092e7d33ac10c0f3d55976342f37555df05da5b742956d56a62ae2367769079d2'
);
// receiver = transferLeg.receiver from DvpLegAllocation — the actual settlement counterparty
assert.equal(
parsedData.receiver,
'ravi-2-step-party-new::122092e7d33ac10c0f3d55976342f37555df05da5b742956d56a62ae2367769079d2'
);
assert.equal(parsedData.amount, '50000000000');
});
});

describe('Wallet init transaction', function () {
Expand Down
Loading