@fastxyz/allset-sdk 0.1.12 → 1.0.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.
Files changed (44) hide show
  1. package/README.md +322 -266
  2. package/dist/index.d.ts +658 -5
  3. package/dist/index.js +927 -7
  4. package/package.json +21 -47
  5. package/dist/browser/index.d.ts +0 -2
  6. package/dist/browser/index.d.ts.map +0 -1
  7. package/dist/browser/index.js +0 -1
  8. package/dist/core/address.d.ts +0 -5
  9. package/dist/core/address.d.ts.map +0 -1
  10. package/dist/core/address.js +0 -29
  11. package/dist/core/deposit.d.ts +0 -59
  12. package/dist/core/deposit.d.ts.map +0 -1
  13. package/dist/core/deposit.js +0 -92
  14. package/dist/core/index.d.ts +0 -6
  15. package/dist/core/index.d.ts.map +0 -1
  16. package/dist/core/index.js +0 -3
  17. package/dist/default-config.d.ts +0 -78
  18. package/dist/default-config.d.ts.map +0 -1
  19. package/dist/default-config.js +0 -78
  20. package/dist/index.d.ts.map +0 -1
  21. package/dist/intents.d.ts +0 -94
  22. package/dist/intents.d.ts.map +0 -1
  23. package/dist/intents.js +0 -119
  24. package/dist/node/bridge.d.ts +0 -38
  25. package/dist/node/bridge.d.ts.map +0 -1
  26. package/dist/node/bridge.js +0 -519
  27. package/dist/node/config.d.ts +0 -45
  28. package/dist/node/config.d.ts.map +0 -1
  29. package/dist/node/config.js +0 -48
  30. package/dist/node/eip7702.d.ts +0 -54
  31. package/dist/node/eip7702.d.ts.map +0 -1
  32. package/dist/node/eip7702.js +0 -275
  33. package/dist/node/evm-executor.d.ts +0 -130
  34. package/dist/node/evm-executor.d.ts.map +0 -1
  35. package/dist/node/evm-executor.js +0 -160
  36. package/dist/node/index.d.ts +0 -15
  37. package/dist/node/index.d.ts.map +0 -1
  38. package/dist/node/index.js +0 -17
  39. package/dist/node/provider.d.ts +0 -162
  40. package/dist/node/provider.d.ts.map +0 -1
  41. package/dist/node/provider.js +0 -272
  42. package/dist/node/types.d.ts +0 -110
  43. package/dist/node/types.d.ts.map +0 -1
  44. package/dist/node/types.js +0 -4
