@0xsequence/relayer 2.3.35 → 3.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/CHANGELOG.md +3862 -0
  3. package/LICENSE +0 -17
  4. package/README.md +1 -2
  5. package/dist/index.d.ts +4 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +3 -0
  8. package/dist/preconditions/codec.d.ts +12 -0
  9. package/dist/preconditions/codec.d.ts.map +1 -0
  10. package/dist/preconditions/codec.js +125 -0
  11. package/dist/preconditions/index.d.ts +4 -0
  12. package/dist/preconditions/index.d.ts.map +1 -0
  13. package/dist/preconditions/index.js +3 -0
  14. package/dist/preconditions/selectors.d.ts +7 -0
  15. package/dist/preconditions/selectors.d.ts.map +1 -0
  16. package/dist/preconditions/selectors.js +27 -0
  17. package/dist/preconditions/types.d.ts +70 -0
  18. package/dist/preconditions/types.d.ts.map +1 -0
  19. package/dist/preconditions/types.js +203 -0
  20. package/dist/relayer/index.d.ts +45 -0
  21. package/dist/relayer/index.d.ts.map +1 -0
  22. package/dist/relayer/index.js +3 -0
  23. package/dist/relayer/relayer.d.ts +26 -0
  24. package/dist/relayer/relayer.d.ts.map +1 -0
  25. package/dist/relayer/relayer.js +7 -0
  26. package/dist/relayer/rpc-relayer/index.d.ts +38 -0
  27. package/dist/relayer/rpc-relayer/index.d.ts.map +1 -0
  28. package/dist/relayer/rpc-relayer/index.js +375 -0
  29. package/dist/{declarations/src → relayer}/rpc-relayer/relayer.gen.d.ts +218 -178
  30. package/dist/relayer/rpc-relayer/relayer.gen.d.ts.map +1 -0
  31. package/dist/relayer/rpc-relayer/relayer.gen.js +1246 -0
  32. package/dist/relayer/standard/abi.d.ts +73 -0
  33. package/dist/relayer/standard/abi.d.ts.map +1 -0
  34. package/dist/relayer/standard/abi.js +10 -0
  35. package/dist/relayer/standard/eip6963.d.ts +31 -0
  36. package/dist/relayer/standard/eip6963.d.ts.map +1 -0
  37. package/dist/relayer/standard/eip6963.js +51 -0
  38. package/dist/relayer/standard/index.d.ts +5 -0
  39. package/dist/relayer/standard/index.d.ts.map +1 -0
  40. package/dist/relayer/standard/index.js +4 -0
  41. package/dist/relayer/standard/local.d.ts +60 -0
  42. package/dist/relayer/standard/local.d.ts.map +1 -0
  43. package/dist/relayer/standard/local.js +285 -0
  44. package/dist/relayer/standard/pk-relayer.d.ts +28 -0
  45. package/dist/relayer/standard/pk-relayer.d.ts.map +1 -0
  46. package/dist/relayer/standard/pk-relayer.js +112 -0
  47. package/dist/relayer/standard/sequence.d.ts +27 -0
  48. package/dist/relayer/standard/sequence.d.ts.map +1 -0
  49. package/dist/relayer/standard/sequence.js +84 -0
  50. package/package.json +28 -25
  51. package/src/index.ts +3 -111
  52. package/src/preconditions/codec.ts +190 -0
  53. package/src/preconditions/index.ts +3 -0
  54. package/src/preconditions/selectors.ts +38 -0
  55. package/src/preconditions/types.ts +201 -0
  56. package/src/relayer/index.ts +60 -0
  57. package/src/relayer/relayer.ts +37 -0
  58. package/src/relayer/rpc-relayer/index.ts +449 -0
  59. package/src/relayer/rpc-relayer/relayer.gen.ts +2268 -0
  60. package/src/relayer/standard/abi.ts +13 -0
  61. package/src/relayer/standard/eip6963.ts +74 -0
  62. package/src/relayer/standard/index.ts +4 -0
  63. package/src/relayer/standard/local.ts +353 -0
  64. package/src/relayer/standard/pk-relayer.ts +138 -0
  65. package/src/relayer/standard/sequence.ts +110 -0
  66. package/test/preconditions/codec.test.ts +531 -0
  67. package/test/preconditions/preconditions.test.ts +283 -0
  68. package/test/preconditions/selectors.test.ts +415 -0
  69. package/test/preconditions/types.test.ts +443 -0
  70. package/test/relayer/relayer.test.ts +355 -0
  71. package/tsconfig.json +10 -0
  72. package/dist/0xsequence-relayer.cjs.d.ts +0 -2
  73. package/dist/0xsequence-relayer.cjs.dev.js +0 -1626
  74. package/dist/0xsequence-relayer.cjs.js +0 -7
  75. package/dist/0xsequence-relayer.cjs.prod.js +0 -1626
  76. package/dist/0xsequence-relayer.esm.js +0 -1613
  77. package/dist/declarations/src/index.d.ts +0 -42
  78. package/dist/declarations/src/local-relayer.d.ts +0 -35
  79. package/dist/declarations/src/provider-relayer.d.ts +0 -47
  80. package/dist/declarations/src/rpc-relayer/index.d.ts +0 -72
  81. package/src/local-relayer.ts +0 -125
  82. package/src/provider-relayer.ts +0 -284
  83. package/src/rpc-relayer/index.ts +0 -380
  84. package/src/rpc-relayer/relayer.gen.ts +0 -1900
