@getomnichain/omnichain 0.2.1 → 0.3.0

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 (51) hide show
  1. package/dist/cjs/chain.base.js.map +1 -1
  2. package/dist/cjs/errors.js +55 -6
  3. package/dist/cjs/errors.js.map +1 -1
  4. package/dist/cjs/evm/evm_chain.js +421 -2
  5. package/dist/cjs/evm/evm_chain.js.map +1 -1
  6. package/dist/cjs/evm/evm_transaction_status.js +4 -0
  7. package/dist/cjs/evm/evm_transaction_status.js.map +1 -1
  8. package/dist/cjs/evm/unsigned_evm_transaction.js +12 -0
  9. package/dist/cjs/evm/unsigned_evm_transaction.js.map +1 -1
  10. package/dist/cjs/solana/solana_chain.js +618 -21
  11. package/dist/cjs/solana/solana_chain.js.map +1 -1
  12. package/dist/cjs/solana/unsigned_solana_transaction.js +33 -0
  13. package/dist/cjs/solana/unsigned_solana_transaction.js.map +1 -1
  14. package/dist/cjs/utxo/utxo_chain.js +94 -10
  15. package/dist/cjs/utxo/utxo_chain.js.map +1 -1
  16. package/dist/esm/chain.base.d.ts +38 -1
  17. package/dist/esm/chain.base.d.ts.map +1 -1
  18. package/dist/esm/chain.base.js.map +1 -1
  19. package/dist/esm/errors.d.ts +13 -0
  20. package/dist/esm/errors.d.ts.map +1 -1
  21. package/dist/esm/errors.js +51 -6
  22. package/dist/esm/errors.js.map +1 -1
  23. package/dist/esm/evm/evm_chain.d.ts +48 -2
  24. package/dist/esm/evm/evm_chain.d.ts.map +1 -1
  25. package/dist/esm/evm/evm_chain.js +423 -4
  26. package/dist/esm/evm/evm_chain.js.map +1 -1
  27. package/dist/esm/evm/evm_transaction_status.d.ts +4 -0
  28. package/dist/esm/evm/evm_transaction_status.d.ts.map +1 -1
  29. package/dist/esm/evm/evm_transaction_status.js +4 -0
  30. package/dist/esm/evm/evm_transaction_status.js.map +1 -1
  31. package/dist/esm/evm/unsigned_evm_transaction.d.ts +13 -0
  32. package/dist/esm/evm/unsigned_evm_transaction.d.ts.map +1 -1
  33. package/dist/esm/evm/unsigned_evm_transaction.js +12 -0
  34. package/dist/esm/evm/unsigned_evm_transaction.js.map +1 -1
  35. package/dist/esm/solana/solana_chain.d.ts +87 -3
  36. package/dist/esm/solana/solana_chain.d.ts.map +1 -1
  37. package/dist/esm/solana/solana_chain.js +618 -23
  38. package/dist/esm/solana/solana_chain.js.map +1 -1
  39. package/dist/esm/solana/unsigned_solana_transaction.d.ts +20 -1
  40. package/dist/esm/solana/unsigned_solana_transaction.d.ts.map +1 -1
  41. package/dist/esm/solana/unsigned_solana_transaction.js +33 -0
  42. package/dist/esm/solana/unsigned_solana_transaction.js.map +1 -1
  43. package/dist/esm/utxo/utxo_chain.d.ts +15 -2
  44. package/dist/esm/utxo/utxo_chain.d.ts.map +1 -1
  45. package/dist/esm/utxo/utxo_chain.js +96 -13
  46. package/dist/esm/utxo/utxo_chain.js.map +1 -1
  47. package/docs/CONNECTIONS.md +33 -0
  48. package/docs/EIP7702.md +77 -0
  49. package/docs/JITO.md +41 -0
  50. package/docs/MULTI_RPC.md +28 -0
  51. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"chain.base.js","sourceRoot":"","sources":["../../chain.base.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chain.base.js","sourceRoot":"","sources":["../../chain.base.ts"],"names":[],"mappings":"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"}
@@ -4,23 +4,51 @@ exports.ChainError = exports.ChainErrorKinds = void 0;
4
4
  exports.sanitizeMessage = sanitizeMessage;
5
5
  exports.sanitizeCause = sanitizeCause;
6
6
  exports.isChainError = isChainError;
7
+ exports.isBlockhashExpiredError = isBlockhashExpiredError;
8
+ exports.isSimulationError = isSimulationError;
9
+ exports.isNonceError = isNonceError;
10
+ exports.isTransactionTooLargeError = isTransactionTooLargeError;
7
11
  /**
8
12
  * Replace `rpcUrl` in `message` with its host-only form so API keys carried
9
13
  * in the URL path/query don't leak into error strings. Falls back to a
10
14
  * `<rpc>` placeholder if the URL is unparseable.
11
15
  */
