@ledgerhq/coin-tezos 9.0.0 → 10.1.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/CHANGELOG.md +38 -0
- package/jest.config.js +1 -13
- package/lib/api/index.d.ts +3 -2
- package/lib/api/index.d.ts.map +1 -1
- package/lib/api/index.js +1 -0
- package/lib/api/index.js.map +1 -1
- package/lib/api/types.d.ts +5 -16
- package/lib/api/types.d.ts.map +1 -1
- package/lib/logic/validateIntent.d.ts.map +1 -1
- package/lib/logic/validateIntent.js +38 -3
- package/lib/logic/validateIntent.js.map +1 -1
- package/lib-es/api/index.d.ts +3 -2
- package/lib-es/api/index.d.ts.map +1 -1
- package/lib-es/api/index.js +2 -1
- package/lib-es/api/index.js.map +1 -1
- package/lib-es/api/types.d.ts +5 -16
- package/lib-es/api/types.d.ts.map +1 -1
- package/lib-es/logic/validateIntent.d.ts.map +1 -1
- package/lib-es/logic/validateIntent.js +38 -3
- package/lib-es/logic/validateIntent.js.map +1 -1
- package/package.json +2 -3
- package/src/api/index-mainnet.integ.test.ts +1 -2
- package/src/api/index.integ.test.ts +1 -2
- package/src/api/index.test.ts +7 -5
- package/src/api/index.ts +10 -4
- package/src/api/types.ts +5 -17
- package/src/logic/getBalance.test.ts +118 -140
- package/src/logic/validateIntent.test.ts +281 -0
- package/src/logic/validateIntent.ts +40 -3
- package/src/network/bakers.integ.test.ts +4 -11
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import type { TezosCoinConfig, TezosConfig, TezosContext } from '../config'
|
|
5
|
-
import type { TezosApi } from './types'
|
|
6
5
|
import { localForger } from '@taquito/local-forging'
|
|
7
6
|
import { OpKind } from '@taquito/rpc'
|
|
8
7
|
import { createTzktApi } from '../network/tzkt'
|
|
@@ -55,7 +54,7 @@ type DecodedOperation = {
|
|
|
55
54
|
* Use this test suite in last resort
|
|
56
55
|
*/
|
|
57
56
|
describe('Tezos Api - Mainnet', () => {
|
|
58
|
-
const module
|
|
57
|
+
const module = createApi()
|
|
59
58
|
const context: TezosContext = {
|
|
60
59
|
config: async () => defaultConfig as TezosCoinConfig,
|
|
61
60
|
logger: () => undefined,
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import type { TezosCoinConfig, TezosConfig, TezosContext } from '../config'
|
|
5
|
-
import type { TezosApi } from './types'
|
|
6
5
|
import type { SendTransactionIntent } from '@ledgerhq/coin-module-framework/api/types'
|
|
7
6
|
import { localForger } from '@taquito/local-forging'
|
|
8
7
|
import { createApi } from '.'
|
|
@@ -26,7 +25,7 @@ const defaultConfig: TezosConfig = {
|
|
|
26
25
|
* https://api.shadownet.tzkt.io
|
|
27
26
|
*/
|
|
28
27
|
describe('Tezos Api', () => {
|
|
29
|
-
const module
|
|
28
|
+
const module = createApi()
|
|
30
29
|
const context: TezosContext = {
|
|
31
30
|
config: async () => defaultConfig as TezosCoinConfig,
|
|
32
31
|
logger: () => undefined,
|
package/src/api/index.test.ts
CHANGED
|
@@ -995,6 +995,8 @@ describe('getBalance', () => {
|
|
|
995
995
|
})
|
|
996
996
|
|
|
997
997
|
describe('getAccountInfo', () => {
|
|
998
|
+
// `getAccountInfo` is optional on the generic CoinModuleApi (ADR-045 / LIVE-33431); coin-tezos
|
|
999
|
+
// always implements it, so the non-null assertions below are safe.
|
|
998
1000
|
afterEach(() => {
|
|
999
1001
|
mockGetAccountByAddress.mockClear()
|
|
1000
1002
|
})
|
|
@@ -1013,7 +1015,7 @@ describe('getAccountInfo', () => {
|
|
|
1013
1015
|
firstActivityTime: '2021-01-01T00:00:00Z',
|
|
1014
1016
|
} as APIAccount)
|
|
1015
1017
|
|
|
1016
|
-
await expect(api.getAccountInfo(context, 'tz1test')).resolves.toEqual({
|
|
1018
|
+
await expect(api.getAccountInfo!(context, 'tz1test')).resolves.toEqual({
|
|
1017
1019
|
type: 'tezos',
|
|
1018
1020
|
revealed: true,
|
|
1019
1021
|
})
|
|
@@ -1034,7 +1036,7 @@ describe('getAccountInfo', () => {
|
|
|
1034
1036
|
firstActivityTime: '2021-01-01T00:00:00Z',
|
|
1035
1037
|
} as APIAccount)
|
|
1036
1038
|
|
|
1037
|
-
await expect(api.getAccountInfo(context, 'tz2unrevealed')).resolves.toEqual({
|
|
1039
|
+
await expect(api.getAccountInfo!(context, 'tz2unrevealed')).resolves.toEqual({
|
|
1038
1040
|
type: 'tezos',
|
|
1039
1041
|
revealed: false,
|
|
1040
1042
|
})
|
|
@@ -1054,7 +1056,7 @@ describe('getAccountInfo', () => {
|
|
|
1054
1056
|
} as APIAccount)
|
|
1055
1057
|
|
|
1056
1058
|
await expect(
|
|
1057
|
-
api.getAccountInfo(context, 'tz3RDC3Jdn4j15J7bBHZd29EUee9gVB1CxD9')
|
|
1059
|
+
api.getAccountInfo!(context, 'tz3RDC3Jdn4j15J7bBHZd29EUee9gVB1CxD9')
|
|
1058
1060
|
).resolves.toEqual({
|
|
1059
1061
|
type: 'tezos',
|
|
1060
1062
|
revealed: true,
|
|
@@ -1069,7 +1071,7 @@ describe('getAccountInfo', () => {
|
|
|
1069
1071
|
counter: 0,
|
|
1070
1072
|
} as APIAccount)
|
|
1071
1073
|
|
|
1072
|
-
await expect(api.getAccountInfo(context, 'tz1empty')).resolves.toEqual({
|
|
1074
|
+
await expect(api.getAccountInfo!(context, 'tz1empty')).resolves.toEqual({
|
|
1073
1075
|
type: 'tezos',
|
|
1074
1076
|
revealed: false,
|
|
1075
1077
|
})
|
|
@@ -1079,6 +1081,6 @@ describe('getAccountInfo', () => {
|
|
|
1079
1081
|
it('propagates errors from getAccountByAddress', async () => {
|
|
1080
1082
|
mockGetAccountByAddress.mockRejectedValueOnce(new Error('boom'))
|
|
1081
1083
|
|
|
1082
|
-
await expect(api.getAccountInfo(context, 'tz1boom')).rejects.toThrow('boom')
|
|
1084
|
+
await expect(api.getAccountInfo!(context, 'tz1boom')).rejects.toThrow('boom')
|
|
1083
1085
|
})
|
|
1084
1086
|
})
|
package/src/api/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import type { TezosContext } from '../config'
|
|
4
|
+
import type { TezosCoinConfig, TezosContext } from '../config'
|
|
5
5
|
import type { CoreAccountInfo, CoreTransactionInfo, EstimatedFees } from '../logic/estimateFees'
|
|
6
6
|
import type { TezosOperationMode } from '../types/model'
|
|
7
|
-
import type { TezosAccountInfo,
|
|
7
|
+
import type { TezosAccountInfo, TezosFeeEstimation } from './types'
|
|
8
8
|
import type {
|
|
9
9
|
CraftedTransaction,
|
|
10
10
|
Cursor,
|
|
@@ -15,11 +15,16 @@ import type {
|
|
|
15
15
|
} from '@ledgerhq/coin-module-framework/api/index'
|
|
16
16
|
import type {
|
|
17
17
|
BalanceOptions,
|
|
18
|
+
CoinModuleApi,
|
|
18
19
|
FeeEstimation,
|
|
19
20
|
TransactionIntent,
|
|
20
21
|
} from '@ledgerhq/coin-module-framework/api/types'
|
|
21
22
|
import { rejectBalanceOptions } from '@ledgerhq/coin-module-framework/api/getBalance/rejectBalanceOptions'
|
|
22
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
IncorrectTypeError,
|
|
25
|
+
notSupported,
|
|
26
|
+
type Operation,
|
|
27
|
+
} from '@ledgerhq/coin-module-framework/api/index'
|
|
23
28
|
import { craftTransactionData } from '@ledgerhq/coin-module-framework/logic/craftTransactionData'
|
|
24
29
|
import { log } from '@ledgerhq/logs'
|
|
25
30
|
import { getRevealFee } from '@taquito/taquito'
|
|
@@ -52,7 +57,7 @@ import {
|
|
|
52
57
|
} from '../utils'
|
|
53
58
|
|
|
54
59
|
// The caller builds the {@link TezosContext} (config + logger) and passes it to each method (ADR-019).
|
|
55
|
-
export function createApi():
|
|
60
|
+
export function createApi(): CoinModuleApi<TezosCoinConfig> {
|
|
56
61
|
return {
|
|
57
62
|
broadcast: (context, tx) => broadcast(context, tx),
|
|
58
63
|
async call() {
|
|
@@ -96,6 +101,7 @@ export function createApi(): TezosApi {
|
|
|
96
101
|
getRewards(_context, _address: string, _options?: { cursor?: Cursor }): Promise<Page<Reward>> {
|
|
97
102
|
throw new Error('getRewards is not supported')
|
|
98
103
|
},
|
|
104
|
+
register: notSupported('register'),
|
|
99
105
|
getValidators(_context, _options?: { cursor?: Cursor }): Promise<Page<Validator>> {
|
|
100
106
|
throw new Error('getValidators is not supported')
|
|
101
107
|
},
|
package/src/api/types.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import type {
|
|
5
|
-
import type { CoinModuleApi, FeeEstimation } from '@ledgerhq/coin-module-framework/api/types'
|
|
4
|
+
import type { FeeEstimation } from '@ledgerhq/coin-module-framework/api/types'
|
|
6
5
|
|
|
7
6
|
export type TezosFeeParameters = {
|
|
8
7
|
gasLimit: bigint
|
|
@@ -19,20 +18,9 @@ export type TezosSender = { address: string; xpub?: string }
|
|
|
19
18
|
/**
|
|
20
19
|
* Network-specific account metadata for Tezos (ADR-045 shape).
|
|
21
20
|
* `revealed` is `true` once the account has published its public key on-chain.
|
|
22
|
-
*/
|
|
23
|
-
export type TezosAccountInfo = { type: 'tezos'; revealed: boolean }
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* coin-tezos public API surface: the generic {@link CoinModuleApi} plus `getAccountInfo`,
|
|
27
|
-
* which exposes the account's on-chain reveal state.
|
|
28
21
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* shape
|
|
32
|
-
* ships its own `getAccountInfo`, migrating means dropping this local override entirely (the two
|
|
33
|
-
* signatures would otherwise intersect into an incompatible-return-type collision) and letting the
|
|
34
|
-
* implementation satisfy the generic method directly.
|
|
22
|
+
* Concrete return type of the module's `getAccountInfo`. The framework's generic
|
|
23
|
+
* `CoinModuleApi.getAccountInfo` (added by ADR-045 / LIVE-33431) widens to the open
|
|
24
|
+
* `AccountInfo` shape; this alias keeps the reveal state strongly typed at the implementation.
|
|
35
25
|
*/
|
|
36
|
-
export type
|
|
37
|
-
getAccountInfo: (context: TezosContext, address: string) => Promise<TezosAccountInfo>
|
|
38
|
-
}
|
|
26
|
+
export type TezosAccountInfo = { type: 'tezos'; revealed: boolean }
|
|
@@ -3,10 +3,27 @@
|
|
|
3
3
|
|
|
4
4
|
import type { TezosCoinConfig, TezosContext } from '../config'
|
|
5
5
|
import type { APITokenBalance } from '../network/types'
|
|
6
|
-
import { HttpResponse, http } from 'msw'
|
|
7
|
-
import { setupServer } from 'msw/node'
|
|
8
6
|
import { getBalance } from './getBalance'
|
|
9
7
|
|
|
8
|
+
const mockedGetAccountByAddress = jest.fn()
|
|
9
|
+
const mockedGetTokensBalances = jest.fn()
|
|
10
|
+
const mockedGetUnstakeRequests = jest.fn()
|
|
11
|
+
|
|
12
|
+
jest.mock('../network/tzkt', () => {
|
|
13
|
+
const originalModule = jest.requireActual('../network/tzkt')
|
|
14
|
+
return {
|
|
15
|
+
...originalModule,
|
|
16
|
+
createTzktApi: () => {
|
|
17
|
+
return {
|
|
18
|
+
...originalModule.api,
|
|
19
|
+
getAccountByAddress: mockedGetAccountByAddress,
|
|
20
|
+
getTokensBalances: mockedGetTokensBalances,
|
|
21
|
+
getUnstakeRequests: mockedGetUnstakeRequests,
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
10
27
|
const mockTokensBalancesWithAllInfo: APITokenBalance[] = [
|
|
11
28
|
{
|
|
12
29
|
id: 2398642347442177,
|
|
@@ -51,14 +68,8 @@ const mockTokensBalancesWithMissingInfo: APITokenBalance[] = [
|
|
|
51
68
|
]
|
|
52
69
|
|
|
53
70
|
describe('getBalance', () => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
beforeAll(() => {
|
|
57
|
-
mockServer.listen({ onUnhandledRequest: 'error' })
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
afterAll(() => {
|
|
61
|
-
mockServer.close()
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
jest.resetAllMocks()
|
|
62
73
|
})
|
|
63
74
|
|
|
64
75
|
const context: TezosContext = {
|
|
@@ -70,35 +81,31 @@ describe('getBalance', () => {
|
|
|
70
81
|
logger: () => undefined,
|
|
71
82
|
}
|
|
72
83
|
it('gets the balance of a Tezos account', async () => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
http.get('http://tezos.explorer.com/v1/accounts/tz1TzrmTBSuiVHV2VfMnGRMYvTEPCP42oMMM', () =>
|
|
81
|
-
HttpResponse.json({
|
|
84
|
+
mockedGetAccountByAddress.mockImplementation((address: string) => {
|
|
85
|
+
if (address === 'tz1WvvbEGpBXGeTVbLiR6DYBe1izmgiYuZbq') {
|
|
86
|
+
return { type: 'empty' }
|
|
87
|
+
} else if (address === 'tz1TzrmTBSuiVHV2VfMnGRMYvTEPCP42oSM8') {
|
|
88
|
+
return { type: 'user', balance: 25 }
|
|
89
|
+
} else if (address === 'tz1TzrmTBSuiVHV2VfMnGRMYvTEPCP42oMMM') {
|
|
90
|
+
return {
|
|
82
91
|
type: 'user',
|
|
83
92
|
balance: 15,
|
|
84
93
|
delegate: { address: 'tz1TzrmTBSuiVHV2VfMnGRMYvTEPCP42oMMM' },
|
|
85
|
-
})
|
|
86
|
-
),
|
|
87
|
-
http.get('http://tezos.explorer.com/v1/tokens/balances', ({ request }) => {
|
|
88
|
-
const url = new URL(request.url)
|
|
89
|
-
const account = url.searchParams.get('account')
|
|
90
|
-
|
|
91
|
-
// Return a mocked JSON response when the "account"
|
|
92
|
-
// search parameter equals a specific address.
|
|
93
|
-
if (account === 'tz1TzrmTBSuiVHV2VfMnGRMYvTEPCP42oSM8') {
|
|
94
|
-
return HttpResponse.json(mockTokensBalancesWithAllInfo)
|
|
95
|
-
} else if (account === 'tz1TzrmTBSuiVHV2VfMnGRMYvTEPCP42oMMM') {
|
|
96
|
-
return HttpResponse.json(mockTokensBalancesWithMissingInfo)
|
|
97
|
-
} else {
|
|
98
|
-
return HttpResponse.json([])
|
|
99
94
|
}
|
|
100
|
-
}
|
|
101
|
-
|
|
95
|
+
} else {
|
|
96
|
+
throw new Error('address not handled by the test: ' + address)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
mockedGetTokensBalances.mockImplementation((address: string) => {
|
|
101
|
+
if (address === 'tz1TzrmTBSuiVHV2VfMnGRMYvTEPCP42oSM8') {
|
|
102
|
+
return mockTokensBalancesWithAllInfo
|
|
103
|
+
} else if (address === 'tz1TzrmTBSuiVHV2VfMnGRMYvTEPCP42oMMM') {
|
|
104
|
+
return mockTokensBalancesWithMissingInfo
|
|
105
|
+
} else {
|
|
106
|
+
return []
|
|
107
|
+
}
|
|
108
|
+
})
|
|
102
109
|
|
|
103
110
|
expect(await getBalance(context, 'tz1WvvbEGpBXGeTVbLiR6DYBe1izmgiYuZbq')).toEqual([
|
|
104
111
|
{
|
|
@@ -164,15 +171,9 @@ describe('getBalance', () => {
|
|
|
164
171
|
const delegateAddress = 'tz1BakerAddr'
|
|
165
172
|
|
|
166
173
|
function mockAccount(account: Record<string, unknown>) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
),
|
|
171
|
-
http.get('http://tezos.explorer.com/v1/tokens/balances', () => HttpResponse.json([])),
|
|
172
|
-
http.get('http://tezos.explorer.com/v1/staking/unstake_requests', () =>
|
|
173
|
-
HttpResponse.json([])
|
|
174
|
-
)
|
|
175
|
-
)
|
|
174
|
+
mockedGetAccountByAddress.mockResolvedValue({ type: 'user', ...account })
|
|
175
|
+
mockedGetTokensBalances.mockResolvedValue([])
|
|
176
|
+
mockedGetUnstakeRequests.mockResolvedValue([])
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
it('attaches a delegation Stake when only delegate is set', async () => {
|
|
@@ -271,31 +272,25 @@ describe('getBalance', () => {
|
|
|
271
272
|
})
|
|
272
273
|
|
|
273
274
|
it("attaches an unstaking Stake with state 'deactivating' when a pending request exists", async () => {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
actualAmount: 10,
|
|
294
|
-
},
|
|
295
|
-
])
|
|
296
|
-
),
|
|
297
|
-
http.get('http://tezos.explorer.com/v1/tokens/balances', () => HttpResponse.json([]))
|
|
298
|
-
)
|
|
275
|
+
mockedGetAccountByAddress.mockResolvedValue({
|
|
276
|
+
type: 'user',
|
|
277
|
+
balance: 100,
|
|
278
|
+
stakedBalance: 30,
|
|
279
|
+
unstakedBalance: 10,
|
|
280
|
+
delegate: { address: delegateAddress },
|
|
281
|
+
})
|
|
282
|
+
mockedGetTokensBalances.mockResolvedValue([])
|
|
283
|
+
mockedGetUnstakeRequests.mockResolvedValue([
|
|
284
|
+
{
|
|
285
|
+
id: 77,
|
|
286
|
+
cycle: 100,
|
|
287
|
+
baker: { address: delegateAddress },
|
|
288
|
+
staker: { address },
|
|
289
|
+
firstTime: '2026-05-01T00:00:00Z',
|
|
290
|
+
status: 'pending',
|
|
291
|
+
actualAmount: 10,
|
|
292
|
+
},
|
|
293
|
+
])
|
|
299
294
|
|
|
300
295
|
const result = await getBalance(context, address)
|
|
301
296
|
|
|
@@ -318,39 +313,33 @@ describe('getBalance', () => {
|
|
|
318
313
|
})
|
|
319
314
|
|
|
320
315
|
it('emits per-request unstaking and finalizable Stakes when both statuses are present', async () => {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
actualAmount: 30,
|
|
349
|
-
},
|
|
350
|
-
])
|
|
351
|
-
),
|
|
352
|
-
http.get('http://tezos.explorer.com/v1/tokens/balances', () => HttpResponse.json([]))
|
|
353
|
-
)
|
|
316
|
+
mockedGetAccountByAddress.mockResolvedValue({
|
|
317
|
+
type: 'user',
|
|
318
|
+
balance: 100,
|
|
319
|
+
unstakedBalance: 50,
|
|
320
|
+
delegate: { address: delegateAddress },
|
|
321
|
+
})
|
|
322
|
+
mockedGetUnstakeRequests.mockResolvedValue([
|
|
323
|
+
{
|
|
324
|
+
id: 1,
|
|
325
|
+
cycle: 100,
|
|
326
|
+
baker: { address: delegateAddress },
|
|
327
|
+
staker: { address },
|
|
328
|
+
firstTime: '2026-05-01T00:00:00Z',
|
|
329
|
+
status: 'pending',
|
|
330
|
+
actualAmount: 20,
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
id: 2,
|
|
334
|
+
cycle: 99,
|
|
335
|
+
baker: { address: delegateAddress },
|
|
336
|
+
staker: { address },
|
|
337
|
+
firstTime: '2026-04-25T00:00:00Z',
|
|
338
|
+
status: 'finalizable',
|
|
339
|
+
actualAmount: 30,
|
|
340
|
+
},
|
|
341
|
+
])
|
|
342
|
+
mockedGetTokensBalances.mockResolvedValue([])
|
|
354
343
|
|
|
355
344
|
const result = await getBalance(context, address)
|
|
356
345
|
|
|
@@ -396,30 +385,25 @@ describe('getBalance', () => {
|
|
|
396
385
|
// A delegate has no `delegate` field (it is its own baker), so there is no delegation position —
|
|
397
386
|
// only its self-stake and any unstake requests. The unstakedBalance > 0 exercises the
|
|
398
387
|
// delegate → fetchUnstakeRequests path.
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
actualAmount: 10,
|
|
419
|
-
},
|
|
420
|
-
])
|
|
421
|
-
)
|
|
422
|
-
)
|
|
388
|
+
|
|
389
|
+
mockedGetAccountByAddress.mockResolvedValue({
|
|
390
|
+
type: 'delegate',
|
|
391
|
+
balance: 100,
|
|
392
|
+
stakedBalance: 30,
|
|
393
|
+
unstakedBalance: 10,
|
|
394
|
+
})
|
|
395
|
+
mockedGetTokensBalances.mockResolvedValue([])
|
|
396
|
+
mockedGetUnstakeRequests.mockResolvedValue([
|
|
397
|
+
{
|
|
398
|
+
id: 77,
|
|
399
|
+
cycle: 100,
|
|
400
|
+
baker: { address },
|
|
401
|
+
staker: { address },
|
|
402
|
+
firstTime: '2026-05-01T00:00:00Z',
|
|
403
|
+
status: 'pending',
|
|
404
|
+
actualAmount: 10,
|
|
405
|
+
},
|
|
406
|
+
])
|
|
423
407
|
|
|
424
408
|
expect(await getBalance(context, address)).toEqual([
|
|
425
409
|
{ value: 100n, asset: { type: 'native' }, locked: 40n },
|
|
@@ -461,20 +445,14 @@ describe('getBalance', () => {
|
|
|
461
445
|
})
|
|
462
446
|
|
|
463
447
|
it('still excludes unstaked-frozen funds from spendable when the unstake_requests endpoint fails', async () => {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
),
|
|
473
|
-
http.get('http://tezos.explorer.com/v1/staking/unstake_requests', () =>
|
|
474
|
-
HttpResponse.json({ error: 'internal' }, { status: 500 })
|
|
475
|
-
),
|
|
476
|
-
http.get('http://tezos.explorer.com/v1/tokens/balances', () => HttpResponse.json([]))
|
|
477
|
-
)
|
|
448
|
+
mockedGetAccountByAddress.mockResolvedValue({
|
|
449
|
+
type: 'user',
|
|
450
|
+
balance: 100,
|
|
451
|
+
unstakedBalance: 10,
|
|
452
|
+
delegate: { address: delegateAddress },
|
|
453
|
+
})
|
|
454
|
+
mockedGetUnstakeRequests.mockRejectedValue(new Error('endpoint failure'))
|
|
455
|
+
mockedGetTokensBalances.mockResolvedValue([])
|
|
478
456
|
|
|
479
457
|
const result = await getBalance(context, address)
|
|
480
458
|
|