@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,375 @@
1
+ import { Relayer as GenRelayer, FeeTokenType, ETHTxnStatus, } from './relayer.gen.js';
2
+ import { Address, Hex, Bytes, AbiFunction } from 'ox';
3
+ import { Constants, Payload, Network } from '@0xsequence/wallet-primitives';
4
+ import { decodePrecondition } from '../../preconditions/index.js';
5
+ import { erc20BalanceOf, erc20Allowance, erc721OwnerOf, erc721GetApproved, erc1155BalanceOf, erc1155IsApprovedForAll, } from '../standard/abi.js';
6
+ import { createPublicClient, http } from 'viem';
7
+ import * as chains from 'viem/chains';
8
+ /**
9
+ * Convert a Sequence Network to a viem Chain
10
+ */
11
+ const networkToChain = (network) => {
12
+ return {
13
+ id: network.chainId,
14
+ name: network.title || network.name,
15
+ nativeCurrency: {
16
+ name: network.nativeCurrency.name,
17
+ symbol: network.nativeCurrency.symbol,
18
+ decimals: network.nativeCurrency.decimals,
19
+ },
20
+ rpcUrls: {
21
+ default: {
22
+ http: [network.rpcUrl],
23
+ },
24
+ },
25
+ blockExplorers: network.blockExplorer
26
+ ? {
27
+ default: {
28
+ name: network.blockExplorer.name || 'Explorer',
29
+ url: network.blockExplorer.url,
30
+ },
31
+ }
32
+ : undefined,
33
+ contracts: network.ensAddress
34
+ ? {
35
+ ensUniversalResolver: {
36
+ address: network.ensAddress,
37
+ },
38
+ }
39
+ : undefined,
40
+ };
41
+ };
42
+ export const getChain = (chainId) => {
43
+ // First try to get the chain from Sequence's network configurations
44
+ const sequenceNetwork = Network.getNetworkFromChainId(chainId);
45
+ if (sequenceNetwork) {
46
+ return networkToChain(sequenceNetwork);
47
+ }
48
+ // Fall back to viem's built-in chains
49
+ const viemChain = Object.values(chains).find((c) => typeof c === 'object' && 'id' in c && c.id === chainId);
50
+ if (viemChain) {
51
+ return viemChain;
52
+ }
53
+ throw new Error(`Chain with id ${chainId} not found in Sequence networks or viem chains`);
54
+ };
55
+ export class RpcRelayer {
56
+ kind = 'relayer';
57
+ type = 'rpc';
58
+ id;
59
+ chainId;
60
+ client;
61
+ fetch;
62
+ provider;
63
+ projectAccessKey;
64
+ constructor(hostname, chainId, rpcUrl, fetchImpl, projectAccessKey) {
65
+ this.id = `rpc:${hostname}`;
66
+ this.chainId = chainId;
67
+ this.projectAccessKey = projectAccessKey;
68
+ const effectiveFetch = fetchImpl || (typeof window !== 'undefined' ? window.fetch.bind(window) : undefined);
69
+ if (!effectiveFetch) {
70
+ throw new Error('Fetch implementation is required but not available in this environment.');
71
+ }
72
+ this.fetch = effectiveFetch;
73
+ this.client = new GenRelayer(hostname, this.fetch);
74
+ // Get the chain from the chainId
75
+ const chain = getChain(chainId);
76
+ // Create viem PublicClient with the provided RPC URL
77
+ this.provider = createPublicClient({
78
+ chain,
79
+ transport: http(rpcUrl),
80
+ });
81
+ }
82
+ isAvailable(_wallet, chainId) {
83
+ return Promise.resolve(this.chainId === chainId);
84
+ }
85
+ async feeTokens() {
86
+ try {
87
+ const { isFeeRequired, tokens, paymentAddress } = await this.client.feeTokens();
88
+ if (isFeeRequired) {
89
+ Address.assert(paymentAddress);
90
+ return {
91
+ isFeeRequired,
92
+ tokens,
93
+ paymentAddress,
94
+ };
95
+ }
96
+ // Not required
97
+ return {
98
+ isFeeRequired,
99
+ };
100
+ }
101
+ catch (e) {
102
+ console.warn('RpcRelayer.feeTokens failed:', e);
103
+ return { isFeeRequired: false };
104
+ }
105
+ }
106
+ async feeOptions(wallet, chainId, calls) {
107
+ const callsStruct = { type: 'call', space: 0n, nonce: 0n, calls: calls };
108
+ const data = Payload.encode(callsStruct);
109
+ try {
110
+ const result = await this.client.feeOptions({
111
+ wallet: wallet,
112
+ to: wallet,
113
+ data: Bytes.toHex(data),
114
+ }, { ...(this.projectAccessKey ? { 'X-Access-Key': this.projectAccessKey } : undefined) });
115
+ const quote = result.quote ? { _tag: 'FeeQuote', _quote: result.quote } : undefined;
116
+ const options = result.options.map((option) => ({
117
+ token: {
118
+ ...option.token,
119
+ contractAddress: this.mapRpcFeeTokenToAddress(option.token),
120
+ },
121
+ to: option.to,
122
+ value: option.value,
123
+ gasLimit: option.gasLimit,
124
+ }));
125
+ return { options, quote };
126
+ }
127
+ catch (e) {
128
+ console.warn('RpcRelayer.feeOptions failed:', e);
129
+ return { options: [] };
130
+ }
131
+ }
132
+ async sendMetaTxn(walletAddress, to, data, chainId, quote, preconditions) {
133
+ console.log('sendMetaTxn', walletAddress, to, data, chainId, quote, preconditions);
134
+ const rpcCall = {
135
+ walletAddress: walletAddress,
136
+ contract: to,
137
+ input: data,
138
+ };
139
+ const result = await this.client.sendMetaTxn({
140
+ call: rpcCall,
141
+ quote: quote ? JSON.stringify(quote._quote) : undefined,
142
+ preconditions: preconditions,
143
+ }, { ...(this.projectAccessKey ? { 'X-Access-Key': this.projectAccessKey } : undefined) });
144
+ if (!result.status) {
145
+ console.error('RpcRelayer.relay failed', result);
146
+ throw new Error(`Relay failed: TxnHash ${result.txnHash}`);
147
+ }
148
+ return { opHash: Hex.fromString(result.txnHash) };
149
+ }
150
+ async relay(to, data, chainId, quote, preconditions) {
151
+ console.log('relay', to, data, chainId, quote, preconditions);
152
+ const rpcCall = {
153
+ walletAddress: to,
154
+ contract: to,
155
+ input: data,
156
+ };
157
+ const result = await this.client.sendMetaTxn({
158
+ call: rpcCall,
159
+ quote: quote ? JSON.stringify(quote._quote) : undefined,
160
+ preconditions: preconditions,
161
+ }, { ...(this.projectAccessKey ? { 'X-Access-Key': this.projectAccessKey } : undefined) });
162
+ if (!result.status) {
163
+ console.error('RpcRelayer.relay failed', result);
164
+ throw new Error(`Relay failed: TxnHash ${result.txnHash}`);
165
+ }
166
+ return { opHash: `0x${result.txnHash}` };
167
+ }
168
+ async status(opHash, chainId) {
169
+ try {
170
+ const cleanedOpHash = opHash.startsWith('0x') ? opHash.substring(2) : opHash;
171
+ const result = await this.client.getMetaTxnReceipt({ metaTxID: cleanedOpHash });
172
+ const receipt = result.receipt;
173
+ if (!receipt) {
174
+ console.warn(`RpcRelayer.status: receipt not found for opHash ${opHash}`);
175
+ return { status: 'unknown' };
176
+ }
177
+ if (!receipt.status) {
178
+ console.warn(`RpcRelayer.status: receipt status not found for opHash ${opHash}`);
179
+ return { status: 'unknown' };
180
+ }
181
+ switch (receipt.status) {
182
+ case ETHTxnStatus.QUEUED:
183
+ case ETHTxnStatus.PENDING_PRECONDITION:
184
+ case ETHTxnStatus.SENT:
185
+ return { status: 'pending' };
186
+ case ETHTxnStatus.SUCCEEDED:
187
+ return { status: 'confirmed', transactionHash: receipt.txnHash, data: result };
188
+ case ETHTxnStatus.FAILED:
189
+ case ETHTxnStatus.PARTIALLY_FAILED:
190
+ return {
191
+ status: 'failed',
192
+ transactionHash: receipt.txnHash ? receipt.txnHash : undefined,
193
+ reason: receipt.revertReason || 'Relayer reported failure',
194
+ data: result,
195
+ };
196
+ case ETHTxnStatus.DROPPED:
197
+ return { status: 'failed', reason: 'Transaction dropped' };
198
+ case ETHTxnStatus.UNKNOWN:
199
+ default:
200
+ return { status: 'unknown' };
201
+ }
202
+ }
203
+ catch (error) {
204
+ console.error(`RpcRelayer.status failed for opHash ${opHash}:`, error);
205
+ return { status: 'failed', reason: 'Failed to fetch status' };
206
+ }
207
+ }
208
+ async checkPrecondition(precondition) {
209
+ const decoded = decodePrecondition(precondition);
210
+ if (!decoded) {
211
+ return false;
212
+ }
213
+ switch (decoded.type()) {
214
+ case 'native-balance': {
215
+ const native = decoded;
216
+ try {
217
+ const balance = await this.provider.getBalance({ address: native.address.toString() });
218
+ const minWei = native.min !== undefined ? BigInt(native.min) : undefined;
219
+ const maxWei = native.max !== undefined ? BigInt(native.max) : undefined;
220
+ if (minWei !== undefined && maxWei !== undefined) {
221
+ return balance >= minWei && balance <= maxWei;
222
+ }
223
+ if (minWei !== undefined) {
224
+ return balance >= minWei;
225
+ }
226
+ if (maxWei !== undefined) {
227
+ return balance <= maxWei;
228
+ }
229
+ // If no min or max specified, this is an invalid precondition
230
+ console.warn('Native balance precondition has neither min nor max specified');
231
+ return false;
232
+ }
233
+ catch (error) {
234
+ console.error('Error checking native balance:', error);
235
+ return false;
236
+ }
237
+ }
238
+ case 'erc20-balance': {
239
+ const erc20 = decoded;
240
+ try {
241
+ const data = AbiFunction.encodeData(erc20BalanceOf, [erc20.address.toString()]);
242
+ const result = await this.provider.call({
243
+ to: erc20.token.toString(),
244
+ data: data,
245
+ });
246
+ const balance = BigInt(result.toString());
247
+ const minWei = erc20.min !== undefined ? BigInt(erc20.min) : undefined;
248
+ const maxWei = erc20.max !== undefined ? BigInt(erc20.max) : undefined;
249
+ if (minWei !== undefined && maxWei !== undefined) {
250
+ return balance >= minWei && balance <= maxWei;
251
+ }
252
+ if (minWei !== undefined) {
253
+ return balance >= minWei;
254
+ }
255
+ if (maxWei !== undefined) {
256
+ return balance <= maxWei;
257
+ }
258
+ console.warn('ERC20 balance precondition has neither min nor max specified');
259
+ return false;
260
+ }
261
+ catch (error) {
262
+ console.error('Error checking ERC20 balance:', error);
263
+ return false;
264
+ }
265
+ }
266
+ case 'erc20-approval': {
267
+ const erc20 = decoded;
268
+ try {
269
+ const data = AbiFunction.encodeData(erc20Allowance, [erc20.address.toString(), erc20.operator.toString()]);
270
+ const result = await this.provider.call({
271
+ to: erc20.token.toString(),
272
+ data: data,
273
+ });
274
+ const allowance = BigInt(result.toString());
275
+ const minAllowance = BigInt(erc20.min);
276
+ return allowance >= minAllowance;
277
+ }
278
+ catch (error) {
279
+ console.error('Error checking ERC20 approval:', error);
280
+ return false;
281
+ }
282
+ }
283
+ case 'erc721-ownership': {
284
+ const erc721 = decoded;
285
+ try {
286
+ const data = AbiFunction.encodeData(erc721OwnerOf, [erc721.tokenId]);
287
+ const result = await this.provider.call({
288
+ to: erc721.token.toString(),
289
+ data: data,
290
+ });
291
+ const resultHex = result.toString();
292
+ const owner = resultHex.slice(-40);
293
+ const isOwner = owner.toLowerCase() === erc721.address.toString().slice(2).toLowerCase();
294
+ const expectedOwnership = erc721.owned !== undefined ? erc721.owned : true;
295
+ return isOwner === expectedOwnership;
296
+ }
297
+ catch (error) {
298
+ console.error('Error checking ERC721 ownership:', error);
299
+ return false;
300
+ }
301
+ }
302
+ case 'erc721-approval': {
303
+ const erc721 = decoded;
304
+ try {
305
+ const data = AbiFunction.encodeData(erc721GetApproved, [erc721.tokenId]);
306
+ const result = await this.provider.call({
307
+ to: erc721.token.toString(),
308
+ data: data,
309
+ });
310
+ const resultHex = result.toString();
311
+ const approved = resultHex.slice(-40);
312
+ return approved.toLowerCase() === erc721.operator.toString().slice(2).toLowerCase();
313
+ }
314
+ catch (error) {
315
+ console.error('Error checking ERC721 approval:', error);
316
+ return false;
317
+ }
318
+ }
319
+ case 'erc1155-balance': {
320
+ const erc1155 = decoded;
321
+ try {
322
+ const data = AbiFunction.encodeData(erc1155BalanceOf, [erc1155.address.toString(), erc1155.tokenId]);
323
+ const result = await this.provider.call({
324
+ to: erc1155.token.toString(),
325
+ data: data,
326
+ });
327
+ const balance = BigInt(result.toString());
328
+ const minWei = erc1155.min !== undefined ? BigInt(erc1155.min) : undefined;
329
+ const maxWei = erc1155.max !== undefined ? BigInt(erc1155.max) : undefined;
330
+ if (minWei !== undefined && maxWei !== undefined) {
331
+ return balance >= minWei && balance <= maxWei;
332
+ }
333
+ if (minWei !== undefined) {
334
+ return balance >= minWei;
335
+ }
336
+ if (maxWei !== undefined) {
337
+ return balance <= maxWei;
338
+ }
339
+ console.warn('ERC1155 balance precondition has neither min nor max specified');
340
+ return false;
341
+ }
342
+ catch (error) {
343
+ console.error('Error checking ERC1155 balance:', error);
344
+ return false;
345
+ }
346
+ }
347
+ case 'erc1155-approval': {
348
+ const erc1155 = decoded;
349
+ try {
350
+ const data = AbiFunction.encodeData(erc1155IsApprovedForAll, [
351
+ erc1155.address.toString(),
352
+ erc1155.operator.toString(),
353
+ ]);
354
+ const result = await this.provider.call({
355
+ to: erc1155.token.toString(),
356
+ data: data,
357
+ });
358
+ return BigInt(result.toString()) === 1n;
359
+ }
360
+ catch (error) {
361
+ console.error('Error checking ERC1155 approval:', error);
362
+ return false;
363
+ }
364
+ }
365
+ default:
366
+ return false;
367
+ }
368
+ }
369
+ mapRpcFeeTokenToAddress(rpcToken) {
370
+ if (rpcToken.type === FeeTokenType.ERC20_TOKEN && rpcToken.contractAddress) {
371
+ return Address.from(rpcToken.contractAddress);
372
+ }
373
+ return Constants.ZeroAddress; // Default to zero address for native token or unsupported types
374
+ }
375
+ }