12
16
  function sanitizeMessage(message, rpcUrl) {
17
+ let out = message;
18
+ // Long hex runs (with or without 0x prefix) are signed-tx bytes or opaque
19
+ // payloads. Threshold 128 hex chars = 64 bytes, above the largest transient
20
+ // identifier (tx hash / signature = 64 hex / 88 base58) so we don't clobber
21
+ // hashes but do catch even a minimal signed EVM tx (~110 bytes).
22
+ out = out.replace(/(?:0x)?[0-9a-fA-F]{128,}/g, (m) => {
23
+ const hexLen = m.startsWith('0x') || m.startsWith('0X') ? m.length - 2 : m.length;
24
+ return `<signed-bytes:${hexLen / 2}B>`;
25
+ });
26
+ // Long base64 runs (Solana / Jito payloads). base64 signed-tx is ~200 chars.
27
+ out = out.replace(/[A-Za-z0-9+/]{160,}={0,2}/g, (m) => `<signed-bytes-b64:${m.length}c>`);
28
+ out = out.replace(/([?&])([a-zA-Z_-]*(?:api[-_]?key|key|token|access[-_]?token|secret|auth))=[^\s"&]+/gi, '$1$2=<redacted>');
29
+ out = out.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer <redacted>');
30
+ out = out.replace(/Authorization:\s*[A-Za-z0-9._~+/=-]+/gi, 'Authorization: <redacted>');
31
+ out = out.replace(/((?:x-)?api[-_]?key)\s*:\s*[A-Za-z0-9._~+/=-]+/gi, '$1: <redacted>');
32
+ out = out.replace(/(\/v\d+|\/api|\/rpc)\/[A-Za-z0-9_-]{16,}(?=[\s/,)\];.'"]|$)/gi, '$1/<redacted>');
33
+ out = out.replace(/https?:\/\/[^\s"'`]*\.(?:alchemy\.com|infura\.io|quiknode\.pro|helius-rpc\.com|ankr\.com|blastapi\.io|drpc\.org)\/[A-Za-z0-9_./=-]{8,}/gi, (m) => {
34
+ try {
35
+ const u = new URL(m);
36
+ return `${u.protocol}//${u.host}/<redacted>`;
37
+ }
38
+ catch {
39
+ return '<rpc>';
40
+ }
41
+ });
13
42
  if (!rpcUrl)
14
- return message;
15
- let host;
43
+ return out;
16
44
  try {
17
45
  const u = new URL(rpcUrl);
18
- host = `${u.protocol}//${u.host}`;
46
+ const host = `${u.protocol}//${u.host}`;
47
+ return out.replaceAll(rpcUrl, host);
19
48
  }
20
49
  catch {
21
- return message.replaceAll(rpcUrl, '<rpc>');
50
+ return out.replaceAll(rpcUrl, '<rpc>');
22
51
  }
23
- return message.replaceAll(rpcUrl, host);
24
52
  }
25
53
  /**
26
54
  * Build a fresh `Error` from `cause` with a sanitized message and stack,
@@ -34,7 +62,9 @@ function sanitizeMessage(message, rpcUrl) {
34
62
  function sanitizeCause(cause, rpcUrl) {
35
63
  if (!(cause instanceof Error))
36
64
  return undefined;
37
- const safe = new Error(sanitizeMessage(cause.message, rpcUrl));
65
+ const short = cause.shortMessage;
66
+ const source = typeof short === 'string' && short.length > 0 ? short : cause.message;
67
+ const safe = new Error(sanitizeMessage(source, rpcUrl));
38
68
  safe.name = cause.name;
39
69
  if (cause.stack)
40
70
  safe.stack = sanitizeMessage(cause.stack, rpcUrl);
@@ -49,6 +79,13 @@ exports.ChainErrorKinds = {
49
79
  InvalidAddress: 'invalid_address',
50
80
  InvalidTokenIdentifier: 'invalid_token_identifier',
51
81
  InvalidArgument: 'invalid_argument',
82
+ BroadcastRejected: 'broadcast_rejected',
83
+ NonceTooLow: 'nonce_too_low',
84
+ InsufficientFunds: 'insufficient_funds',
85
+ BlockhashExpired: 'blockhash_expired',
86
+ SimulationFailed: 'simulation_failed',
87
+ TransactionTooLarge: 'transaction_too_large',
88
+ FeatureNotSupported: 'feature_not_supported',
52
89
  };
53
90
  class ChainError extends Error {
54
91
  kind;
@@ -66,4 +103,16 @@ function isChainError(err, kind) {
66
103
  return false;
67
104
  return kind === undefined || err.kind === kind;
68
105
  }
106
+ function isBlockhashExpiredError(err) {
107
+ return isChainError(err, exports.ChainErrorKinds.BlockhashExpired);
108
+ }
109
+ function isSimulationError(err) {
110
+ return isChainError(err, exports.ChainErrorKinds.SimulationFailed);
111
+ }
112
+ function isNonceError(err) {
113
+ return isChainError(err, exports.ChainErrorKinds.NonceTooLow);
114
+ }
115
+ function isTransactionTooLargeError(err) {
116
+ return isChainError(err, exports.ChainErrorKinds.TransactionTooLarge);
117
+ }
69
118
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../errors.ts"],"names":[],"mappings":";;;AAKA,0CAUC;AAWD,sCAMC;AAoCD,oCAGC;AAvED;;;;GAIG;AACH,SAAgB,eAAe,CAAC,OAAe,EAAE,MAAqB;IACpE,IAAI,CAAC,MAAM;QAAE,OAAO,OAAO,CAAC;IAC5B,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,GAAG,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,aAAa,CAAC,KAAc,EAAE,MAAqB;IACjE,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/D,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACvB,IAAI,KAAK,CAAC,KAAK;QAAE,IAAI,CAAC,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACnE,OAAO,IAAI,CAAC;AACd,CAAC;AAEY,QAAA,eAAe,GAAG;IAC7B,iBAAiB,EAAE,qBAAqB;IACxC,cAAc,EAAE,iBAAiB;IACjC,gBAAgB,EAAE,oBAAoB;IACtC,QAAQ,EAAE,WAAW;IACrB,uBAAuB,EAAE,2BAA2B;IACpD,cAAc,EAAE,iBAAiB;IACjC,sBAAsB,EAAE,0BAA0B;IAClD,eAAe,EAAE,kBAAkB;CAC3B,CAAC;AAaX,MAAa,UAAW,SAAQ,KAAK;IAC1B,IAAI,CAAiB;IACrB,IAAI,CAAiB;IAE9B,YAAY,IAAoB,EAAE,OAAe,EAAE,OAAuB,EAAE,EAAE,KAAe;QAC3F,KAAK,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAVD,gCAUC;AAED,SAAgB,YAAY,CAAC,GAAY,EAAE,IAAqB;IAC9D,IAAI,CAAC,CAAC,GAAG,YAAY,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/C,OAAO,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../errors.ts"],"names":[],"mappings":"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"}
@@ -57,6 +57,7 @@ const ERC20_ABI = [
57
57
  const ERC20_INTERFACE = new ethers_1.Interface(ERC20_ABI);
58
58
  class EvmChain extends chain_base_ts_1.Chain {
59
59
  rpcUrl;
60
+ rpcUrls;
60
61
  supportsEip1559;
61
62
  hasL1Fee;
62
63
  /**
@@ -74,6 +75,7 @@ class EvmChain extends chain_base_ts_1.Chain {
74
75
  * `nativeTransferGasLimit`.
75
76
  */
76
77
  nativeTransferGasMultiplier;
78
+ supports7702;
77
79
  _nativeToken;
78
80
  _provider = null;
79
81
  _resolvedRpcUrl = null;
@@ -90,10 +92,15 @@ class EvmChain extends chain_base_ts_1.Chain {
90
92
  }
91
93
  super(init.chainId, init.name, network_type_ts_1.NetworkType.EVM, init.blockTimeSeconds, init.nativeSymbol, init.explorerBaseUrl);
92
94
  this.rpcUrl = init.rpcUrl;
95
+ this.rpcUrls = init.rpcUrls ?? [];
96
+ if (this.rpcUrls.length > 1) {
97
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `EvmChainInit.rpcUrls accepts only ONE endpoint in 0.3.0 (got ${this.rpcUrls.length}). Automatic failover retry is deferred to a follow-up release; passing >1 endpoint would silently discard entries.`, { chainId: init.chainId });
98
+ }
93
99
  this.supportsEip1559 = init.supportsEip1559 ?? true;
94
100
  this.hasL1Fee = init.hasL1Fee ?? false;
95
101
  this.nativeTransferGasLimit = init.nativeTransferGasLimit ?? 21000;
96
102
  this.nativeTransferGasMultiplier = init.nativeTransferGasMultiplier ?? 1.4;
103
+ this.supports7702 = init.supports7702 ?? false;
97
104
  this._nativeToken = evm_token_ts_1.EvmToken.native(init.chainId, init.nativeSymbol, init.nativeDecimals ?? 18);
98
105
  // NOTE: no `registerNonEvmChain(id, EVM)` call — a module-scope write on
99
106
  // 48 pre-baked chains would let a consumer's earlier
@@ -236,6 +243,13 @@ class EvmChain extends chain_base_ts_1.Chain {
236
243
  readRpcUrl() {
237
244
  if (this.rpcUrl && this.rpcUrl.trim().length > 0)
238
245
  return this.rpcUrl.trim();
246
+ if (this.rpcUrls.length > 0) {
247
+ for (const candidate of this.rpcUrls) {
248
+ if (candidate && candidate.trim().length > 0)
249
+ return candidate.trim();
250
+ }
251
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcNotConfigured, `${this.name}: rpcUrls was supplied but every entry is blank; refusing to fall back to env or public defaults`, { chainId: this.chainId });
252
+ }
239
253
  const env = globalThis.process?.env;
240
254
  const candidates = envCandidatesFor(this.name, this.chainId);
241
255
  for (const key of candidates) {
@@ -243,7 +257,7 @@ class EvmChain extends chain_base_ts_1.Chain {
243
257
  if (value && value.trim().length > 0)
244
258
  return value.trim();
245
259
  }
246
- throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcNotConfigured, `${this.name} RPC URL is not configured (pass rpcUrl at construction, or set one of: ${candidates.join(', ')})`, { chainId: this.chainId, envCandidates: candidates });
260
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcNotConfigured, `${this.name} RPC URL is not configured (pass rpcUrl, rpcUrls at construction, or set one of: ${candidates.join(', ')})`, { chainId: this.chainId, envCandidates: candidates });
247
261
  }
248
262
  rpcHost() {
249
263
  const url = this._resolvedRpcUrl ?? this.rpcUrl;
@@ -279,6 +293,15 @@ class EvmChain extends chain_base_ts_1.Chain {
279
293
  return false;
280
294
  }
281
295
  }
296
+ normalizeAddressOrThrow(raw, field) {
297
+ const body = raw.replace(/^0[xX]/, '');
298
+ try {
299
+ return (0, ethers_1.getAddress)(`0x${body}`);
300
+ }
301
+ catch (err) {
302
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `Invalid EVM address for ${field}: ${raw}`, { chainId: this.chainId, address: raw }, err instanceof Error ? err : undefined);
303
+ }
304
+ }
282
305
  validateTokenIdentifier(raw) {
283
306
  if (raw === undefined)
284
307
  return true;
@@ -377,7 +400,323 @@ class EvmChain extends chain_base_ts_1.Chain {
377
400
  throw this.rpcError('Failed to read chain tip', err);
378
401
  }
379
402
  }
