Skip to content

Commit 34c8bc9

Browse files
committed
feat(sdk-coin-canton): added allocation allocate parsing
Ticket: CHALO-498
1 parent f7c3244 commit 34c8bc9

6 files changed

Lines changed: 144 additions & 10 deletions

File tree

modules/sdk-coin-canton/src/canton.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
import {
22
AuditDecryptedKeyParams,
33
BaseCoin,
4+
BaseTransaction,
45
BitGoBase,
56
CantonCommand,
67
CantonCommandParams,
78
CantonCreateCommand,
89
CantonExerciseCommand,
10+
EDDSAMethods,
11+
extractCommonKeychain,
12+
InvalidAddressError,
913
KeyPair,
1014
MPCAlgorithm,
1115
MultisigType,
1216
multisigTypes,
1317
ParsedTransaction,
1418
ParseTransactionOptions,
19+
PopulatedIntent,
20+
PrebuildTransactionWithIntentOptions,
1521
SignedTransaction,
1622
SignTransactionOptions,
23+
TokenEnablementConfig,
24+
TransactionExplanation as BaseTransactionExplanation,
1725
TransactionParams,
1826
TransactionType,
19-
VerifyTransactionOptions,
20-
TransactionExplanation as BaseTransactionExplanation,
21-
BaseTransaction,
22-
PopulatedIntent,
23-
PrebuildTransactionWithIntentOptions,
2427
TssVerifyAddressOptions,
25-
InvalidAddressError,
26-
extractCommonKeychain,
27-
EDDSAMethods,
28-
TokenEnablementConfig,
28+
VerifyTransactionOptions,
2929
} from '@bitgo/sdk-core';
3030
import { auditEddsaPrivateKey } from '@bitgo/sdk-lib-mpc';
3131
import { BaseCoin as StaticsBaseCoin, coins } from '@bitgo/statics';
@@ -126,7 +126,9 @@ export class Canton extends BaseCoin {
126126
case TransactionType.TransferOfferWithdrawn:
127127
case TransactionType.CosignDelegationAccept:
128128
case TransactionType.CosignDelegationProposal:
129-
// There is no input for these type of transactions, so always return true.
129+
case TransactionType.AllocationAllocate:
130+
case TransactionType.AllocationRequest:
131+
// There is no recipient info to verify for these transaction types, so always return true.
130132
return true;
131133
case TransactionType.CantonCommand:
132134
return this.verifyCantonCommandTransaction(

modules/sdk-coin-canton/src/lib/transaction/transaction.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ export class Transaction extends BaseTransaction {
349349
inputAmount = txData.amount;
350350
break;
351351
}
352+
case TransactionType.AllocationAllocate:
352353
case TransactionType.Send: {
353354
const txData = this.toJson();
354355
const output: ITransactionRecipient = {

modules/sdk-coin-canton/src/lib/utils.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,49 @@ export class Utils implements BaseUtils {
308308
break;
309309
}
310310

311+
case TransactionType.AllocationAllocate: {
312+
// DvpLegAllocation create node → allocation.transferLeg contains the full settlement transfer details:
313+
// sender, receiver (the actual settlement counterparty), amount, and instrumentId
314+
const dvpFields = findCreateNodeFields('DvpLegAllocation');
315+
if (dvpFields) {
316+
const allocationField = getField(dvpFields, 'allocation');
317+
if (allocationField?.oneofKind === 'record') {
318+
const allocationFields = allocationField.record?.fields ?? [];
319+
const transferLegField = getField(allocationFields, 'transferLeg');
320+
if (transferLegField?.oneofKind === 'record') {
321+
const transferLegFields = transferLegField.record?.fields ?? [];
322+
const senderData = getField(transferLegFields, 'sender');
323+
if (senderData?.oneofKind === 'party') sender = senderData.party ?? '';
324+
const receiverData = getField(transferLegFields, 'receiver');
325+
if (receiverData?.oneofKind === 'party') receiver = receiverData.party ?? '';
326+
const amountData = getField(transferLegFields, 'amount');
327+
if (amountData?.oneofKind === 'numeric') amount = amountData.numeric ?? '';
328+
const instrumentIdField = getField(transferLegFields, 'instrumentId');
329+
if (instrumentIdField?.oneofKind === 'record') {
330+
const instrumentIdFields = instrumentIdField.record?.fields ?? [];
331+
const adminData = getField(instrumentIdFields, 'admin');
332+
if (adminData?.oneofKind === 'party') instrumentAdmin = adminData.party ?? '';
333+
const idData = getField(instrumentIdFields, 'id');
334+
if (idData?.oneofKind === 'text') instrumentId = idData.text ?? '';
335+
}
336+
}
337+
}
338+
}
339+
// Fallback: if DvpLegAllocation is absent, use the operator as receiver
340+
if (!receiver) {
341+
const allocateFields = findExerciseNodeFields('AllocationFactory_Allocate');
342+
if (allocateFields) {
343+
const adminData = getField(allocateFields, 'expectedAdmin');
344+
if (adminData?.oneofKind === 'party') receiver = adminData.party ?? '';
345+
}
346+
}
347+
if (!sender) {
348+
const senderParty = findExerciseActingParty('AllocationFactory_Allocate');
349+
if (senderParty) sender = senderParty;
350+
}
351+
break;
352+
}
353+
311354
case TransactionType.CosignDelegationAccept: {
312355
// exercise CosignDelegationProposal_Accept → actingParties[0] = signer (sender)
313356
const signerParty = findExerciseActingParty('CosignDelegationProposal_Accept');

modules/sdk-coin-canton/test/resources.ts

Lines changed: 28 additions & 0 deletions
Large diffs are not rendered by default.

modules/sdk-coin-canton/test/unit/builder/allocationAllocate/allocationAllocateBuilder.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { coins } from '@bitgo/statics';
55

66
import { AllocationAllocateBuilder, Transaction } from '../../../../src';
77
import { CantonAllocationAllocateRequest } from '../../../../src/lib/iface';
8+
import { CantonAllocationAllocatePrepareResponse } from '../../../resources';
89

910
const commandId = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
1011
const amount = 100;
@@ -330,4 +331,44 @@ describe('AllocationAllocate Builder', () => {
330331
txBuilder.setTransaction(prepareCommandResponse);
331332
assert.deepEqual(tx.prepareCommand, prepareCommandResponse);
332333
});
334+
335+
describe('Prepared transaction parsing', () => {
336+
it('should parse the allocation allocate prepared transaction', function () {
337+
const txBuilder = new AllocationAllocateBuilder(coins.get('tcanton'));
338+
const tx = new Transaction(coins.get('tcanton'));
339+
txBuilder.initBuilder(tx);
340+
txBuilder.commandId(commandId);
341+
txBuilder.setTransaction(CantonAllocationAllocatePrepareResponse);
342+
const txData = txBuilder.transaction.toJson();
343+
should.exist(txData);
344+
assert.equal(
345+
txData.sender,
346+
'ravi-2-step-party::122092e7d33ac10c0f3d55976342f37555df05da5b742956d56a62ae2367769079d2'
347+
);
348+
// receiver = transferLeg.receiver from DvpLegAllocation — the actual settlement counterparty
349+
assert.equal(
350+
txData.receiver,
351+
'ravi-2-step-party-new::122092e7d33ac10c0f3d55976342f37555df05da5b742956d56a62ae2367769079d2'
352+
);
353+
assert.equal(txData.amount, '50000000000');
354+
});
355+
356+
it('should validate the raw allocation allocate transaction', async function () {
357+
const txBuilder = new AllocationAllocateBuilder(coins.get('tcanton'));
358+
const tx = new Transaction(coins.get('tcanton'));
359+
txBuilder.initBuilder(tx);
360+
txBuilder.commandId(commandId);
361+
txBuilder.setTransaction(CantonAllocationAllocatePrepareResponse);
362+
await txBuilder.validateRawTransaction(CantonAllocationAllocatePrepareResponse.preparedTransaction);
363+
});
364+
365+
it('should validate the allocation allocate transaction', async function () {
366+
const txBuilder = new AllocationAllocateBuilder(coins.get('tcanton'));
367+
const tx = new Transaction(coins.get('tcanton'));
368+
tx.prepareCommand = CantonAllocationAllocatePrepareResponse;
369+
txBuilder.initBuilder(tx);
370+
txBuilder.setTransaction(CantonAllocationAllocatePrepareResponse);
371+
await txBuilder.validateTransaction(tx);
372+
});
373+
});
333374
});

modules/sdk-coin-canton/test/unit/utils.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Value } from '../../src/lib/resourcesInterface';
88
import {
99
CANTON_ADDRESSES,
1010
CANTON_BLOCK_HEIGHT,
11+
CantonAllocationAllocatePrepareResponse,
1112
CantonExerciseCommandPrepareResponse,
1213
GenerateTopologyResponse,
1314
OneStepPreApprovalPrepareResponse,
@@ -99,6 +100,24 @@ describe('Canton Util', function () {
99100
);
100101
assert.equal(parsedData.amount, '0');
101102
});
103+
104+
it('should parse the allocation allocate prepared transaction', () => {
105+
const parsedData = utils.parseRawCantonTransactionData(
106+
CantonAllocationAllocatePrepareResponse.preparedTransaction,
107+
TransactionType.AllocationAllocate
108+
);
109+
should.exist(parsedData);
110+
assert.equal(
111+
parsedData.sender,
112+
'ravi-2-step-party::122092e7d33ac10c0f3d55976342f37555df05da5b742956d56a62ae2367769079d2'
113+
);
114+
// receiver = transferLeg.receiver from DvpLegAllocation — the actual settlement counterparty
115+
assert.equal(
116+
parsedData.receiver,
117+
'ravi-2-step-party-new::122092e7d33ac10c0f3d55976342f37555df05da5b742956d56a62ae2367769079d2'
118+
);
119+
assert.equal(parsedData.amount, '50000000000');
120+
});
102121
});
103122

104123
describe('Wallet init transaction', function () {

0 commit comments

Comments
 (0)