@ledgerhq/coin-stellar 6.26.0 → 6.26.1
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 +6 -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 +71 -68
- package/lib/network/horizon.js.map +1 -1
- package/lib/network/horizonLedgerErrors.d.ts.map +1 -1
- package/lib/network/horizonLedgerErrors.js +5 -3
- 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 +80 -77
- package/lib-es/network/horizon.js.map +1 -1
- package/lib-es/network/horizonLedgerErrors.d.ts.map +1 -1
- package/lib-es/network/horizonLedgerErrors.js +7 -5
- 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 +165 -162
- 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 +60 -60
- 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 +279 -281
- package/src/network/horizon.unit.test.ts +238 -237
- package/src/network/horizonLedgerErrors.test.ts +81 -78
- package/src/network/horizonLedgerErrors.ts +22 -19
- 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
|
+
})
|