@ledgerhq/coin-xrp 7.24.0 → 7.25.2

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 (63) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/lib/api/craftRawTransaction.d.ts.map +1 -1
  3. package/lib/api/craftRawTransaction.js +2 -1
  4. package/lib/api/craftRawTransaction.js.map +1 -1
  5. package/lib/api/index.d.ts.map +1 -1
  6. package/lib/api/index.js +5 -1
  7. package/lib/api/index.js.map +1 -1
  8. package/lib/api/listOperations.js +1 -1
  9. package/lib/api/listOperations.js.map +1 -1
  10. package/lib/network/index.d.ts +1 -1
  11. package/lib/network/index.d.ts.map +1 -1
  12. package/lib/network/index.js +4 -5
  13. package/lib/network/index.js.map +1 -1
  14. package/lib/network/types.d.ts +0 -1
  15. package/lib/network/types.d.ts.map +1 -1
  16. package/lib/network/types.js +0 -4
  17. package/lib/network/types.js.map +1 -1
  18. package/lib/utils/predicates.d.ts +5 -0
  19. package/lib/utils/predicates.d.ts.map +1 -0
  20. package/lib/utils/predicates.js +23 -0
  21. package/lib/utils/predicates.js.map +1 -0
  22. package/lib-es/api/craftRawTransaction.d.ts.map +1 -1
  23. package/lib-es/api/craftRawTransaction.js +2 -1
  24. package/lib-es/api/craftRawTransaction.js.map +1 -1
  25. package/lib-es/api/index.d.ts.map +1 -1
  26. package/lib-es/api/index.js +5 -1
  27. package/lib-es/api/index.js.map +1 -1
  28. package/lib-es/api/listOperations.js +1 -1
  29. package/lib-es/api/listOperations.js.map +1 -1
  30. package/lib-es/network/index.d.ts +1 -1
  31. package/lib-es/network/index.d.ts.map +1 -1
  32. package/lib-es/network/index.js +3 -4
  33. package/lib-es/network/index.js.map +1 -1
  34. package/lib-es/network/types.d.ts +0 -1
  35. package/lib-es/network/types.d.ts.map +1 -1
  36. package/lib-es/network/types.js +0 -3
  37. package/lib-es/network/types.js.map +1 -1
  38. package/lib-es/utils/predicates.d.ts +5 -0
  39. package/lib-es/utils/predicates.d.ts.map +1 -0
  40. package/lib-es/utils/predicates.js +19 -0
  41. package/lib-es/utils/predicates.js.map +1 -0
  42. package/package.json +9 -4
  43. package/src/api/combine.test.ts +18 -27
  44. package/src/api/craftRawTransaction.test.ts +5 -6
  45. package/src/api/craftRawTransaction.ts +4 -1
  46. package/src/api/getBalance.test.ts +2 -4
  47. package/src/api/getBlock.integ.test.ts +5 -7
  48. package/src/api/getBlockInfo.integ.test.ts +5 -7
  49. package/src/api/index.integ.test.ts +65 -42
  50. package/src/api/index.test.ts +56 -45
  51. package/src/api/index.ts +7 -2
  52. package/src/api/listOperations.test.ts +4 -2
  53. package/src/api/listOperations.ts +3 -5
  54. package/src/api/validateIntent.test.ts +56 -128
  55. package/src/network/index.test.ts +23 -28
  56. package/src/network/index.ts +13 -16
  57. package/src/network/types.ts +0 -3
  58. package/src/utils/predicates.ts +25 -0
  59. package/jest.config.js +0 -32
  60. package/jest.integ.config.js +0 -21
  61. package/knip.json +0 -11
  62. package/tsconfig.build.json +0 -22
  63. package/tsconfig.json +0 -23
