@ledgerhq/coin-sui 0.15.0-nightly.2 → 0.15.0-nightly.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -185,7 +185,7 @@ const mockTransaction = {
185
185
  ],
186
186
  timestampMs: "1742294454878",
187
187
  checkpoint: "313024",
188
- };
188
+ } as SuiTransactionBlockResponse;
189
189
 
190
190
  const mockApi = new SuiClient({ url: "mock" }) as jest.Mocked<SuiClient>;
191
191
 
@@ -254,16 +254,12 @@ describe("SDK Functions", () => {
254
254
 
255
255
  test("getOperationType should return IN for incoming tx", () => {
256
256
  const address = "0x33444cf803c690db96527cec67e3c9ab512596f4ba2d4eace43f0b4f716e0164";
257
- expect(sdk.getOperationType(address, mockTransaction as SuiTransactionBlockResponse)).toBe(
258
- "IN",
259
- );
257
+ expect(sdk.getOperationType(address, mockTransaction)).toBe("IN");
260
258
  });
261
259
 
262
260
  test("getOperationType should return OUT for outgoing tx", () => {
263
261
  const address = "0x65449f57946938c84c512732f1d69405d1fce417d9c9894696ddf4522f479e24";
264
- expect(sdk.getOperationType(address, mockTransaction as SuiTransactionBlockResponse)).toBe(
265
- "OUT",
266
- );
262
+ expect(sdk.getOperationType(address, mockTransaction)).toBe("OUT");
267
263
  });
268
264
 
269
265
  test("getOperationSenders should return sender address", () => {
@@ -278,36 +274,12 @@ describe("SDK Functions", () => {
278
274
  ).toEqual(["0x6e143fe0a8ca010a86580dafac44298e5b1b7d73efc345356a59a15f0d7824f0"]);
279
275
  });
280
276
 
281
- test("getOperationAmount should calculate amount correctly for SUI", () => {
282
- const address = "0x6e143fe0a8ca010a86580dafac44298e5b1b7d73efc345356a59a15f0d7824f0";
283
- expect(
284
- sdk.getOperationAmount(
285
- address,
286
- mockTransaction as SuiTransactionBlockResponse,
287
- sdk.DEFAULT_COIN_TYPE,
288
- ),
289
- ).toEqual(new BigNumber("9998990120"));
290
- });
291
-
292
- test("getOperationAmount should calculate amount correctly for tokens", () => {
293
- const address = "0x6e143fe0a8ca010a86580dafac44298e5b1b7d73efc345356a59a15f0d7824f0";
294
- expect(
295
- sdk.getOperationAmount(
296
- address,
297
- mockTransaction as SuiTransactionBlockResponse,
298
- "0x123::test::TOKEN",
299
- ),
300
- ).toEqual(new BigNumber("500000"));
301
- });
302
-
303
277
  test("getOperationFee should calculate fee correctly", () => {
304
- expect(sdk.getOperationFee(mockTransaction as SuiTransactionBlockResponse)).toEqual(
305
- new BigNumber(1009880),
306
- );
278
+ expect(sdk.getOperationFee(mockTransaction)).toEqual(new BigNumber(1009880));
307
279
  });
308
280
 
309
281
  test("getOperationDate should return correct date", () => {
310
- const date = sdk.getOperationDate(mockTransaction as SuiTransactionBlockResponse);
282
+ const date = sdk.getOperationDate(mockTransaction);
311
283
  expect(date).toBeDefined();
312
284
  expect(date).toBeInstanceOf(Date);
313
285
  });
@@ -477,7 +449,7 @@ describe("SDK Functions", () => {
477
449
  ],
478
450
  };
479
451
 
480
- const operation = sdk.transactionToOp(address, tokenTx as SuiTransactionBlockResponse);
452
+ const operation = sdk.alpacaTransactionToOp(address, tokenTx as SuiTransactionBlockResponse);
481
453
  expect(operation.id).toEqual("DhKLpX5kwuKuyRa71RGqpX5EY2M8Efw535ZVXYXsRiDt");
482
454
  expect(operation.type).toEqual("IN");
483
455
  expect(operation.senders).toEqual([
@@ -643,41 +615,34 @@ describe("Staking Operations", () => {
643
615
  });
644
616
 
645
617
  describe("Operation Amount Calculation", () => {
646
- test("getOperationAmount should calculate staking amount correctly", () => {
647
- const address = "0x65449f57946938c84c512732f1d69405d1fce417d9c9894696ddf4522f479e24";
618
+ const address = "0x6e143fe0a8ca010a86580dafac44298e5b1b7d73efc345356a59a15f0d7824f0";
648
619
 
649
- const mockStakingTx = {
650
- transaction: {
651
- data: {
652
- transaction: {
653
- kind: "ProgrammableTransaction",
654
- transactions: [
655
- {
656
- MoveCall: {
657
- function: "request_add_stake",
658
- },
620
+ const mockStakingTx = {
621
+ transaction: {
622
+ data: {
623
+ transaction: {
624
+ kind: "ProgrammableTransaction",
625
+ transactions: [
626
+ {
627
+ MoveCall: {
628
+ function: "request_add_stake",
659
629
  },
660
- ],
661
- },
630
+ },
631
+ ],
662
632
  },
663
633
  },
664
- balanceChanges: [
665
- {
666
- owner: { AddressOwner: address },
667
- coinType: "0x2::sui::SUI",
668
- amount: "-1000000000",
669
- },
670
- ],
671
- } as unknown as SuiTransactionBlockResponse;
672
-
673
- const amount = sdk.getOperationAmount(address, mockStakingTx, sdk.DEFAULT_COIN_TYPE);
674
- expect(amount).toEqual(new BigNumber("1000000000")); // The function returns minus of the balance change
675
- });
676
-
677
- test("getOperationAmount should calculate unstaking amount correctly", () => {
678
- const address = "0x65449f57946938c84c512732f1d69405d1fce417d9c9894696ddf4522f479e24";
634
+ },
635
+ balanceChanges: [
636
+ {
637
+ owner: { AddressOwner: address },
638
+ coinType: "0x2::sui::SUI",
639
+ amount: "-1000000000",
640
+ },
641
+ ],
642
+ } as SuiTransactionBlockResponse;
679
643
 
680
- const mockUnstakingTx = {
644
+ function mockUnstakingTx(amount: string) {
645
+ return {
681
646
  transaction: {
682
647
  data: {
683
648
  transaction: {
@@ -696,14 +661,59 @@ describe("Staking Operations", () => {
696
661
  {
697
662
  owner: { AddressOwner: address },
698
663
  coinType: "0x2::sui::SUI",
699
- amount: "0",
664
+ amount: amount,
700
665
  },
701
666
  ],
702
- } as unknown as SuiTransactionBlockResponse;
667
+ } as SuiTransactionBlockResponse;
668
+ }
703
669
 
704
- const amount = sdk.getOperationAmount(address, mockUnstakingTx, sdk.DEFAULT_COIN_TYPE);
705
- expect(amount).toEqual(new BigNumber("0"));
706
- });
670
+ function bridgeOperationAmount(
671
+ mock: SuiTransactionBlockResponse,
672
+ coinType: string = sdk.DEFAULT_COIN_TYPE,
673
+ ) {
674
+ return sdk.getOperationAmount(address, mock, coinType);
675
+ }
676
+
677
+ test("getOperationAmount should calculate staking amount", () =>
678
+ expect(bridgeOperationAmount(mockStakingTx)).toEqual(new BigNumber("1000000000")));
679
+
680
+ test("getOperationAmount should calculate unstaking amount of 1000", () =>
681
+ expect(bridgeOperationAmount(mockUnstakingTx("1000"))).toEqual(new BigNumber("-1000")));
682
+
683
+ test("getOperationAmount should calculate unstaking amount of 0", () =>
684
+ expect(bridgeOperationAmount(mockUnstakingTx("0"))).toEqual(new BigNumber("0")));
685
+
686
+ test("getOperationAmount should calculate amount correctly for SUI", () =>
687
+ expect(bridgeOperationAmount(mockTransaction)).toEqual(new BigNumber("9998990120")));
688
+
689
+ test("getOperationAmount should calculate amount correctly for tokens", () =>
690
+ expect(bridgeOperationAmount(mockTransaction, "0x123::test::TOKEN")).toEqual(
691
+ new BigNumber("500000"),
692
+ ));
693
+
694
+ function alpacaOperationAmount(
695
+ mock: SuiTransactionBlockResponse,
696
+ coinType: string = sdk.DEFAULT_COIN_TYPE,
697
+ ) {
698
+ return sdk.alpacaGetOperationAmount(address, mock, coinType);
699
+ }
700
+
701
+ test("alpaca getOperationAmount should calculate staking amount", () =>
702
+ expect(alpacaOperationAmount(mockStakingTx)).toEqual(new BigNumber("1000000000")));
703
+
704
+ test("alpaca getOperationAmount should calculate unstaking amount of 1000", () =>
705
+ expect(alpacaOperationAmount(mockUnstakingTx("1000"))).toEqual(new BigNumber("1000")));
706
+
707
+ test("alpaca getOperationAmount should calculate unstaking amount of 0", () =>
708
+ expect(alpacaOperationAmount(mockUnstakingTx("0"))).toEqual(new BigNumber("0")));
709
+
710
+ test("alpaca getOperationAmount should calculate amount correctly for SUI", () =>
711
+ expect(alpacaOperationAmount(mockTransaction)).toEqual(new BigNumber("9998990120")));
712
+
713
+ test("alpaca getOperationAmount should calculate amount correctly for tokens", () =>
714
+ expect(alpacaOperationAmount(mockTransaction, "0x123::test::TOKEN")).toEqual(
715
+ new BigNumber("500000"),
716
+ ));
707
717
  });
708
718
 
709
719
  describe("Operation Recipients", () => {
@@ -984,7 +994,7 @@ describe("Staking Operations", () => {
984
994
  checkpoint: "313024",
985
995
  } as unknown as SuiTransactionBlockResponse;
986
996
 
987
- const operation = sdk.transactionToOp(address, mockStakingTx);
997
+ const operation = sdk.alpacaTransactionToOp(address, mockStakingTx);
988
998
 
989
999
  expect(operation.id).toEqual("delegate_tx_digest_123");
990
1000
  expect(operation.type).toEqual("DELEGATE");
@@ -1035,7 +1045,7 @@ describe("Staking Operations", () => {
1035
1045
  checkpoint: "313024",
1036
1046
  } as unknown as SuiTransactionBlockResponse;
1037
1047
 
1038
- const operation = sdk.transactionToOp(address, mockUnstakingTx);
1048
+ const operation = sdk.alpacaTransactionToOp(address, mockUnstakingTx);
1039
1049
 
1040
1050
  expect(operation.id).toEqual("undelegate_tx_digest_456");
1041
1051
  expect(operation.type).toEqual("UNDELEGATE");
@@ -108,32 +108,31 @@ export const getAllBalancesCached = makeLRUCache(
108
108
  (owner: string) => owner,
109
109
  minutes(1),
110
110
  );
111
- function isStaking(block?: SuiTransactionBlockKind): block is {
111
+
112
+ type ProgrammableTransaction = {
112
113
  inputs: SuiCallArg[];
113
114
  kind: "ProgrammableTransaction";
114
115
  transactions: SuiTransaction[];
115
- } {
116
- if (!block) return false;
117
- if (block.kind === "ProgrammableTransaction") {
116
+ };
117
+
118
+ function isMoveCallWithFunction(
119
+ functionName: string,
120
+ block?: SuiTransactionBlockKind,
121
+ ): block is ProgrammableTransaction {
122
+ if (block?.kind === "ProgrammableTransaction") {
118
123
  const move = block.transactions.find(item => "MoveCall" in item) as any;
119
- return move?.MoveCall.function === "request_add_stake";
124
+ return move?.MoveCall.function === functionName;
125
+ } else {
126
+ return false;
120
127
  }
121
- return false;
122
128
  }
123
129
 
124
- function isUnstaking(block?: SuiTransactionBlockKind): block is {
125
- inputs: SuiCallArg[];
126
- kind: "ProgrammableTransaction";
127
- transactions: SuiTransaction[];
128
- } {
129
- if (!block) return false;
130
- if (block.kind === "ProgrammableTransaction") {
131
- const move = block.transactions.find(
132
- item => "MoveCall" in item && item["MoveCall"].function === "request_withdraw_stake",
133
- ) as any;
134
- return Boolean(move);
135
- }
136
- return false;
130
+ function isStaking(block?: SuiTransactionBlockKind): block is ProgrammableTransaction {
131
+ return isMoveCallWithFunction("request_add_stake", block);
132
+ }
133
+
134
+ function isUnstaking(block?: SuiTransactionBlockKind): block is ProgrammableTransaction {
135
+ return isMoveCallWithFunction("request_withdraw_stake", block);
137
136
  }
138
137
 
139
138
  /**
@@ -342,11 +341,48 @@ export function transactionToOperation(
342
341
  };
343
342
  }
344
343
 
344
+ // This function is only used by alpaca code path
345
+ // Logic is similar to getOperationAmount, but we guarantee to return a positive amount in any case
346
+ // If there is need to display negative amount for staking or unstaking, the view can handle it based on the type of the operation
347
+ export const alpacaGetOperationAmount = (
348
+ address: string,
349
+ transaction: SuiTransactionBlockResponse,
350
+ coinType: string,
351
+ ): BigNumber => {
352
+ const absoluteAmount = (balanceChange: BalanceChange | undefined) =>
353
+ new BigNumber(balanceChange?.amount || 0).abs();
354
+
355
+ const zero = BigNumber(0);
356
+
357
+ const tx = transaction.transaction?.data.transaction;
358
+
359
+ if (isStaking(tx) || isUnstaking(tx)) return absoluteAmount(transaction.balanceChanges?.[0]);
360
+ else {
361
+ return (
362
+ transaction.balanceChanges
363
+ ?.filter(
364
+ balanceChange =>
365
+ typeof balanceChange.owner !== "string" &&
366
+ "AddressOwner" in balanceChange.owner &&
367
+ balanceChange.owner.AddressOwner === address &&
368
+ balanceChange.coinType === coinType,
369
+ )
370
+ .map(absoluteAmount)
371
+ .reduce((acc, curr) => acc.plus(curr), zero) || zero
372
+ );
373
+ }
374
+ };
375
+
345
376
  /**
377
+ * This function is only used by alpaca code path
378
+ *
346
379
  * @returns the operation converted. Note that if param `transaction` was retrieved as an "IN" operations, the type may be converted to "OUT".
347
380
  * It happens for most "OUT" operations because the sender receive a new version of the coin objects.
348
381
  */
349
- export function transactionToOp(address: string, transaction: SuiTransactionBlockResponse): Op {
382
+ export function alpacaTransactionToOp(
383
+ address: string,
384
+ transaction: SuiTransactionBlockResponse,
385
+ ): Op {
350
386
  const type = getOperationType(address, transaction);
351
387
  const coinType = getOperationCoinType(transaction);
352
388
  const hash = transaction.digest;
@@ -365,7 +401,7 @@ export function transactionToOp(address: string, transaction: SuiTransactionBloc
365
401
  recipients: getOperationRecipients(transaction.transaction?.data),
366
402
  senders: getOperationSenders(transaction.transaction?.data),
367
403
  type,
368
- value: BigInt(getOperationAmount(address, transaction, coinType).toString()),
404
+ value: BigInt(alpacaGetOperationAmount(address, transaction, coinType).toString()),
369
405
  };
370
406
  }
371
407
 
@@ -576,7 +612,7 @@ export const getListOperations = async (
576
612
 
577
613
  const operations = ops.operations
578
614
  .sort((a, b) => Number(b.timestampMs) - Number(a.timestampMs))
579
- .map(t => transactionToOp(addr, t));
615
+ .map(t => alpacaTransactionToOp(addr, t));
580
616
 
581
617
  return {
582
618
  items: operations,