@ledgerhq/coin-xrp 6.2.0-nightly.3 → 6.2.1-next.0

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.
Files changed (190) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.unimportedrc.json +1 -3
  3. package/CHANGELOG.md +72 -16
  4. package/lib/api/index.d.ts +11 -2
  5. package/lib/api/index.d.ts.map +1 -1
  6. package/lib/api/index.integ.test.js +7 -24
  7. package/lib/api/index.integ.test.js.map +1 -1
  8. package/lib/api/index.js +5 -15
  9. package/lib/api/index.js.map +1 -1
  10. package/lib/api/index.test.js +9 -18
  11. package/lib/api/index.test.js.map +1 -1
  12. package/lib/bridge/broadcast.d.ts +4 -0
  13. package/lib/bridge/broadcast.d.ts.map +1 -0
  14. package/lib/bridge/broadcast.js +11 -0
  15. package/lib/bridge/broadcast.js.map +1 -0
  16. package/lib/bridge/createTransaction.d.ts +4 -0
  17. package/lib/bridge/createTransaction.d.ts.map +1 -0
  18. package/lib/bridge/createTransaction.js +18 -0
  19. package/lib/bridge/createTransaction.js.map +1 -0
  20. package/lib/bridge/estimateMaxSpendable.d.ts +4 -0
  21. package/lib/bridge/estimateMaxSpendable.d.ts.map +1 -0
  22. package/lib/bridge/estimateMaxSpendable.js +26 -0
  23. package/lib/bridge/estimateMaxSpendable.js.map +1 -0
  24. package/lib/bridge/getTransactionStatus.d.ts +4 -0
  25. package/lib/bridge/getTransactionStatus.d.ts.map +1 -0
  26. package/lib/{logic → bridge}/getTransactionStatus.js +19 -15
  27. package/lib/bridge/getTransactionStatus.js.map +1 -0
  28. package/lib/bridge/index.d.ts +11 -0
  29. package/lib/bridge/index.d.ts.map +1 -0
  30. package/lib/bridge/index.js +47 -0
  31. package/lib/bridge/index.js.map +1 -0
  32. package/lib/bridge/prepareTransaction.d.ts +4 -0
  33. package/lib/bridge/prepareTransaction.d.ts.map +1 -0
  34. package/lib/bridge/prepareTransaction.js +14 -0
  35. package/lib/bridge/prepareTransaction.js.map +1 -0
  36. package/lib/bridge/signOperation.d.ts +5 -0
  37. package/lib/bridge/signOperation.d.ts.map +1 -0
  38. package/lib/bridge/signOperation.js +76 -0
  39. package/lib/bridge/signOperation.js.map +1 -0
  40. package/lib/bridge/synchronization.d.ts +3 -0
  41. package/lib/bridge/synchronization.d.ts.map +1 -0
  42. package/lib/bridge/synchronization.js +85 -0
  43. package/lib/bridge/synchronization.js.map +1 -0
  44. package/lib/bridge/synchronization.test.d.ts +2 -0
  45. package/lib/bridge/synchronization.test.d.ts.map +1 -0
  46. package/lib/bridge/synchronization.test.js +140 -0
  47. package/lib/bridge/synchronization.test.js.map +1 -0
  48. package/lib/{transaction.d.ts → bridge/transaction.d.ts} +1 -1
  49. package/lib/bridge/transaction.d.ts.map +1 -0
  50. package/lib/bridge/transaction.js.map +1 -0
  51. package/lib/index.d.ts +1 -0
  52. package/lib/index.d.ts.map +1 -1
  53. package/lib/index.js +3 -0
  54. package/lib/index.js.map +1 -1
  55. package/lib/logic/combine.d.ts.map +1 -1
  56. package/lib/logic/combine.js +3 -8
  57. package/lib/logic/combine.js.map +1 -1
  58. package/lib/logic/getBalance.d.ts.map +1 -1
  59. package/lib/logic/getBalance.js +1 -13
  60. package/lib/logic/getBalance.js.map +1 -1
  61. package/lib/logic/getBalance.test.js +1 -14
  62. package/lib/logic/getBalance.test.js.map +1 -1
  63. package/lib/logic/index.d.ts +1 -2
  64. package/lib/logic/index.d.ts.map +1 -1
  65. package/lib/logic/index.js +2 -3
  66. package/lib/logic/index.js.map +1 -1
  67. package/lib/logic/utils.d.ts +1 -0
  68. package/lib/logic/utils.d.ts.map +1 -1
  69. package/lib/logic/utils.js +10 -14
  70. package/lib/logic/utils.js.map +1 -1
  71. package/lib/test/bridgeDatasetTest.d.ts.map +1 -1
  72. package/lib/test/bridgeDatasetTest.js +7 -7
  73. package/lib/test/bridgeDatasetTest.js.map +1 -1
  74. package/lib/types/model.d.ts +0 -7
  75. package/lib/types/model.d.ts.map +1 -1
  76. package/lib-es/api/index.d.ts +11 -2
  77. package/lib-es/api/index.d.ts.map +1 -1
  78. package/lib-es/api/index.integ.test.js +7 -24
  79. package/lib-es/api/index.integ.test.js.map +1 -1
  80. package/lib-es/api/index.js +6 -16
  81. package/lib-es/api/index.js.map +1 -1
  82. package/lib-es/api/index.test.js +9 -18
  83. package/lib-es/api/index.test.js.map +1 -1
  84. package/lib-es/bridge/broadcast.d.ts +4 -0
  85. package/lib-es/bridge/broadcast.d.ts.map +1 -0
  86. package/lib-es/bridge/broadcast.js +7 -0
  87. package/lib-es/bridge/broadcast.js.map +1 -0
  88. package/lib-es/bridge/createTransaction.d.ts +4 -0
  89. package/lib-es/bridge/createTransaction.d.ts.map +1 -0
  90. package/lib-es/bridge/createTransaction.js +11 -0
  91. package/lib-es/bridge/createTransaction.js.map +1 -0
  92. package/lib-es/bridge/estimateMaxSpendable.d.ts +4 -0
  93. package/lib-es/bridge/estimateMaxSpendable.d.ts.map +1 -0
  94. package/lib-es/bridge/estimateMaxSpendable.js +19 -0
  95. package/lib-es/bridge/estimateMaxSpendable.js.map +1 -0
  96. package/lib-es/bridge/getTransactionStatus.d.ts +4 -0
  97. package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -0
  98. package/lib-es/{logic → bridge}/getTransactionStatus.js +14 -13
  99. package/lib-es/bridge/getTransactionStatus.js.map +1 -0
  100. package/lib-es/bridge/index.d.ts +11 -0
  101. package/lib-es/bridge/index.d.ts.map +1 -0
  102. package/lib-es/bridge/index.js +41 -0
  103. package/lib-es/bridge/index.js.map +1 -0
  104. package/lib-es/bridge/prepareTransaction.d.ts +4 -0
  105. package/lib-es/bridge/prepareTransaction.d.ts.map +1 -0
  106. package/lib-es/bridge/prepareTransaction.js +10 -0
  107. package/lib-es/bridge/prepareTransaction.js.map +1 -0
  108. package/lib-es/bridge/signOperation.d.ts +5 -0
  109. package/lib-es/bridge/signOperation.d.ts.map +1 -0
  110. package/lib-es/bridge/signOperation.js +72 -0
  111. package/lib-es/bridge/signOperation.js.map +1 -0
  112. package/lib-es/bridge/synchronization.d.ts +3 -0
  113. package/lib-es/bridge/synchronization.d.ts.map +1 -0
  114. package/lib-es/bridge/synchronization.js +78 -0
  115. package/lib-es/bridge/synchronization.js.map +1 -0
  116. package/lib-es/bridge/synchronization.test.d.ts +2 -0
  117. package/lib-es/bridge/synchronization.test.d.ts.map +1 -0
  118. package/lib-es/bridge/synchronization.test.js +135 -0
  119. package/lib-es/bridge/synchronization.test.js.map +1 -0
  120. package/lib-es/{transaction.d.ts → bridge/transaction.d.ts} +1 -1
  121. package/lib-es/bridge/transaction.d.ts.map +1 -0
  122. package/lib-es/bridge/transaction.js.map +1 -0
  123. package/lib-es/index.d.ts +1 -0
  124. package/lib-es/index.d.ts.map +1 -1
  125. package/lib-es/index.js +1 -0
  126. package/lib-es/index.js.map +1 -1
  127. package/lib-es/logic/combine.d.ts.map +1 -1
  128. package/lib-es/logic/combine.js +3 -8
  129. package/lib-es/logic/combine.js.map +1 -1
  130. package/lib-es/logic/getBalance.d.ts.map +1 -1
  131. package/lib-es/logic/getBalance.js +2 -14
  132. package/lib-es/logic/getBalance.js.map +1 -1
  133. package/lib-es/logic/getBalance.test.js +1 -14
  134. package/lib-es/logic/getBalance.test.js.map +1 -1
  135. package/lib-es/logic/index.d.ts +1 -2
  136. package/lib-es/logic/index.d.ts.map +1 -1
  137. package/lib-es/logic/index.js +1 -2
  138. package/lib-es/logic/index.js.map +1 -1
  139. package/lib-es/logic/utils.d.ts +1 -0
  140. package/lib-es/logic/utils.d.ts.map +1 -1
  141. package/lib-es/logic/utils.js +8 -13
  142. package/lib-es/logic/utils.js.map +1 -1
  143. package/lib-es/test/bridgeDatasetTest.d.ts.map +1 -1
  144. package/lib-es/test/bridgeDatasetTest.js +7 -7
  145. package/lib-es/test/bridgeDatasetTest.js.map +1 -1
  146. package/lib-es/types/model.d.ts +0 -7
  147. package/lib-es/types/model.d.ts.map +1 -1
  148. package/package.json +13 -13
  149. package/src/api/index.integ.test.ts +8 -24
  150. package/src/api/index.test.ts +22 -23
  151. package/src/api/index.ts +19 -28
  152. package/src/bridge/broadcast.ts +11 -0
  153. package/src/bridge/createTransaction.ts +13 -0
  154. package/src/bridge/estimateMaxSpendable.ts +25 -0
  155. package/src/{logic → bridge}/getTransactionStatus.ts +21 -18
  156. package/src/bridge/index.ts +59 -0
  157. package/src/bridge/prepareTransaction.ts +18 -0
  158. package/src/bridge/signOperation.ts +100 -0
  159. package/src/bridge/synchronization.test.ts +153 -0
  160. package/src/bridge/synchronization.ts +108 -0
  161. package/src/{transaction.ts → bridge/transaction.ts} +1 -1
  162. package/src/index.ts +1 -0
  163. package/src/logic/combine.ts +3 -10
  164. package/src/logic/getBalance.test.ts +1 -14
  165. package/src/logic/getBalance.ts +2 -18
  166. package/src/logic/index.ts +6 -2
  167. package/src/logic/utils.ts +8 -24
  168. package/src/test/bridgeDatasetTest.ts +7 -8
  169. package/src/types/model.ts +0 -11
  170. package/lib/logic/getTransactionStatus.d.ts +0 -3
  171. package/lib/logic/getTransactionStatus.d.ts.map +0 -1
  172. package/lib/logic/getTransactionStatus.js.map +0 -1
  173. package/lib/logic/getTransactionStatus.test.d.ts +0 -2
  174. package/lib/logic/getTransactionStatus.test.d.ts.map +0 -1
  175. package/lib/logic/getTransactionStatus.test.js +0 -184
  176. package/lib/logic/getTransactionStatus.test.js.map +0 -1
  177. package/lib/transaction.d.ts.map +0 -1
  178. package/lib/transaction.js.map +0 -1
  179. package/lib-es/logic/getTransactionStatus.d.ts +0 -3
  180. package/lib-es/logic/getTransactionStatus.d.ts.map +0 -1
  181. package/lib-es/logic/getTransactionStatus.js.map +0 -1
  182. package/lib-es/logic/getTransactionStatus.test.d.ts +0 -2
  183. package/lib-es/logic/getTransactionStatus.test.d.ts.map +0 -1
  184. package/lib-es/logic/getTransactionStatus.test.js +0 -159
  185. package/lib-es/logic/getTransactionStatus.test.js.map +0 -1
  186. package/lib-es/transaction.d.ts.map +0 -1
  187. package/lib-es/transaction.js.map +0 -1
  188. package/src/logic/getTransactionStatus.test.ts +0 -215
  189. /package/lib/{transaction.js → bridge/transaction.js} +0 -0
  190. /package/lib-es/{transaction.js → bridge/transaction.js} +0 -0
