Skip to content

Commit 4d3d93f

Browse files
Merge pull request #6096 from BitGo/WIN-5377
feat(sdk-coin-icp): support setting ingressEnd
2 parents b95302c + a27e67d commit 4d3d93f

File tree

6 files changed

+38
-7
lines changed

6 files changed

+38
-7
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export class Transaction extends BaseTransaction {
108108
fee: parsedTx.operations[2].amount.value,
109109
senderPublicKeyHex: senderPublicKeyHex,
110110
transactionType: transactionType,
111-
expiryTime: Number(parsedTx.metadata.created_at_time + (MAX_INGRESS_TTL - PERMITTED_DRIFT)),
111+
expiryTime: Number(parsedTx.metadata.ingress_end ?? parsedTx.metadata.created_at_time + MAX_INGRESS_TTL),
112112
memo: parsedTx.metadata.memo,
113113
};
114114

@@ -225,6 +225,7 @@ export class Transaction extends BaseTransaction {
225225
) as CborUnsignedTransaction;
226226
const update = unsignedTransaction.updates[0];
227227
const httpCanisterUpdate = (update as unknown as [string, HttpCanisterUpdate])[1];
228+
httpCanisterUpdate.ingress_expiry = BigInt(unsignedTransaction.ingress_expiries[0]);
228229
return await this.getParsedTransactionFromUpdate(httpCanisterUpdate, false);
229230
}
230231

@@ -284,6 +285,7 @@ export class Transaction extends BaseTransaction {
284285
metadata: {
285286
created_at_time: args.createdAtTime.timestampNanos,
286287
memo: Number(args.memo.memo),
288+
ingress_end: Number(httpCanisterUpdate.ingress_expiry) + PERMITTED_DRIFT,
287289
},
288290
account_identifier_signers: accountIdentifierSigners,
289291
};
@@ -294,6 +296,7 @@ export class Transaction extends BaseTransaction {
294296
async parseSignedTransaction(rawTransaction: string): Promise<ParsedTransaction> {
295297
const signedTransaction = this._utils.cborDecode(this._utils.blobFromHex(rawTransaction));
296298
const httpCanisterUpdate = (signedTransaction as UpdateEnvelope).content as HttpCanisterUpdate;
299+
httpCanisterUpdate.ingress_expiry = BigInt((signedTransaction as UpdateEnvelope).content.ingress_expiry);
297300
return await this.getParsedTransactionFromUpdate(httpCanisterUpdate, true);
298301
}
299302

modules/sdk-coin-icp/src/lib/transactionBuilder.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
1111
protected _sender: string;
1212
protected _publicKey: string;
1313
protected _memo: number | BigInt;
14+
protected _ingressEnd: number | BigInt;
1415
protected _receiverId: string;
1516
protected _amount: string;
1617

@@ -64,6 +65,19 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
6465
return this;
6566
}
6667