@@ -0,0 +1,5 @@
1
+ import type { ErrorResponse } from '../network/types';
2
+ import type { SignerEntry } from '../types';
3
+ export declare function isSignerEntry(obj: unknown): obj is SignerEntry;
4
+ export declare function isErrorResponse(obj: object): obj is ErrorResponse;
5
+ //# sourceMappingURL=predicates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"predicates.d.ts","sourceRoot":"","sources":["../../src/utils/predicates.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,WAAW,CAc9D;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,aAAa,CAEjE"}
@@ -0,0 +1,19 @@
1
+ // SPDX-FileCopyrightText: © 2024 LEDGER SAS
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export function isSignerEntry(obj) {
4
+ return (typeof obj === 'object' &&
5
+ obj !== null &&
6
+ 'Signer' in obj &&
7
+ typeof obj.Signer === 'object' &&
8
+ obj.Signer !== null &&
9
+ 'Account' in obj.Signer &&
10
+ typeof obj.Signer.Account === 'string' &&
11
+ 'SigningPubKey' in obj.Signer &&
12
+ typeof obj.Signer.SigningPubKey === 'string' &&
13
+ 'TxnSignature' in obj.Signer &&
14
+ typeof obj.Signer.TxnSignature === 'string');
15
+ }
16
+ export function isErrorResponse(obj) {
17
+ return 'status' in obj && obj.status === 'error';
18
+ }
19
+ //# sourceMappingURL=predicates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"predicates.js","sourceRoot":"","sources":["../../src/utils/predicates.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,sCAAsC;AAKtC,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;QACvB,GAAG,KAAK,IAAI;QACZ,QAAQ,IAAI,GAAG;QACf,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ;QAC9B,GAAG,CAAC,MAAM,KAAK,IAAI;QACnB,SAAS,IAAI,GAAG,CAAC,MAAM;QACvB,OAAO,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,QAAQ;QACtC,eAAe,IAAI,GAAG,CAAC,MAAM;QAC7B,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,KAAK,QAAQ;QAC5C,cAAc,IAAI,GAAG,CAAC,MAAM;QAC5B,OAAO,GAAG,CAAC,MAAM,CAAC,YAAY,KAAK,QAAQ,CAC5C,CAAA;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,QAAQ,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,OAAO,CAAA;AAClD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ledgerhq/coin-xrp",
3
- "version": "7.24.0",
3
+ "version": "7.25.2",
4
4
  "description": "Ledger XRP Coin integration",