@@ -9,37 +9,40 @@ import {
9
9
  NotEnoughSpendableBalance,
10
10
  RecipientRequired,
11
11
  } from "@ledgerhq/errors";
12
+ import BigNumber from "bignumber.js";
12
13
  import { isValidClassicAddress } from "ripple-address-codec";
14
+ import { Account, AccountBridge } from "@ledgerhq/types-live";
13
15
  import { formatCurrencyUnit } from "@ledgerhq/coin-framework/currencies/index";
14
16
  import { getServerInfos } from "../network";
15
- import { cachedRecipientIsNew, parseAPIValue } from ".";
16
- import { Transaction, TransactionValidation, Account } from "@ledgerhq/coin-framework/api/types";
17
+ import { cachedRecipientIsNew, parseAPIValue } from "../logic";
18
+ import { Transaction, TransactionStatus } from "../types";
17
19
 
18
- export const getTransactionStatus = async (
19
- account: Account,
20
- transaction: Transaction,
21
- ): Promise<TransactionValidation> => {
20
+ export const getTransactionStatus: AccountBridge<
21
+ Transaction,
22
+ Account,
23
+ TransactionStatus
24
+ >["getTransactionStatus"] = async (account, transaction) => {
22
25
  const errors: Record<string, Error> = {};
23
26
  const warnings: Record<string, Error> = {};
24
27
  const serverInfos = await getServerInfos();
25
28
  const reserveBaseXRP = parseAPIValue(
26
29
  serverInfos.info.validated_ledger.reserve_base_xrp.toString(),
27
30
  );
28
- const estimatedFees = transaction.fee || 0n;
29
- const totalSpent = transaction.amount + estimatedFees;
30
- const amount = transaction.amount;
31
+ const estimatedFees = new BigNumber(transaction.fee || 0);
32
+ const totalSpent = new BigNumber(transaction.amount).plus(estimatedFees);
33
+ const amount = new BigNumber(transaction.amount);
31
34
 
32
- if (amount > 0 && estimatedFees * 10n > amount) {
35
+ if (amount.gt(0) && estimatedFees.times(10).gt(amount)) {
33
36
  warnings.feeTooHigh = new FeeTooHigh();
34
37
  }
35
38
 
36
39
  if (!transaction.fee) {
37
40
  errors.fee = new FeeNotLoaded();
38
- } else if (transaction.fee == 0n) {
41
+ } else if (transaction.fee.eq(0)) {
39
42
  errors.fee = new FeeRequired();
40
- } else if (totalSpent > account.balance - BigInt(reserveBaseXRP.toString())) {
43
+ } else if (totalSpent.gt(account.balance.minus(reserveBaseXRP))) {
41
44
  errors.amount = new NotEnoughSpendableBalance("", {
42
- minimumAmount: formatCurrencyUnit(account.currencyUnit, reserveBaseXRP, {
45
+ minimumAmount: formatCurrencyUnit(account.currency.units[0], reserveBaseXRP, {
43
46
  disableRounding: true,
44
47
  useGrouping: false,
45
48
  showCode: true,
@@ -48,10 +51,10 @@ export const getTransactionStatus = async (
48
51
  } else if (
49
52
  transaction.recipient &&
50
53
  (await cachedRecipientIsNew(transaction.recipient)) &&
51
- transaction.amount < BigInt(reserveBaseXRP.toString())
54
+ transaction.amount.lt(reserveBaseXRP)
52
55
  ) {
53
56
  errors.amount = new NotEnoughBalanceBecauseDestinationNotCreated("", {
54
- minimalAmount: formatCurrencyUnit(account.currencyUnit, reserveBaseXRP, {
57
+ minimalAmount: formatCurrencyUnit(account.currency.units[0], reserveBaseXRP, {
55
58
  disableRounding: true,
56
59
  useGrouping: false,
57
60
  showCode: true,
@@ -61,15 +64,15 @@ export const getTransactionStatus = async (
61
64
 
62
65
  if (!transaction.recipient) {
63
66
  errors.recipient = new RecipientRequired("");
64
- } else if (account.address === transaction.recipient) {
67
+ } else if (account.freshAddress === transaction.recipient) {
65
68
  errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource();
66
69
  } else if (!isValidClassicAddress(transaction.recipient)) {
67
70
  errors.recipient = new InvalidAddress("", {
68
- currencyName: account.currencyName,
71
+ currencyName: account.currency.name,
69
72
  });
70
73
  }
71
74
 
72
- if (!errors.amount && amount == 0n) {
75
+ if (!errors.amount && amount.eq(0)) {
73
76
  errors.amount = new AmountRequired();
74
77
  }
75
78
 
@@ -0,0 +1,59 @@
1
+ import getAddressWrapper from "@ledgerhq/coin-framework/bridge/getAddressWrapper";
2
+ import {
3
+ getSerializedAddressParameters,
4
+ updateTransaction,
5
+ makeAccountBridgeReceive,
6
+ makeScanAccounts,
7
+ makeSync,
8
+ } from "@ledgerhq/coin-framework/bridge/jsHelpers";
9
+ import { CoinConfig } from "@ledgerhq/coin-framework/config";
10
+ import { SignerContext } from "@ledgerhq/coin-framework/signer";
11
+ import type { AccountBridge, CurrencyBridge } from "@ledgerhq/types-live";
12
+ import xrpCoinConfig, { type XrpCoinConfig } from "../config";
13
+ import resolver from "../signer";
14
+ import { XrpSigner } from "../types";
15
+ import type { Transaction } from "../types";
16
+ import { broadcast } from "./broadcast";
17
+ import { createTransaction } from "./createTransaction";
18
+ import { estimateMaxSpendable } from "./estimateMaxSpendable";
19
+ import { getTransactionStatus } from "./getTransactionStatus";
20
+ import { prepareTransaction } from "./prepareTransaction";
21
+ import { buildSignOperation } from "./signOperation";
22
+ import { getAccountShape } from "./synchronization";
23
+
24
+ export function createBridges(
25
+ signerContext: SignerContext<XrpSigner>,
26
+ coinConfig: CoinConfig<XrpCoinConfig>,
27
+ ) {
28
+ xrpCoinConfig.setCoinConfig(coinConfig);
29
+
30
+ const getAddress = resolver(signerContext);
31
+
32
+ const scanAccounts = makeScanAccounts({ getAccountShape, getAddressFn: getAddress });
33
+ const currencyBridge: CurrencyBridge = {
34
+ preload: () => Promise.resolve({}),
35
+ hydrate: () => {},
36
+ scanAccounts,
37
+ };
38
+
39
+ const receive = makeAccountBridgeReceive(getAddressWrapper(getAddress));
40
+ const signOperation = buildSignOperation(signerContext);
41
+ const sync = makeSync({ getAccountShape });
42
+ const accountBridge: AccountBridge<Transaction> = {
43
+ createTransaction,
44
+ updateTransaction: updateTransaction<Transaction>,
45
+ prepareTransaction,
46
+ getTransactionStatus,
47
+ estimateMaxSpendable,
48
+ sync,
49
+ receive,
50
+ signOperation,
51
+ broadcast,
52
+ getSerializedAddressParameters,
53
+ };
54
+
55
+ return {
56
+ currencyBridge,
57
+ accountBridge,
58
+ };
59
+ }
@@ -0,0 +1,18 @@
1
+ import { AccountBridge } from "@ledgerhq/types-live";
2
+ import { Transaction } from "../types";
3
+ import { estimateFees } from "../logic";
4
+
5
+ export const prepareTransaction: AccountBridge<Transaction>["prepareTransaction"] = async (
6
+ _account,
7
+ transaction,
8
+ ) => {
9
+ const { networkInfo } = await estimateFees(transaction.networkInfo);
10
+
11
+ const fee = transaction.fee || networkInfo.serverFee;
12
+
13
+ if (transaction.networkInfo !== networkInfo || transaction.fee !== fee) {
14
+ return { ...transaction, networkInfo, fee };
15
+ }
16
+
17
+ return transaction;
18
+ };
@@ -0,0 +1,100 @@
1
+ import { Observable } from "rxjs";
2
+ import { encode } from "ripple-binary-codec";
3
+ import { FeeNotLoaded } from "@ledgerhq/errors";
4
+ import { AccountBridge, Operation } from "@ledgerhq/types-live";
5
+ import { SignerContext } from "@ledgerhq/coin-framework/signer";
6
+ import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
7
+ import { craftTransaction, getNextValidSequence, removeCachedRecipientIsNew } from "../logic";
8
+ import { Transaction, XrpSignature, XrpSigner } from "../types";
9
+
10
+ export const buildSignOperation =
11
+ (signerContext: SignerContext<XrpSigner>): AccountBridge<Transaction>["signOperation"] =>
12
+ ({ account, deviceId, transaction }) =>
13
+ new Observable(o => {
14
+ removeCachedRecipientIsNew(transaction.recipient);
15
+
16
+ async function main() {
17
+ const { fee } = transaction;
18
+ if (!fee) throw new FeeNotLoaded();
19
+
20
+ try {
21
+ o.next({
22
+ type: "device-signature-requested",
23
+ });
24
+
25
+ const nextSequenceNumber = await getNextValidSequence(account.freshAddress);
26
+
27
+ const signature = await signerContext(deviceId, async signer => {
28
+ const { freshAddressPath: derivationPath } = account;
29
+ const { publicKey } = await signer.getAddress(derivationPath);
30
+
31
+ const { xrplTransaction, serializedTransaction } = await craftTransaction(
32
+ {
33
+ address: account.freshAddress,
34
+ nextSequenceNumber,
35
+ },
36
+ {
37
+ recipient: transaction.recipient,
38
+ amount: BigInt(transaction.amount.toString()),
39
+ fee: BigInt(fee.toString()),
40
+ destinationTag: transaction.tag,
41
+ },
42
+ publicKey,
43
+ );
44
+
45
+ const transactionSignature = await signer.signTransaction(
46
+ derivationPath,
47
+ serializedTransaction,
48
+ );
49
+
50
+ return encode({
51
+ ...xrplTransaction,
52
+ SigningPubKey: publicKey,
53
+ TxnSignature: transactionSignature,
54
+ }).toUpperCase() as XrpSignature;
55
+ });
56
+
57
+ o.next({
58
+ type: "device-signature-granted",
59
+ });
60
+
61
+ const hash = "";
62
+ const operation: Operation = {
63
+ id: encodeOperationId(account.id, hash, "OUT"),
64
+ hash,
65
+ accountId: account.id,
66
+ type: "OUT",
67
+ value: transaction.amount,
68
+ fee,
69
+ blockHash: null,
70
+ blockHeight: null,
71
+ senders: [account.freshAddress],
72
+ recipients: [transaction.recipient],
73
+ date: new Date(),
74
+ transactionSequenceNumber: nextSequenceNumber,
75
+ extra: {},
76
+ };
77
+
78
+ o.next({
79
+ type: "signed",
80
+ signedOperation: {
81
+ operation,
82
+ signature,
83
+ },
84
+ });
85
+ } catch (e) {
86
+ if (e instanceof Error) {
87
+ throw new Error(
88
+ (e as Error & { data?: { resultMessage?: string } })?.data?.resultMessage,
89
+ );
90
+ }
91
+
92
+ throw e;
93
+ }
94
+ }
95
+
96
+ main().then(
97
+ () => o.complete(),
98
+ e => o.error(e),
99
+ );
100
+ });
@@ -0,0 +1,153 @@
1
+ import { AccountShapeInfo } from "@ledgerhq/coin-framework/bridge/jsHelpers";
2
+ import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets/index";
3
+ import { Account, SyncConfig } from "@ledgerhq/types-live";
4
+ import BigNumber from "bignumber.js";
5
+ import { getAccountShape } from "./synchronization";
6
+
7
+ const mockGetServerInfos = jest.fn().mockResolvedValue({
8
+ info: {
9
+ complete_ledgers: "1-2",
10
+ validated_ledger: {
11
+ reserve_base_xrp: "0",
12
+ reserve_inc_xrp: "0",
13
+ },
14
+ },
15
+ });
16
+ const mockGetAccountInfo = jest.fn().mockResolvedValue({
17
+ isNewAccount: true,
18
+ balance: "0",
19
+ ownerCount: 0,
20
+ sequence: 0,
21
+ });
22
+ const mockGetTransactions = jest.fn();
23
+ jest.mock("../network", () => ({
24
+ getServerInfos: () => mockGetServerInfos(),
25
+ getAccountInfo: () => mockGetAccountInfo(),
26
+ getTransactions: () => mockGetTransactions(),
27
+ }));
28
+
29
+ describe("getAccountShape", () => {
30
+ beforeEach(() => {
31
+ mockGetServerInfos.mockClear();
32
+ mockGetAccountInfo.mockClear();
33
+ mockGetTransactions.mockClear();
34
+ });
35
+
36
+ it("returns early when account is new", async () => {
37
+ // Given
38
+ mockGetAccountInfo.mockResolvedValue({
39
+ isNewAccount: true,
40
+ balance: "0",
41
+ ownerCount: 0,
42
+ sequence: 0,
43
+ });
44
+
45
+ // When
46
+ const account = await getAccountShape(
47
+ {
48
+ address: "address",
49
+ currency: getCryptoCurrencyById("ripple"),
50
+ derivationMode: "",
51
+ index: 0,
52
+ } as AccountShapeInfo<Account>,
53
+ {} as SyncConfig,
54
+ );
55
+
56
+ // Then
57
+ expect(mockGetAccountInfo).toHaveBeenCalledTimes(1);
58
+ expect(mockGetServerInfos).toHaveBeenCalledTimes(0);
59
+ expect(mockGetTransactions).toHaveBeenCalledTimes(0);
60
+ expect(account).toEqual({
61
+ id: "js:2:ripple:address:",
62
+ xpub: "address",
63
+ blockHeight: 0,
64
+ balance: new BigNumber(0),
65
+ spendableBalance: new BigNumber(0),
66
+ operations: [],
67
+ operationsCount: 0,
68
+ });
69
+ });
70
+
71
+ const someMarker = { ledger: 1, seq: 1 };
72
+ function mockNetworkTxs(txs: unknown): unknown {
73
+ return {
74
+ account: "account",
75
+ ledger_index_max: 1,
76
+ ledger_index_min: 1,
77
+ limit: 1,
78
+ validated: false,
79
+ transactions: txs,
80
+ marker: someMarker,
81
+ error: "",
82
+ };
83
+ }
84
+
85
+ it("convert correctly operations", async () => {
86
+ // Given
87
+ mockGetAccountInfo.mockResolvedValue({
88
+ isNewAccount: false,
89
+ balance: "0",
90
+ ownerCount: 0,
91
+ sequence: 0,
92
+ });
93
+ mockGetTransactions.mockResolvedValue(
94
+ mockNetworkTxs([
95
+ {
96
+ ledger_hash: "HASH_VALUE_BLOCK",
97
+ hash: "HASH_VALUE",
98
+ meta: { delivered_amount: "100" },
99
+ tx_json: {
100
+ TransactionType: "Payment",
101
+ Fee: "10",
102
+ ledger_index: 1,
103
+ date: 1000,
104
+ Account: "account_addr",
105
+ Destination: "destination_addr",
106
+ Sequence: 1,
107
+ },
108
+ },
109
+ ]),
110
+ );
111
+
112
+ // When
113
+ const account = await getAccountShape(
114
+ {
115
+ address: "address",
116
+ currency: getCryptoCurrencyById("ripple"),
117
+ derivationMode: "",
118
+ index: 0,
119
+ } as AccountShapeInfo<Account>,
120
+ {} as SyncConfig,
121
+ );
122
+
123
+ // Then
124
+ expect(mockGetAccountInfo).toHaveBeenCalledTimes(1);
125
+ expect(mockGetServerInfos).toHaveBeenCalled();
126
+ expect(mockGetTransactions).toHaveBeenCalledTimes(1);
127
+ expect(account).toEqual({
128
+ id: "js:2:ripple:address:",
129
+ xpub: "address",
130
+ blockHeight: 2,
131
+ operationsCount: 1,
132
+ balance: new BigNumber(0),
133
+ spendableBalance: new BigNumber(0),
134
+ operations: [
135
+ {
136
+ accountId: "js:2:ripple:address:",
137
+ blockHash: "HASH_VALUE_BLOCK",
138
+ blockHeight: 1,
139
+ date: new Date("2000-01-01T00:16:40.000Z"),
140
+ hash: "HASH_VALUE",
141
+ id: "js:2:ripple:address:-HASH_VALUE-IN",
142
+ recipients: ["destination_addr"],
143
+ senders: ["account_addr"],
144
+ transactionSequenceNumber: 1,
145
+ type: "IN",
146
+ value: new BigNumber("100"),
147
+ fee: new BigNumber("10"),
148
+ extra: {},
149
+ },
150
+ ],
151
+ });
152
+ });
153
+ });
@@ -0,0 +1,108 @@
1
+ import { encodeAccountId } from "@ledgerhq/coin-framework/account/index";
2
+ import { GetAccountShape, mergeOps } from "@ledgerhq/coin-framework/bridge/jsHelpers";
3
+ import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
4
+ import { Operation, OperationType } from "@ledgerhq/types-live";
5
+ import { Operation as CoreOperation } from "@ledgerhq/coin-framework/api/types";
6
+ import BigNumber from "bignumber.js";
7
+ import { listOperations, parseAPIValue } from "../logic";
8
+ import { getAccountInfo, getServerInfos } from "../network";
9
+ import { ServerInfoResponse } from "../network/types";
10
+ import { AccountInfo, XrpAsset } from "../types";
11
+
12
+ export const getAccountShape: GetAccountShape = async info => {
13
+ const { address, initialAccount, currency, derivationMode } = info;
14
+ const accountId = encodeAccountId({
15
+ type: "js",
16
+ version: "2",
17
+ currencyId: currency.id,
18
+ xpubOrAddress: address,
19
+ derivationMode,
20
+ });
21
+ const accountInfo = await getAccountInfo(address);
22
+
23
+ if (accountInfo.isNewAccount) {
24
+ return {
25
+ id: accountId,
26
+ xpub: address,
27
+ blockHeight: 0,
28
+ balance: new BigNumber(0),
29
+ spendableBalance: new BigNumber(0),
30
+ operations: [],
31
+ operationsCount: 0,
32
+ };
33
+ }
34
+
35
+ const oldOperations = initialAccount?.operations || [];
36
+ const blockHeight = oldOperations.length ? (oldOperations[0].blockHeight || 0) + 1 : 0;
37
+
38
+ const serverInfo = await getServerInfos();
39
+ const ledgers = serverInfo.info.complete_ledgers.split("-");
40
+ const maxLedgerVersion = Number(ledgers[1]);
41
+
42
+ const balance = new BigNumber(accountInfo.balance);
43
+ const spendableBalance = await calculateSpendableBalance(accountInfo, serverInfo);
44
+
45
+ const newOperations = await filterOperations(accountId, address, blockHeight);
46
+
47
+ const operations = mergeOps(oldOperations, newOperations);
48
+
49
+ const shape = {
50
+ id: accountId,
51
+ xpub: address,
52
+ blockHeight: maxLedgerVersion,
53
+ balance,
54
+ spendableBalance,
55
+ operations,
56
+ operationsCount: operations.length,
57
+ };
58
+
59
+ return shape;
60
+ };
61
+
62
+ async function filterOperations(
63
+ accountId: string,
64
+ address: string,
65
+ blockHeight: number,
66
+ ): Promise<Operation[]> {
67
+ const [operations, _] = await listOperations(address, { minHeight: blockHeight });
68
+
69
+ return operations.map(op => adaptCoreOperationToLiveOperation(accountId, op) satisfies Operation);
70
+ }
71
+
72
+ function adaptCoreOperationToLiveOperation(
73
+ accountId: string,
74
+ op: CoreOperation<XrpAsset>,
75
+ ): Operation {
76
+ return {
77
+ id: encodeOperationId(accountId, op.tx.hash, op.type),
78
+ hash: op.tx.hash,
79
+ accountId,
80
+ type: op.type as OperationType,
81
+ value: new BigNumber(op.value.toString()),
82
+ fee: new BigNumber(op.tx.fees.toString()),
83
+ blockHash: op.tx.block.hash,
84
+ blockHeight: op.tx.block.height,
85
+ senders: op.senders,
86
+ recipients: op.recipients,
87
+ date: op.tx.date,
88
+ transactionSequenceNumber: op.details?.sequence as number,
89
+ extra: {},
90
+ };
91
+ }
92
+
93
+ async function calculateSpendableBalance(
94
+ account: AccountInfo,
95
+ serverInfo: ServerInfoResponse,
96
+ ): Promise<BigNumber> {
97
+ const reserveMinXRP = parseAPIValue(serverInfo.info.validated_ledger.reserve_base_xrp.toString());
98
+ const reservePerTrustline = parseAPIValue(
99
+ serverInfo.info.validated_ledger.reserve_inc_xrp.toString(),
100
+ );
101
+ const trustlines = account.ownerCount;
102
+
103
+ const spendableBalance = new BigNumber(account.balance)
104
+ .minus(reserveMinXRP)
105
+ .minus(reservePerTrustline.times(trustlines));
106
+
107
+ return spendableBalance;
108
+ }
@@ -1,5 +1,5 @@
1
1
  import { BigNumber } from "bignumber.js";
2
- import type { Transaction, TransactionRaw } from "./types";
2
+ import type { Transaction, TransactionRaw } from "../types";
3
3
  import { formatTransactionStatus } from "@ledgerhq/coin-framework/formatters";
4
4
  import {
5
5
  fromTransactionCommonRaw,
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./types";
2
2
 
3
+ export { createBridges } from "./bridge/index";
3
4
  export type { XrpCoinConfig } from "./config";
@@ -8,18 +8,11 @@ type XRPTransaction = JsonObject & {
8
8
 
9
9
  export function combine(transaction: string, signature: string, publicKey?: string): string {
10
10
  const xrplTransaction: JsonObject = decode(transaction);
11
-
12
- let transactionWithSignature: XRPTransaction = { ...xrplTransaction } as any;
11
+ let transactionWithSignature: XRPTransaction = { ...xrplTransaction, TxnSignature: signature };
13
12
 
14
13
  if (publicKey) {
15
- transactionWithSignature = {
16
- ...transactionWithSignature,
17
- SigningPubKey: publicKey,
18
- };
14
+ transactionWithSignature = { ...transactionWithSignature, SigningPubKey: publicKey };
19
15
  }
20
16
 
21
- transactionWithSignature = { ...transactionWithSignature, TxnSignature: signature };
22
-
23
- const encoded = encode(transactionWithSignature).toUpperCase();
24
- return encoded;
17
+ return encode(transactionWithSignature);
25
18
  }
@@ -2,33 +2,21 @@ import { faker } from "@faker-js/faker";
2
2
  import { getBalance } from "./getBalance";
3
3
 
4
4
  const mockGetAccountInfo = jest.fn();
5
- const mockGetServerInfos = jest.fn();
6
5
  jest.mock("../network", () => ({
7
6
  getAccountInfo: (address: string) => mockGetAccountInfo(address),
8
- getServerInfos: () => mockGetServerInfos(),
9
7
  }));
10
8
 
11
9
  describe("getBalance", () => {
12
10
  afterEach(() => {
13
11
  mockGetAccountInfo.mockClear();
14
- mockGetServerInfos.mockClear();
15
12
  });
16
13
 
17
14
  it("returns the balance from Explorer", async () => {
18
- mockGetServerInfos.mockResolvedValue({
19
- info: {
20
- validated_ledger: {
21
- reserve_base_xrp: 23,
22
- reserve_inc_xrp: 5,
23
- },
24
- },
25
- });
26
15
  // Given
27
16
  const balance = faker.number.bigInt(100_000_000);
28
17
  const address = "ACCOUNT_ADDRESS";
29
18
  mockGetAccountInfo.mockResolvedValue({
30
19
  balance,
31
- ownerCount: 0,
32
20
  });
33
21
 
34
22
  // When
@@ -36,8 +24,7 @@ describe("getBalance", () => {
36
24
 
37
25
  // Then
38
26
  expect(mockGetAccountInfo).toHaveBeenCalledTimes(1);
39
- expect(mockGetServerInfos).toHaveBeenCalledTimes(1);
40
27
  expect(mockGetAccountInfo.mock.lastCall[0]).toEqual(address);
41
- expect(result).toEqual([{ value: balance, asset: { type: "native" }, locked: 23000000n }]);
28
+ expect(result).toEqual([{ value: balance, asset: { type: "native" } }]);
42
29
  });
43
30
  });
@@ -1,24 +1,8 @@
1
1
  import { Balance } from "@ledgerhq/coin-framework/api/types";
2
- import { getAccountInfo, getServerInfos } from "../network";
2
+ import { getAccountInfo } from "../network";
3
3
  import { XrpAsset } from "../types";
4
- import { parseAPIValue } from "./common";
5
4
 
6
5
  export async function getBalance(address: string): Promise<Balance<XrpAsset>[]> {
7
6
  const accountInfo = await getAccountInfo(address);
8
- const serverInfo = await getServerInfos();
9
-
10
- const reserveMinXRP = parseAPIValue(serverInfo.info.validated_ledger.reserve_base_xrp.toString());
11
- const reservePerTrustline = parseAPIValue(
12
- serverInfo.info.validated_ledger.reserve_inc_xrp.toString(),
13
- );
14
- const trustlines = accountInfo.ownerCount;
15
-
16
- const locked = reserveMinXRP.plus(reservePerTrustline.times(trustlines));
17
- return [
18
- {
19
- value: BigInt(accountInfo.balance),
20
- asset: { type: "native" },
21
- locked: BigInt(locked.toString()),
22
- },
23
- ];
7
+ return [{ value: BigInt(accountInfo.balance), asset: { type: "native" } }];
24
8
  }
@@ -6,7 +6,11 @@ export { estimateFees } from "./estimateFees";
6
6
  export { getBalance } from "./getBalance";
7
7
  export { lastBlock } from "./lastBlock";
8
8
  export { listOperations } from "./listOperations";
9
- export { getTransactionStatus } from "./getTransactionStatus";
10
- export { RIPPLE_EPOCH, cachedRecipientIsNew, getNextValidSequence } from "./utils";
9
+ export {
10
+ RIPPLE_EPOCH,
11
+ cachedRecipientIsNew,
12
+ getNextValidSequence,
13
+ removeCachedRecipientIsNew,
14
+ } from "./utils";
11
15
 
12
16
  export { parseAPIValue } from "./common";