68+
/**
69+
* Set the ingressEnd timestamp
70+
* @param {number} ingressEnd - timestamp in nanoseconds
71+
* @returns {TransactionBuilder} This transaction builder
72+
*/
73+
public ingressEnd(ingressEnd: number | BigInt): this {
74+
if (BigInt(ingressEnd.toString()) < 0n) {
75+
throw new BuildTransactionError(`Invalid timestamp: ${ingressEnd}`);
76+
}
77+
this._ingressEnd = ingressEnd;
78+
return this;
79+
}
80+
6781
/**
6882
* Sets the account Id of the receiver of this transaction.
6983
*
@@ -138,6 +152,7 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
138152
this._publicKey = icpTransactionData.senderPublicKeyHex;
139153
this._amount = icpTransactionData.amount;
140154
this._memo = icpTransactionData.memo ?? DEFAULT_MEMO;
155+
this._ingressEnd = Number(icpTransactionData.expiryTime);
141156
}
142157

143158
validateAddress(address: BaseAddress): void {

modules/sdk-coin-icp/src/lib/transferBuilder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export class TransferBuilder extends TransactionBuilder {
9595
};
9696

9797
const createdTimestamp = this._transaction.createdTimestamp;
98-
const { metaData, ingressEndTime } = this._utils.getMetaData(this._memo, createdTimestamp);
98+
const { metaData, ingressEndTime } = this._utils.getMetaData(this._memo, createdTimestamp, this._ingressEnd);
9999

100100
const icpTransaction: IcpTransaction = {
101101
public_keys: [publicKey],

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
CurveType,
2222
AccountIdentifierHash,
2323
CborUnsignedTransaction,
24+
MAX_INGRESS_TTL,
2425
} from './iface';
2526
import { KeyPair as IcpKeyPair } from './keyPair';
2627
const messageCompiled = require('../../resources/messageCompiled');
@@ -581,14 +582,21 @@ export class Utils implements BaseUtils {
581582

582583
getMetaData(
583584
memo: number | BigInt,
584-
timestamp: number | bigint | undefined
585+
timestamp: number | bigint | undefined,
586+
ingressEnd: number | BigInt | undefined
585587
): { metaData: MetaData; ingressEndTime: number | BigInt } {
586588
let currentTime = Date.now() * 1000000;
587589
if (timestamp) {
588590
currentTime = Number(timestamp);
589591
}
590-
const ingressStartTime = currentTime;
591-
const ingressEndTime = ingressStartTime + 5 * 60 * 1000000000; // 5 mins in nanoseconds
592+
let ingressStartTime: number, ingressEndTime: number;
593+
if (ingressEnd) {
594+
ingressEndTime = Number(ingressEnd);
595+
ingressStartTime = ingressEndTime - MAX_INGRESS_TTL; // 5 mins in nanoseconds
596+
} else {
597+
ingressStartTime = currentTime;
598+
ingressEndTime = ingressStartTime + MAX_INGRESS_TTL; // 5 mins in nanoseconds
599+
}
592600
const metaData: MetaData = {
593601
created_at_time: currentTime,
594602
ingress_start: ingressStartTime,

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ export const ParsedUnsignedTransaction = {
306306
metadata: {
307307
created_at_time: 1743689749455000000,
308308
memo: 1234,
309+
ingress_end: 1743690049455000000,
309310
},
310311
};
311312

@@ -355,6 +356,7 @@ export const ParsedSignedTransaction = {
355356
metadata: {
356357
created_at_time: 1743689749455000000,
357358
memo: 1234,
359+
ingress_end: 1743690049455000000,
358360
},
359361
};
360362

modules/sdk-coin-icp/test/unit/transactionBuilder/transactionBuilder.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getBuilderFactory } from '../getBuilderFactory';
33
import { BaseKey } from '@bitgo/sdk-core';
44
import * as testData from '../../resources/icp';
55
import sinon from 'sinon';
6-
import { DEFAULT_MEMO } from '../../../src/lib/iface';
6+
import { DEFAULT_MEMO, MAX_INGRESS_TTL } from '../../../src/lib/iface';
77

88
describe('ICP Transaction Builder', async () => {
99
const factory = getBuilderFactory('ticp');
@@ -122,7 +122,7 @@ describe('ICP Transaction Builder', async () => {
122122
txBuilder.receiverId(testData.Accounts.account2.address);
123123
txBuilder.amount('10');
124124
txBuilder.memo(testData.MetaDataWithMemo.memo);
125-
125+
txBuilder.ingressEnd(1904384564000000000n);
126126
await txBuilder.build();
127127
txn = txBuilder.transaction;
128128
const unsignedTxn = txBuilder.transaction.unsignedTransaction;
@@ -139,6 +139,9 @@ describe('ICP Transaction Builder', async () => {
139139
txBuilder.combine();
140140
const signedTxn = txBuilder.transaction.signedTransaction;
141141
signedTxn.should.be.a.String();
142+
txBuilder.transaction.icpTransaction.metadata.ingress_start.should.equal(
143+
Number(1904384564000000000n) - MAX_INGRESS_TTL
144+
);
142145
});
143146
});
144147

0 commit comments

Comments
 (0)