@ledgerhq/coin-xrp 7.22.0 → 7.23.3
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.
- package/CHANGELOG.md +65 -28
- package/LICENSE +194 -0
- package/jest.config.js +4 -1
- package/jest.integ.config.js +3 -0
- package/knip.json +11 -0
- package/lib/api/broadcast.d.ts.map +1 -0
- package/lib/{logic → api}/broadcast.js +4 -2
- package/lib/api/broadcast.js.map +1 -0
- package/lib/api/combine.d.ts.map +1 -0
- package/lib/{logic → api}/combine.js +4 -2
- package/lib/api/combine.js.map +1 -0
- package/lib/{logic → api}/craftRawTransaction.d.ts +1 -1
- package/lib/api/craftRawTransaction.d.ts.map +1 -0
- package/lib/{logic → api}/craftRawTransaction.js +9 -8
- package/lib/api/craftRawTransaction.js.map +1 -0
- package/lib/api/craftTransaction.d.ts.map +1 -0
- package/lib/{logic → api}/craftTransaction.js +4 -3
- package/lib/api/craftTransaction.js.map +1 -0
- package/lib/{logic → api}/estimateFees.d.ts +1 -1
- package/lib/api/estimateFees.d.ts.map +1 -0
- package/lib/{logic → api}/estimateFees.js +5 -3
- package/lib/api/estimateFees.js.map +1 -0
- package/lib/api/getBalance.d.ts +3 -0
- package/lib/api/getBalance.d.ts.map +1 -0
- package/lib/{logic → api}/getBalance.js +5 -3
- package/lib/api/getBalance.js.map +1 -0
- package/lib/api/getBlock.d.ts +3 -0
- package/lib/api/getBlock.d.ts.map +1 -0
- package/lib/{logic → api}/getBlock.js +14 -12
- package/lib/api/getBlock.js.map +1 -0
- package/lib/api/getBlockInfo.d.ts +3 -0
- package/lib/api/getBlockInfo.d.ts.map +1 -0
- package/lib/{logic → api}/getBlockInfo.js +3 -1
- package/lib/api/getBlockInfo.js.map +1 -0
- package/lib/api/getSequence.d.ts +2 -0
- package/lib/api/getSequence.d.ts.map +1 -0
- package/lib/api/getSequence.js +11 -0
- package/lib/api/getSequence.js.map +1 -0
- package/lib/api/index.d.ts +4 -4
- package/lib/api/index.d.ts.map +1 -1
- package/lib/api/index.js +44 -32
- package/lib/api/index.js.map +1 -1
- package/lib/api/lastBlock.d.ts +3 -0
- package/lib/api/lastBlock.d.ts.map +1 -0
- package/lib/{logic → api}/lastBlock.js +2 -0
- package/lib/api/lastBlock.js.map +1 -0
- package/lib/{logic → api}/listOperations.d.ts +2 -2
- package/lib/api/listOperations.d.ts.map +1 -0
- package/lib/{logic → api}/listOperations.js +18 -16
- package/lib/api/listOperations.js.map +1 -0
- package/lib/api/validateAddress.d.ts +3 -0
- package/lib/api/validateAddress.d.ts.map +1 -0
- package/lib/{logic → api}/validateAddress.js +2 -0
- package/lib/api/validateAddress.js.map +1 -0
- package/lib/{logic → api}/validateIntent.d.ts +2 -2
- package/lib/api/validateIntent.d.ts.map +1 -0
- package/lib/{logic → api}/validateIntent.js +17 -15
- package/lib/api/validateIntent.js.map +1 -0
- package/lib/config.d.ts +1 -1
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +2 -0
- package/lib/config.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/network/index.d.ts +4 -4
- package/lib/network/index.d.ts.map +1 -1
- package/lib/network/index.js +17 -15
- package/lib/network/index.js.map +1 -1
- package/lib/network/types.d.ts +4 -4
- package/lib/network/types.d.ts.map +1 -1
- package/lib/network/types.js +4 -2
- package/lib/network/types.js.map +1 -1
- package/lib/supportedFeatures.d.ts +1 -1
- package/lib/supportedFeatures.d.ts.map +1 -1
- package/lib/supportedFeatures.js +3 -1
- package/lib/supportedFeatures.js.map +1 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +2 -0
- package/lib/types/index.js.map +1 -1
- package/lib/types/model.d.ts +6 -6
- package/lib/types/model.d.ts.map +1 -1
- package/lib/types/model.js +2 -0
- package/lib/types/model.js.map +1 -1
- package/lib/utils/common.d.ts +3 -0
- package/lib/utils/common.d.ts.map +1 -0
- package/lib/{logic → utils}/common.js +7 -2
- package/lib/utils/common.js.map +1 -0
- package/lib/utils/errors.d.ts.map +1 -0
- package/lib/{logic → utils}/errors.js +3 -1
- package/lib/utils/errors.js.map +1 -0
- package/lib/utils/getAccountInfo.d.ts +3 -0
- package/lib/utils/getAccountInfo.d.ts.map +1 -0
- package/lib/{logic → utils}/getAccountInfo.js +2 -0
- package/lib/utils/getAccountInfo.js.map +1 -0
- package/{lib-es/logic/utils.d.ts → lib/utils/index.d.ts} +4 -5
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/{logic/utils.js → utils/index.js} +7 -25
- package/lib/utils/index.js.map +1 -0
- package/{lib-es/logic → lib/utils}/validateMemo.d.ts +1 -1
- package/lib/utils/validateMemo.d.ts.map +1 -0
- package/lib/{logic → utils}/validateMemo.js +2 -0
- package/lib/utils/validateMemo.js.map +1 -0
- package/lib-es/api/broadcast.d.ts.map +1 -0
- package/lib-es/api/broadcast.js +13 -0
- package/lib-es/api/broadcast.js.map +1 -0
- package/lib-es/api/combine.d.ts.map +1 -0
- package/lib-es/{logic → api}/combine.js +5 -3
- package/lib-es/api/combine.js.map +1 -0
- package/lib-es/{logic → api}/craftRawTransaction.d.ts +1 -1
- package/lib-es/api/craftRawTransaction.d.ts.map +1 -0
- package/lib-es/{logic → api}/craftRawTransaction.js +12 -11
- package/lib-es/api/craftRawTransaction.js.map +1 -0
- package/lib-es/api/craftTransaction.d.ts.map +1 -0
- package/lib-es/{logic → api}/craftTransaction.js +9 -8
- package/lib-es/api/craftTransaction.js.map +1 -0
- package/lib-es/{logic → api}/estimateFees.d.ts +1 -1
- package/lib-es/api/estimateFees.d.ts.map +1 -0
- package/lib-es/{logic → api}/estimateFees.js +7 -5
- package/lib-es/api/estimateFees.js.map +1 -0
- package/lib-es/api/getBalance.d.ts +3 -0
- package/lib-es/api/getBalance.d.ts.map +1 -0
- package/lib-es/{logic → api}/getBalance.js +7 -5
- package/lib-es/api/getBalance.js.map +1 -0
- package/lib-es/api/getBlock.d.ts +3 -0
- package/lib-es/api/getBlock.d.ts.map +1 -0
- package/lib-es/{logic → api}/getBlock.js +15 -13
- package/lib-es/api/getBlock.js.map +1 -0
- package/lib-es/api/getBlockInfo.d.ts +3 -0
- package/lib-es/api/getBlockInfo.d.ts.map +1 -0
- package/lib-es/{logic → api}/getBlockInfo.js +4 -2
- package/lib-es/api/getBlockInfo.js.map +1 -0
- package/lib-es/api/getSequence.d.ts +2 -0
- package/lib-es/api/getSequence.d.ts.map +1 -0
- package/lib-es/api/getSequence.js +8 -0
- package/lib-es/api/getSequence.js.map +1 -0
- package/lib-es/api/index.d.ts +4 -4
- package/lib-es/api/index.d.ts.map +1 -1
- package/lib-es/api/index.js +35 -23
- package/lib-es/api/index.js.map +1 -1
- package/lib-es/api/lastBlock.d.ts +3 -0
- package/lib-es/api/lastBlock.d.ts.map +1 -0
- package/lib-es/{logic → api}/lastBlock.js +3 -1
- package/lib-es/api/lastBlock.js.map +1 -0
- package/lib-es/{logic → api}/listOperations.d.ts +2 -2
- package/lib-es/api/listOperations.d.ts.map +1 -0
- package/lib-es/{logic → api}/listOperations.js +19 -17
- package/lib-es/api/listOperations.js.map +1 -0
- package/lib-es/api/validateAddress.d.ts +3 -0
- package/lib-es/api/validateAddress.d.ts.map +1 -0
- package/lib-es/api/validateAddress.js +7 -0
- package/lib-es/api/validateAddress.js.map +1 -0
- package/lib-es/{logic → api}/validateIntent.d.ts +2 -2
- package/lib-es/api/validateIntent.d.ts.map +1 -0
- package/lib-es/{logic → api}/validateIntent.js +21 -19
- package/lib-es/api/validateIntent.js.map +1 -0
- package/lib-es/config.d.ts +1 -1
- package/lib-es/config.d.ts.map +1 -1
- package/lib-es/config.js +3 -1
- package/lib-es/config.js.map +1 -1
- package/lib-es/index.d.ts +2 -2
- package/lib-es/index.d.ts.map +1 -1
- package/lib-es/index.js +3 -1
- package/lib-es/index.js.map +1 -1
- package/lib-es/network/index.d.ts +4 -4
- package/lib-es/network/index.d.ts.map +1 -1
- package/lib-es/network/index.js +21 -19
- package/lib-es/network/index.js.map +1 -1
- package/lib-es/network/types.d.ts +4 -4
- package/lib-es/network/types.d.ts.map +1 -1
- package/lib-es/network/types.js +4 -2
- package/lib-es/network/types.js.map +1 -1
- package/lib-es/supportedFeatures.d.ts +1 -1
- package/lib-es/supportedFeatures.d.ts.map +1 -1
- package/lib-es/supportedFeatures.js +3 -1
- package/lib-es/supportedFeatures.js.map +1 -1
- package/lib-es/types/index.d.ts +1 -1
- package/lib-es/types/index.d.ts.map +1 -1
- package/lib-es/types/index.js +3 -1
- package/lib-es/types/index.js.map +1 -1
- package/lib-es/types/model.d.ts +6 -6
- package/lib-es/types/model.d.ts.map +1 -1
- package/lib-es/types/model.js +2 -0
- package/lib-es/types/model.js.map +1 -1
- package/lib-es/utils/common.d.ts +3 -0
- package/lib-es/utils/common.d.ts.map +1 -0
- package/lib-es/utils/common.js +10 -0
- package/lib-es/utils/common.js.map +1 -0
- package/lib-es/utils/errors.d.ts.map +1 -0
- package/lib-es/utils/errors.js +5 -0
- package/lib-es/utils/errors.js.map +1 -0
- package/lib-es/utils/getAccountInfo.d.ts +3 -0
- package/lib-es/utils/getAccountInfo.d.ts.map +1 -0
- package/lib-es/utils/getAccountInfo.js +8 -0
- package/lib-es/utils/getAccountInfo.js.map +1 -0
- package/{lib/logic/utils.d.ts → lib-es/utils/index.d.ts} +4 -5
- package/lib-es/utils/index.d.ts.map +1 -0
- package/lib-es/{logic/utils.js → utils/index.js} +9 -26
- package/lib-es/utils/index.js.map +1 -0
- package/{lib/logic → lib-es/utils}/validateMemo.d.ts +1 -1
- package/lib-es/utils/validateMemo.d.ts.map +1 -0
- package/lib-es/{logic → utils}/validateMemo.js +3 -1
- package/lib-es/utils/validateMemo.js.map +1 -0
- package/package.json +15 -26
- package/src/api/broadcast.integ.test.ts +45 -0
- package/src/api/broadcast.test.ts +57 -0
- package/src/api/broadcast.ts +18 -0
- package/src/api/combine.test.ts +103 -0
- package/src/api/combine.ts +41 -0
- package/src/api/craftRawTransaction.test.ts +363 -0
- package/src/{logic → api}/craftRawTransaction.ts +35 -33
- package/src/api/craftTransaction.test.ts +116 -0
- package/src/api/craftTransaction.ts +108 -0
- package/src/api/estimateFees.test.ts +55 -0
- package/src/api/estimateFees.ts +40 -0
- package/src/api/getBalance.test.ts +70 -0
- package/src/api/getBalance.ts +30 -0
- package/src/api/getBlock.integ.test.ts +76 -0
- package/src/api/getBlock.test.ts +785 -0
- package/src/{logic → api}/getBlock.ts +65 -62
- package/src/api/getBlockInfo.integ.test.ts +63 -0
- package/src/api/getBlockInfo.test.ts +109 -0
- package/src/api/getBlockInfo.ts +23 -0
- package/src/api/getSequence.test.ts +26 -0
- package/src/api/getSequence.ts +9 -0
- package/src/api/index.integ.test.ts +293 -290
- package/src/api/index.test.ts +236 -233
- package/src/api/index.ts +55 -58
- package/src/api/lastBlock.ts +14 -0
- package/src/api/listOperations.test.ts +458 -0
- package/src/{logic → api}/listOperations.ts +79 -76
- package/src/api/validateAddress.test.ts +30 -0
- package/src/api/validateAddress.ts +12 -0
- package/src/{logic → api}/validateIntent.test.ts +148 -145
- package/src/{logic → api}/validateIntent.ts +45 -42
- package/src/config.ts +11 -8
- package/src/index.ts +5 -2
- package/src/network/index.test.ts +120 -117
- package/src/network/index.ts +57 -54
- package/src/network/types.ts +192 -189
- package/src/supportedFeatures.ts +6 -3
- package/src/types/index.ts +4 -1
- package/src/types/model.ts +36 -33
- package/src/utils/common.ts +12 -0
- package/src/utils/errors.ts +6 -0
- package/src/utils/getAccountInfo.ts +10 -0
- package/src/utils/index.test.ts +133 -0
- package/src/utils/index.ts +47 -0
- package/src/utils/validateMemo.test.ts +48 -0
- package/src/{logic → utils}/validateMemo.ts +9 -6
- package/tsconfig.json +5 -2
- package/.turbo/turbo-build.log +0 -4
- package/.unimportedrc.json +0 -25
- package/LICENSE.txt +0 -21
- package/lib/logic/broadcast.d.ts.map +0 -1
- package/lib/logic/broadcast.js.map +0 -1
- package/lib/logic/combine.d.ts.map +0 -1
- package/lib/logic/combine.js.map +0 -1
- package/lib/logic/common.d.ts +0 -3
- package/lib/logic/common.d.ts.map +0 -1
- package/lib/logic/common.js.map +0 -1
- package/lib/logic/craftRawTransaction.d.ts.map +0 -1
- package/lib/logic/craftRawTransaction.js.map +0 -1
- package/lib/logic/craftTransaction.d.ts.map +0 -1
- package/lib/logic/craftTransaction.js.map +0 -1
- package/lib/logic/errors.d.ts.map +0 -1
- package/lib/logic/errors.js.map +0 -1
- package/lib/logic/estimateFees.d.ts.map +0 -1
- package/lib/logic/estimateFees.js.map +0 -1
- package/lib/logic/getAccountInfo.d.ts +0 -3
- package/lib/logic/getAccountInfo.d.ts.map +0 -1
- package/lib/logic/getAccountInfo.js.map +0 -1
- package/lib/logic/getBalance.d.ts +0 -3
- package/lib/logic/getBalance.d.ts.map +0 -1
- package/lib/logic/getBalance.js.map +0 -1
- package/lib/logic/getBlock.d.ts +0 -3
- package/lib/logic/getBlock.d.ts.map +0 -1
- package/lib/logic/getBlock.js.map +0 -1
- package/lib/logic/getBlockInfo.d.ts +0 -3
- package/lib/logic/getBlockInfo.d.ts.map +0 -1
- package/lib/logic/getBlockInfo.js.map +0 -1
- package/lib/logic/index.d.ts +0 -16
- package/lib/logic/index.d.ts.map +0 -1
- package/lib/logic/index.js +0 -34
- package/lib/logic/index.js.map +0 -1
- package/lib/logic/lastBlock.d.ts +0 -3
- package/lib/logic/lastBlock.d.ts.map +0 -1
- package/lib/logic/lastBlock.js.map +0 -1
- package/lib/logic/listOperations.d.ts.map +0 -1
- package/lib/logic/listOperations.js.map +0 -1
- package/lib/logic/utils.d.ts.map +0 -1
- package/lib/logic/utils.js.map +0 -1
- package/lib/logic/validateAddress.d.ts +0 -3
- package/lib/logic/validateAddress.d.ts.map +0 -1
- package/lib/logic/validateAddress.js.map +0 -1
- package/lib/logic/validateIntent.d.ts.map +0 -1
- package/lib/logic/validateIntent.js.map +0 -1
- package/lib/logic/validateMemo.d.ts.map +0 -1
- package/lib/logic/validateMemo.js.map +0 -1
- package/lib-es/logic/broadcast.d.ts.map +0 -1
- package/lib-es/logic/broadcast.js +0 -11
- package/lib-es/logic/broadcast.js.map +0 -1
- package/lib-es/logic/combine.d.ts.map +0 -1
- package/lib-es/logic/combine.js.map +0 -1
- package/lib-es/logic/common.d.ts +0 -3
- package/lib-es/logic/common.d.ts.map +0 -1
- package/lib-es/logic/common.js +0 -5
- package/lib-es/logic/common.js.map +0 -1
- package/lib-es/logic/craftRawTransaction.d.ts.map +0 -1
- package/lib-es/logic/craftRawTransaction.js.map +0 -1
- package/lib-es/logic/craftTransaction.d.ts.map +0 -1
- package/lib-es/logic/craftTransaction.js.map +0 -1
- package/lib-es/logic/errors.d.ts.map +0 -1
- package/lib-es/logic/errors.js +0 -3
- package/lib-es/logic/errors.js.map +0 -1
- package/lib-es/logic/estimateFees.d.ts.map +0 -1
- package/lib-es/logic/estimateFees.js.map +0 -1
- package/lib-es/logic/getAccountInfo.d.ts +0 -3
- package/lib-es/logic/getAccountInfo.d.ts.map +0 -1
- package/lib-es/logic/getAccountInfo.js +0 -6
- package/lib-es/logic/getAccountInfo.js.map +0 -1
- package/lib-es/logic/getBalance.d.ts +0 -3
- package/lib-es/logic/getBalance.d.ts.map +0 -1
- package/lib-es/logic/getBalance.js.map +0 -1
- package/lib-es/logic/getBlock.d.ts +0 -3
- package/lib-es/logic/getBlock.d.ts.map +0 -1
- package/lib-es/logic/getBlock.js.map +0 -1
- package/lib-es/logic/getBlockInfo.d.ts +0 -3
- package/lib-es/logic/getBlockInfo.d.ts.map +0 -1
- package/lib-es/logic/getBlockInfo.js.map +0 -1
- package/lib-es/logic/index.d.ts +0 -16
- package/lib-es/logic/index.d.ts.map +0 -1
- package/lib-es/logic/index.js +0 -15
- package/lib-es/logic/index.js.map +0 -1
- package/lib-es/logic/lastBlock.d.ts +0 -3
- package/lib-es/logic/lastBlock.d.ts.map +0 -1
- package/lib-es/logic/lastBlock.js.map +0 -1
- package/lib-es/logic/listOperations.d.ts.map +0 -1
- package/lib-es/logic/listOperations.js.map +0 -1
- package/lib-es/logic/utils.d.ts.map +0 -1
- package/lib-es/logic/utils.js.map +0 -1
- package/lib-es/logic/validateAddress.d.ts +0 -3
- package/lib-es/logic/validateAddress.d.ts.map +0 -1
- package/lib-es/logic/validateAddress.js +0 -5
- package/lib-es/logic/validateAddress.js.map +0 -1
- package/lib-es/logic/validateIntent.d.ts.map +0 -1
- package/lib-es/logic/validateIntent.js.map +0 -1
- package/lib-es/logic/validateMemo.d.ts.map +0 -1
- package/lib-es/logic/validateMemo.js.map +0 -1
- package/src/logic/broadcast.test.ts +0 -54
- package/src/logic/broadcast.ts +0 -15
- package/src/logic/combine.test.ts +0 -100
- package/src/logic/combine.ts +0 -38
- package/src/logic/common.ts +0 -6
- package/src/logic/craftRawTransaction.test.ts +0 -362
- package/src/logic/craftTransaction.test.ts +0 -113
- package/src/logic/craftTransaction.ts +0 -106
- package/src/logic/errors.ts +0 -3
- package/src/logic/estimateFees.test.ts +0 -52
- package/src/logic/estimateFees.ts +0 -37
- package/src/logic/getAccountInfo.ts +0 -7
- package/src/logic/getBalance.test.ts +0 -67
- package/src/logic/getBalance.ts +0 -27
- package/src/logic/getBlock.integ.test.ts +0 -75
- package/src/logic/getBlock.test.ts +0 -782
- package/src/logic/getBlockInfo.integ.test.ts +0 -62
- package/src/logic/getBlockInfo.test.ts +0 -106
- package/src/logic/getBlockInfo.ts +0 -20
- package/src/logic/index.ts +0 -16
- package/src/logic/lastBlock.ts +0 -11
- package/src/logic/listOperations.test.ts +0 -455
- package/src/logic/utils.test.ts +0 -130
- package/src/logic/utils.ts +0 -75
- package/src/logic/validateAddress.test.ts +0 -27
- package/src/logic/validateAddress.ts +0 -9
- package/src/logic/validateMemo.test.ts +0 -45
- /package/lib/{logic → api}/broadcast.d.ts +0 -0
- /package/lib/{logic → api}/combine.d.ts +0 -0
- /package/lib/{logic → api}/craftTransaction.d.ts +0 -0
- /package/lib/{logic → utils}/errors.d.ts +0 -0
- /package/lib-es/{logic → api}/broadcast.d.ts +0 -0
- /package/lib-es/{logic → api}/combine.d.ts +0 -0
- /package/lib-es/{logic → api}/craftTransaction.d.ts +0 -0
- /package/lib-es/{logic → utils}/errors.d.ts +0 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: © 2024 LEDGER SAS
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { encode, decode } from 'ripple-binary-codec'
|
|
5
|
+
import { SignerEntry } from '../types'
|
|
6
|
+
import { craftRawTransaction } from './craftRawTransaction'
|
|
7
|
+
|
|
8
|
+
// --- Mocks ---
|
|
9
|
+
const mockEstimateFees = jest.fn()
|
|
10
|
+
const mockGetLedgerIndex = jest.fn()
|
|
11
|
+
|
|
12
|
+
jest.mock('./estimateFees', () => ({
|
|
13
|
+
estimateFees: () => mockEstimateFees(),
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
jest.mock('../network', () => ({
|
|
17
|
+
getLedgerIndex: () => mockGetLedgerIndex(),
|
|
18
|
+
}))
|
|
19
|
+
|
|
20
|
+
// Mock ripple-address-codec globally so ordering logic in sortSignersByNumericAddress uses deterministic data.
|
|
21
|
+
const decodeAccountIDMock = jest.fn((account: string) => {
|
|
22
|
+
const bytes = new Uint8Array(20).fill(0)
|
|
23
|
+
const ordering: Record<string, number> = { alpha: 1, beta: 2, gamma: 3, delta: 4 }
|
|
24
|
+
if (ordering[account]) bytes[19] = ordering[account]
|
|
25
|
+
return bytes
|
|
26
|
+
})
|
|
27
|
+
jest.mock('ripple-address-codec', () => ({
|
|
28
|
+
decodeAccountID: (addr: string) => decodeAccountIDMock(addr),
|
|
29
|
+
isValidClassicAddress: () => true,
|
|
30
|
+
}))
|
|
31
|
+
|
|
32
|
+
describe('craftRawTransaction', () => {
|
|
33
|
+
const sender = 'rPDf6SQStnNmw1knCu1ei7h6BcDAEUUqn5'
|
|
34
|
+
const destination = 'rJe1St1G6BWMFmdrrcT7NdD3XT1NxTMEWN'
|
|
35
|
+
// XRPL Ed25519 public keys must be 33 bytes: 0xED prefix + 32 bytes payload (66 hex chars total)
|
|
36
|
+
// Using a deterministic dummy key so ripple-binary-codec encode/decode roundtrips without alteration.
|
|
37
|
+
const publicKey = 'ED0123456789ABCDEFFEDCBA98765432100123456789ABCDEFFEDCBA9876543211' // 66 hex chars
|
|
38
|
+
// Pre-set key used in the test that ensures existing SigningPubKey is preserved.
|
|
39
|
+
const alreadySetPublicKey = 'EDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' // 66 hex chars
|
|
40
|
+
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
mockEstimateFees.mockReset()
|
|
43
|
+
mockGetLedgerIndex.mockReset()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// Helper to safely extract Signers array without relying on type assertions elsewhere
|
|
47
|
+
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
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
it('fills missing Fee, Sequence, LastLedgerSequence & SigningPubKey for a standard payment', async () => {
|
|
57
|
+
// Given: base transaction missing common autofill fields
|
|
58
|
+
const baseTx = {
|
|
59
|
+
TransactionType: 'Payment',
|
|
60
|
+
Account: sender,
|
|
61
|
+
Amount: '1000',
|
|
62
|
+
Destination: destination,
|
|
63
|
+
} as const
|
|
64
|
+
const serialized = encode(baseTx)
|
|
65
|
+
|
|
66
|
+
mockEstimateFees.mockResolvedValue({ fees: BigInt(123) })
|
|
67
|
+
mockGetLedgerIndex.mockResolvedValue(1000)
|
|
68
|
+
|
|
69
|
+
// When
|
|
70
|
+
const result = await craftRawTransaction(serialized, sender, publicKey, 10n)
|
|
71
|
+
|
|
72
|
+
// Then
|
|
73
|
+
const decoded = decode(result.transaction)
|
|
74
|
+
expect(decoded).toMatchObject({
|
|
75
|
+
TransactionType: 'Payment',
|
|
76
|
+
Account: sender,
|
|
77
|
+
Amount: '1000',
|
|
78
|
+
Destination: destination,
|
|
79
|
+
Fee: '123',
|
|
80
|
+
Sequence: 10,
|
|
81
|
+
LastLedgerSequence: 1020, // 1000 + 20 offset
|
|
82
|
+
SigningPubKey: publicKey,
|
|
83
|
+
})
|
|
84
|
+
expect(mockEstimateFees).toHaveBeenCalledTimes(1)
|
|
85
|
+
expect(mockGetLedgerIndex).toHaveBeenCalledTimes(1)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('autofills only missing Fee & LastLedgerSequence when Sequence provided', async () => {
|
|
89
|
+
// Given: Sequence present; Fee, LastLedgerSequence & SigningPubKey missing
|
|
90
|
+
const baseTx = {
|
|
91
|
+
TransactionType: 'Payment',
|
|
92
|
+
Account: sender,
|
|
93
|
+
Amount: '1234',
|
|
94
|
+
Destination: destination,
|
|
95
|
+
Sequence: 77,
|
|
96
|
+
} as const
|
|
97
|
+
const serialized = encode(baseTx)
|
|
98
|
+
|
|
99
|
+
mockEstimateFees.mockResolvedValue({ fees: BigInt(456) })
|
|
100
|
+
mockGetLedgerIndex.mockResolvedValue(2000)
|
|
101
|
+
|
|
102
|
+
// When: pass a different sequence param to ensure existing Sequence is not overwritten
|
|
103
|
+
const result = await craftRawTransaction(serialized, sender, publicKey, 999n)
|
|
104
|
+
|
|
105
|
+
// Then
|
|
106
|
+
const decoded = decode(result.transaction)
|
|
107
|
+
expect(decoded.Sequence).toBe(77) // preserved
|
|
108
|
+
expect(decoded.Fee).toBe('456') // autofilled
|
|
109
|
+
expect(decoded.LastLedgerSequence).toBe(2020) // 2000 + 20
|
|
110
|
+
expect(decoded.SigningPubKey).toBe(publicKey) // autofilled
|
|
111
|
+
expect(mockEstimateFees).toHaveBeenCalledTimes(1)
|
|
112
|
+
expect(mockGetLedgerIndex).toHaveBeenCalledTimes(1)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('does not overwrite provided Fee / Sequence / LastLedgerSequence / SigningPubKey', async () => {
|
|
116
|
+
// Given
|
|
117
|
+
const baseTx = {
|
|
118
|
+
TransactionType: 'Payment',
|
|
119
|
+
Account: sender,
|
|
120
|
+
Amount: '2500',
|
|
121
|
+
Destination: destination,
|
|
122
|
+
Fee: '999',
|
|
123
|
+
Sequence: 42,
|
|
124
|
+
LastLedgerSequence: 5000,
|
|
125
|
+
SigningPubKey: alreadySetPublicKey,
|
|
126
|
+
} as const
|
|
127
|
+
const serialized = encode(baseTx)
|
|
128
|
+
|
|
129
|
+
// Mocks would be used if fields missing; ensure they are ignored
|
|
130
|
+
mockEstimateFees.mockResolvedValue({ fees: BigInt(1) })
|
|
131
|
+
mockGetLedgerIndex.mockResolvedValue(1)
|
|
132
|
+
|
|
133
|
+
// When
|
|
134
|
+
const result = await craftRawTransaction(serialized, sender, publicKey, 999n)
|
|
135
|
+
|
|
136
|
+
// Then
|
|
137
|
+
const decoded = decode(result.transaction)
|
|
138
|
+
expect(decoded.Fee).toBe('999')
|
|
139
|
+
expect(decoded.Sequence).toBe(42)
|
|
140
|
+
expect(decoded.LastLedgerSequence).toBe(5000)
|
|
141
|
+
expect(decoded.SigningPubKey).toBe(alreadySetPublicKey)
|
|
142
|
+
expect(mockEstimateFees).not.toHaveBeenCalled()
|
|
143
|
+
expect(mockGetLedgerIndex).not.toHaveBeenCalled()
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('sets Sequence to 0 when TicketSequence is provided and Sequence missing', async () => {
|
|
147
|
+
// Given
|
|
148
|
+
const baseTx = {
|
|
149
|
+
TransactionType: 'Payment',
|
|
150
|
+
Account: sender,
|
|
151
|
+
Amount: '1',
|
|
152
|
+
Destination: destination,
|
|
153
|
+
TicketSequence: 555,
|
|
154
|
+
Fee: '10',
|
|
155
|
+
// Sequence intentionally omitted
|
|
156
|
+
} as const
|
|
157
|
+
const serialized = encode(baseTx)
|
|
158
|
+
|
|
159
|
+
mockGetLedgerIndex.mockResolvedValue(10)
|
|
160
|
+
|
|
161
|
+
// When
|
|
162
|
+
const result = await craftRawTransaction(serialized, sender, publicKey, 77n)
|
|
163
|
+
|
|
164
|
+
// Then
|
|
165
|
+
const decoded = decode(result.transaction)
|
|
166
|
+
expect(decoded.TicketSequence).toBe(555)
|
|
167
|
+
expect(decoded.Sequence).toBe(0)
|
|
168
|
+
expect(decoded.LastLedgerSequence).toBe(30) // 10 + 20
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('forces Sequence to 0 when TicketSequence present even if non-zero Sequence provided', async () => {
|
|
172
|
+
// Given: TicketSequence provided AND a non-zero Sequence already set (we enforce spec by overriding)
|
|
173
|
+
const baseTx = {
|
|
174
|
+
TransactionType: 'Payment',
|
|
175
|
+
Account: sender,
|
|
176
|
+
Amount: '123',
|
|
177
|
+
Destination: destination,
|
|
178
|
+
TicketSequence: 777,
|
|
179
|
+
Sequence: 55, // non-zero pre-set
|
|
180
|
+
Fee: '10',
|
|
181
|
+
} as const
|
|
182
|
+
const serialized = encode(baseTx)
|
|
183
|
+
|
|
184
|
+
mockGetLedgerIndex.mockResolvedValue(111)
|
|
185
|
+
|
|
186
|
+
// When
|
|
187
|
+
const result = await craftRawTransaction(serialized, sender, publicKey, 9999n)
|
|
188
|
+
|
|
189
|
+
// Then
|
|
190
|
+
const decoded = decode(result.transaction)
|
|
191
|
+
expect(decoded.Sequence).toBe(0) // overridden to comply with spec
|
|
192
|
+
expect(decoded.TicketSequence).toBe(777)
|
|
193
|
+
expect(decoded.LastLedgerSequence).toBe(131) // 111 + 20
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('throws when sender does not match Account for standard transaction', async () => {
|
|
197
|
+
// Given
|
|
198
|
+
const baseTx = {
|
|
199
|
+
TransactionType: 'Payment',
|
|
200
|
+
Account: sender,
|
|
201
|
+
Amount: '10',
|
|
202
|
+
Destination: destination,
|
|
203
|
+
} as const
|
|
204
|
+
const serialized = encode(baseTx)
|
|
205
|
+
|
|
206
|
+
// When & Then
|
|
207
|
+
await expect(craftRawTransaction(serialized, 'rOTHERADDRESS', publicKey, 1n)).rejects.toThrow(
|
|
208
|
+
'Sender address does not match the transaction account'
|
|
209
|
+
)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
describe('multi-sign', () => {
|
|
213
|
+
it('adds a Signers array when absent (single existing signer)', async () => {
|
|
214
|
+
// Given: multi-sign transaction (SigningPubKey empty) with required Fee & Sequence
|
|
215
|
+
const multi = {
|
|
216
|
+
TransactionType: 'Payment',
|
|
217
|
+
Account: sender, // Account can differ from signer in multi-sign, we keep same for simplicity
|
|
218
|
+
Amount: '100',
|
|
219
|
+
Destination: destination,
|
|
220
|
+
SigningPubKey: '', // signals multi-sign
|
|
221
|
+
Fee: '500',
|
|
222
|
+
Sequence: 9,
|
|
223
|
+
} as const
|
|
224
|
+
const serialized = encode(multi)
|
|
225
|
+
|
|
226
|
+
// When
|
|
227
|
+
const result = await craftRawTransaction(serialized, sender, publicKey, 9n)
|
|
228
|
+
|
|
229
|
+
// Then
|
|
230
|
+
const decoded = decode(result.transaction)
|
|
231
|
+
const signers = getSigners(decoded)
|
|
232
|
+
expect(signers?.length).toBe(1)
|
|
233
|
+
if (signers && signers[0]) {
|
|
234
|
+
expect(signers[0]).toMatchObject({
|
|
235
|
+
Signer: { Account: sender, SigningPubKey: publicKey, TxnSignature: '' },
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
// No autofill of LastLedgerSequence in multi-sign path (not required for this assertion)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('does not add LastLedgerSequence for multi-sign when absent', async () => {
|
|
242
|
+
// Given: multi-sign tx w/o LastLedgerSequence
|
|
243
|
+
const multi = {
|
|
244
|
+
TransactionType: 'Payment',
|
|
245
|
+
Account: sender,
|
|
246
|
+
Amount: '50',
|
|
247
|
+
Destination: destination,
|
|
248
|
+
SigningPubKey: '', // multi-sign
|
|
249
|
+
Fee: '12',
|
|
250
|
+
Sequence: 2,
|
|
251
|
+
} as const
|
|
252
|
+
const serialized = encode(multi)
|
|
253
|
+
mockGetLedgerIndex.mockResolvedValue(500) // should NOT be used
|
|
254
|
+
|
|
255
|
+
// When
|
|
256
|
+
const result = await craftRawTransaction(serialized, sender, publicKey, 2n)
|
|
257
|
+
|
|
258
|
+
// Then
|
|
259
|
+
const decoded = decode(result.transaction)
|
|
260
|
+
expect(decoded.LastLedgerSequence).toBeUndefined()
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
it('appends signer and sorts existing Signers numerically', async () => {
|
|
264
|
+
// Given existing unsorted signers
|
|
265
|
+
const existing: SignerEntry[] = [
|
|
266
|
+
{
|
|
267
|
+
Signer: {
|
|
268
|
+
Account: 'rPDf6SQStnNmw1knCu1ei7h6BcDAEUUqn5',
|
|
269
|
+
SigningPubKey: 'K3',
|
|
270
|
+
TxnSignature: '',
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
Signer: {
|
|
275
|
+
Account: 'rJe1St1G6BWMFmdrrcT7NdD3XT1NxTMEWN',
|
|
276
|
+
SigningPubKey: 'K1',
|
|
277
|
+
TxnSignature: '',
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
Signer: {
|
|
282
|
+
Account: 'rDKsbvy9uaNpPtvVFraJyNGfjvTw8xivgK',
|
|
283
|
+
SigningPubKey: 'K4',
|
|
284
|
+
TxnSignature: '',
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
]
|
|
288
|
+
const multi: {
|
|
289
|
+
TransactionType: string
|
|
290
|
+
Account: string
|
|
291
|
+
Amount: string
|
|
292
|
+
Destination: string
|
|
293
|
+
SigningPubKey: string
|
|
294
|
+
Fee: string
|
|
295
|
+
Sequence: number
|
|
296
|
+
Signers: SignerEntry[]
|
|
297
|
+
} = {
|
|
298
|
+
TransactionType: 'Payment',
|
|
299
|
+
Account: sender,
|
|
300
|
+
Amount: '200',
|
|
301
|
+
Destination: destination,
|
|
302
|
+
SigningPubKey: '',
|
|
303
|
+
Fee: '700',
|
|
304
|
+
Sequence: 3,
|
|
305
|
+
Signers: existing,
|
|
306
|
+
}
|
|
307
|
+
const serialized = encode(multi)
|
|
308
|
+
|
|
309
|
+
// When
|
|
310
|
+
const result = await craftRawTransaction(
|
|
311
|
+
serialized,
|
|
312
|
+
'r94uo44ukDHWVjYDJLZJYwDdZjo1F2QYgq',
|
|
313
|
+
publicKey,
|
|
314
|
+
3n
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
// Then
|
|
318
|
+
const decoded = decode(result.transaction)
|
|
319
|
+
const signers = getSigners(decoded) ?? []
|
|
320
|
+
const accounts = signers.map((s) => s.Signer.Account)
|
|
321
|
+
expect(accounts).toEqual([
|
|
322
|
+
'rPDf6SQStnNmw1knCu1ei7h6BcDAEUUqn5',
|
|
323
|
+
'rJe1St1G6BWMFmdrrcT7NdD3XT1NxTMEWN',
|
|
324
|
+
'rDKsbvy9uaNpPtvVFraJyNGfjvTw8xivgK',
|
|
325
|
+
'r94uo44ukDHWVjYDJLZJYwDdZjo1F2QYgq',
|
|
326
|
+
])
|
|
327
|
+
const betaEntry = signers.find(
|
|
328
|
+
(s) => s.Signer.Account === 'r94uo44ukDHWVjYDJLZJYwDdZjo1F2QYgq'
|
|
329
|
+
)
|
|
330
|
+
expect(betaEntry?.Signer.SigningPubKey).toBe(publicKey)
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it('throws when Fee missing for multi-sign', async () => {
|
|
334
|
+
const multi = {
|
|
335
|
+
TransactionType: 'Payment',
|
|
336
|
+
Account: sender,
|
|
337
|
+
Amount: '10',
|
|
338
|
+
Destination: destination,
|
|
339
|
+
SigningPubKey: '',
|
|
340
|
+
Sequence: 1,
|
|
341
|
+
} as const
|
|
342
|
+
const serialized = encode(multi)
|
|
343
|
+
await expect(craftRawTransaction(serialized, sender, publicKey, 1n)).rejects.toThrow(
|
|
344
|
+
'Fee is required for multi sign transactions'
|
|
345
|
+
)
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
it('throws when Sequence missing for multi-sign', async () => {
|
|
349
|
+
const multi = {
|
|
350
|
+
TransactionType: 'Payment',
|
|
351
|
+
Account: sender,
|
|
352
|
+
Amount: '10',
|
|
353
|
+
Destination: destination,
|
|
354
|
+
SigningPubKey: '',
|
|
355
|
+
Fee: '1',
|
|
356
|
+
} as const
|
|
357
|
+
const serialized = encode(multi)
|
|
358
|
+
await expect(craftRawTransaction(serialized, sender, publicKey, 1n)).rejects.toThrow(
|
|
359
|
+
'Sequence is required for multi sign transactions'
|
|
360
|
+
)
|
|
361
|
+
})
|
|
362
|
+
})
|
|
363
|
+
})
|
|
@@ -1,91 +1,93 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { JsonObject } from "ripple-binary-codec/dist/types/serialized-type";
|
|
4
|
-
import { getLedgerIndex } from "../network";
|
|
5
|
-
import { SignerEntry } from "../types";
|
|
6
|
-
import { estimateFees } from "./estimateFees";
|
|
7
|
-
import { sortSignersByNumericAddress } from "./utils";
|
|
1
|
+
// SPDX-FileCopyrightText: © 2024 LEDGER SAS
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
8
3
|
|
|
9
|
-
|
|
4
|
+
import { CraftedTransaction } from '@ledgerhq/coin-module-framework/api/index'
|
|
5
|
+
import { decode, encode } from 'ripple-binary-codec'
|
|
6
|
+
import { JsonObject } from 'ripple-binary-codec/dist/types/serialized-type'
|
|
7
|
+
import { getLedgerIndex } from '../network'
|
|
8
|
+
import { SignerEntry } from '../types'
|
|
9
|
+
import { sortSignersByNumericAddress } from '../utils'
|
|
10
|
+
import { estimateFees } from './estimateFees'
|
|
11
|
+
|
|
12
|
+
const LEDGER_OFFSET = 20
|
|
10
13
|
|
|
11
14
|
function craftRawTransactionMultiSign(
|
|
12
15
|
xrplTransaction: JsonObject,
|
|
13
16
|
sender: string,
|
|
14
|
-
publicKey: string
|
|
17
|
+
publicKey: string
|
|
15
18
|
): CraftedTransaction {
|
|
16
19
|
if (!xrplTransaction.Fee) {
|
|
17
|
-
throw new Error(
|
|
20
|
+
throw new Error('Fee is required for multi sign transactions')
|
|
18
21
|
}
|
|
19
22
|
if (!xrplTransaction.Sequence) {
|
|
20
|
-
throw new Error(
|
|
23
|
+
throw new Error('Sequence is required for multi sign transactions')
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
const signer: SignerEntry = {
|
|
24
27
|
Signer: {
|
|
25
28
|
Account: sender,
|
|
26
29
|
SigningPubKey: publicKey,
|
|
27
|
-
TxnSignature:
|
|
30
|
+
TxnSignature: '',
|
|
28
31
|
},
|
|
29
|
-
}
|
|
32
|
+
}
|
|
30
33
|
|
|
31
34
|
if (!xrplTransaction.Signers || !Array.isArray(xrplTransaction.Signers)) {
|
|
32
|
-
xrplTransaction.Signers = [signer]
|
|
35
|
+
xrplTransaction.Signers = [signer]
|
|
33
36
|
} else {
|
|
34
|
-
xrplTransaction.Signers.push(signer)
|
|
37
|
+
xrplTransaction.Signers.push(signer)
|
|
35
38
|
// The Signers array must be sorted based on the numeric value of the signer addresses
|
|
36
|
-
// We could probably insert it at the right place directly but like this we make sure the rest is also sorted
|
|
37
|
-
|
|
38
|
-
xrplTransaction.Signers = sortSignersByNumericAddress(xrplTransaction.Signers as SignerEntry[]);
|
|
39
|
+
// 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[])
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
const serializedTransaction = encode(xrplTransaction)
|
|
43
|
+
const serializedTransaction = encode(xrplTransaction)
|
|
42
44
|
|
|
43
|
-
return { transaction: serializedTransaction }
|
|
45
|
+
return { transaction: serializedTransaction }
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export async function craftRawTransaction(
|
|
47
49
|
transaction: string,
|
|
48
50
|
sender: string,
|
|
49
51
|
publicKey: string,
|
|
50
|
-
sequence: bigint
|
|
52
|
+
sequence: bigint
|
|
51
53
|
): Promise<CraftedTransaction> {
|
|
52
|
-
const xrplTransaction: JsonObject = decode(transaction)
|
|
54
|
+
const xrplTransaction: JsonObject = decode(transaction)
|
|
53
55
|
|
|
54
56
|
// Multi sign transactions have an empty SigningPubKey
|
|
55
57
|
// We only check some of the fields and cannot autofill others
|
|
56
58
|
// The sender cannot be checked because the transaction can be signed by another account
|
|
57
59
|
// https://xrpl.org/docs/concepts/accounts/multi-signing#sending-multi-signed-transactions
|
|
58
60
|
// https://xrpl.org/docs/tutorials/how-tos/manage-account-settings/send-a-multi-signed-transaction
|
|
59
|
-
if (xrplTransaction.SigningPubKey ===
|
|
60
|
-
return craftRawTransactionMultiSign(xrplTransaction, sender, publicKey)
|
|
61
|
+
if (xrplTransaction.SigningPubKey === '') {
|
|
62
|
+
return craftRawTransactionMultiSign(xrplTransaction, sender, publicKey)
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
if (sender !== xrplTransaction.Account) {
|
|
64
|
-
throw new Error(
|
|
66
|
+
throw new Error('Sender address does not match the transaction account')
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
if (!xrplTransaction.Fee) {
|
|
68
|
-
const { fees } = await estimateFees()
|
|
69
|
-
xrplTransaction.Fee = fees.toString()
|
|
70
|
+
const { fees } = await estimateFees()
|
|
71
|
+
xrplTransaction.Fee = fees.toString()
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
// Enforce XRPL spec: If TicketSequence is provided, Sequence MUST be 0 regardless of any pre-set value.
|
|
73
75
|
// https://xrpl.org/docs/references/protocol/transactions/common-fields#transaction-common-fields
|
|
74
76
|
if (xrplTransaction.TicketSequence) {
|
|
75
|
-
xrplTransaction.Sequence = 0
|
|
77
|
+
xrplTransaction.Sequence = 0
|
|
76
78
|
} else if (!xrplTransaction.Sequence) {
|
|
77
|
-
xrplTransaction.Sequence = Number(sequence)
|
|
79
|
+
xrplTransaction.Sequence = Number(sequence)
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
if (!xrplTransaction.LastLedgerSequence) {
|
|
81
|
-
xrplTransaction.LastLedgerSequence = (await getLedgerIndex()) + LEDGER_OFFSET
|
|
83
|
+
xrplTransaction.LastLedgerSequence = (await getLedgerIndex()) + LEDGER_OFFSET
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
if (!xrplTransaction.SigningPubKey) {
|
|
85
|
-
xrplTransaction.SigningPubKey = publicKey
|
|
87
|
+
xrplTransaction.SigningPubKey = publicKey
|
|
86
88
|
}
|
|
87
89
|
|
|
88
|
-
const serializedTransaction = encode(xrplTransaction)
|
|
90
|
+
const serializedTransaction = encode(xrplTransaction)
|
|
89
91
|
|
|
90
|
-
return { transaction: serializedTransaction }
|
|
92
|
+
return { transaction: serializedTransaction }
|
|
91
93
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: © 2024 LEDGER SAS
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { decode, encode } from 'ripple-binary-codec'
|
|
5
|
+
import { craftTransaction } from './craftTransaction'
|
|
6
|
+
jest.mock('../network', () => ({
|
|
7
|
+
getLedgerIndex: () => 1,
|
|
8
|
+
}))
|
|
9
|
+
|
|
10
|
+
describe('craftTransaction', () => {
|
|
11
|
+
it('returns a valid transaction object when no pubkey is provided', async () => {
|
|
12
|
+
// Given
|
|
13
|
+
const account = {
|
|
14
|
+
address: 'rPDf6SQStnNmw1knCu1ei7h6BcDAEUUqn5',
|
|
15
|
+
nextSequenceNumber: 2,
|
|
16
|
+
}
|
|
17
|
+
const transaction = {
|
|
18
|
+
recipient: 'rJe1St1G6BWMFmdrrcT7NdD3XT1NxTMEWN',
|
|
19
|
+
amount: BigInt(100_000_000),
|
|
20
|
+
fees: BigInt(100),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// When
|
|
24
|
+
const result = await craftTransaction(account, transaction)
|
|
25
|
+
|
|
26
|
+
// Then
|
|
27
|
+
expect(result).toEqual({
|
|
28
|
+
xrplTransaction: {
|
|
29
|
+
TransactionType: 'Payment',
|
|
30
|
+
Account: 'rPDf6SQStnNmw1knCu1ei7h6BcDAEUUqn5',
|
|
31
|
+
Amount: '100000000',
|
|
32
|
+
Destination: 'rJe1St1G6BWMFmdrrcT7NdD3XT1NxTMEWN',
|
|
33
|
+
DestinationTag: undefined,
|
|
34
|
+
Fee: '100',
|
|
35
|
+
Flags: 2147483648,
|
|
36
|
+
Sequence: 2,
|
|
37
|
+
LastLedgerSequence: 21,
|
|
38
|
+
},
|
|
39
|
+
serializedTransaction: encode(result.xrplTransaction),
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('returns a valid transaction object when pubkey is provided', async () => {
|
|
44
|
+
// Given
|
|
45
|
+
const account = {
|
|
46
|
+
address: 'rPDf6SQStnNmw1knCu1ei7h6BcDAEUUqn5',
|
|
47
|
+
nextSequenceNumber: 2,
|
|
48
|
+
}
|
|
49
|
+
const transaction = {
|
|
50
|
+
recipient: 'rJe1St1G6BWMFmdrrcT7NdD3XT1NxTMEWN',
|
|
51
|
+
amount: BigInt(100_000_000),
|
|
52
|
+
fees: BigInt(100),
|
|
53
|
+
memos: [{ data: '01', format: '02', type: '03' }],
|
|
54
|
+
destinationTag: 123,
|
|
55
|
+
}
|
|
56
|
+
const pubKey = 'public_key'
|
|
57
|
+
|
|
58
|
+
// When
|
|
59
|
+
const result = await craftTransaction(account, transaction, pubKey)
|
|
60
|
+
|
|
61
|
+
// Then
|
|
62
|
+
expect(result).toEqual({
|
|
63
|
+
xrplTransaction: {
|
|
64
|
+
TransactionType: 'Payment',
|
|
65
|
+
Account: 'rPDf6SQStnNmw1knCu1ei7h6BcDAEUUqn5',
|
|
66
|
+
Amount: '100000000',
|
|
67
|
+
Destination: 'rJe1St1G6BWMFmdrrcT7NdD3XT1NxTMEWN',
|
|
68
|
+
DestinationTag: 123,
|
|
69
|
+
Fee: '100',
|
|
70
|
+
Flags: 2147483648,
|
|
71
|
+
Sequence: 2,
|
|
72
|
+
LastLedgerSequence: 21,
|
|
73
|
+
Memos: [{ Memo: { MemoData: '01', MemoFormat: '02', MemoType: '03' } }],
|
|
74
|
+
},
|
|
75
|
+
serializedTransaction: encode({ ...result.xrplTransaction, SigningPubKey: pubKey }),
|
|
76
|
+
})
|
|
77
|
+
const binDecodedTx = decode(result.serializedTransaction)
|
|
78
|
+
expect(binDecodedTx.Memos).toEqual([
|
|
79
|
+
{ Memo: { MemoData: '01', MemoFormat: '02', MemoType: '03' } },
|
|
80
|
+
])
|
|
81
|
+
expect(binDecodedTx.DestinationTag).toEqual(123)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('returns expected transaction when destinationTag is set to zero', async () => {
|
|
85
|
+
// Given
|
|
86
|
+
const account = {
|
|
87
|
+
address: 'rPDf6SQStnNmw1knCu1ei7h6BcDAEUUqn5',
|
|
88
|
+
nextSequenceNumber: 2,
|
|
89
|
+
}
|
|
90
|
+
const transaction = {
|
|
91
|
+
recipient: 'rJe1St1G6BWMFmdrrcT7NdD3XT1NxTMEWN',
|
|
92
|
+
amount: BigInt(100_000_000),
|
|
93
|
+
fees: BigInt(100),
|
|
94
|
+
destinationTag: 0,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// When
|
|
98
|
+
const result = await craftTransaction(account, transaction)
|
|
99
|
+
|
|
100
|
+
// Then
|
|
101
|
+
expect(result).toEqual({
|
|
102
|
+
xrplTransaction: {
|
|
103
|
+
TransactionType: 'Payment',
|
|
104
|
+
Account: 'rPDf6SQStnNmw1knCu1ei7h6BcDAEUUqn5',
|
|
105
|
+
Amount: '100000000',
|
|
106
|
+
Destination: 'rJe1St1G6BWMFmdrrcT7NdD3XT1NxTMEWN',
|
|
107
|
+
DestinationTag: 0,
|
|
108
|
+
Fee: '100',
|
|
109
|
+
Flags: 2147483648,
|
|
110
|
+
Sequence: 2,
|
|
111
|
+
LastLedgerSequence: 21,
|
|
112
|
+
},
|
|
113
|
+
serializedTransaction: encode(result.xrplTransaction),
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
})
|