Skip to content

Commit 3578419

Browse files
authored
Merge pull request #8950 from BitGo/SCAAS-9624
feat(sdk-coin-canton): forward token and make choiceArgument optional
2 parents 1165435 + f2f4244 commit 3578419

5 files changed

Lines changed: 74 additions & 2 deletions

File tree

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export class CantonCommandBuilder extends TransactionBuilder {
1717
private _readAs: string[] = [];
1818
private _command: CantonCommand;
1919
private _resolveContracts: CantonCommandResolveContractSpec[] = [];
20+
private _token?: string;
2021

2122
constructor(_coinConfig: Readonly<CoinConfig>) {
2223
super(_coinConfig);
@@ -137,6 +138,21 @@ export class CantonCommandBuilder extends TransactionBuilder {
137138
return this;
138139
}
139140

141+
/**
142+
* Sets the Canton token identifier (e.g. 'tcanton:stgusd1') forwarded to IMS for
143+
* choice-context resolution on token-specific commands such as mint and burn.
144+
*
145+
* @param name - Registered BitGo canton token name
146+
* @returns The current builder instance for chaining.
147+
*/
148+
token(name: string): this {
149+
if (typeof name !== 'string' || !name.trim()) {
150+
throw new Error('token must be a non-empty string');
151+
}
152+
this._token = name.trim();
153+
return this;
154+
}
155+
140156
/**
141157
* Builds and returns the CantonCommandRequest from the builder's internal state.
142158
*
@@ -146,13 +162,17 @@ export class CantonCommandBuilder extends TransactionBuilder {
146162
toRequestObject(): CantonCommandRequest {
147163
this.validate();
148164

149-
return {
165+
const req: CantonCommandRequest = {
150166
commandId: this._commandId,
151167
actAs: this._actAs,
152168
readAs: this._readAs ?? [],
153169
command: this._command,
154170
resolveContracts: this._resolveContracts ?? [],
155171
};
172+
if (this._token) {
173+
req.token = this._token;
174+
}
175+
return req;
156176
}
157177

158178
private validate(): void {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ export interface CantonCommandRequest {
231231
readAs?: string[];
232232
command: CantonCommand;
233233
resolveContracts?: CantonCommandResolveContractSpec[];
234+
token?: string;
234235
}
235236

236237
// Root command decoded from the prepared Canton transaction protobuf, used during verifyTransaction.

modules/sdk-coin-canton/test/unit/builder/cantonCommand/cantonCommandBuilder.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,38 @@ describe('CantonCommandBuilder', () => {
138138
});
139139
});
140140

141+
describe('token()', () => {
142+
it('should set the token', function () {
143+
const builder = new CantonCommandBuilder(coins.get('tcanton'));
144+
const tx = new Transaction(coins.get('tcanton'));
145+
builder.initBuilder(tx);
146+
builder.commandId('cmd-tok-1').actAs([PARTY_A]).command(sampleExerciseCommand).token('tcanton:testtoken');
147+
assert.equal(builder.toRequestObject().token, 'tcanton:testtoken');
148+
});
149+
150+
it('should trim whitespace', function () {
151+
const builder = new CantonCommandBuilder(coins.get('tcanton'));
152+
const tx = new Transaction(coins.get('tcanton'));
153+
builder.initBuilder(tx);
154+
builder.commandId('cmd-tok-2').actAs([PARTY_A]).command(sampleExerciseCommand).token(' tcanton:testtoken ');
155+
assert.equal(builder.toRequestObject().token, 'tcanton:testtoken');
156+
});
157+
158+
it('should throw on empty string', function () {
159+
const builder = new CantonCommandBuilder(coins.get('tcanton'));
160+
const tx = new Transaction(coins.get('tcanton'));
161+
builder.initBuilder(tx);
162+
assert.throws(() => builder.token(''), /token must be a non-empty string/);
163+
});
164+
165+
it('should throw on whitespace-only string', function () {
166+
const builder = new CantonCommandBuilder(coins.get('tcanton'));
167+
const tx = new Transaction(coins.get('tcanton'));
168+
builder.initBuilder(tx);
169+
assert.throws(() => builder.token(' '), /token must be a non-empty string/);
170+
});
171+
});
172+
141173
describe('resolveContracts()', () => {
142174
it('should set the spec array', function () {
143175
const spec = [{ templateId: TEMPLATE_ID, actAs: [PARTY_A], injectAs: 'command.ExerciseCommand.contractId' }];
@@ -206,6 +238,24 @@ describe('CantonCommandBuilder', () => {
206238
const req = builder.toRequestObject();
207239
assert.deepEqual(req.resolveContracts, []);
208240
});
241+
242+
it('should include token when set', function () {
243+
const builder = new CantonCommandBuilder(coins.get('tcanton'));
244+
const tx = new Transaction(coins.get('tcanton'));
245+
builder.initBuilder(tx);
246+
builder.commandId('cmd-003').actAs([PARTY_A]).command(sampleExerciseCommand).token('tcanton:testtoken');
247+
const req = builder.toRequestObject();
248+
assert.equal(req.token, 'tcanton:testtoken');
249+
});
250+
251+
it('should not include token key when not set', function () {
252+
const builder = new CantonCommandBuilder(coins.get('tcanton'));
253+
const tx = new Transaction(coins.get('tcanton'));
254+
builder.initBuilder(tx);
255+
builder.commandId('cmd-004').actAs([PARTY_A]).command(sampleExerciseCommand);
256+
const req = builder.toRequestObject();
257+
assert.ok(!('token' in req));
258+
});
209259
});
210260

211261
describe('initBuilder()', () => {

modules/sdk-core/src/bitgo/utils/tss/baseTypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export interface CantonExerciseCommand {
9393
templateId: string;
9494
contractId?: string;
9595
choice: string;
96-
choiceArgument: Record<string, unknown>;
96+
choiceArgument?: Record<string, unknown>;
9797
};
9898
}
9999

modules/sdk-core/src/bitgo/wallet/wallet.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4301,6 +4301,7 @@ export class Wallet implements IWallet {
43014301
reqId,
43024302
intentType: 'cantonCommand',
43034303
cantonCommandParams: params.cantonCommandParams,
4304+
tokenName: params.tokenName,
43044305
sequenceId: params.sequenceId,
43054306
comment: params.comment,
43064307
},

0 commit comments

Comments
 (0)