package/dist/intents.js DELETED
@@ -1,119 +0,0 @@
1
- /**
2
- * intents.ts — Intent builders for AllSet external execution
3
- *
4
- * Intents define what actions to perform on EVM chains after
5
- * transferring tokens from Fast network.
6
- */
7
- import { encodeAbiParameters } from 'viem';
8
- import { fastAddressToBytes32 } from './core/address.js';
9
- // ---------------------------------------------------------------------------
10
- // Types
11
- // ---------------------------------------------------------------------------
12
- /**
13
- * Intent action types supported by AllSet bridge.
14
- */
15
- export var IntentAction;
16
- (function (IntentAction) {
17
- /** Generic contract call */
18
- IntentAction[IntentAction["Execute"] = 0] = "Execute";
19
- /** ERC-20 transfer to address */
20
- IntentAction[IntentAction["DynamicTransfer"] = 1] = "DynamicTransfer";
21
- /** Deposit tokens back to Fast network */
22
- IntentAction[IntentAction["DynamicDeposit"] = 2] = "DynamicDeposit";
23
- /** Cancel/revoke pending intent */
24
- IntentAction[IntentAction["Revoke"] = 3] = "Revoke";
25
- })(IntentAction || (IntentAction = {}));
26
- // ---------------------------------------------------------------------------
27
- // Intent Builders
28
- // ---------------------------------------------------------------------------
29
- /**
30
- * Build a transfer intent to send ERC-20 tokens to an address.
31
- *
32
- * @param token - ERC-20 token contract address
33
- * @param receiver - Recipient EVM address
34
- * @returns Intent for DynamicTransfer action
35
- *
36
- * @example
37
- * ```ts
38
- * const intent = buildTransferIntent(
39
- * '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d', // USDC
40
- * '0xRecipientAddress'
41
- * );
42
- * ```
43
- */
44
- export function buildTransferIntent(token, receiver) {
45
- const payload = encodeAbiParameters([{ type: 'address' }, { type: 'address' }], [token, receiver]);
46
- return {
47
- action: IntentAction.DynamicTransfer,
48
- payload,
49
- value: 0n,
50
- };
51
- }
52
- /**
53
- * Build a generic execute intent for arbitrary contract calls.
54
- *
55
- * @param target - Target contract address
56
- * @param calldata - ABI-encoded function call data
57
- * @param value - Native token value to send (default: 0)
58
- * @returns Intent for Execute action
59
- *
60
- * @example
61
- * ```ts
62
- * // Call a contract function
63
- * const calldata = encodeFunctionData({
64
- * abi: contractAbi,
65
- * functionName: 'someFunction',
66
- * args: [arg1, arg2],
67
- * });
68
- * const intent = buildExecuteIntent('0xContractAddress', calldata);
69
- * ```
70
- */
71
- export function buildExecuteIntent(target, calldata, value = 0n) {
72
- const payload = encodeAbiParameters([{ type: 'address' }, { type: 'bytes' }], [target, calldata]);
73
- return {
74
- action: IntentAction.Execute,
75
- payload,
76
- value,
77
- };
78
- }
79
- /**
80
- * Build an intent to deposit tokens back to Fast network.
81
- *
82
- * @param token - ERC-20 token contract address on EVM
83
- * @param fastReceiver - Fast network recipient address (fast1...)
84
- * @returns Intent for DynamicDeposit action
85
- *
86
- * @example
87
- * ```ts
88
- * const intent = buildDepositBackIntent(
89
- * '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d', // USDC
90
- * 'fast1recipientaddress...'
91
- * );
92
- * ```
93
- */
94
- export function buildDepositBackIntent(token, fastReceiver) {
95
- const receiverBytes = fastAddressToBytes32(fastReceiver);
96
- const payload = encodeAbiParameters([{ type: 'address' }, { type: 'bytes32' }], [token, receiverBytes]);
97
- return {
98
- action: IntentAction.DynamicDeposit,
99
- payload,
100
- value: 0n,
101
- };
102
- }
103
- /**
104
- * Build a revoke intent to cancel pending operations.
105
- *
106
- * @returns Intent for Revoke action
107
- *
108
- * @example
109
- * ```ts
110
- * const intent = buildRevokeIntent();
111
- * ```
112
- */
113
- export function buildRevokeIntent() {
114
- return {
115
- action: IntentAction.Revoke,
116
- payload: '0x',
117
- value: 0n,
118
- };
119
- }
@@ -1,38 +0,0 @@
1
- /**
2
- * bridge.ts — AllSet bridge provider
3
- *
4
- * Bridges between Fast network and the SDK's supported EVM routes.
5
- *
6
- * Two directions:
7
- * Deposit (EVM → Fast): call bridge.deposit(token, amount, receiver) on the EVM bridge contract
8
- * Withdraw (Fast → EVM): transfer on Fast network + submit ExternalClaim intent + POST to relayer
9
- */
10
- import type { BridgeParams, BridgeResult, ExecuteIntentParams } from './types.js';
11
- import { type ChainConfig, type TokenConfig } from './config.js';
12
- interface BridgeProviderConfig {
13
- network: 'testnet' | 'mainnet';
14
- crossSignUrl?: string;
15
- getChainConfig(chain: string): ChainConfig | null;
16
- getTokenConfig(chain: string, token: string): TokenConfig | null;
17
- getNetworkConfig?(): {
18
- chains: Record<string, ChainConfig>;
19
- };
20
- }
21
- export interface EvmSignResult {
22
- transaction: number[];
23
- signature: string;
24
- }
25
- export declare function evmSign(certificate: unknown, crossSignUrl?: string): Promise<EvmSignResult>;
26
- /**
27
- * Execute a bridge operation with optional provider configuration.
28
- * Called by AllSetProvider.bridge() or directly for low-level usage.
29
- */
30
- export declare function executeBridge(params: BridgeParams, provider?: BridgeProviderConfig): Promise<BridgeResult>;
31
- /**
32
- * Execute intents on an EVM chain after transferring tokens from Fast network.
33
- * This is the core function used by sendToExternal and can be used directly for
34
- * advanced use cases like swaps, multi-step operations, etc.
35
- */
36
- export declare function executeIntent(params: ExecuteIntentParams, provider?: BridgeProviderConfig): Promise<BridgeResult>;
37
- export {};
38
- //# sourceMappingURL=bridge.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/node/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAsC,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtH,OAAO,EAAoD,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AA0GnH,UAAU,oBAAoB;IAC5B,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IAClD,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IACjE,gBAAgB,CAAC,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;KAAE,CAAC;CAC9D;AAiKD,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAwCD,wBAAsB,OAAO,CAC3B,WAAW,EAAE,OAAO,EACpB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,CAAC,CA6CxB;AAID;;;GAGG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,YAAY,CAAC,CAiChH;AAqID;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,YAAY,CAAC,CAuLvB"}
@@ -1,519 +0,0 @@
1
- /**
2
- * bridge.ts — AllSet bridge provider
3
- *
4
- * Bridges between Fast network and the SDK's supported EVM routes.
5
- *
6
- * Two directions:
7
- * Deposit (EVM → Fast): call bridge.deposit(token, amount, receiver) on the EVM bridge contract
8
- * Withdraw (Fast → EVM): transfer on Fast network + submit ExternalClaim intent + POST to relayer
9
- */
10
- import { decodeAbiParameters, encodeAbiParameters } from 'viem';
11
- import { fastAddressToBytes } from '../core/address.js';
12
- import { getNetworkConfig, getChainConfig, getTokenConfig } from './config.js';
13
- import { buildDepositTransactionFromRoute } from '../core/deposit.js';
14
- import { IntentAction, buildTransferIntent } from '../intents.js';
15
- import { ERC20_ABI } from './evm-executor.js';
16
- // ─── Constants ────────────────────────────────────────────────────────────────
17
- // Default network (can be overridden via environment variable)
18
- const DEFAULT_NETWORK = process.env.ALLSET_NETWORK || 'testnet';
19
- class LocalFastError extends Error {
20
- code;
21
- note;
22
- constructor(code, message, opts) {
23
- super(message);
24
- this.name = 'FastError';
25
- this.code = code;
26
- this.note = opts?.note ?? '';
27
- }
28
- toJSON() {
29
- return {
30
- error: true,
31
- code: this.code,
32
- message: this.message,
33
- note: this.note,
34
- };
35
- }
36
- }
37
- let sdkFastErrorPromise = null;
38
- // Keep @fastxyz/sdk runtime-optional for deposit-only consumers.
39
- async function loadFastErrorConstructor() {
40
- sdkFastErrorPromise ??= import('@fastxyz/sdk')
41
- .then((mod) => (typeof mod.FastError === 'function' ? mod.FastError : null))
42
- .catch(() => null);
43
- return sdkFastErrorPromise;
44
- }
45
- async function createFastError(code, message, opts) {
46
- const FastError = await loadFastErrorConstructor();
47
- return FastError ? new FastError(code, message, opts) : new LocalFastError(code, message, opts);
48
- }
49
- function isFastError(err) {
50
- return (err instanceof Error &&
51
- err.name === 'FastError' &&
52
- typeof err.code === 'string' &&
53
- typeof err.note === 'string');
54
- }
55
- /**
56
- * Convert decimal amount string to hex for BCS serialization.
57
- * The Fast network BCS expects amounts as hex strings.
58
- */
59
- function amountToHex(amount) {
60
- return BigInt(amount).toString(16);
61
- }
62
- /**
63
- * Convert ChainConfig from config.ts to AllSetChainConfig used internally.
64
- */
65
- function toAllSetChainConfig(config) {
66
- return {
67
- chainId: config.chainId,
68
- bridgeContract: config.bridgeContract,
69
- fastsetBridgeAddress: config.fastBridgeAddress,
70
- relayerUrl: config.relayerUrl,
71
- };
72
- }
73
- /**
74
- * Convert TokenConfig from config.ts to AllSetTokenInfo used internally.
75
- */
76
- function toAllSetTokenInfo(config) {
77
- return {
78
- evmAddress: config.evmAddress,
79
- fastsetTokenId: hexToUint8Array(config.fastTokenId),
80
- decimals: config.decimals,
81
- isNative: false,
82
- };
83
- }
84
- function resolveChainConfig(chain, network = DEFAULT_NETWORK, provider) {
85
- return provider?.getChainConfig(chain) ?? getChainConfig(chain, network);
86
- }
87
- function resolveTokenConfig(chain, token, network = DEFAULT_NETWORK, provider) {
88
- return provider?.getTokenConfig(chain, token) ?? getTokenConfig(chain, token, network);
89
- }
90
- function getSupportedChains(network = DEFAULT_NETWORK, provider) {
91
- return Object.keys(provider?.getNetworkConfig?.().chains ?? getNetworkConfig(network).chains);
92
- }
93
- function resolveAllSetToken(token, evmChain, network = DEFAULT_NETWORK, provider) {
94
- // Normalize token name - testUSDC on Fast testnet maps to USDC on EVM
95
- const lowerToken = token.toLowerCase();
96
- const normalizedToken = (lowerToken === 'fastusdc' || lowerToken === 'testusdc') ? 'USDC' : token;
97
- // Try exact match first
98
- const tokenConfig = resolveTokenConfig(evmChain, normalizedToken, network, provider);
99
- if (tokenConfig) {
100
- return toAllSetTokenInfo(tokenConfig);
101
- }
102
- // Try uppercase
103
- const upperConfig = resolveTokenConfig(evmChain, normalizedToken.toUpperCase(), network, provider);
104
- if (upperConfig) {
105
- return toAllSetTokenInfo(upperConfig);
106
- }
107
- // Try matching by EVM address
108
- const chainConfig = resolveChainConfig(evmChain, network, provider);
109
- if (chainConfig) {
110
- for (const [, info] of Object.entries(chainConfig.tokens)) {
111
- if (info.evmAddress.toLowerCase() === token.toLowerCase()) {
112
- return toAllSetTokenInfo(info);
113
- }
114
- }
115
- }
116
- return null;
117
- }
118
- function hexToUint8Array(hex) {
119
- const clean = hex.startsWith('0x') ? hex.slice(2) : hex;
120
- const bytes = new Uint8Array(clean.length / 2);
121
- for (let i = 0; i < bytes.length; i += 1) {
122
- bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
123
- }
124
- return bytes;
125
- }
126
- // ─── EVM Transaction Helpers ──────────────────────────────────────────────────
127
- async function sendTx(clients, tx) {
128
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
- const walletClient = clients.walletClient;
130
- const hash = await walletClient.sendTransaction({
131
- to: tx.to,
132
- data: tx.data,
133
- value: BigInt(tx.value),
134
- gas: tx.gas ? BigInt(tx.gas) : undefined,
135
- });
136
- const receipt = await clients.publicClient.waitForTransactionReceipt({ hash });
137
- return {
138
- txHash: hash,
139
- status: receipt.status === 'success' ? 'success' : 'reverted',
140
- };
141
- }
142
- async function checkAllowance(clients, token, spender, owner) {
143
- const allowance = await clients.publicClient.readContract({
144
- address: token,
145
- abi: ERC20_ABI,
146
- functionName: 'allowance',
147
- args: [owner, spender],
148
- });
149
- return allowance;
150
- }
151
- async function approveErc20(clients, token, spender, amount) {
152
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
153
- const walletClient = clients.walletClient;
154
- const hash = await walletClient.writeContract({
155
- address: token,
156
- abi: ERC20_ABI,
157
- functionName: 'approve',
158
- args: [spender, BigInt(amount)],
159
- });
160
- await clients.publicClient.waitForTransactionReceipt({ hash });
161
- return hash;
162
- }
163
- function resolveExternalAddress(intents, externalAddressOverride) {
164
- if (externalAddressOverride) {
165
- return externalAddressOverride;
166
- }
167
- for (const intent of intents) {
168
- if (intent.action === IntentAction.DynamicTransfer) {
169
- try {
170
- const [, receiver] = decodeAbiParameters([{ type: 'address' }, { type: 'address' }], intent.payload);
171
- return receiver;
172
- }
173
- catch {
174
- continue;
175
- }
176
- }
177
- if (intent.action === IntentAction.Execute) {
178
- try {
179
- const [target] = decodeAbiParameters([{ type: 'address' }, { type: 'bytes' }], intent.payload);
180
- return target;
181
- }
182
- catch {
183
- continue;
184
- }
185
- }
186
- }
187
- return null;
188
- }
189
- /**
190
- * Request EVM cross-signing for a Fast network certificate.
191
- *
192
- * This is an AllSet-specific operation that requests the AllSet committee
193
- * to sign a certificate for verification on EVM chains.
194
- *
195
- * @param certificate - The certificate from a FastWallet.send() or FastWallet.submit() call
196
- * @param crossSignUrl - Optional custom cross-sign service URL
197
- * @returns The signed transaction bytes and signature for relayer submission
198
- *
199
- * @example
200
- * ```ts
201
- * const result = await fastWallet.send({ to: bridgeAddress, amount: '1000000', token: 'USDC' });
202
- * const signed = await evmSign(result.certificate);
203
- * // Use signed.transaction and signed.signature with the relayer
204
- * ```
205
- */
206
- /**
207
- * Convert BigInt values to numbers recursively for JSON serialization.
208
- * The cross-sign service expects numbers, not BigInt strings.
209
- */
210
- function bigIntToNumber(obj) {
211
- if (typeof obj === 'bigint') {
212
- return Number(obj);
213
- }
214
- if (Array.isArray(obj)) {
215
- return obj.map(bigIntToNumber);
216
- }
217
- if (obj !== null && typeof obj === 'object') {
218
- const result = {};
219
- for (const [key, value] of Object.entries(obj)) {
220
- result[key] = bigIntToNumber(value);
221
- }
222
- return result;
223
- }
224
- return obj;
225
- }
226
- export async function evmSign(certificate, crossSignUrl) {
227
- const url = crossSignUrl ?? getNetworkConfig(DEFAULT_NETWORK).crossSignUrl;
228
- // Convert BigInt values to numbers for cross-sign compatibility
229
- const serializedCertificate = bigIntToNumber(certificate);
230
- const res = await fetch(url, {
231
- method: 'POST',
232
- headers: { 'Content-Type': 'application/json' },
233
- body: JSON.stringify({
234
- jsonrpc: '2.0',
235
- id: 1,
236
- method: 'crossSign_evmSignCertificate',
237
- params: { certificate: serializedCertificate },
238
- }),
239
- });
240
- if (!res.ok) {
241
- throw await createFastError('TX_FAILED', `Cross-sign request failed: ${res.status}`, { note: 'The AllSet cross-sign service rejected the request.' });
242
- }
243
- const json = await res.json();
244
- if (json.error) {
245
- throw await createFastError('TX_FAILED', `Cross-sign error: ${json.error.message}`, { note: 'The certificate could not be cross-signed.' });
246
- }
247
- if (!json.result?.transaction || !json.result?.signature) {
248
- throw await createFastError('TX_FAILED', 'Cross-sign returned invalid response', { note: 'Missing transaction or signature in response.' });
249
- }
250
- return json.result;
251
- }
252
- // ─── Bridge Execution ─────────────────────────────────────────────────────────
253
- /**
254
- * Execute a bridge operation with optional provider configuration.
255
- * Called by AllSetProvider.bridge() or directly for low-level usage.
256
- */
257
- export async function executeBridge(params, provider) {
258
- const network = provider?.network ?? DEFAULT_NETWORK;
259
- try {
260
- const isDeposit = params.fromChain !== 'fast' && params.toChain === 'fast';
261
- const isWithdraw = params.fromChain === 'fast';
262
- if (!isDeposit && !isWithdraw) {
263
- throw await createFastError('UNSUPPORTED_OPERATION', `AllSet only supports bridging between Fast network and configured EVM chains (${getSupportedChains(network, provider).join(', ') || 'none'}). Got: ${params.fromChain} → ${params.toChain}`, {
264
- note: 'Use fromChain: "fast" for withdrawals, or toChain: "fast" for deposits.\n Example: await allset.bridge({ fromChain: "base", toChain: "fast", fromToken: "USDC", toToken: "USDC", amount: "1000000", senderAddress: "0x...", receiverAddress: "fast1..." })',
265
- });
266
- }
267
- if (isDeposit) {
268
- return await handleDeposit(params, network, provider);
269
- }
270
- return await handleWithdraw(params, network, provider);
271
- }
272
- catch (err) {
273
- if (isFastError(err))
274
- throw err;
275
- const msg = err instanceof Error ? err.message : String(err);
276
- throw await createFastError('TX_FAILED', `AllSet bridge failed: ${msg}`, {
277
- note: 'Check that both chains are configured and have sufficient balance.',
278
- });
279
- }
280
- }
281
- // ─── Deposit (EVM → Fast) ─────────────────────────────────────────────────────
282
- async function handleDeposit(params, network = DEFAULT_NETWORK, provider) {
283
- if (!params.evmClients) {
284
- throw await createFastError('INVALID_PARAMS', 'AllSet deposit (EVM → Fast) requires evmClients', {
285
- note: 'Provide evmClients created with createEvmExecutor().\n Example: const account = createEvmWallet(path); const clients = createEvmExecutor(account, rpcUrl, chainId)',
286
- });
287
- }
288
- const chainConfigRaw = resolveChainConfig(params.fromChain, network, provider);
289
- if (!chainConfigRaw) {
290
- throw await createFastError('UNSUPPORTED_OPERATION', `AllSet does not support EVM chain "${params.fromChain}". Supported: ${getSupportedChains(network, provider).join(', ')}`, {
291
- note: 'Use one of the configured EVM chains as the source chain for AllSet deposits.',
292
- });
293
- }
294
- const chainConfig = toAllSetChainConfig(chainConfigRaw);
295
- let tokenInfo = resolveAllSetToken(params.fromToken, params.fromChain, network, provider);
296
- if (!tokenInfo) {
297
- tokenInfo = resolveAllSetToken(params.toToken, params.fromChain, network, provider);
298
- }
299
- if (!tokenInfo) {
300
- throw await createFastError('TOKEN_NOT_FOUND', `Cannot resolve token "${params.fromToken}" on AllSet for chain "${params.fromChain}".`, {
301
- note: 'Supported tokens: USDC (mainnet), testUSDC (testnet).\n Example: await allset.bridge({ fromChain: "base", toChain: "fast", fromToken: "USDC", toToken: "USDC", amount: "1000000", senderAddress: "0x...", receiverAddress: "fast1..." })',
302
- });
303
- }
304
- let depositPlan;
305
- try {
306
- depositPlan = buildDepositTransactionFromRoute({
307
- network,
308
- chain: params.fromChain,
309
- token: params.fromToken,
310
- chainId: chainConfig.chainId,
311
- bridgeAddress: chainConfig.bridgeContract,
312
- tokenAddress: tokenInfo.evmAddress,
313
- decimals: tokenInfo.decimals,
314
- isNative: tokenInfo.isNative,
315
- }, BigInt(params.amount), params.receiverAddress);
316
- }
317
- catch (err) {
318
- const msg = err instanceof Error ? err.message : String(err);
319
- throw await createFastError('INVALID_ADDRESS', `Failed to decode Fast network receiver address "${params.receiverAddress}": ${msg}`, {
320
- note: 'The receiver address must be a valid Fast network bech32m address (fast1...).\n Example: fast1abc...',
321
- });
322
- }
323
- let txHash;
324
- if (depositPlan.route.isNative) {
325
- const receipt = await sendTx(params.evmClients, {
326
- to: depositPlan.to,
327
- data: depositPlan.data,
328
- value: depositPlan.value.toString(),
329
- });
330
- if (receipt.status === 'reverted') {
331
- throw await createFastError('TX_FAILED', `AllSet deposit transaction reverted: ${receipt.txHash}`, {
332
- note: 'The deposit transaction was reverted. Check that you have sufficient ETH balance.',
333
- });
334
- }
335
- txHash = receipt.txHash;
336
- }
337
- else {
338
- const requiredAmount = BigInt(params.amount);
339
- const currentAllowance = await checkAllowance(params.evmClients, depositPlan.route.tokenAddress, depositPlan.to, params.senderAddress);
340
- if (currentAllowance < requiredAmount) {
341
- await approveErc20(params.evmClients, depositPlan.route.tokenAddress, depositPlan.to, params.amount);
342
- }
343
- const receipt = await sendTx(params.evmClients, {
344
- to: depositPlan.to,
345
- data: depositPlan.data,
346
- value: depositPlan.value.toString(),
347
- });
348
- if (receipt.status === 'reverted') {
349
- throw await createFastError('TX_FAILED', `AllSet deposit transaction reverted: ${receipt.txHash}`, {
350
- note: 'The deposit transaction was reverted. Check that you have sufficient token balance and the approval succeeded.',
351
- });
352
- }
353
- txHash = receipt.txHash;
354
- }
355
- return {
356
- txHash,
357
- orderId: txHash,
358
- estimatedTime: '1-5 minutes',
359
- };
360
- }
361
- // ─── Execute Intent (Core) ────────────────────────────────────────────────────
362
- /**
363
- * Execute intents on an EVM chain after transferring tokens from Fast network.
364
- * This is the core function used by sendToExternal and can be used directly for
365
- * advanced use cases like swaps, multi-step operations, etc.
366
- */
367
- export async function executeIntent(params, provider) {
368
- const network = provider?.network ?? DEFAULT_NETWORK;
369
- const crossSignUrl = provider?.crossSignUrl;
370
- const { fastWallet, chain, token, amount, intents, externalAddress: externalAddressOverride, deadlineSeconds = 3600, } = params;
371
- if (!fastWallet) {
372
- throw await createFastError('INVALID_PARAMS', 'executeIntent requires fastWallet', {
373
- note: 'Provide a compatible Fast wallet.\n Example: const wallet = await FastWallet.fromKeyfile("~/.fast/keys/default.json", provider)',
374
- });
375
- }
376
- if (!intents || intents.length === 0) {
377
- throw await createFastError('INVALID_PARAMS', 'executeIntent requires at least one intent', {
378
- note: 'Use intent builders like buildTransferIntent(), buildExecuteIntent(), etc.',
379
- });
380
- }
381
- if (externalAddressOverride && !externalAddressOverride.startsWith('0x')) {
382
- throw await createFastError('INVALID_PARAMS', 'executeIntent externalAddress must be an EVM address', {
383
- note: 'Pass a 0x-prefixed address for the relayer target.',
384
- });
385
- }
386
- const chainConfigRaw = resolveChainConfig(chain, network, provider);
387
- if (!chainConfigRaw) {
388
- throw await createFastError('UNSUPPORTED_OPERATION', `AllSet does not support EVM chain "${chain}". Supported: ${getSupportedChains(network, provider).join(', ')}`, {
389
- note: 'Use one of the configured EVM chains.',
390
- });
391
- }
392
- const chainConfig = toAllSetChainConfig(chainConfigRaw);
393
- const tokenInfo = resolveAllSetToken(token, chain, network, provider);
394
- if (!tokenInfo) {
395
- throw await createFastError('TOKEN_NOT_FOUND', `Cannot resolve token "${token}" on AllSet for chain "${chain}".`, {
396
- note: 'Supported tokens: USDC (mainnet), testUSDC (testnet).',
397
- });
398
- }
399
- // Step 1: Transfer tokens to bridge address on Fast network
400
- const transferResult = await fastWallet.submit({
401
- claim: {
402
- TokenTransfer: {
403
- token_id: tokenInfo.fastsetTokenId,
404
- recipient: fastAddressToBytes(chainConfig.fastsetBridgeAddress),
405
- amount: amountToHex(amount),
406
- user_data: null,
407
- },
408
- },
409
- });
410
- // Step 2: Cross-sign the transfer certificate
411
- const transferCrossSign = await evmSign(transferResult.certificate, crossSignUrl);
412
- // Use SDK's txHash directly - proven to match cross-sign bytes[32:64] exactly
413
- // See: https://github.com/fastxyz/allset-sdk/pull/26 (20/20 test transactions matched)
414
- const transferFastTxId = transferResult.txHash;
415
- // Step 3: Build intent claim with provided intents
416
- const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineSeconds);
417
- const intentClaimEncoded = encodeAbiParameters([{
418
- type: 'tuple',
419
- components: [
420
- { name: 'transferFastTxId', type: 'bytes32' },
421
- { name: 'deadline', type: 'uint256' },
422
- {
423
- name: 'intents',
424
- type: 'tuple[]',
425
- components: [
426
- { name: 'action', type: 'uint8' },
427
- { name: 'payload', type: 'bytes' },
428
- { name: 'value', type: 'uint256' },
429
- ],
430
- },
431
- ],
432
- }], [{
433
- transferFastTxId,
434
- deadline,
435
- intents: intents.map(i => ({
436
- action: i.action,
437
- payload: i.payload,
438
- value: i.value,
439
- })),
440
- }]);
441
- const intentBytes = hexToUint8Array(intentClaimEncoded);
442
- // Step 4: Submit intent claim on Fast
443
- const intentResult = await fastWallet.submit({
444
- claim: {
445
- ExternalClaim: {
446
- claim: {
447
- verifier_committee: [],
448
- verifier_quorum: 0,
449
- claim_data: Array.from(intentBytes),
450
- },
451
- signatures: [],
452
- },
453
- },
454
- });
455
- // Step 5: Cross-sign the intent certificate
456
- const intentCrossSign = await evmSign(intentResult.certificate, crossSignUrl);
457
- // Step 6: Submit to relayer
458
- const externalAddress = resolveExternalAddress(intents, externalAddressOverride);
459
- if (!externalAddress) {
460
- throw await createFastError('INVALID_PARAMS', 'executeIntent requires externalAddress when intents do not include a transfer recipient or execute target', {
461
- note: 'Pass externalAddress for flows like buildDepositBackIntent() or buildRevokeIntent().',
462
- });
463
- }
464
- const relayerBody = {
465
- encoded_transfer_claim: Array.from(new Uint8Array(transferCrossSign.transaction.map(Number))),
466
- transfer_proof: transferCrossSign.signature,
467
- transfer_fast_tx_id: transferResult.txHash,
468
- transfer_claim_id: transferResult.txHash,
469
- fastset_address: fastWallet.address,
470
- external_address: externalAddress,
471
- encoded_intent_claim: Array.from(new Uint8Array(intentCrossSign.transaction.map(Number))),
472
- intent_proof: intentCrossSign.signature,
473
- intent_fast_tx_id: intentResult.txHash,
474
- intent_claim_id: intentResult.txHash,
475
- external_token_address: tokenInfo.evmAddress,
476
- };
477
- const relayRes = await fetch(`${chainConfig.relayerUrl}/relay`, {
478
- method: 'POST',
479
- headers: { 'Content-Type': 'application/json' },
480
- body: JSON.stringify(relayerBody),
481
- });
482
- if (!relayRes.ok) {
483
- const text = await relayRes.text();
484
- throw await createFastError('TX_FAILED', `AllSet relayer request failed (${relayRes.status}): ${text}`, {
485
- note: 'The intent was submitted to Fast network but the relayer rejected it. Try again.',
486
- });
487
- }
488
- return {
489
- txHash: transferResult.txHash,
490
- orderId: transferFastTxId,
491
- estimatedTime: '1-5 minutes',
492
- };
493
- }
494
- // ─── Withdraw (Fast → EVM) ────────────────────────────────────────────────────
495
- async function handleWithdraw(params, network = DEFAULT_NETWORK, provider) {
496
- if (!params.fastWallet) {
497
- throw await createFastError('INVALID_PARAMS', 'AllSet withdrawal (Fast → EVM) requires fastWallet', {
498
- note: 'Provide a compatible Fast wallet.\n Example: const wallet = await FastWallet.fromKeyfile("~/.fast/keys/default.json", provider)',
499
- });
500
- }
501
- // Resolve token to get EVM address for the transfer intent
502
- const tokenInfo = resolveAllSetToken(params.fromToken, params.toChain, network, provider)
503
- ?? resolveAllSetToken(params.toToken, params.toChain, network, provider);
504
- if (!tokenInfo) {
505
- throw await createFastError('TOKEN_NOT_FOUND', `Cannot resolve token "${params.fromToken}" on AllSet for destination chain "${params.toChain}".`, {
506
- note: 'Supported tokens: USDC (mainnet), testUSDC (testnet).',
507
- });
508
- }
509
- // Build a simple transfer intent
510
- const transferIntent = buildTransferIntent(tokenInfo.evmAddress, params.receiverAddress);
511
- // Execute the intent
512
- return executeIntent({
513
- chain: params.toChain,
514
- fastWallet: params.fastWallet,
515
- token: params.fromToken,
516
- amount: params.amount,
517
- intents: [transferIntent],
518
- }, provider);
519
- }