380
- async getTransactionStatus(txHash) {
403
+ async broadcast(signed, opts) {
404
+ if (opts && opts.signal !== undefined) {
405
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.FeatureNotSupported, `EVM broadcast: signal is not honored in 0.3.0 (silently ignoring would let a caller conclude 'not sent' while the tx still lands). Cancellation returns in 0.3.1.`, { chainId: this.chainId });
406
+ }
407
+ let hex;
408
+ if (typeof signed === 'string') {
409
+ const stripped = signed.startsWith('0x') ? signed.slice(2) : signed;
410
+ if (!/^[0-9a-fA-F]+$/.test(stripped) || stripped.length % 2 !== 0 || stripped.length === 0) {
411
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `EVM broadcast: signed transaction must be 0x-prefixed hex or Uint8Array (got malformed string of length ${signed.length})`, { chainId: this.chainId });
412
+ }
413
+ hex = `0x${stripped}`;
414
+ }
415
+ else {
416
+ if (signed.length === 0) {
417
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `EVM broadcast: signed transaction bytes are empty`, { chainId: this.chainId });
418
+ }
419
+ hex = (0, ethers_1.hexlify)(signed);
420
+ }
421
+ try {
422
+ const resp = await this.getProvider().broadcastTransaction(hex);
423
+ return resp.hash;
424
+ }
425
+ catch (err) {
426
+ const rawMsg = (err instanceof Error ? err.message : String(err)).toLowerCase();
427
+ if (/already[-_ ]?known|known\s+transaction|transaction[-_ ]?already[-_ ]?known|already\s+in\s+(?:the\s+)?(?:mempool|pool)|already[-_ ]?present/.test(rawMsg)) {
428
+ // Node "already known" is authoritative for THAT node's mempool —
429
+ // the identical signed bytes are pending or landed. Return the
430
+ // deterministic hash so the consumer stays on the polling path.
431
+ // Do NOT gate on a follow-up getTransaction read: load-balanced
432
+ // providers (Alchemy/Infura/QuickNode round-robin) routinely serve
433
+ // the confirmation read from a different backend that has not yet
434
+ // seen the pending tx → null → falsely-terminal BroadcastRejected
435
+ // → consumer re-signs against a fresh nonce → double-send while
436
+ // the original is live. If the tx really never lands, the
437
+ // consumer's getTransactionStatus poll will surface NotFound and
438
+ // the safe-broadcast rule (re-broadcast same bytes, never re-sign)
439
+ // still applies. Matches SolanaChain/UtxoChain already-known paths.
440
+ return (0, ethers_1.keccak256)(hex);
441
+ }
442
+ throw this.classifyBroadcastError(err);
443
+ }
444
+ }
445
+ async getPendingNonce(address) {
446
+ if (!this.validateAddress(address)) {
447
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `Invalid EVM address for getPendingNonce: ${address}`, { chainId: this.chainId, address });
448
+ }
449
+ const normalized = this.normalizeAddressOrThrow(address, 'getPendingNonce.address');
450
+ try {
451
+ const n = await this.getProvider().getTransactionCount(normalized, 'pending');
452
+ return BigInt(n);
453
+ }
454
+ catch (err) {
455
+ throw this.rpcError(`Failed to read pending nonce for ${normalized}`, err, { address: normalized });
456
+ }
457
+ }
458
+ async getDelegation(address) {
459
+ if (!this.supports7702) {
460
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.FeatureNotSupported, `EVM chain ${this.name} was not initialized with supports7702: true`, { chainId: this.chainId });
461
+ }
462
+ try {
463
+ (0, ethers_1.getAddress)(address);
464
+ }
465
+ catch (err) {
466
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `Invalid EVM address: ${address}`, { chainId: this.chainId, address }, err instanceof Error ? err : undefined);
467
+ }
468
+ let code;
469
+ try {
470
+ code = await this.getProvider().getCode(address);
471
+ }
472
+ catch (err) {
473
+ throw this.rpcError(`Failed to read code for ${address}`, err, { address });
474
+ }
475
+ if (!code.startsWith('0x'))
476
+ return null;
477
+ const stripped = code.slice(2).toLowerCase();
478
+ if (stripped.length !== 46)
479
+ return null;
480
+ if (!stripped.startsWith('ef0100'))
481
+ return null;
482
+ return { delegate: (0, ethers_1.getAddress)(`0x${stripped.slice(6)}`) };
483
+ }
484
+ async call(req) {
485
+ if (!this.validateAddress(req.to)) {
486
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `Invalid EVM 'to' address for call: ${req.to}`, { chainId: this.chainId, address: req.to });
487
+ }
488
+ if (req.from !== undefined && !this.validateAddress(req.from)) {
489
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `Invalid EVM 'from' address for call: ${req.from}`, { chainId: this.chainId, address: req.from });
490
+ }
491
+ if (!/^0x([0-9a-fA-F]{2})*$/.test(req.data)) {
492
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `EvmCallRequest.data must be 0x-prefixed even-length hex`, { chainId: this.chainId });
493
+ }
494
+ const provider = this.getProvider();
495
+ const tx = {
496
+ to: this.normalizeAddressOrThrow(req.to, 'call.to'),
497
+ data: req.data,
498
+ from: req.from === undefined ? undefined : this.normalizeAddressOrThrow(req.from, 'call.from'),
499
+ value: req.value,
500
+ };
501
+ if (req.estimateGas && req.blockTag !== undefined) {
502
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `EvmChain.call: blockTag is not honored when estimateGas: true (ethers v6 eth_estimateGas has no block-tag plumbing). Split the call: use estimateGas without blockTag, or drop estimateGas to run eth_call at the tag.`, { chainId: this.chainId });
503
+ }
504
+ try {
505
+ if (req.estimateGas) {
506
+ const g = await provider.estimateGas(tx);
507
+ return { gasEstimate: BigInt(g) };
508
+ }
509
+ const result = await provider.call(req.blockTag !== undefined ? { ...tx, blockTag: req.blockTag } : tx);
510
+ return { result };
511
+ }
512
+ catch (err) {
513
+ const strCode = ethersErrCode(err);
514
+ const rawData = err.data
515
+ ?? err.info?.error?.data;
516
+ const revertData = typeof rawData === 'string' && /^0x[0-9a-fA-F]*$/.test(rawData) ? rawData : undefined;
517
+ if (strCode === 'CALL_EXCEPTION' || revertData !== undefined) {
518
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.SimulationFailed, (0, errors_ts_1.sanitizeMessage)(`eth_${req.estimateGas ? 'estimateGas' : 'call'} on ${req.to} reverted`, this._resolvedRpcUrl), { chainId: this.chainId, revertData }, (0, errors_ts_1.sanitizeCause)(err, this._resolvedRpcUrl));
519
+ }
520
+ if (strCode === 'INSUFFICIENT_FUNDS') {
521
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InsufficientFunds, (0, errors_ts_1.sanitizeMessage)(`eth_${req.estimateGas ? 'estimateGas' : 'call'} on ${req.to}: insufficient funds`, this._resolvedRpcUrl), { chainId: this.chainId }, (0, errors_ts_1.sanitizeCause)(err, this._resolvedRpcUrl));
522
+ }
523
+ throw this.rpcError(`eth_${req.estimateGas ? 'estimateGas' : 'call'} on ${req.to} failed`, err);
524
+ }
525
+ }
526
+ buildAuthorizationDigest(input) {
527
+ if (!this.supports7702) {
528
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.FeatureNotSupported, `EVM chain ${this.name} was not initialized with supports7702: true`, { chainId: this.chainId });
529
+ }
530
+ if (input.chainId !== this.chainId) {
531
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Authorization chainId ${input.chainId} does not match ${this.chainId}. Cross-chain replay guard: chainId=0 wildcards are rejected by default.`, { chainId: this.chainId });
532
+ }
533
+ if (input.nonce < 0n) {
534
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Authorization nonce must be >= 0`, { chainId: this.chainId });
535
+ }
536
+ let normalizedDelegate;
537
+ try {
538
+ normalizedDelegate = (0, ethers_1.getAddress)(input.delegate);
539
+ }
540
+ catch (err) {
541
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `buildAuthorizationDigest: delegate is not a valid EVM address: ${input.delegate}`, { chainId: this.chainId, address: input.delegate }, err instanceof Error ? err : undefined);
542
+ }
543
+ const rlp = (0, ethers_1.encodeRlp)([
544
+ (0, ethers_1.toBeArray)(BigInt(input.chainId)),
545
+ normalizedDelegate,
546
+ (0, ethers_1.toBeArray)(input.nonce),
547
+ ]);
548
+ const payload = (0, ethers_1.concat)(['0x05', rlp]);
549
+ return (0, ethers_1.getBytes)((0, ethers_1.keccak256)(payload));
550
+ }
551
+ async createUnsignedTransaction(req) {
552
+ if (req && req.signal !== undefined) {
553
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.FeatureNotSupported, `EVM createUnsignedTransaction: signal is not honored in 0.3.0 (silently ignoring would let a caller conclude 'not built' while the blockhash fetch/build proceeds against stale state). Cancellation returns in 0.3.1.`, { chainId: this.chainId });
554
+ }
555
+ if (!req.from) {
556
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, 'CreateEvmUnsignedTransactionRequest.from is required', { chainId: this.chainId });
557
+ }
558
+ if (!this.validateAddress(req.from)) {
559
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `Invalid EVM 'from' address (fails EIP-55 checksum or byte length): ${req.from}`, { chainId: this.chainId, address: req.from });
560
+ }
561
+ if (!req.to) {
562
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, 'CreateEvmUnsignedTransactionRequest.to is required', { chainId: this.chainId });
563
+ }
564
+ if (!this.validateAddress(req.to)) {
565
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `Invalid EVM 'to' address (fails EIP-55 checksum or byte length): ${req.to}`, { chainId: this.chainId, address: req.to });
566
+ }
567
+ if (req.data !== undefined && !/^0x([0-9a-fA-F]{2})*$/.test(req.data)) {
568
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `CreateEvmUnsignedTransactionRequest.data must be a 0x-prefixed even-length hex string`, { chainId: this.chainId });
569
+ }
570
+ if (req.authorizationList !== undefined) {
571
+ if (!Array.isArray(req.authorizationList) || req.authorizationList.length === 0) {
572
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, 'authorizationList must be a non-empty array when provided', { chainId: this.chainId });
573
+ }
574
+ if (!this.supports7702) {
575
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.FeatureNotSupported, `EVM chain ${this.name} was not initialized with supports7702: true`, { chainId: this.chainId });
576
+ }
577
+ for (let i = 0; i < req.authorizationList.length; i++) {
578
+ const auth = req.authorizationList[i];
579
+ if (auth.chainId !== this.chainId) {
580
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Authorization[${i}].chainId ${auth.chainId} does not match ${this.chainId}. Cross-chain replay guard: chainId=0 wildcards are rejected by default.`, { chainId: this.chainId });
581
+ }
582
+ if (!this.validateAddress(auth.address)) {
583
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `Authorization[${i}].address is not a valid EVM address: ${auth.address}`, { chainId: this.chainId, address: auth.address });
584
+ }
585
+ if (typeof auth.nonce !== 'bigint' || auth.nonce < 0n) {
586
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Authorization[${i}].nonce must be a non-negative bigint (got ${typeof auth.nonce})`, { chainId: this.chainId });
587
+ }
588
+ const sig = auth.signature;
589
+ if (!sig || typeof sig !== 'object'
590
+ || typeof sig.r !== 'string' || !/^0x[0-9a-fA-F]{64}$/.test(sig.r)
591
+ || typeof sig.s !== 'string' || !/^0x[0-9a-fA-F]{64}$/.test(sig.s)
592
+ || (sig.yParity !== 0 && sig.yParity !== 1)) {
593
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Authorization[${i}].signature must be { r: 0x + 64-hex, s: 0x + 64-hex, yParity: 0 | 1 }`, { chainId: this.chainId });
594
+ }
595
+ const rBig = BigInt(sig.r);
596
+ const sBig = BigInt(sig.s);
597
+ if (rBig === 0n || sBig === 0n) {
598
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Authorization[${i}].signature: r and s must both be non-zero`, { chainId: this.chainId });
599
+ }
600
+ // EIP-2 low-s canonical form. An authorization with s > secp256k1n/2
601
+ // is silently rejected by the EVM at execution — tx lands, gas is
602
+ // spent, no delegation installed. Catch at build time.
603
+ const SECP256K1_HALF_N = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0n;
604
+ if (sBig > SECP256K1_HALF_N) {
605
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Authorization[${i}].signature.s violates EIP-2 low-s canonical form (s must be <= secp256k1n/2). Non-canonical authorizations are silently skipped by the EVM.`, { chainId: this.chainId });
606
+ }
607
+ }
608
+ }
609
+ if (!this.supportsEip1559 && (req.maxFeePerGas !== undefined || req.maxPriorityFeePerGas !== undefined)) {
610
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `EVM chain ${this.name} is legacy (supportsEip1559=false); maxFeePerGas/maxPriorityFeePerGas are not accepted. Use gasPrice via UnsignedEvmTransaction consumers.`, { chainId: this.chainId });
611
+ }
612
+ const emittedType = req.authorizationList !== undefined ? 4 : this.supportsEip1559 ? 2 : 0;
613
+ return new unsigned_evm_transaction_ts_1.UnsignedEvmTransaction({
614
+ chainId: this.chainId,
615
+ from: this.normalizeAddressOrThrow(req.from, 'createUnsignedTransaction.from'),
616
+ to: this.normalizeAddressOrThrow(req.to, 'createUnsignedTransaction.to'),
617
+ value: req.value ?? 0n,
618
+ data: req.data ?? '0x',
619
+ type: emittedType,
620
+ authorizationList: req.authorizationList,
621
+ gasLimit: req.gasLimit,
622
+ nonce: req.nonce,
623
+ maxFeePerGas: req.maxFeePerGas,
624
+ maxPriorityFeePerGas: req.maxPriorityFeePerGas,
625
+ });
626
+ }
627
+ classifyBroadcastError(err) {
628
+ const rpc = this._resolvedRpcUrl;
629
+ const strCode = ethersErrCode(err);
630
+ const numCode = errCode(err);
631
+ const rawMsg = err instanceof Error ? err.message : String(err);
632
+ const msg = rawMsg.toLowerCase();
633
+ const safeCause = (0, errors_ts_1.sanitizeCause)(err, rpc);
634
+ if (strCode === 'NONCE_EXPIRED' || msg.includes('nonce too low') || msg.includes('nonce_too_low') || msg.includes('nonce has already been used')) {
635
+ return new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.NonceTooLow, (0, errors_ts_1.sanitizeMessage)(`EVM broadcast rejected: nonce too low on ${this.name}`, rpc), { chainId: this.chainId, rpcHost: this.rpcHost() }, safeCause);
636
+ }
637
+ if (strCode === 'INSUFFICIENT_FUNDS' || msg.includes('insufficient funds')) {
638
+ return new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InsufficientFunds, (0, errors_ts_1.sanitizeMessage)(`EVM broadcast rejected: insufficient funds on ${this.name}`, rpc), { chainId: this.chainId, rpcHost: this.rpcHost() }, safeCause);
639
+ }
640
+ if (strCode === 'REPLACEMENT_UNDERPRICED' || msg.includes('replacement') || msg.includes('underpriced')) {
641
+ return new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.BroadcastRejected, (0, errors_ts_1.sanitizeMessage)(`EVM broadcast rejected on ${this.name} (replacement/known-nonce)`, rpc), { chainId: this.chainId, rpcHost: this.rpcHost() }, safeCause);
642
+ }
643
+ const rateLimitCodes = new Set([-32005, -32007, -32016, -32029, 429]);
644
+ if (numCode !== undefined && rateLimitCodes.has(numCode)) {
645
+ return this.rpcError(`EVM broadcast rate-limited on ${this.name}`, err);
646
+ }
647
+ const transportSignals = /econnreset|econnrefused|econnaborted|etimedout|enotfound|network request failed|fetch failed|socket hang up|too\s+many\s+requests|rate.?limit|network error/;
648
+ const httpStatus = err.info?.status
649
+ ?? err.status;
650
+ const isTransportHttp = typeof httpStatus === 'number' && (httpStatus === 429 || httpStatus === 502 || httpStatus === 503 || httpStatus === 504);
651
+ if (transportSignals.test(msg) || isTransportHttp
652
+ || strCode === 'NETWORK_ERROR' || strCode === 'SERVER_ERROR' || strCode === 'TIMEOUT') {
653
+ return this.rpcError(`EVM broadcast transport failure on ${this.name}`, err);
654
+ }
655
+ // Default terminal: any unrecognized rejection is a node reject, NOT a
656
+ // transient transport failure. This matches Solana/UTXO defaults and
657
+ // prevents a relayer retry-loop from re-broadcasting a permanently-
658
+ // invalid tx forever.
659
+ return new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.BroadcastRejected, (0, errors_ts_1.sanitizeMessage)(`EVM broadcast rejected on ${this.name}`, rpc), { chainId: this.chainId, rpcHost: this.rpcHost() }, safeCause);
660
+ }
661
+ async getTransactionStatus(txHash, opts) {
662
+ if (Array.isArray(txHash)) {
663
+ return runBatchStatus(txHash, (h, batchSignal) => {
664
+ const composedSignal = opts?.signal
665
+ ? anySignal(opts.signal, batchSignal)
666
+ : batchSignal;
667
+ return this.getSingleTransactionStatus(h, { ...opts, signal: composedSignal });
668
+ });
669
+ }
670
+ return this.getSingleTransactionStatus(txHash, opts);
671
+ }
672
+ async getSingleTransactionStatus(txHash, opts) {
673
+ if (opts?.confirmations !== undefined && opts.confirmations > 1 && !opts.wait) {
674
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `getTransactionStatus: confirmations > 1 requires wait: true (a single status read cannot enforce block depth)`, { chainId: this.chainId, txHash });
675
+ }
676
+ if (!opts?.wait)
677
+ return this.getTransactionStatusOnce(txHash);
678
+ if (opts.signal?.aborted) {
679
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `getTransactionStatus aborted before first poll`, { chainId: this.chainId, txHash });
680
+ }
681
+ if (opts.timeoutMs === undefined) {
682
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `getTransactionStatus: wait: true requires an explicit timeoutMs. Unbounded polling would pin an RPC worker + connection forever if the tx is dropped.`, { chainId: this.chainId, txHash });
683
+ }
684
+ if (opts.timeoutMs < 0) {
685
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `getTransactionStatus: timeoutMs must be >= 0`, { chainId: this.chainId, txHash });
686
+ }
687
+ const deadline = Date.now() + opts.timeoutMs;
688
+ const pollMs = Math.max(500, this.blockTimeSeconds * 1000);
689
+ const minConfirmations = Math.max(1, opts.confirmations ?? 1);
690
+ let last;
691
+ while (true) {
692
+ last = await this.getTransactionStatusOnce(txHash);
693
+ if (last.status === 'Success' || last.status === 'Failed') {
694
+ if (minConfirmations <= 1)
695
+ return last;
696
+ let tip;
697
+ try {
698
+ tip = await this.getProvider().getBlockNumber();
699
+ }
700
+ catch (err) {
701
+ throw this.rpcError(`Failed to read chain tip for confirmations check`, err, { txHash });
702
+ }
703
+ const depth = last.blockNumber === null ? 0 : tip - last.blockNumber + 1;
704
+ if (depth >= minConfirmations)
705
+ return last;
706
+ if (Date.now() >= deadline) {
707
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, `getTransactionStatus timed out after ${opts.timeoutMs}ms with only ${depth} confirmation(s); required ${minConfirmations}. Consumer must NOT credit as final.`, { chainId: this.chainId, txHash });
708
+ }
709
+ }
710
+ else if (Date.now() >= deadline) {
711
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, `getTransactionStatus timed out after ${opts.timeoutMs}ms; last observed status was ${last.status}. Consumer must NOT credit as final AND must NOT re-sign — the tx may still land. Re-broadcast the same signed bytes or continue polling with the same txHash.`, { chainId: this.chainId, txHash });
712
+ }
713
+ await interruptibleSleep(pollMs, opts.signal);
714
+ if (opts.signal?.aborted) {
715
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `getTransactionStatus aborted mid-poll`, { chainId: this.chainId, txHash });
716
+ }
717
+ }
718
+ }
719
+ async getTransactionStatusOnce(txHash) {
381
720
  const provider = this.getProvider();
382
721
  let tx = null;
383
722
  let receipt = null;
@@ -473,6 +812,7 @@ class EvmChain extends chain_base_ts_1.Chain {
473
812
  inclusionAt,
474
813
  error: errorInfo,
475
814
  fees,
815
+ blockNumber: receipt.blockNumber,
476
816
  });
477
817
  }
478
818
  // Only allocated on the Success path — failed txs carry logs: null.
@@ -499,6 +839,7 @@ class EvmChain extends chain_base_ts_1.Chain {
499
839
  balanceChanges,
500
840
  logs,
501
841
  fees,
842
+ blockNumber: receipt.blockNumber,
502
843
  });
503
844
  }
504
845
  async decodeBalanceChanges(args) {
@@ -683,6 +1024,84 @@ function stringifyErr(err) {
683
1024
  return err.message;
684
1025
  return String(err);
685
1026
  }
1027
+ const BATCH_STATUS_CONCURRENCY = 8;
1028
+ async function runBatchStatus(items, fetchOne) {
1029
+ const results = new Array(items.length);
1030
+ const ac = new AbortController();
1031
+ let cursor = 0;
1032
+ const workers = [];
1033
+ const spawn = async () => {
1034
+ while (!ac.signal.aborted) {
1035
+ const idx = cursor++;
1036
+ if (idx >= items.length)
1037
+ return;
1038
+ try {
1039
+ results[idx] = await fetchOne(items[idx], ac.signal);
1040
+ }
1041
+ catch (err) {
1042
+ ac.abort();
1043
+ throw err;
1044
+ }
1045
+ }
1046
+ };
1047
+ const workerCount = Math.min(BATCH_STATUS_CONCURRENCY, items.length);
1048
+ for (let i = 0; i < workerCount; i++)
1049
+ workers.push(spawn());
1050
+ await Promise.all(workers);
1051
+ return results;
1052
+ }
1053
+ function anySignal(a, b) {
1054
+ if (typeof AbortSignal.any === 'function')
1055
+ return AbortSignal.any([a, b]);
1056
+ const ac = new AbortController();
1057
+ const onAbort = () => ac.abort();
1058
+ if (a.aborted || b.aborted)
1059
+ ac.abort();
1060
+ else {
1061
+ a.addEventListener('abort', onAbort, { once: true });
1062
+ b.addEventListener('abort', onAbort, { once: true });
1063
+ }
1064
+ return ac.signal;
1065
+ }
1066
+ async function interruptibleSleep(ms, signal) {
1067
+ if (signal?.aborted)
1068
+ return;
1069
+ await new Promise((resolve) => {
1070
+ const t = setTimeout(() => {
1071
+ signal?.removeEventListener('abort', onAbort);
1072
+ resolve();
1073
+ }, ms);
1074
+ const onAbort = () => {
1075
+ clearTimeout(t);
1076
+ resolve();
1077
+ };
1078
+ signal?.addEventListener('abort', onAbort, { once: true });
1079
+ });
1080
+ }
1081
+ function errCode(err) {
1082
+ if (typeof err !== 'object' || err === null)
1083
+ return undefined;
1084
+ const e = err;
1085
+ const candidates = [
1086
+ typeof e.code === 'number' ? e.code : undefined,
1087
+ e.error?.code,
1088
+ e.info?.error?.code,
1089
+ typeof e.code === 'string' && /^-?\d+$/.test(e.code) ? e.code : undefined,
1090
+ ];
1091
+ for (const raw of candidates) {
1092
+ if (typeof raw === 'number')
1093
+ return raw;
1094
+ if (typeof raw === 'string' && /^-?\d+$/.test(raw))
1095
+ return Number(raw);
1096
+ }
1097
+ return undefined;
1098
+ }
1099
+ function ethersErrCode(err) {
1100
+ if (typeof err !== 'object' || err === null)
1101
+ return undefined;
1102
+ const e = err;
1103
+ return typeof e.code === 'string' ? e.code : undefined;
1104
+ }
686
1105
  function envCandidatesFor(name, chainId) {
687
1106
  const normalized = name.replace(/ /g, '_').toUpperCase() + '_RPC_URL';
688
1107
  return [normalized, `EVM_${chainId}_RPC_URL`];