5
5
  "keywords": [
6
6
  "Ledger",
@@ -20,6 +20,12 @@
20
20
  "publishConfig": {
21
21
  "access": "public"
22
22
  },
23
+ "files": [
24
+ "lib",
25
+ "lib-es",
26
+ "src",
27
+ "CHANGELOG.md"
28
+ ],
23
29
  "typesVersions": {
24
30
  "*": {
25
31
  "lib/*": [
@@ -72,10 +78,9 @@
72
78
  "invariant": "^2.2.4",
73
79
  "ripple-address-codec": "^5.0.0",
74
80
  "ripple-binary-codec": "^1.3.0",
75
- "@ledgerhq/coin-module-framework": "^3.5.1"
81
+ "@ledgerhq/coin-module-framework": "^4.0.0"
76
82
  },
77
83
  "devDependencies": {
78
- "@faker-js/faker": "^8.4.1",
79
84
  "@swc/core": "1.15.11",
80
85
  "@swc/jest": "0.2.39",
81
86
  "@types/invariant": "^2.2.37",
@@ -104,7 +109,7 @@
104
109
  "test": "jest",
105
110
  "test:debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand",
106
111
  "test-watch": "jest --watch",
107
- "test-integ": "jest --config=jest.integ.config.js --passWithNoTests",
112
+ "test-integ": "jest --config=jest.integ.config.js",
108
113
  "test-integ:debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --config=jest.integ.config.js",
109
114
  "typecheck": "tsc --noEmit --customConditions node",
110
115
  "unimported": "knip --include=files,dependencies",
@@ -5,12 +5,6 @@ import { decode, encode } from 'ripple-binary-codec'
5
5
  import { SignerEntry } from '../types'
6
6
  import { combine } from './combine'
7
7
 
8
- type DecodedTx = {
9
- SigningPubKey?: string
10
- TxnSignature?: string
11
- Signers?: SignerEntry[]
12
- }
13
-
14
8
  function signerEntry(account: string, signingPubKey: string, txnSignature = ''): SignerEntry {
15
9
  return {
16
10
  Signer: {
@@ -45,12 +39,13 @@ describe('combine', () => {
45
39
  ],
46
40
  })
47
41
 
48
- const combined = combine(tx, 'A1B2')
49
- const decoded = decode(combined) as DecodedTx
50
-
51
- expect(decoded.Signers?.[0]?.Signer.TxnSignature).toBe('A1B2')
52
- expect(decoded.Signers?.[1]?.Signer.TxnSignature).toBe('')
53
- expect(decoded.Signers?.[2]?.Signer.TxnSignature).toBe('DEADBEEF')
42
+ expect(decode(combine(tx, 'A1B2'))).toMatchObject({
43
+ Signers: [
44
+ { Signer: { TxnSignature: 'A1B2' } },
45
+ { Signer: { TxnSignature: '' } },
46
+ { Signer: { TxnSignature: 'DEADBEEF' } },
47
+ ],
48
+ })
54
49
  })
55
50
 
56
51
  it('fills the matching multi-sign signer signature when public key is provided', () => {
@@ -63,11 +58,9 @@ describe('combine', () => {
63
58
  Signers: [signerEntry(signer1, pubKey1, ''), signerEntry(signer2, pubKey2, '')],
64
59
  })
65
60
 
66
- const combined = combine(tx, 'B2C3', pubKey2)
67
- const decoded = decode(combined) as DecodedTx
68
-
69
- expect(decoded.Signers?.[0]?.Signer.TxnSignature).toBe('')
70
- expect(decoded.Signers?.[1]?.Signer.TxnSignature).toBe('B2C3')
61
+ expect(decode(combine(tx, 'B2C3', pubKey2))).toMatchObject({
62
+ Signers: [{ Signer: { TxnSignature: '' } }, { Signer: { TxnSignature: 'B2C3' } }],
63
+ })
71
64
  })
72
65
 
73
66
  it('sets TxnSignature and missing SigningPubKey for single-sign transactions', () => {
@@ -78,11 +71,10 @@ describe('combine', () => {
78
71
  Destination: destination,
79
72
  })
80
73
 
81
- const combined = combine(tx, 'C3D4', pubKey1)
82
- const decoded = decode(combined) as DecodedTx
83
-
84
- expect(decoded.TxnSignature).toBe('C3D4')
85
- expect(decoded.SigningPubKey).toBe(pubKey1)
74
+ expect(decode(combine(tx, 'C3D4', pubKey1))).toMatchObject({
75
+ TxnSignature: 'C3D4',
76
+ SigningPubKey: pubKey1,
77
+ })
86
78
  })
87
79
 
88
80
  it('does not overwrite existing SigningPubKey for single-sign transactions', () => {
@@ -94,10 +86,9 @@ describe('combine', () => {
94
86
  SigningPubKey: pubKey2,
95
87
  })
96
88
 
97
- const combined = combine(tx, 'D4E5', pubKey3)
98
- const decoded = decode(combined) as DecodedTx
99
-
100
- expect(decoded.TxnSignature).toBe('D4E5')
101
- expect(decoded.SigningPubKey).toBe(pubKey2)
89
+ expect(decode(combine(tx, 'D4E5', pubKey3))).toMatchObject({
90
+ TxnSignature: 'D4E5',
91
+ SigningPubKey: pubKey2,
92
+ })
102
93
  })
103
94
  })
@@ -3,6 +3,7 @@
3
3
 
4
4
  import { encode, decode } from 'ripple-binary-codec'
5
5
  import { SignerEntry } from '../types'
6
+ import { isSignerEntry } from '../utils/predicates'
6
7
  import { craftRawTransaction } from './craftRawTransaction'
7
8
 
8
9
  // --- Mocks ---
@@ -45,12 +46,10 @@ describe('craftRawTransaction', () => {
45
46
 
46
47
  // Helper to safely extract Signers array without relying on type assertions elsewhere
47
48
  function getSigners(obj: unknown): SignerEntry[] | undefined {
48
- if (!obj || typeof obj !== 'object') return undefined
49
- const potential = (obj as { Signers?: unknown }).Signers
50
- if (Array.isArray(potential)) {
51
- return potential as SignerEntry[]
52
- }
53
- return undefined
49
+ if (!obj || typeof obj !== 'object' || !('Signers' in obj)) return undefined
50
+ const signers = obj.Signers
51
+ if (!Array.isArray(signers)) return undefined
52
+ return signers.filter(isSignerEntry)
54
53
  }
55
54
 
56
55
  it('fills missing Fee, Sequence, LastLedgerSequence & SigningPubKey for a standard payment', async () => {
@@ -7,6 +7,7 @@ import { JsonObject } from 'ripple-binary-codec/dist/types/serialized-type'
7
7
  import { getLedgerIndex } from '../network'
8
8
  import { SignerEntry } from '../types'
9
9
  import { sortSignersByNumericAddress } from '../utils'
10
+ import { isSignerEntry } from '../utils/predicates'
10
11
  import { estimateFees } from './estimateFees'
11
12
 
12
13
  const LEDGER_OFFSET = 20
@@ -37,7 +38,9 @@ function craftRawTransactionMultiSign(
37
38
  xrplTransaction.Signers.push(signer)
38
39
  // The Signers array must be sorted based on the numeric value of the signer addresses
39
40
  // We could probably insert it at the right place directly but like this we make sure the rest is also sorted.
40
- xrplTransaction.Signers = sortSignersByNumericAddress(xrplTransaction.Signers as SignerEntry[])
41
+ xrplTransaction.Signers = sortSignersByNumericAddress(
42
+ xrplTransaction.Signers.filter(isSignerEntry)
43
+ )
41
44
  }
42
45
 
43
46
  const serializedTransaction = encode(xrplTransaction)
@@ -1,7 +1,6 @@
1
1
  // SPDX-FileCopyrightText: © 2024 LEDGER SAS
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { faker } from '@faker-js/faker'
5
4
  import { getBalance } from './getBalance'
6
5
 
7
6
  const address = 'ACCOUNT_ADDRESS'
@@ -28,9 +27,8 @@ describe('getBalance', () => {
28
27
  },
29
28
  })
30
29
  // Given
31
- const balance = faker.number.bigInt(100_000_000)
32
30
  mockGetAccountInfo.mockResolvedValue({
33
- balance: balance.toString(),
31
+ balance: '100000000',
34
32
  ownerCount: 0,
35
33
  })
36
34
 
@@ -41,7 +39,7 @@ describe('getBalance', () => {
41
39
  expect(mockGetAccountInfo).toHaveBeenCalledTimes(1)
42
40
  expect(mockGetServerInfos).toHaveBeenCalledTimes(1)
43
41
  expect(mockGetAccountInfo.mock.lastCall[0]).toEqual(address)
44
- expect(result).toEqual([{ value: balance, asset: { type: 'native' }, locked: 23000000n }])
42
+ expect(result).toEqual([{ value: 100000000n, asset: { type: 'native' }, locked: 23000000n }])
45
43
  })
46
44
 
47
45
  it('returns from account with empty balance', async () => {
@@ -1,7 +1,7 @@
1
1
  // SPDX-FileCopyrightText: © 2024 LEDGER SAS
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import coinConfig, { XrpCoinConfig } from '../config'
4
+ import coinConfig, { type XrpCoinConfig } from '../config'
5
5
  import { getBlock } from './getBlock'
6
6
 
7
7
  let originalGetCoinConfig: () => XrpCoinConfig
@@ -9,12 +9,10 @@ let originalGetCoinConfig: () => XrpCoinConfig
9
9
  describe('getBlock', () => {
10
10
  beforeAll(() => {
11
11
  originalGetCoinConfig = coinConfig.getCoinConfig
12
- coinConfig.setCoinConfig(
13
- () =>
14
- ({
15
- node: 'https://s.altnet.rippletest.net:51234',
16
- }) as XrpCoinConfig
17
- )
12
+ coinConfig.setCoinConfig(() => ({
13
+ node: 'https://s.altnet.rippletest.net:51234',
14
+ status: { type: 'active' },
15
+ }))
18
16
  })
19
17
 
20
18
  afterAll(() => {
@@ -1,7 +1,7 @@
1
1
  // SPDX-FileCopyrightText: © 2024 LEDGER SAS
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import coinConfig, { XrpCoinConfig } from '../config'
4
+ import coinConfig, { type XrpCoinConfig } from '../config'
5
5
  import { getBlock } from './getBlock'
6
6
  import { getBlockInfo } from './getBlockInfo'
7
7
 
@@ -10,12 +10,10 @@ let originalGetCoinConfig: () => XrpCoinConfig
10
10
  describe('getBlockInfo', () => {
11
11
  beforeAll(() => {
12
12
  originalGetCoinConfig = coinConfig.getCoinConfig
13
- coinConfig.setCoinConfig(
14
- () =>
15
- ({
16
- node: 'https://s.altnet.rippletest.net:51234',
17
- }) as XrpCoinConfig
18
- )
13
+ coinConfig.setCoinConfig(() => ({
14
+ node: 'https://s.altnet.rippletest.net:51234',
15
+ status: { type: 'active' },
16
+ }))
19
17
  })
20
18
 
21
19
  afterAll(() => {
@@ -1,7 +1,7 @@
1
1
  // SPDX-FileCopyrightText: © 2024 LEDGER SAS
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { Operation } from '@ledgerhq/coin-module-framework/api/types'
4
+ import type { Operation } from '@ledgerhq/coin-module-framework/api/types'
5
5
  import { decode, encodeForSigning } from 'ripple-binary-codec'
6
6
  import { deriveKeypair, generateSeed, sign } from 'ripple-keypairs'
7
7
  import { createApi } from '.'
@@ -313,14 +313,21 @@ describe('Xrp Api (mainnet)', () => {
313
313
  type: 'IN',
314
314
  fees: 0.00001,
315
315
  }
316
- const op = ops.find((o) => o.tx.hash === inTx.hash) as Operation
317
- expect(op.tx.hash).toEqual(inTx.hash)
318
- expect(op.value).toEqual(BigInt(inTx.amount * 1e6))
319
- expect(op.recipients).toContain(inTx.recipient)
320
- expect(op.senders).toContain(inTx.sender)
321
- expect(op.type).toEqual(inTx.type)
322
- expect(op.tx.fees).toEqual(BigInt(inTx.fees * 1e6))
323
- expect(op.tx.feesPayer).toBe(inTx.sender)
316
+ expect(ops).toEqual(
317
+ expect.arrayContaining([
318
+ expect.objectContaining({
319
+ tx: expect.objectContaining({
320
+ hash: inTx.hash,
321
+ fees: BigInt(inTx.fees * 1e6),
322
+ feesPayer: inTx.sender,
323
+ }),
324
+ value: BigInt(inTx.amount * 1e6),
325
+ recipients: expect.arrayContaining([inTx.recipient]),
326
+ senders: expect.arrayContaining([inTx.sender]),
327
+ type: inTx.type,
328
+ }),
329
+ ])
330
+ )
324
331
  })
325
332
 
326
333
  it('returns OUT operation', async () => {
@@ -333,14 +340,21 @@ describe('Xrp Api (mainnet)', () => {
333
340
  type: 'OUT',
334
341
  fees: 0.00001,
335
342
  }
336
- const op = ops.find((o) => o.tx.hash === outTx.hash) as Operation
337
- expect(op.tx.hash).toEqual(outTx.hash)
338
- expect(op.value).toEqual(BigInt(outTx.amount * 1e6))
339
- expect(op.recipients).toContain(outTx.recipient)
340
- expect(op.senders).toContain(outTx.sender)
341
- expect(op.type).toEqual(outTx.type)
342
- expect(op.tx.fees).toEqual(BigInt(outTx.fees * 1e6))
343
- expect(op.tx.feesPayer).toBe(SENDER)
343
+ expect(ops).toEqual(
344
+ expect.arrayContaining([
345
+ expect.objectContaining({
346
+ tx: expect.objectContaining({
347
+ hash: outTx.hash,
348
+ fees: BigInt(outTx.fees * 1e6),
349
+ feesPayer: SENDER,
350
+ }),
351
+ value: BigInt(outTx.amount * 1e6),
352
+ recipients: expect.arrayContaining([outTx.recipient]),
353
+ senders: expect.arrayContaining([outTx.sender]),
354
+ type: outTx.type,
355
+ }),
356
+ ])
357
+ )
344
358
  })
345
359
 
346
360
  it('returns OUT operation for OfferCreate that trades XRP via DEX', async () => {
@@ -356,14 +370,20 @@ describe('Xrp Api (mainnet)', () => {
356
370
  type: 'OUT',
357
371
  fees: 0.00001,
358
372
  }
359
- const op = ops.find((o) => o.tx.hash === outTx.hash) as Operation
360
-
361
- expect(op.tx.hash).toEqual(outTx.hash)
362
- expect(op.value).toEqual(BigInt(outTx.amount * 1e6))
363
- expect(op.recipients).toEqual([])
364
- expect(op.senders).toContain(outTx.sender)
365
- expect(op.type).toEqual(outTx.type)
366
- expect(op.tx.fees).toEqual(BigInt(outTx.fees * 1e6))
373
+ expect(ops).toEqual(
374
+ expect.arrayContaining([
375
+ expect.objectContaining({
376
+ tx: expect.objectContaining({
377
+ hash: outTx.hash,
378
+ fees: BigInt(outTx.fees * 1e6),
379
+ }),
380
+ value: BigInt(outTx.amount * 1e6),
381
+ recipients: [],
382
+ senders: expect.arrayContaining([outTx.sender]),
383
+ type: outTx.type,
384
+ }),
385
+ ])
386
+ )
367
387
  })
368
388
 
369
389
  it('returns IN operation on account delete', async () => {
@@ -380,23 +400,26 @@ describe('Xrp Api (mainnet)', () => {
380
400
  type: 'IN',
381
401
  fees: 2.0,
382
402
  }
383
- const op = opsAccountDelete.find((o) => o.tx.hash === inTx.hash) as Operation
384
- expect(op).toMatchObject({
385
- id: inTx.hash,
386
- tx: {
387
- hash: inTx.hash,
388
- fees: BigInt(inTx.fees * 1e6),
389
- feesPayer: inTx.sender,
390
- failed: false,
391
- },
392
- type: inTx.type,
393
- value: BigInt(inTx.amount * 1e6),
394
- senders: [inTx.sender],
395
- recipients: [inTx.recipient],
396
- details: {
397
- xrpTxType: 'AccountDelete',
398
- },
399
- })
403
+ expect(opsAccountDelete).toEqual(
404
+ expect.arrayContaining([
405
+ expect.objectContaining({
406
+ id: inTx.hash,
407
+ tx: expect.objectContaining({
408
+ hash: inTx.hash,
409
+ fees: BigInt(inTx.fees * 1e6),
410
+ feesPayer: inTx.sender,
411
+ failed: false,
412
+ }),
413
+ type: inTx.type,
414
+ value: BigInt(inTx.amount * 1e6),
415
+ senders: [inTx.sender],
416
+ recipients: [inTx.recipient],
417
+ details: expect.objectContaining({
418
+ xrpTxType: 'AccountDelete',
419
+ }),
420
+ }),
421
+ ])
422
+ )
400
423
  })
401
424
  })
402
425
 
@@ -1,7 +1,9 @@
1
1
  // SPDX-FileCopyrightText: © 2024 LEDGER SAS
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { Operation, SendTransactionIntent } from '@ledgerhq/coin-module-framework/api/types'
4
+ import type { Operation } from '@ledgerhq/coin-module-framework/api/types'
5
+ import { createSendTransactionIntentMockData } from '@ledgerhq/coin-module-framework/test-utils'
6
+ import BigNumber from 'bignumber.js'
5
7
  import { GetTransactionsOptions } from '../network'
6
8
  import { NetworkInfo, XrpMapMemo } from '../types'
7
9
  import { RIPPLE_EPOCH } from '../utils'
@@ -280,7 +282,7 @@ describe('listOperations', () => {
280
282
  }
281
283
  expect(mockGetTransactions).toHaveBeenNthCalledWith(1, 'src', baseOptions)
282
284
  expect(token).toEqual(expect.any(String))
283
- await api.listOperations('src', { minHeight: 0, cursor: token as string, order: 'asc' })
285
+ await api.listOperations('src', { minHeight: 0, cursor: token, order: 'asc' })
284
286
  const optionsWithToken = {
285
287
  ...baseOptions,
286
288
  marker: defaultMarker,
@@ -415,14 +417,15 @@ describe('Testing craftTransaction function', () => {
415
417
 
416
418
  beforeAll(() => {
417
419
  mockCraftTransaction.mockImplementation((_address, _transaction, _publicKey) => {
418
- return Promise.resolve({ xrplTransaction: {} as never, serializedTransaction: '' })
420
+ return Promise.resolve({ xrplTransaction: {}, serializedTransaction: '' })
419
421
  })
420
422
 
421
423
  mockGetSequence.mockImplementation((_address) => Promise.resolve(0n))
422
424
 
423
425
  mockEstimateFees.mockImplementation((_networkInfo) => {
426
+ const networkInfo: NetworkInfo = { family: 'xrp', serverFee: new BigNumber(0) }
424
427
  return Promise.resolve({
425
- networkInfo: {} as NetworkInfo,
428
+ networkInfo,
426
429
  fees: DEFAULT_ESTIMATED_FEES,
427
430
  })
428
431
  })
@@ -435,10 +438,12 @@ describe('Testing craftTransaction function', () => {
435
438
  it('should use custom user fees when user provides it for crafting a transaction', async () => {
436
439
  const customFees = 99n
437
440
  await api.craftTransaction(
438
- { intentType: 'transaction', sender: 'foo' } as SendTransactionIntent<XrpMapMemo>,
439
- {
440
- value: customFees,
441
- }
441
+ createSendTransactionIntentMockData<XrpMapMemo>({
442
+ type: 'Payment',
443
+ sender: 'foo',
444
+ memo: { type: 'map', memos: new Map() },
445
+ }),
446
+ { value: customFees }
442
447
  )
443
448
 
444
449
  expect(mockCraftTransaction).toHaveBeenCalledWith(
@@ -451,10 +456,13 @@ describe('Testing craftTransaction function', () => {
451
456
  })
452
457
 
453
458
  it('should use default fees when user does not provide them for crafting a transaction', async () => {
454
- await api.craftTransaction({
455
- intentType: 'transaction',
456
- sender: 'foo',
457
- } as SendTransactionIntent<XrpMapMemo>)
459
+ await api.craftTransaction(
460
+ createSendTransactionIntentMockData<XrpMapMemo>({
461
+ type: 'Payment',
462
+ sender: 'foo',
463
+ memo: { type: 'map', memos: new Map() },
464
+ })
465
+ )
458
466
 
459
467
  expect(mockCraftTransaction).toHaveBeenCalledWith(
460
468
  expect.any(Object),
@@ -466,24 +474,26 @@ describe('Testing craftTransaction function', () => {
466
474
  })
467
475
 
468
476
  it('should pass signing pub key when user provides it for crafting a transaction', async () => {
469
- await api.craftTransaction({
470
- intentType: 'transaction',
471
- sender: 'foo',
472
- senderPublicKey: 'bar',
473
- } as SendTransactionIntent<XrpMapMemo>)
477
+ await api.craftTransaction(
478
+ createSendTransactionIntentMockData<XrpMapMemo>({
479
+ type: 'Payment',
480
+ sender: 'foo',
481
+ senderPublicKey: 'bar',
482
+ memo: { type: 'map', memos: new Map() },
483
+ })
484
+ )
474
485
 
475
486
  expect(mockCraftTransaction).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), 'bar')
476
487
  })
477
488
 
478
489
  it('should pass memos when user provides it for crafting a transaction', async () => {
479
- await api.craftTransaction({
480
- intentType: 'transaction',
481
- sender: 'foo',
482
- memo: {
483
- type: 'map',
484
- memos: new Map([['memos', ['testdata']]]),
485
- },
486
- } as SendTransactionIntent<XrpMapMemo>)
490
+ await api.craftTransaction(
491
+ createSendTransactionIntentMockData<XrpMapMemo>({
492
+ type: 'Payment',
493
+ sender: 'foo',
494
+ memo: { type: 'map', memos: new Map([['memos', ['testdata']]]) },
495
+ })
496
+ )
487
497
 
488
498
  expect(mockCraftTransaction).toHaveBeenCalledWith(
489
499
  expect.any(Object),
@@ -497,10 +507,13 @@ describe('Testing craftTransaction function', () => {
497
507
  })
498
508
 
499
509
  it('should not pass memos when user does not provide it for crafting a transaction', async () => {
500
- await api.craftTransaction({
501
- intentType: 'transaction',
502
- sender: 'foo',
503
- } as SendTransactionIntent<XrpMapMemo>)
510
+ await api.craftTransaction(
511
+ createSendTransactionIntentMockData<XrpMapMemo>({
512
+ type: 'Payment',
513
+ sender: 'foo',
514
+ memo: { type: 'map', memos: new Map() },
515
+ })
516
+ )
504
517
 
505
518
  expect(mockCraftTransaction).toHaveBeenCalledWith(
506
519
  expect.any(Object),
@@ -512,14 +525,13 @@ describe('Testing craftTransaction function', () => {
512
525
  })
513
526
 
514
527
  it('should not pass memos when user provides an empty memo list it for crafting a transaction', async () => {
515
- await api.craftTransaction({
516
- intentType: 'transaction',
517
- sender: 'foo',
518
- memo: {
519
- type: 'map',
520
- memos: new Map(),
521
- },
522
- } as SendTransactionIntent<XrpMapMemo>)
528
+ await api.craftTransaction(
529
+ createSendTransactionIntentMockData<XrpMapMemo>({
530
+ type: 'Payment',
531
+ sender: 'foo',
532
+ memo: { type: 'map', memos: new Map() },
533
+ })
534
+ )
523
535
 
524
536
  expect(mockCraftTransaction).toHaveBeenCalledWith(
525
537
  expect.any(Object),
@@ -531,14 +543,13 @@ describe('Testing craftTransaction function', () => {
531
543
  })
532
544
 
533
545
  it('should pass destination tag when user provides it for crafting a transaction', async () => {
534
- await api.craftTransaction({
535
- intentType: 'transaction',
536
- sender: 'foo',
537
- memo: {
538
- type: 'map',
539
- memos: new Map([['destinationTag', '1337']]),
540
- },
541
- } as SendTransactionIntent<XrpMapMemo>)
546
+ await api.craftTransaction(
547
+ createSendTransactionIntentMockData<XrpMapMemo>({
548
+ type: 'Payment',
549
+ sender: 'foo',
550
+ memo: { type: 'map', memos: new Map([['destinationTag', '1337']]) },
551
+ })
552
+ )
542
553
 
543
554
  expect(mockCraftTransaction).toHaveBeenCalledWith(
544
555
  expect.any(Object),
package/src/api/index.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  TransactionIntent,
14
14
  CraftedTransaction,
15
15
  CoinModuleApi,
16
+ notSupported,
16
17
  } from '@ledgerhq/coin-module-framework/api/index'
17
18
  import { craftTransactionData } from '@ledgerhq/coin-module-framework/logic/craftTransactionData'
18
19
  import { isSendTransactionIntent } from '@ledgerhq/coin-module-framework/utils'
@@ -48,6 +49,7 @@ export function createApi(config: XrpConfig): CoinModuleApi<XrpMapMemo> {
48
49
  validateIntent,
49
50
  getBlock,
50
51
  getBlockInfo,
52
+ call: notSupported('call'),
51
53
  getNextSequence: getSequence,
52
54
  getStakes(_address: string, _cursor?: Cursor): Promise<Page<Stake>> {
53
55
  throw new Error('getStakes is not supported')
@@ -73,13 +75,16 @@ async function craft(
73
75
  const nextSequenceNumber = Number(await getSequence(transactionIntent.sender))
74
76
  const estimatedFees = customFees?.value ?? (await estimateFees()).fees
75
77
 
76
- const memosMap = transactionIntent.memo?.type === 'map' ? transactionIntent.memo.memos : new Map()
78
+ const memosMap =
79
+ transactionIntent.memo?.type === 'map'
80
+ ? transactionIntent.memo.memos
81
+ : new Map<string, string | string[]>()
77
82
 
78
83
  const destinationTagValue = memosMap.get('destinationTag')
79
84
  const destinationTag =
80
85
  typeof destinationTagValue === 'string' ? Number(destinationTagValue) : undefined
81
86
 
82
- const memoStrings = memosMap.get('memos') as string[] | undefined
87
+ const memoStrings = memosMap.get('memos')
83
88
 
84
89
  let memoEntries: MemoInput[] | undefined = undefined
85
90
 
@@ -1,9 +1,10 @@
1
1
  // SPDX-FileCopyrightText: © 2024 LEDGER SAS
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
+ import type { GetTransactionsOptions } from '../network'
5
+ import type { Marker } from '../network/types'
4
6
  import { assert } from 'console'
5
7
  import { Operation } from '@ledgerhq/coin-module-framework/api/types'
6
- import { Marker } from '../network/types'
7
8
  import { RIPPLE_EPOCH } from '../utils'
8
9
  import { listOperations } from './listOperations'
9
10
 
@@ -17,7 +18,8 @@ const mockGetServerInfos = jest.fn().mockResolvedValue({
17
18
  const mockNetworkGetTransactions = jest.fn()
18
19
  jest.mock('../network', () => ({
19
20
  getServerInfos: () => mockGetServerInfos(),
20
- getTransactions: (...args: any[]) => mockNetworkGetTransactions(...args),
21
+ getTransactions: (address: string, options: GetTransactionsOptions | undefined) =>
22
+ mockNetworkGetTransactions(address, options),
21
23
  }))
22
24
 
23
25
  describe('listOperations', () => {