@ledgerhq/coin-stellar 6.26.0 → 6.26.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.
- package/.oxlintrc.json +7 -0
- package/CHANGELOG.md +12 -0
- package/LICENSE +194 -0
- package/knip.json +12 -0
- package/lib/api/index.d.ts +5 -5
- package/lib/api/index.d.ts.map +1 -1
- package/lib/api/index.js +12 -10
- package/lib/api/index.js.map +1 -1
- 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/constants.d.ts.map +1 -1
- package/lib/constants.js +3 -1
- package/lib/constants.js.map +1 -1
- package/lib/logic/broadcast.d.ts.map +1 -1
- package/lib/logic/broadcast.js +2 -0
- package/lib/logic/broadcast.js.map +1 -1
- package/lib/logic/combine.d.ts +1 -1
- package/lib/logic/combine.d.ts.map +1 -1
- package/lib/logic/combine.js +2 -0
- package/lib/logic/combine.js.map +1 -1
- package/lib/logic/common.d.ts +1 -1
- package/lib/logic/common.d.ts.map +1 -1
- package/lib/logic/common.js +4 -2
- package/lib/logic/common.js.map +1 -1
- package/lib/logic/craftTransaction.d.ts +1 -1
- package/lib/logic/craftTransaction.d.ts.map +1 -1
- package/lib/logic/craftTransaction.js +10 -8
- package/lib/logic/craftTransaction.js.map +1 -1
- package/lib/logic/estimateFees.d.ts.map +1 -1
- package/lib/logic/estimateFees.js +2 -0
- package/lib/logic/estimateFees.js.map +1 -1
- package/lib/logic/getBalance.d.ts +1 -1
- package/lib/logic/getBalance.d.ts.map +1 -1
- package/lib/logic/getBalance.js +4 -2
- package/lib/logic/getBalance.js.map +1 -1
- package/lib/logic/getBlock.d.ts +1 -1
- package/lib/logic/getBlock.d.ts.map +1 -1
- package/lib/logic/getBlock.js +39 -34
- package/lib/logic/getBlock.js.map +1 -1
- package/lib/logic/getBlockInfo.d.ts +1 -1
- package/lib/logic/getBlockInfo.d.ts.map +1 -1
- package/lib/logic/getBlockInfo.js +2 -0
- package/lib/logic/getBlockInfo.js.map +1 -1
- package/lib/logic/index.d.ts +12 -12
- package/lib/logic/index.d.ts.map +1 -1
- package/lib/logic/index.js +2 -0
- package/lib/logic/index.js.map +1 -1
- package/lib/logic/lastBlock.d.ts +1 -1
- package/lib/logic/lastBlock.d.ts.map +1 -1
- package/lib/logic/lastBlock.js +2 -0
- package/lib/logic/lastBlock.js.map +1 -1
- package/lib/logic/listOperations.d.ts +1 -1
- package/lib/logic/listOperations.d.ts.map +1 -1
- package/lib/logic/listOperations.js +6 -4
- package/lib/logic/listOperations.js.map +1 -1
- package/lib/logic/sdkWrapper.d.ts +1 -1
- package/lib/logic/sdkWrapper.d.ts.map +1 -1
- package/lib/logic/sdkWrapper.js +2 -0
- package/lib/logic/sdkWrapper.js.map +1 -1
- package/lib/logic/utils.d.ts.map +1 -1
- package/lib/logic/utils.js +4 -2
- package/lib/logic/utils.js.map +1 -1
- package/lib/logic/validateAddress.d.ts +1 -1
- package/lib/logic/validateAddress.d.ts.map +1 -1
- package/lib/logic/validateAddress.js +2 -0
- package/lib/logic/validateAddress.js.map +1 -1
- package/lib/logic/validateIntent.d.ts +2 -2
- package/lib/logic/validateIntent.d.ts.map +1 -1
- package/lib/logic/validateIntent.js +21 -19
- package/lib/logic/validateIntent.js.map +1 -1
- package/lib/logic/validateMemo.d.ts +1 -1
- package/lib/logic/validateMemo.d.ts.map +1 -1
- package/lib/logic/validateMemo.js +7 -5
- package/lib/logic/validateMemo.js.map +1 -1
- package/lib/network/horizon.d.ts +6 -7
- package/lib/network/horizon.d.ts.map +1 -1
- package/lib/network/horizon.js +48 -95
- package/lib/network/horizon.js.map +1 -1
- package/lib/network/horizonErrorBody.d.ts +29 -0
- package/lib/network/horizonErrorBody.d.ts.map +1 -0
- package/lib/network/horizonErrorBody.js +139 -0
- package/lib/network/horizonErrorBody.js.map +1 -0
- package/lib/network/horizonLedgerErrors.d.ts.map +1 -1
- package/lib/network/horizonLedgerErrors.js +28 -4
- package/lib/network/horizonLedgerErrors.js.map +1 -1
- package/lib/network/index.d.ts +1 -1
- package/lib/network/index.d.ts.map +1 -1
- package/lib/network/index.js +2 -0
- package/lib/network/index.js.map +1 -1
- package/lib/network/serialization.d.ts +6 -4
- package/lib/network/serialization.d.ts.map +1 -1
- package/lib/network/serialization.js +95 -91
- package/lib/network/serialization.js.map +1 -1
- package/lib/polyfill.d.ts.map +1 -1
- package/lib/polyfill.js +6 -4
- package/lib/polyfill.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/errors.d.ts.map +1 -1
- package/lib/types/errors.js +14 -12
- package/lib/types/errors.js.map +1 -1
- package/lib/types/index.d.ts +2 -2
- 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 +7 -5
- package/lib/types/model.js.map +1 -1
- package/lib-es/api/index.d.ts +5 -5
- package/lib-es/api/index.d.ts.map +1 -1
- package/lib-es/api/index.js +20 -18
- package/lib-es/api/index.js.map +1 -1
- 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/constants.d.ts.map +1 -1
- package/lib-es/constants.js +3 -1
- package/lib-es/constants.js.map +1 -1
- package/lib-es/logic/broadcast.d.ts.map +1 -1
- package/lib-es/logic/broadcast.js +3 -1
- package/lib-es/logic/broadcast.js.map +1 -1
- package/lib-es/logic/combine.d.ts +1 -1
- package/lib-es/logic/combine.d.ts.map +1 -1
- package/lib-es/logic/combine.js +3 -1
- package/lib-es/logic/combine.js.map +1 -1
- package/lib-es/logic/common.d.ts +1 -1
- package/lib-es/logic/common.d.ts.map +1 -1
- package/lib-es/logic/common.js +5 -3
- package/lib-es/logic/common.js.map +1 -1
- package/lib-es/logic/craftTransaction.d.ts +1 -1
- package/lib-es/logic/craftTransaction.d.ts.map +1 -1
- package/lib-es/logic/craftTransaction.js +15 -13
- package/lib-es/logic/craftTransaction.js.map +1 -1
- package/lib-es/logic/estimateFees.d.ts.map +1 -1
- package/lib-es/logic/estimateFees.js +3 -1
- package/lib-es/logic/estimateFees.js.map +1 -1
- package/lib-es/logic/getBalance.d.ts +1 -1
- package/lib-es/logic/getBalance.d.ts.map +1 -1
- package/lib-es/logic/getBalance.js +5 -3
- package/lib-es/logic/getBalance.js.map +1 -1
- package/lib-es/logic/getBlock.d.ts +1 -1
- package/lib-es/logic/getBlock.d.ts.map +1 -1
- package/lib-es/logic/getBlock.js +42 -37
- package/lib-es/logic/getBlock.js.map +1 -1
- package/lib-es/logic/getBlockInfo.d.ts +1 -1
- package/lib-es/logic/getBlockInfo.d.ts.map +1 -1
- package/lib-es/logic/getBlockInfo.js +3 -1
- package/lib-es/logic/getBlockInfo.js.map +1 -1
- package/lib-es/logic/index.d.ts +12 -12
- package/lib-es/logic/index.d.ts.map +1 -1
- package/lib-es/logic/index.js +14 -12
- package/lib-es/logic/index.js.map +1 -1
- package/lib-es/logic/lastBlock.d.ts +1 -1
- package/lib-es/logic/lastBlock.d.ts.map +1 -1
- package/lib-es/logic/lastBlock.js +3 -1
- package/lib-es/logic/lastBlock.js.map +1 -1
- package/lib-es/logic/listOperations.d.ts +1 -1
- package/lib-es/logic/listOperations.d.ts.map +1 -1
- package/lib-es/logic/listOperations.js +7 -5
- package/lib-es/logic/listOperations.js.map +1 -1
- package/lib-es/logic/sdkWrapper.d.ts +1 -1
- package/lib-es/logic/sdkWrapper.d.ts.map +1 -1
- package/lib-es/logic/sdkWrapper.js +4 -2
- package/lib-es/logic/sdkWrapper.js.map +1 -1
- package/lib-es/logic/utils.d.ts.map +1 -1
- package/lib-es/logic/utils.js +6 -4
- package/lib-es/logic/utils.js.map +1 -1
- package/lib-es/logic/validateAddress.d.ts +1 -1
- package/lib-es/logic/validateAddress.d.ts.map +1 -1
- package/lib-es/logic/validateAddress.js +3 -1
- package/lib-es/logic/validateAddress.js.map +1 -1
- package/lib-es/logic/validateIntent.d.ts +2 -2
- package/lib-es/logic/validateIntent.d.ts.map +1 -1
- package/lib-es/logic/validateIntent.js +30 -28
- package/lib-es/logic/validateIntent.js.map +1 -1
- package/lib-es/logic/validateMemo.d.ts +1 -1
- package/lib-es/logic/validateMemo.d.ts.map +1 -1
- package/lib-es/logic/validateMemo.js +8 -6
- package/lib-es/logic/validateMemo.js.map +1 -1
- package/lib-es/network/horizon.d.ts +6 -7
- package/lib-es/network/horizon.d.ts.map +1 -1
- package/lib-es/network/horizon.js +57 -104
- package/lib-es/network/horizon.js.map +1 -1
- package/lib-es/network/horizonErrorBody.d.ts +29 -0
- package/lib-es/network/horizonErrorBody.d.ts.map +1 -0
- package/lib-es/network/horizonErrorBody.js +134 -0
- package/lib-es/network/horizonErrorBody.js.map +1 -0
- package/lib-es/network/horizonLedgerErrors.d.ts.map +1 -1
- package/lib-es/network/horizonLedgerErrors.js +30 -6
- package/lib-es/network/horizonLedgerErrors.js.map +1 -1
- package/lib-es/network/index.d.ts +1 -1
- package/lib-es/network/index.d.ts.map +1 -1
- package/lib-es/network/index.js +3 -1
- package/lib-es/network/index.js.map +1 -1
- package/lib-es/network/serialization.d.ts +6 -4
- package/lib-es/network/serialization.d.ts.map +1 -1
- package/lib-es/network/serialization.js +97 -95
- package/lib-es/network/serialization.js.map +1 -1
- package/lib-es/polyfill.d.ts.map +1 -1
- package/lib-es/polyfill.js +7 -5
- package/lib-es/polyfill.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/errors.d.ts.map +1 -1
- package/lib-es/types/errors.js +15 -13
- package/lib-es/types/errors.js.map +1 -1
- package/lib-es/types/index.d.ts +2 -2
- package/lib-es/types/index.d.ts.map +1 -1
- package/lib-es/types/index.js +4 -2
- 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 +7 -5
- package/lib-es/types/model.js.map +1 -1
- package/package.json +13 -16
- package/src/api/index.integ.test.ts +168 -163
- package/src/api/index.test.ts +106 -103
- package/src/api/index.ts +49 -46
- package/src/api/index_error.test.ts +48 -25
- package/src/config.ts +15 -12
- package/src/constants.ts +4 -1
- package/src/globals.d.ts +7 -5
- package/src/logic/broadcast.integ.test.ts +22 -19
- package/src/logic/broadcast.ts +5 -2
- package/src/logic/combine.test.ts +18 -15
- package/src/logic/combine.ts +8 -5
- package/src/logic/common.ts +9 -6
- package/src/logic/craftTransaction.test.ts +76 -75
- package/src/logic/craftTransaction.ts +46 -43
- package/src/logic/estimateFees.ts +6 -3
- package/src/logic/getBalance.test.ts +31 -28
- package/src/logic/getBalance.ts +13 -10
- package/src/logic/getBlock.integ.test.ts +40 -39
- package/src/logic/getBlock.test.ts +462 -415
- package/src/logic/getBlock.ts +142 -127
- package/src/logic/getBlockInfo.test.ts +72 -69
- package/src/logic/getBlockInfo.ts +15 -12
- package/src/logic/index.ts +15 -12
- package/src/logic/lastBlock.ts +6 -3
- package/src/logic/listOperations.ts +14 -11
- package/src/logic/listOperations.unit.test.ts +212 -209
- package/src/logic/networkWrappers.test.ts +32 -29
- package/src/logic/sdkWrapper.ts +19 -16
- package/src/logic/utils.ts +13 -10
- package/src/logic/validateAddress.test.ts +21 -18
- package/src/logic/validateAddress.ts +7 -4
- package/src/logic/validateIntent.test.ts +62 -59
- package/src/logic/validateIntent.ts +87 -84
- package/src/logic/validateMemo.test.ts +50 -47
- package/src/logic/validateMemo.ts +16 -13
- package/src/network/broadcastTransaction.test.ts +83 -63
- package/src/network/fetchLedgerOperations.unit.test.ts +155 -0
- package/src/network/horizon.integ.test.ts +45 -42
- package/src/network/horizon.test.ts +17 -14
- package/src/network/horizon.ts +244 -314
- package/src/network/horizon.unit.test.ts +238 -237
- package/src/network/horizonErrorBody.test.ts +133 -0
- package/src/network/horizonErrorBody.ts +178 -0
- package/src/network/horizonLedgerErrors.test.ts +150 -78
- package/src/network/horizonLedgerErrors.ts +47 -20
- package/src/network/index.ts +4 -1
- package/src/network/registerHorizonInterceptors.test.ts +163 -160
- package/src/network/serialization.ts +232 -227
- package/src/network/serialization.unit.test.ts +801 -798
- package/src/polyfill.ts +18 -15
- package/src/supportedFeatures.ts +6 -3
- package/src/types/errors.ts +33 -32
- package/src/types/index.ts +5 -2
- package/src/types/model.ts +55 -52
- package/tsconfig.json +1 -1
- package/.unimportedrc.json +0 -17
- package/LICENSE.txt +0 -21
- package/src/network/fetchLedgerOperations.nock.unit.test.ts +0 -156
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
1
4
|
import {
|
|
2
5
|
Keypair,
|
|
3
6
|
TransactionBuilder,
|
|
@@ -6,24 +9,24 @@ import {
|
|
|
6
9
|
BASE_FEE,
|
|
7
10
|
Asset,
|
|
8
11
|
Account,
|
|
9
|
-
} from
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
12
|
+
} from '@stellar/stellar-sdk'
|
|
13
|
+
import coinConfig from '../config'
|
|
14
|
+
import { StellarBroadcastFailedError } from '../types'
|
|
15
|
+
import { broadcast } from './broadcast'
|
|
13
16
|
|
|
14
|
-
describe(
|
|
17
|
+
describe('Broadcast', () => {
|
|
15
18
|
beforeAll(() => {
|
|
16
19
|
coinConfig.setCoinConfig(() => ({
|
|
17
|
-
status: { type:
|
|
20
|
+
status: { type: 'active' },
|
|
18
21
|
explorer: {
|
|
19
|
-
url:
|
|
22
|
+
url: 'https://stellar.coin.ledger.com',
|
|
20
23
|
},
|
|
21
|
-
}))
|
|
22
|
-
})
|
|
24
|
+
}))
|
|
25
|
+
})
|
|
23
26
|
|
|
24
|
-
it(
|
|
25
|
-
const source = Keypair.random()
|
|
26
|
-
const account = new Account(source.publicKey(),
|
|
27
|
+
it('throws on insufficient funds', async () => {
|
|
28
|
+
const source = Keypair.random()
|
|
29
|
+
const account = new Account(source.publicKey(), '0')
|
|
27
30
|
|
|
28
31
|
const tx = new TransactionBuilder(account, {
|
|
29
32
|
fee: BASE_FEE,
|
|
@@ -33,13 +36,13 @@ describe("Broadcast", () => {
|
|
|
33
36
|
Operation.payment({
|
|
34
37
|
destination: source.publicKey(),
|
|
35
38
|
asset: Asset.native(),
|
|
36
|
-
amount:
|
|
37
|
-
})
|
|
39
|
+
amount: '1',
|
|
40
|
+
})
|
|
38
41
|
)
|
|
39
42
|
.setTimeout(0)
|
|
40
|
-
.build()
|
|
41
|
-
tx.sign(source)
|
|
43
|
+
.build()
|
|
44
|
+
tx.sign(source)
|
|
42
45
|
|
|
43
|
-
await expect(broadcast(tx.toXDR())).rejects.toThrow(StellarBroadcastFailedError)
|
|
44
|
-
})
|
|
45
|
-
})
|
|
46
|
+
await expect(broadcast(tx.toXDR())).rejects.toThrow(StellarBroadcastFailedError)
|
|
47
|
+
})
|
|
48
|
+
})
|
package/src/logic/broadcast.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { broadcastTransaction } from '../network'
|
|
2
5
|
|
|
3
6
|
export async function broadcast(signature: string): Promise<string> {
|
|
4
|
-
return broadcastTransaction(signature)
|
|
7
|
+
return broadcastTransaction(signature)
|
|
5
8
|
}
|
|
@@ -1,24 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
import { Networks, Transaction as StellarSdkTransaction } from '@stellar/stellar-sdk'
|
|
5
|
+
import { combine } from './combine'
|
|
6
|
+
|
|
7
|
+
jest.mock('@stellar/stellar-sdk', () => ({
|
|
5
8
|
Networks: {
|
|
6
|
-
PUBLIC:
|
|
9
|
+
PUBLIC: 'PUBLIC',
|
|
7
10
|
},
|
|
8
11
|
Transaction: jest.fn().mockImplementation(() => ({
|
|
9
12
|
addSignature: jest.fn(),
|
|
10
|
-
toXDR: jest.fn().mockReturnValue(
|
|
13
|
+
toXDR: jest.fn().mockReturnValue('signed-xdr'),
|
|
11
14
|
})),
|
|
12
|
-
}))
|
|
15
|
+
}))
|
|
13
16
|
|
|
14
|
-
describe(
|
|
15
|
-
it(
|
|
16
|
-
const result = combine(
|
|
17
|
+
describe('combine', () => {
|
|
18
|
+
it('creates a transaction, adds signature and returns xdr', () => {
|
|
19
|
+
const result = combine('unsigned-xdr', 'signature', 'public-key')
|
|
17
20
|
|
|
18
|
-
expect(StellarSdkTransaction).toHaveBeenCalledWith(
|
|
21
|
+
expect(StellarSdkTransaction).toHaveBeenCalledWith('unsigned-xdr', Networks.PUBLIC)
|
|
19
22
|
|
|
20
|
-
const txInstance = (StellarSdkTransaction as unknown as jest.Mock).mock.results[0].value
|
|
21
|
-
expect(txInstance.addSignature).toHaveBeenCalledWith(
|
|
22
|
-
expect(result).toBe(
|
|
23
|
-
})
|
|
24
|
-
})
|
|
23
|
+
const txInstance = (StellarSdkTransaction as unknown as jest.Mock).mock.results[0].value
|
|
24
|
+
expect(txInstance.addSignature).toHaveBeenCalledWith('public-key', 'signature')
|
|
25
|
+
expect(result).toBe('signed-xdr')
|
|
26
|
+
})
|
|
27
|
+
})
|
package/src/logic/combine.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { Networks, Transaction as StellarSdkTransaction, xdr } from '@stellar/stellar-sdk'
|
|
2
5
|
|
|
3
6
|
export function combine(
|
|
4
7
|
transaction: string | xdr.TransactionEnvelope,
|
|
5
8
|
signature: string,
|
|
6
|
-
publicKey: string
|
|
9
|
+
publicKey: string
|
|
7
10
|
): string {
|
|
8
|
-
const unsignedTx = new StellarSdkTransaction(transaction, Networks.PUBLIC)
|
|
9
|
-
unsignedTx.addSignature(publicKey, signature)
|
|
10
|
-
return unsignedTx.toXDR()
|
|
11
|
+
const unsignedTx = new StellarSdkTransaction(transaction, Networks.PUBLIC)
|
|
12
|
+
unsignedTx.addSignature(publicKey, signature)
|
|
13
|
+
return unsignedTx.toXDR()
|
|
11
14
|
}
|
package/src/logic/common.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { parseCurrencyUnit } from '@ledgerhq/coin-module-framework/currencies'
|
|
5
|
+
import BigNumber from 'bignumber.js'
|
|
3
6
|
|
|
4
7
|
export const stellarUnit = {
|
|
5
|
-
name:
|
|
6
|
-
code:
|
|
8
|
+
name: 'Lumen',
|
|
9
|
+
code: 'XLM',
|
|
7
10
|
magnitude: 7,
|
|
8
|
-
}
|
|
9
|
-
export const parseAPIValue = (value: string): BigNumber => parseCurrencyUnit(stellarUnit, value)
|
|
11
|
+
}
|
|
12
|
+
export const parseAPIValue = (value: string): BigNumber => parseCurrencyUnit(stellarUnit, value)
|
|
@@ -1,145 +1,146 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { NetworkDown } from '@ledgerhq/errors'
|
|
5
|
+
import { Horizon, Memo } from '@stellar/stellar-sdk'
|
|
6
|
+
import { getRecipientAccount, loadAccount } from '../network'
|
|
7
|
+
import { StellarAssetRequired, StellarMuxedAccountNotExist } from '../types'
|
|
8
|
+
import { craftTransaction } from './craftTransaction'
|
|
6
9
|
import {
|
|
7
10
|
buildChangeTrustOperation,
|
|
8
11
|
buildCreateAccountOperation,
|
|
9
12
|
buildPaymentOperation,
|
|
10
13
|
buildTransactionBuilder,
|
|
11
|
-
} from
|
|
14
|
+
} from './sdkWrapper'
|
|
12
15
|
|
|
13
|
-
jest.mock(
|
|
16
|
+
jest.mock('../network', () => ({
|
|
14
17
|
loadAccount: jest.fn(),
|
|
15
18
|
getRecipientAccount: jest.fn(),
|
|
16
|
-
}))
|
|
19
|
+
}))
|
|
17
20
|
|
|
18
|
-
jest.mock(
|
|
21
|
+
jest.mock('./sdkWrapper', () => ({
|
|
19
22
|
buildChangeTrustOperation: jest.fn(),
|
|
20
23
|
buildCreateAccountOperation: jest.fn(),
|
|
21
24
|
buildPaymentOperation: jest.fn(),
|
|
22
25
|
buildTransactionBuilder: jest.fn(),
|
|
23
|
-
}))
|
|
26
|
+
}))
|
|
24
27
|
|
|
25
|
-
describe(
|
|
26
|
-
const account = { address:
|
|
28
|
+
describe('craftTransaction', () => {
|
|
29
|
+
const account = { address: 'GABC' }
|
|
27
30
|
const transaction = {
|
|
28
|
-
type:
|
|
29
|
-
recipient:
|
|
31
|
+
type: 'payment',
|
|
32
|
+
recipient: 'GRECIPIENT',
|
|
30
33
|
amount: 10000000n,
|
|
31
34
|
fee: 100n,
|
|
32
|
-
}
|
|
35
|
+
}
|
|
33
36
|
|
|
34
37
|
const craftedTransaction = {
|
|
35
|
-
toXDR: jest.fn().mockReturnValue(
|
|
36
|
-
signatureBase: jest.fn().mockReturnValue(Buffer.from(
|
|
37
|
-
}
|
|
38
|
+
toXDR: jest.fn().mockReturnValue('tx-xdr'),
|
|
39
|
+
signatureBase: jest.fn().mockReturnValue(Buffer.from('signature-base')),
|
|
40
|
+
}
|
|
38
41
|
|
|
39
42
|
const txBuilder = {
|
|
40
43
|
addOperation: jest.fn(),
|
|
41
44
|
addMemo: jest.fn(),
|
|
42
45
|
setTimeout: jest.fn(),
|
|
43
46
|
build: jest.fn().mockReturnValue(craftedTransaction),
|
|
44
|
-
}
|
|
47
|
+
}
|
|
45
48
|
|
|
46
49
|
beforeEach(() => {
|
|
47
|
-
jest.clearAllMocks()
|
|
48
|
-
txBuilder.setTimeout.mockReturnValue(txBuilder)
|
|
50
|
+
jest.clearAllMocks()
|
|
51
|
+
txBuilder.setTimeout.mockReturnValue(txBuilder)
|
|
49
52
|
jest
|
|
50
53
|
.mocked(buildTransactionBuilder)
|
|
51
|
-
.mockReturnValue(txBuilder as unknown as ReturnType<typeof buildTransactionBuilder>)
|
|
52
|
-
jest.mocked(loadAccount).mockResolvedValue({ id:
|
|
54
|
+
.mockReturnValue(txBuilder as unknown as ReturnType<typeof buildTransactionBuilder>)
|
|
55
|
+
jest.mocked(loadAccount).mockResolvedValue({ id: 'source-account' } as Horizon.AccountResponse)
|
|
53
56
|
jest.mocked(getRecipientAccount).mockResolvedValue({
|
|
54
|
-
id:
|
|
57
|
+
id: 'GRECIPIENT',
|
|
55
58
|
isMuxedAccount: false,
|
|
56
59
|
assetIds: [],
|
|
57
|
-
})
|
|
60
|
+
})
|
|
58
61
|
jest
|
|
59
62
|
.mocked(buildPaymentOperation)
|
|
60
|
-
.mockReturnValue(
|
|
63
|
+
.mockReturnValue('payment-op' as unknown as ReturnType<typeof buildPaymentOperation>)
|
|
61
64
|
jest
|
|
62
65
|
.mocked(buildCreateAccountOperation)
|
|
63
66
|
.mockReturnValue(
|
|
64
|
-
|
|
65
|
-
)
|
|
67
|
+
'create-account-op' as unknown as ReturnType<typeof buildCreateAccountOperation>
|
|
68
|
+
)
|
|
66
69
|
jest
|
|
67
70
|
.mocked(buildChangeTrustOperation)
|
|
68
|
-
.mockReturnValue(
|
|
69
|
-
|
|
70
|
-
);
|
|
71
|
-
});
|
|
71
|
+
.mockReturnValue('change-trust-op' as unknown as ReturnType<typeof buildChangeTrustOperation>)
|
|
72
|
+
})
|
|
72
73
|
|
|
73
|
-
it(
|
|
74
|
-
jest.mocked(loadAccount).mockResolvedValue(null)
|
|
74
|
+
it('throws NetworkDown when source account cannot be loaded', async () => {
|
|
75
|
+
jest.mocked(loadAccount).mockResolvedValue(null)
|
|
75
76
|
|
|
76
|
-
await expect(craftTransaction(account, transaction)).rejects.toThrow(NetworkDown)
|
|
77
|
-
})
|
|
77
|
+
await expect(craftTransaction(account, transaction)).rejects.toThrow(NetworkDown)
|
|
78
|
+
})
|
|
78
79
|
|
|
79
|
-
it(
|
|
80
|
+
it('throws StellarAssetRequired for changeTrust without asset code and issuer', async () => {
|
|
80
81
|
await expect(
|
|
81
82
|
craftTransaction(account, {
|
|
82
83
|
...transaction,
|
|
83
|
-
type:
|
|
84
|
+
type: 'changeTrust',
|
|
84
85
|
assetCode: undefined,
|
|
85
86
|
assetIssuer: undefined,
|
|
86
|
-
})
|
|
87
|
-
).rejects.toThrow(StellarAssetRequired)
|
|
88
|
-
})
|
|
87
|
+
})
|
|
88
|
+
).rejects.toThrow(StellarAssetRequired)
|
|
89
|
+
})
|
|
89
90
|
|
|
90
|
-
it(
|
|
91
|
+
it('builds a changeTrust operation when type is changeTrust', async () => {
|
|
91
92
|
const result = await craftTransaction(account, {
|
|
92
93
|
...transaction,
|
|
93
|
-
type:
|
|
94
|
-
assetCode:
|
|
95
|
-
assetIssuer:
|
|
96
|
-
})
|
|
94
|
+
type: 'changeTrust',
|
|
95
|
+
assetCode: 'USDC',
|
|
96
|
+
assetIssuer: 'GISSUER',
|
|
97
|
+
})
|
|
97
98
|
|
|
98
|
-
expect(buildChangeTrustOperation).toHaveBeenCalledWith(
|
|
99
|
-
expect(txBuilder.addOperation).toHaveBeenCalledWith(
|
|
100
|
-
expect(getRecipientAccount).not.toHaveBeenCalled()
|
|
101
|
-
expect(result.xdr).toBe(
|
|
102
|
-
expect(result.signatureBase).toBe(
|
|
103
|
-
})
|
|
99
|
+
expect(buildChangeTrustOperation).toHaveBeenCalledWith('USDC', 'GISSUER')
|
|
100
|
+
expect(txBuilder.addOperation).toHaveBeenCalledWith('change-trust-op')
|
|
101
|
+
expect(getRecipientAccount).not.toHaveBeenCalled()
|
|
102
|
+
expect(result.xdr).toBe('tx-xdr')
|
|
103
|
+
expect(result.signatureBase).toBe('c2lnbmF0dXJlLWJhc2U=')
|
|
104
|
+
})
|
|
104
105
|
|
|
105
|
-
it(
|
|
106
|
-
const memoTextSpy = jest.spyOn(Memo,
|
|
106
|
+
it('builds a payment operation and memo when recipient exists', async () => {
|
|
107
|
+
const memoTextSpy = jest.spyOn(Memo, 'text')
|
|
107
108
|
|
|
108
109
|
await craftTransaction(account, {
|
|
109
110
|
...transaction,
|
|
110
|
-
memoType:
|
|
111
|
-
memoValue:
|
|
112
|
-
})
|
|
111
|
+
memoType: 'MEMO_TEXT',
|
|
112
|
+
memoValue: 'hello',
|
|
113
|
+
})
|
|
113
114
|
|
|
114
115
|
expect(buildPaymentOperation).toHaveBeenCalledWith({
|
|
115
|
-
destination:
|
|
116
|
+
destination: 'GRECIPIENT',
|
|
116
117
|
amount: 10000000n,
|
|
117
118
|
assetCode: undefined,
|
|
118
119
|
assetIssuer: undefined,
|
|
119
|
-
})
|
|
120
|
-
expect(txBuilder.addOperation).toHaveBeenCalledWith(
|
|
121
|
-
expect(memoTextSpy).toHaveBeenCalledWith(
|
|
122
|
-
expect(txBuilder.addMemo).toHaveBeenCalledTimes(1)
|
|
123
|
-
})
|
|
120
|
+
})
|
|
121
|
+
expect(txBuilder.addOperation).toHaveBeenCalledWith('payment-op')
|
|
122
|
+
expect(memoTextSpy).toHaveBeenCalledWith('hello')
|
|
123
|
+
expect(txBuilder.addMemo).toHaveBeenCalledTimes(1)
|
|
124
|
+
})
|
|
124
125
|
|
|
125
|
-
it(
|
|
126
|
+
it('throws StellarMuxedAccountNotExist when missing muxed recipient account', async () => {
|
|
126
127
|
jest
|
|
127
128
|
.mocked(getRecipientAccount)
|
|
128
|
-
.mockResolvedValue({ id: null, isMuxedAccount: true, assetIds: [] })
|
|
129
|
+
.mockResolvedValue({ id: null, isMuxedAccount: true, assetIds: [] })
|
|
129
130
|
|
|
130
131
|
await expect(craftTransaction(account, transaction)).rejects.toThrow(
|
|
131
|
-
StellarMuxedAccountNotExist
|
|
132
|
-
)
|
|
133
|
-
})
|
|
132
|
+
StellarMuxedAccountNotExist
|
|
133
|
+
)
|
|
134
|
+
})
|
|
134
135
|
|
|
135
|
-
it(
|
|
136
|
+
it('builds createAccount operation when recipient does not exist and is not muxed', async () => {
|
|
136
137
|
jest
|
|
137
138
|
.mocked(getRecipientAccount)
|
|
138
|
-
.mockResolvedValue({ id: null, isMuxedAccount: false, assetIds: [] })
|
|
139
|
+
.mockResolvedValue({ id: null, isMuxedAccount: false, assetIds: [] })
|
|
139
140
|
|
|
140
|
-
await craftTransaction(account, transaction)
|
|
141
|
+
await craftTransaction(account, transaction)
|
|
141
142
|
|
|
142
|
-
expect(buildCreateAccountOperation).toHaveBeenCalledWith(
|
|
143
|
-
expect(txBuilder.addOperation).toHaveBeenCalledWith(
|
|
144
|
-
})
|
|
145
|
-
})
|
|
143
|
+
expect(buildCreateAccountOperation).toHaveBeenCalledWith('GRECIPIENT', 10000000n)
|
|
144
|
+
expect(txBuilder.addOperation).toHaveBeenCalledWith('create-account-op')
|
|
145
|
+
})
|
|
146
|
+
})
|
|
@@ -1,55 +1,58 @@
|
|
|
1
|
-
|
|
1
|
+
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { NetworkDown } from '@ledgerhq/errors'
|
|
2
5
|
import {
|
|
3
6
|
Memo,
|
|
4
7
|
Operation as StellarSdkOperation,
|
|
5
8
|
Transaction as StellarSdkTransaction,
|
|
6
9
|
xdr,
|
|
7
|
-
} from
|
|
8
|
-
import { getRecipientAccount, loadAccount } from
|
|
9
|
-
import { StellarAssetRequired, StellarMuxedAccountNotExist } from
|
|
10
|
+
} from '@stellar/stellar-sdk'
|
|
11
|
+
import { getRecipientAccount, loadAccount } from '../network'
|
|
12
|
+
import { StellarAssetRequired, StellarMuxedAccountNotExist } from '../types'
|
|
10
13
|
import {
|
|
11
14
|
buildChangeTrustOperation,
|
|
12
15
|
buildCreateAccountOperation,
|
|
13
16
|
buildPaymentOperation,
|
|
14
17
|
buildTransactionBuilder,
|
|
15
|
-
} from
|
|
18
|
+
} from './sdkWrapper'
|
|
16
19
|
|
|
17
20
|
export async function craftTransaction(
|
|
18
21
|
account: {
|
|
19
|
-
address: string
|
|
22
|
+
address: string
|
|
20
23
|
},
|
|
21
24
|
transaction: {
|
|
22
|
-
type: string
|
|
23
|
-
recipient: string
|
|
24
|
-
amount: bigint
|
|
25
|
-
fee: bigint
|
|
26
|
-
assetCode?: string | undefined
|
|
27
|
-
assetIssuer?: string | undefined
|
|
28
|
-
memoType?: string | null | undefined
|
|
29
|
-
memoValue?: string | null | undefined
|
|
30
|
-
}
|
|
25
|
+
type: string
|
|
26
|
+
recipient: string
|
|
27
|
+
amount: bigint
|
|
28
|
+
fee: bigint
|
|
29
|
+
assetCode?: string | undefined
|
|
30
|
+
assetIssuer?: string | undefined
|
|
31
|
+
memoType?: string | null | undefined
|
|
32
|
+
memoValue?: string | null | undefined
|
|
33
|
+
}
|
|
31
34
|
): Promise<{ transaction: StellarSdkTransaction; xdr: string; signatureBase: string }> {
|
|
32
|
-
const { amount, recipient, fee, memoType, memoValue, type, assetCode, assetIssuer } = transaction
|
|
33
|
-
const source = await loadAccount(account.address)
|
|
35
|
+
const { amount, recipient, fee, memoType, memoValue, type, assetCode, assetIssuer } = transaction
|
|
36
|
+
const source = await loadAccount(account.address)
|
|
34
37
|
|
|
35
38
|
if (!source) {
|
|
36
|
-
throw new NetworkDown()
|
|
39
|
+
throw new NetworkDown()
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
const transactionBuilder = buildTransactionBuilder(source, fee)
|
|
40
|
-
let operation: xdr.Operation<StellarSdkOperation.ChangeTrust> | null = null
|
|
42
|
+
const transactionBuilder = buildTransactionBuilder(source, fee)
|
|
43
|
+
let operation: xdr.Operation<StellarSdkOperation.ChangeTrust> | null = null
|
|
41
44
|
|
|
42
|
-
if (type ===
|
|
45
|
+
if (type === 'changeTrust') {
|
|
43
46
|
if (!assetCode || !assetIssuer) {
|
|
44
|
-
throw new StellarAssetRequired(
|
|
47
|
+
throw new StellarAssetRequired('')
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
operation = buildChangeTrustOperation(assetCode, assetIssuer)
|
|
50
|
+
operation = buildChangeTrustOperation(assetCode, assetIssuer)
|
|
48
51
|
} else {
|
|
49
52
|
// Payment
|
|
50
53
|
const recipientAccount = await getRecipientAccount({
|
|
51
54
|
recipient,
|
|
52
|
-
})
|
|
55
|
+
})
|
|
53
56
|
|
|
54
57
|
if (recipientAccount?.id) {
|
|
55
58
|
operation = buildPaymentOperation({
|
|
@@ -57,46 +60,46 @@ export async function craftTransaction(
|
|
|
57
60
|
amount,
|
|
58
61
|
assetCode,
|
|
59
62
|
assetIssuer,
|
|
60
|
-
})
|
|
63
|
+
})
|
|
61
64
|
} else {
|
|
62
65
|
if (recipientAccount?.isMuxedAccount) {
|
|
63
|
-
throw new StellarMuxedAccountNotExist(
|
|
66
|
+
throw new StellarMuxedAccountNotExist('')
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
operation = buildCreateAccountOperation(recipient, amount)
|
|
69
|
+
operation = buildCreateAccountOperation(recipient, amount)
|
|
67
70
|
}
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
transactionBuilder.addOperation(operation)
|
|
73
|
+
transactionBuilder.addOperation(operation)
|
|
71
74
|
|
|
72
|
-
const memo = buildMemo(memoType, memoValue)
|
|
75
|
+
const memo = buildMemo(memoType, memoValue)
|
|
73
76
|
if (memo) {
|
|
74
|
-
transactionBuilder.addMemo(memo)
|
|
77
|
+
transactionBuilder.addMemo(memo)
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
const craftedTransaction = transactionBuilder.setTimeout(0).build()
|
|
80
|
+
const craftedTransaction = transactionBuilder.setTimeout(0).build()
|
|
78
81
|
return {
|
|
79
82
|
transaction: craftedTransaction,
|
|
80
83
|
xdr: craftedTransaction.toXDR(),
|
|
81
|
-
signatureBase: craftedTransaction.signatureBase().toString(
|
|
82
|
-
}
|
|
84
|
+
signatureBase: craftedTransaction.signatureBase().toString('base64'),
|
|
85
|
+
}
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
function buildMemo(
|
|
86
89
|
memoType?: string | null | undefined,
|
|
87
|
-
memoValue?: string | null | undefined
|
|
90
|
+
memoValue?: string | null | undefined
|
|
88
91
|
): Memo | null {
|
|
89
92
|
if (memoType && memoValue) {
|
|
90
93
|
switch (memoType) {
|
|
91
|
-
case
|
|
92
|
-
return Memo.text(memoValue)
|
|
93
|
-
case
|
|
94
|
-
return Memo.id(memoValue)
|
|
95
|
-
case
|
|
96
|
-
return Memo.hash(memoValue)
|
|
97
|
-
case
|
|
98
|
-
return Memo.return(memoValue)
|
|
94
|
+
case 'MEMO_TEXT':
|
|
95
|
+
return Memo.text(memoValue)
|
|
96
|
+
case 'MEMO_ID':
|
|
97
|
+
return Memo.id(memoValue)
|
|
98
|
+
case 'MEMO_HASH':
|
|
99
|
+
return Memo.hash(memoValue)
|
|
100
|
+
case 'MEMO_RETURN':
|
|
101
|
+
return Memo.return(memoValue)
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
|
-
return null
|
|
104
|
+
return null
|
|
102
105
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { fetchBaseFee } from '../network'
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* Estimate the fees for one transaction
|
|
5
8
|
* @see {@link https://developers.stellar.org/docs/learn/fundamentals/fees-resource-limits-metering#inclusion-fee}
|
|
6
9
|
*/
|
|
7
10
|
export async function estimateFees(): Promise<bigint> {
|
|
8
|
-
const baseFee = await fetchBaseFee()
|
|
9
|
-
return BigInt(baseFee.recommendedFee)
|
|
11
|
+
const baseFee = await fetchBaseFee()
|
|
12
|
+
return BigInt(baseFee.recommendedFee)
|
|
10
13
|
}
|
|
@@ -1,61 +1,64 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
import { fetchAccount } from '../network'
|
|
5
|
+
import { getBalance } from './getBalance'
|
|
6
|
+
|
|
7
|
+
jest.mock('../network', () => ({
|
|
5
8
|
fetchAccount: jest.fn(),
|
|
6
|
-
}))
|
|
9
|
+
}))
|
|
7
10
|
|
|
8
|
-
describe(
|
|
9
|
-
it(
|
|
10
|
-
(fetchAccount as jest.Mock).mockResolvedValue({
|
|
11
|
+
describe('getBalance', () => {
|
|
12
|
+
it('returns native balance when no assets are present', async () => {
|
|
13
|
+
;(fetchAccount as jest.Mock).mockResolvedValue({
|
|
11
14
|
balance: 100000000,
|
|
12
15
|
spendableBalance: 99999990,
|
|
13
16
|
assets: [],
|
|
14
|
-
})
|
|
17
|
+
})
|
|
15
18
|
|
|
16
|
-
const result = await getBalance(
|
|
19
|
+
const result = await getBalance('test-address')
|
|
17
20
|
|
|
18
21
|
expect(result).toEqual([
|
|
19
22
|
{
|
|
20
23
|
value: BigInt(100000000),
|
|
21
|
-
asset: { type:
|
|
24
|
+
asset: { type: 'native' },
|
|
22
25
|
locked: BigInt(10),
|
|
23
26
|
},
|
|
24
|
-
])
|
|
25
|
-
})
|
|
27
|
+
])
|
|
28
|
+
})
|
|
26
29
|
|
|
27
|
-
it(
|
|
28
|
-
(fetchAccount as jest.Mock).mockResolvedValue({
|
|
30
|
+
it('returns native and asset balances when assets are present', async () => {
|
|
31
|
+
;(fetchAccount as jest.Mock).mockResolvedValue({
|
|
29
32
|
balance: 100000000,
|
|
30
33
|
spendableBalance: 99999990,
|
|
31
34
|
assets: [
|
|
32
35
|
{
|
|
33
|
-
asset_type:
|
|
34
|
-
asset_code:
|
|
35
|
-
asset_issuer:
|
|
36
|
-
balance:
|
|
36
|
+
asset_type: 'credit_alphanum4',
|
|
37
|
+
asset_code: 'USDC',
|
|
38
|
+
asset_issuer: 'GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN',
|
|
39
|
+
balance: '50.1234567',
|
|
37
40
|
},
|
|
38
41
|
],
|
|
39
|
-
})
|
|
42
|
+
})
|
|
40
43
|
|
|
41
|
-
const result = await getBalance(
|
|
44
|
+
const result = await getBalance('test-address')
|
|
42
45
|
|
|
43
46
|
expect(result).toEqual([
|
|
44
47
|
{
|
|
45
48
|
value: BigInt(100000000),
|
|
46
|
-
asset: { type:
|
|
49
|
+
asset: { type: 'native' },
|
|
47
50
|
locked: BigInt(10),
|
|
48
51
|
},
|
|
49
52
|
{
|
|
50
53
|
value: BigInt(501234567),
|
|
51
54
|
asset: {
|
|
52
|
-
type:
|
|
53
|
-
assetReference:
|
|
54
|
-
assetOwner:
|
|
55
|
+
type: 'credit_alphanum4',
|
|
56
|
+
assetReference: 'USDC',
|
|
57
|
+
assetOwner: 'GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN',
|
|
55
58
|
},
|
|
56
59
|
},
|
|
57
|
-
])
|
|
60
|
+
])
|
|
58
61
|
|
|
59
|
-
expect(fetchAccount).toHaveBeenCalledWith(
|
|
60
|
-
})
|
|
61
|
-
})
|
|
62
|
+
expect(fetchAccount).toHaveBeenCalledWith('test-address')
|
|
63
|
+
})
|
|
64
|
+
})
|