@@ -0,0 +1,283 @@
1
+ import { Address, Provider, RpcTransport, Secp256k1 } from 'ox'
2
+ import { describe, expect, it, vi } from 'vitest'
3
+ import {
4
+ Erc1155ApprovalPrecondition,
5
+ Erc1155BalancePrecondition,
6
+ Erc20ApprovalPrecondition,
7
+ Erc20BalancePrecondition,
8
+ Erc721ApprovalPrecondition,
9
+ Erc721OwnershipPrecondition,
10
+ NativeBalancePrecondition,
11
+ } from '../../src/preconditions/types.js'
12
+ import { LocalRelayer } from '../../src/standard/local.js'
13
+ import { CAN_RUN_LIVE, RPC_URL } from '../../../../wallet/core/test/constants'
14
+ import { Network } from '@0xsequence/wallet-primitives'
15
+
16
+ const ERC20_IMPLICIT_MINT_CONTRACT = '0x041E0CDC028050519C8e6485B2d9840caf63773F'
17
+
18
+ function randomAddress(): Address.Address {
19
+ return Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() }))
20
+ }
21
+
22
+ describe('Preconditions', () => {
23
+ const getProvider = async (): Promise<{ provider: Provider.Provider; chainId: number }> => {
24
+ let provider: Provider.Provider
25
+ let chainId: number = Network.ChainId.MAINNET
26
+ if (CAN_RUN_LIVE) {
27
+ provider = Provider.from(RpcTransport.fromHttp(RPC_URL!!))
28
+ chainId = Number(await provider.request({ method: 'eth_chainId' }))
29
+ } else {
30
+ provider = {
31
+ request: vi.fn(),
32
+ on: vi.fn(),
33
+ removeListener: vi.fn(),
34
+ call: vi.fn(),
35
+ sendTransaction: vi.fn(),
36
+ getBalance: vi.fn(),
37
+ } as unknown as Provider.Provider
38
+ }
39
+
40
+ return { provider: provider!, chainId }
41
+ }
42
+
43
+ const testWalletAddress = randomAddress()
44
+
45
+ const requireContractDeployed = async (provider: Provider.Provider, contract: Address.Address) => {
46
+ const code = await provider.request({ method: 'eth_getCode', params: [contract, 'latest'] })
47
+ if (code === '0x') {
48
+ throw new Error(`Contract ${contract} not deployed`)
49
+ }
50
+ }
51
+
52
+ it('should create and check native balance precondition', async () => {
53
+ const { provider, chainId } = await getProvider()
54
+ const relayer = new LocalRelayer(provider as any)
55
+
56
+ const precondition = new NativeBalancePrecondition(
57
+ testWalletAddress,
58
+ 1000000000000000000n, // 1 ETH min
59
+ 2000000000000000000n, // 2 ETH max
60
+ )
61
+
62
+ const intentPrecondition = {
63
+ type: precondition.type(),
64
+ chainId: chainId.toString(),
65
+ data: JSON.stringify({
66
+ address: precondition.address.toString(),
67
+ min: precondition.min?.toString(),
68
+ max: precondition.max?.toString(),
69
+ }),
70
+ }
71
+
72
+ if (!CAN_RUN_LIVE) {
73
+ // Mock the balance check
74
+ ;(provider as any).request.mockResolvedValue('0x16345785d8a0000') // 1.5 ETH in hex
75
+ }
76
+
77
+ const isValid = await relayer.checkPrecondition(intentPrecondition)
78
+ expect(isValid).toBe(true)
79
+ })
80
+
81
+ it('should create and check ERC20 balance precondition', async () => {
82
+ const { provider, chainId } = await getProvider()
83
+ const relayer = new LocalRelayer(provider as any)
84
+ await requireContractDeployed(provider, ERC20_IMPLICIT_MINT_CONTRACT)
85
+
86
+ const precondition = new Erc20BalancePrecondition(
87
+ testWalletAddress,
88
+ ERC20_IMPLICIT_MINT_CONTRACT,
89
+ 1000000n, // 1 token min
90
+ 2000000n, // 2 tokens max
91
+ )
92
+
93
+ const intentPrecondition = {
94
+ type: precondition.type(),
95
+ chainId: chainId.toString(),
96
+ data: JSON.stringify({
97
+ address: precondition.address.toString(),
98
+ token: precondition.token.toString(),
99
+ min: precondition.min?.toString(),
100
+ max: precondition.max?.toString(),
101
+ }),
102
+ }
103
+
104
+ if (!CAN_RUN_LIVE) {
105
+ // Mock the balanceOf call
106
+ ;(provider as any).call.mockResolvedValue('0x1e8480') // 1.5 tokens in hex
107
+ }
108
+
109
+ const isValid = await relayer.checkPrecondition(intentPrecondition)
110
+ expect(isValid).toBe(true)
111
+ })
112
+
113
+ it('should create and check ERC20 approval precondition', async () => {
114
+ const { provider, chainId } = await getProvider()
115
+ const relayer = new LocalRelayer(provider as any)
116
+ await requireContractDeployed(provider, ERC20_IMPLICIT_MINT_CONTRACT)
117
+
118
+ const operator = randomAddress()
119
+ const precondition = new Erc20ApprovalPrecondition(
120
+ testWalletAddress,
121
+ ERC20_IMPLICIT_MINT_CONTRACT,
122
+ operator,
123
+ 1000000n, // 1 token min approval
124
+ )
125
+
126
+ const intentPrecondition = {
127
+ type: precondition.type(),
128
+ chainId: chainId.toString(),
129
+ data: JSON.stringify({
130
+ address: precondition.address.toString(),
131
+ token: precondition.token.toString(),
132
+ operator: precondition.operator.toString(),
133
+ min: precondition.min.toString(),
134
+ }),
135
+ }
136
+
137
+ if (!CAN_RUN_LIVE) {
138
+ // Mock the allowance call
139
+ ;(provider as any).call.mockResolvedValue('0x1e8480') // 1.5 tokens in hex
140
+ }
141
+
142
+ const isValid = await relayer.checkPrecondition(intentPrecondition)
143
+ expect(isValid).toBe(true)
144
+ })
145
+
146
+ it('should create and check ERC721 ownership precondition', async () => {
147
+ const { provider, chainId } = await getProvider()
148
+ const relayer = new LocalRelayer(provider as any)
149
+ await requireContractDeployed(provider, ERC20_IMPLICIT_MINT_CONTRACT)
150
+
151
+ const precondition = new Erc721OwnershipPrecondition(
152
+ testWalletAddress,
153
+ ERC20_IMPLICIT_MINT_CONTRACT,
154
+ 1n, // tokenId
155
+ true, // must own
156
+ )
157
+
158
+ const intentPrecondition = {
159
+ type: precondition.type(),
160
+ chainId: chainId.toString(),
161
+ data: JSON.stringify({
162
+ address: precondition.address.toString(),
163
+ token: precondition.token.toString(),
164
+ tokenId: precondition.tokenId.toString(),
165
+ owned: precondition.owned,
166
+ }),
167
+ }
168
+
169
+ if (!CAN_RUN_LIVE) {
170
+ // Mock the ownerOf call
171
+ ;(provider as any).call.mockResolvedValue(
172
+ '0x000000000000000000000000' + testWalletAddress.toString().slice(2).toLowerCase(),
173
+ )
174
+ }
175
+
176
+ const isValid = await relayer.checkPrecondition(intentPrecondition)
177
+ expect(isValid).toBe(true)
178
+ })
179
+
180
+ it('should create and check ERC721 approval precondition', async () => {
181
+ const { provider, chainId } = await getProvider()
182
+ const relayer = new LocalRelayer(provider as any)
183
+ await requireContractDeployed(provider, ERC20_IMPLICIT_MINT_CONTRACT)
184
+
185
+ const operator = randomAddress()
186
+ const precondition = new Erc721ApprovalPrecondition(
187
+ testWalletAddress,
188
+ ERC20_IMPLICIT_MINT_CONTRACT,
189
+ 1n, // tokenId
190
+ operator,
191
+ )
192
+
193
+ const intentPrecondition = {
194
+ type: precondition.type(),
195
+ chainId: chainId.toString(),
196
+ data: JSON.stringify({
197
+ address: precondition.address.toString(),
198
+ token: precondition.token.toString(),
199
+ tokenId: precondition.tokenId.toString(),
200
+ operator: precondition.operator.toString(),
201
+ }),
202
+ }
203
+
204
+ if (!CAN_RUN_LIVE) {
205
+ // Mock the getApproved call
206
+ ;(provider as any).call.mockResolvedValue(
207
+ '0x000000000000000000000000' + operator.toString().slice(2).toLowerCase(),
208
+ )
209
+ }
210
+
211
+ const isValid = await relayer.checkPrecondition(intentPrecondition)
212
+ expect(isValid).toBe(true)
213
+ })
214
+
215
+ it('should create and check ERC1155 balance precondition', async () => {
216
+ const { provider, chainId } = await getProvider()
217
+ const relayer = new LocalRelayer(provider as any)
218
+ await requireContractDeployed(provider, ERC20_IMPLICIT_MINT_CONTRACT)
219
+
220
+ const precondition = new Erc1155BalancePrecondition(
221
+ testWalletAddress,
222
+ ERC20_IMPLICIT_MINT_CONTRACT,
223
+ 1n, // tokenId
224
+ 1000000n, // 1 token min
225
+ 2000000n, // 2 tokens max
226
+ )
227
+
228
+ const intentPrecondition = {
229
+ type: precondition.type(),
230
+ chainId: chainId.toString(),
231
+ data: JSON.stringify({
232
+ address: precondition.address.toString(),
233
+ token: precondition.token.toString(),
234
+ tokenId: precondition.tokenId.toString(),
235
+ min: precondition.min?.toString(),
236
+ max: precondition.max?.toString(),
237
+ }),
238
+ }
239
+
240
+ if (!CAN_RUN_LIVE) {
241
+ // Mock the balanceOf call
242
+ ;(provider as any).call.mockResolvedValue('0x1e8480') // 1.5 tokens in hex
243
+ }
244
+
245
+ const isValid = await relayer.checkPrecondition(intentPrecondition)
246
+ expect(isValid).toBe(true)
247
+ })
248
+
249
+ it('should create and check ERC1155 approval precondition', async () => {
250
+ const { provider, chainId } = await getProvider()
251
+ const relayer = new LocalRelayer(provider as any)
252
+ await requireContractDeployed(provider, ERC20_IMPLICIT_MINT_CONTRACT)
253
+
254
+ const operator = randomAddress()
255
+ const precondition = new Erc1155ApprovalPrecondition(
256
+ testWalletAddress,
257
+ ERC20_IMPLICIT_MINT_CONTRACT,
258
+ 1n, // tokenId
259
+ operator,
260
+ 1000000n, // 1 token min approval
261
+ )
262
+
263
+ const intentPrecondition = {
264
+ type: precondition.type(),
265
+ chainId: chainId.toString(),
266
+ data: JSON.stringify({
267
+ address: precondition.address.toString(),
268
+ token: precondition.token.toString(),
269
+ tokenId: precondition.tokenId.toString(),
270
+ operator: precondition.operator.toString(),
271
+ min: precondition.min.toString(),
272
+ }),
273
+ }
274
+
275
+ if (!CAN_RUN_LIVE) {
276
+ // Mock the isApprovedForAll call
277
+ ;(provider as any).call.mockResolvedValue('0x1') // true
278
+ }
279
+
280
+ const isValid = await relayer.checkPrecondition(intentPrecondition)
281
+ expect(isValid).toBe(true)
282
+ })
283
+ })
@@ -0,0 +1,415 @@
1
+ import { Address } from 'ox'
2
+ import { describe, expect, it } from 'vitest'
3
+
4
+ import {
5
+ extractChainID,
6
+ extractSupportedPreconditions,
7
+ extractNativeBalancePreconditions,
8
+ extractERC20BalancePreconditions,
9
+ } from '../../src/preconditions/selectors.js'
10
+ import { TransactionPrecondition } from '../../src/preconditions/codec.js'
11
+ import {
12
+ NativeBalancePrecondition,
13
+ Erc20BalancePrecondition,
14
+ Erc721OwnershipPrecondition,
15
+ } from '../../src/preconditions/types.js'
16
+ import { Network } from '@0xsequence/wallet-primitives'
17
+
18
+ // Test addresses
19
+ const TEST_ADDRESS = Address.from('0x1234567890123456789012345678901234567890')
20
+ const TOKEN_ADDRESS = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd')
21
+
22
+ describe('Preconditions Selectors', () => {
23
+ describe('extractChainID', () => {
24
+ it('should extract chainID from valid precondition data', () => {
25
+ const precondition: TransactionPrecondition = {
26
+ type: 'native-balance',
27
+ data: JSON.stringify({
28
+ address: TEST_ADDRESS,
29
+ chainID: '1',
30
+ min: '1000000000000000000',
31
+ }),
32
+ }
33
+
34
+ const chainId = extractChainID(precondition)
35
+ expect(chainId).toBe(Network.ChainId.MAINNET)
36
+ })
37
+
38
+ it('should extract large chainID values', () => {
39
+ const precondition: TransactionPrecondition = {
40
+ type: 'native-balance',
41
+ data: JSON.stringify({
42
+ address: TEST_ADDRESS,
43
+ chainID: '42161', // Arbitrum chainID
44
+ }),
45
+ }
46
+
47
+ const chainId = extractChainID(precondition)
48
+ expect(chainId).toBe(Network.ChainId.ARBITRUM)
49
+ })
50
+
51
+ it('should return undefined when chainID is not present', () => {
52
+ const precondition: TransactionPrecondition = {
53
+ type: 'native-balance',
54
+ data: JSON.stringify({
55
+ address: TEST_ADDRESS,
56
+ min: '1000000000000000000',
57
+ }),
58
+ }
59
+
60
+ const chainId = extractChainID(precondition)
61
+ expect(chainId).toBeUndefined()
62
+ })
63
+
64
+ it('should return undefined when chainID is falsy', () => {
65
+ const precondition: TransactionPrecondition = {
66
+ type: 'native-balance',
67
+ data: JSON.stringify({
68
+ address: TEST_ADDRESS,
69
+ chainID: '',
70
+ min: '1000000000000000000',
71
+ }),
72
+ }
73
+
74
+ const chainId = extractChainID(precondition)
75
+ expect(chainId).toBeUndefined()
76
+ })
77
+
78
+ it('should return undefined when chainID is null', () => {
79
+ const precondition: TransactionPrecondition = {
80
+ type: 'native-balance',
81
+ data: JSON.stringify({
82
+ address: TEST_ADDRESS,
83
+ chainID: null,
84
+ min: '1000000000000000000',
85
+ }),
86
+ }
87
+
88
+ const chainId = extractChainID(precondition)
89
+ expect(chainId).toBeUndefined()
90
+ })
91
+
92
+ it('should return undefined for null/undefined precondition', () => {
93
+ expect(extractChainID(null as any)).toBeUndefined()
94
+ expect(extractChainID(undefined as any)).toBeUndefined()
95
+ })
96
+
97
+ it('should return undefined for invalid JSON', () => {
98
+ const precondition: TransactionPrecondition = {
99
+ type: 'native-balance',
100
+ data: 'invalid json',
101
+ }
102
+
103
+ const chainId = extractChainID(precondition)
104
+ expect(chainId).toBeUndefined()
105
+ })
106
+
107
+ it('should handle chainID with value 0', () => {
108
+ const precondition: TransactionPrecondition = {
109
+ type: 'native-balance',
110
+ data: JSON.stringify({
111
+ address: TEST_ADDRESS,
112
+ chainID: '0',
113
+ }),
114
+ }
115
+
116
+ const chainId = extractChainID(precondition)
117
+ expect(chainId).toBe(0)
118
+ })
119
+ })
120
+
121
+ describe('extractSupportedPreconditions', () => {
122
+ it('should extract valid preconditions', () => {
123
+ const intents: TransactionPrecondition[] = [
124
+ {
125
+ type: 'native-balance',
126
+ data: JSON.stringify({
127
+ address: TEST_ADDRESS,
128
+ min: '1000000000000000000',
129
+ }),
130
+ },
131
+ {
132
+ type: 'erc20-balance',
133
+ data: JSON.stringify({
134
+ address: TEST_ADDRESS,
135
+ token: TOKEN_ADDRESS,
136
+ min: '1000000',
137
+ }),
138
+ },
139
+ ]
140
+
141
+ const results = extractSupportedPreconditions(intents)
142
+ expect(results).toHaveLength(2)
143
+ expect(results[0]).toBeInstanceOf(NativeBalancePrecondition)
144
+ expect(results[1]).toBeInstanceOf(Erc20BalancePrecondition)
145
+ })
146
+
147
+ it('should filter out invalid preconditions', () => {
148
+ const intents: TransactionPrecondition[] = [
149
+ {
150
+ type: 'native-balance',
151
+ data: JSON.stringify({
152
+ address: TEST_ADDRESS,
153
+ min: '1000000000000000000',
154
+ }),
155
+ },
156
+ {
157
+ type: 'unknown-type',
158
+ data: JSON.stringify({ address: TEST_ADDRESS }),
159
+ },
160
+ {
161
+ type: 'native-balance',
162
+ data: 'invalid json',
163
+ },
164
+ ]
165
+
166
+ const results = extractSupportedPreconditions(intents)
167
+ expect(results).toHaveLength(1)
168
+ expect(results[0]).toBeInstanceOf(NativeBalancePrecondition)
169
+ })
170
+
171
+ it('should return empty array for null/undefined input', () => {
172
+ expect(extractSupportedPreconditions(null as any)).toEqual([])
173
+ expect(extractSupportedPreconditions(undefined as any)).toEqual([])
174
+ })
175
+
176
+ it('should return empty array for empty input', () => {
177
+ const results = extractSupportedPreconditions([])
178
+ expect(results).toEqual([])
179
+ })
180
+
181
+ it('should handle mixed valid and invalid preconditions', () => {
182
+ const intents: TransactionPrecondition[] = [
183
+ {
184
+ type: 'native-balance',
185
+ data: JSON.stringify({
186
+ address: TEST_ADDRESS,
187
+ min: '1000000000000000000',
188
+ }),
189
+ },
190
+ {
191
+ type: 'erc721-ownership',
192
+ data: JSON.stringify({
193
+ address: TEST_ADDRESS,
194
+ token: TOKEN_ADDRESS,
195
+ tokenId: '123',
196
+ }),
197
+ },
198
+ {
199
+ type: 'invalid-type',
200
+ data: JSON.stringify({ address: TEST_ADDRESS }),
201
+ },
202
+ ]
203
+
204
+ const results = extractSupportedPreconditions(intents)
205
+ expect(results).toHaveLength(2)
206
+ expect(results[0]).toBeInstanceOf(NativeBalancePrecondition)
207
+ expect(results[1]).toBeInstanceOf(Erc721OwnershipPrecondition)
208
+ })
209
+ })
210
+
211
+ describe('extractNativeBalancePreconditions', () => {
212
+ it('should extract only native balance preconditions', () => {
213
+ const intents: TransactionPrecondition[] = [
214
+ {
215
+ type: 'native-balance',
216
+ data: JSON.stringify({
217
+ address: TEST_ADDRESS,
218
+ min: '1000000000000000000',
219
+ }),
220
+ },
221
+ {
222
+ type: 'erc20-balance',
223
+ data: JSON.stringify({
224
+ address: TEST_ADDRESS,
225
+ token: TOKEN_ADDRESS,
226
+ min: '1000000',
227
+ }),
228
+ },
229
+ {
230
+ type: 'native-balance',
231
+ data: JSON.stringify({
232
+ address: TEST_ADDRESS,
233
+ max: '2000000000000000000',
234
+ }),
235
+ },
236
+ ]
237
+
238
+ const results = extractNativeBalancePreconditions(intents)
239
+ expect(results).toHaveLength(2)
240
+ expect(results[0]).toBeInstanceOf(NativeBalancePrecondition)
241
+ expect(results[1]).toBeInstanceOf(NativeBalancePrecondition)
242
+
243
+ // Verify the specific properties
244
+ expect(results[0].min).toBe(1000000000000000000n)
245
+ expect(results[1].max).toBe(2000000000000000000n)
246
+ })
247
+
248
+ it('should return empty array when no native balance preconditions exist', () => {
249
+ const intents: TransactionPrecondition[] = [
250
+ {
251
+ type: 'erc20-balance',
252
+ data: JSON.stringify({
253
+ address: TEST_ADDRESS,
254
+ token: TOKEN_ADDRESS,
255
+ min: '1000000',
256
+ }),
257
+ },
258
+ {
259
+ type: 'erc721-ownership',
260
+ data: JSON.stringify({
261
+ address: TEST_ADDRESS,
262
+ token: TOKEN_ADDRESS,
263
+ tokenId: '123',
264
+ }),
265
+ },
266
+ ]
267
+
268
+ const results = extractNativeBalancePreconditions(intents)
269
+ expect(results).toEqual([])
270
+ })
271
+
272
+ it('should return empty array for null/undefined input', () => {
273
+ expect(extractNativeBalancePreconditions(null as any)).toEqual([])
274
+ expect(extractNativeBalancePreconditions(undefined as any)).toEqual([])
275
+ })
276
+
277
+ it('should return empty array for empty input', () => {
278
+ const results = extractNativeBalancePreconditions([])
279
+ expect(results).toEqual([])
280
+ })
281
+
282
+ it('should filter out invalid native balance preconditions', () => {
283
+ const intents: TransactionPrecondition[] = [
284
+ {
285
+ type: 'native-balance',
286
+ data: JSON.stringify({
287
+ address: TEST_ADDRESS,
288
+ min: '1000000000000000000',
289
+ }),
290
+ },
291
+ {
292
+ type: 'native-balance',
293
+ data: 'invalid json', // This will be filtered out
294
+ },
295
+ {
296
+ type: 'native-balance',
297
+ data: JSON.stringify({
298
+ // Missing address - this will be filtered out
299
+ min: '1000000000000000000',
300
+ }),
301
+ },
302
+ ]
303
+
304
+ const results = extractNativeBalancePreconditions(intents)
305
+ expect(results).toHaveLength(1)
306
+ expect(results[0]).toBeInstanceOf(NativeBalancePrecondition)
307
+ expect(results[0].min).toBe(1000000000000000000n)
308
+ })
309
+ })
310
+
311
+ describe('extractERC20BalancePreconditions', () => {
312
+ it('should extract only ERC20 balance preconditions', () => {
313
+ const intents: TransactionPrecondition[] = [
314
+ {
315
+ type: 'native-balance',
316
+ data: JSON.stringify({
317
+ address: TEST_ADDRESS,
318
+ min: '1000000000000000000',
319
+ }),
320
+ },
321
+ {
322
+ type: 'erc20-balance',
323
+ data: JSON.stringify({
324
+ address: TEST_ADDRESS,
325
+ token: TOKEN_ADDRESS,
326
+ min: '1000000',
327
+ }),
328
+ },
329
+ {
330
+ type: 'erc20-balance',
331
+ data: JSON.stringify({
332
+ address: TEST_ADDRESS,
333
+ token: TOKEN_ADDRESS,
334
+ max: '2000000',
335
+ }),
336
+ },
337
+ ]
338
+
339
+ const results = extractERC20BalancePreconditions(intents)
340
+ expect(results).toHaveLength(2)
341
+ expect(results[0]).toBeInstanceOf(Erc20BalancePrecondition)
342
+ expect(results[1]).toBeInstanceOf(Erc20BalancePrecondition)
343
+
344
+ // Verify the specific properties
345
+ expect(results[0].min).toBe(1000000n)
346
+ expect(results[1].max).toBe(2000000n)
347
+ expect(results[0].token).toBe(TOKEN_ADDRESS)
348
+ expect(results[1].token).toBe(TOKEN_ADDRESS)
349
+ })
350
+
351
+ it('should return empty array when no ERC20 balance preconditions exist', () => {
352
+ const intents: TransactionPrecondition[] = [
353
+ {
354
+ type: 'native-balance',
355
+ data: JSON.stringify({
356
+ address: TEST_ADDRESS,
357
+ min: '1000000000000000000',
358
+ }),
359
+ },
360
+ {
361
+ type: 'erc721-ownership',
362
+ data: JSON.stringify({
363
+ address: TEST_ADDRESS,
364
+ token: TOKEN_ADDRESS,
365
+ tokenId: '123',
366
+ }),
367
+ },
368
+ ]
369
+
370
+ const results = extractERC20BalancePreconditions(intents)
371
+ expect(results).toEqual([])
372
+ })
373
+
374
+ it('should return empty array for null/undefined input', () => {
375
+ expect(extractERC20BalancePreconditions(null as any)).toEqual([])
376
+ expect(extractERC20BalancePreconditions(undefined as any)).toEqual([])
377
+ })
378
+
379
+ it('should return empty array for empty input', () => {
380
+ const results = extractERC20BalancePreconditions([])
381
+ expect(results).toEqual([])
382
+ })
383
+
384
+ it('should filter out invalid ERC20 balance preconditions', () => {
385
+ const intents: TransactionPrecondition[] = [
386
+ {
387
+ type: 'erc20-balance',
388
+ data: JSON.stringify({
389
+ address: TEST_ADDRESS,
390
+ token: TOKEN_ADDRESS,
391
+ min: '1000000',
392
+ }),
393
+ },
394
+ {
395
+ type: 'erc20-balance',
396
+ data: 'invalid json', // This will be filtered out
397
+ },
398
+ {
399
+ type: 'erc20-balance',
400
+ data: JSON.stringify({
401
+ address: TEST_ADDRESS,
402
+ // Missing token address - this will be filtered out
403
+ min: '1000000',
404
+ }),
405
+ },
406
+ ]
407
+
408
+ const results = extractERC20BalancePreconditions(intents)
409
+ expect(results).toHaveLength(1)
410
+ expect(results[0]).toBeInstanceOf(Erc20BalancePrecondition)
411
+ expect(results[0].min).toBe(1000000n)
412
+ expect(results[0].token).toBe(TOKEN_ADDRESS)
413
+ })
414
+ })
415
+ })