@circle-fin/adapter-ethers-v6 1.0.0 → 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.
package/index.cjs.js CHANGED
@@ -268,6 +268,7 @@ exports.Blockchain = void 0;
268
268
  Blockchain["Algorand_Testnet"] = "Algorand_Testnet";
269
269
  Blockchain["Aptos"] = "Aptos";
270
270
  Blockchain["Aptos_Testnet"] = "Aptos_Testnet";
271
+ Blockchain["Arc_Testnet"] = "Arc_Testnet";
271
272
  Blockchain["Arbitrum"] = "Arbitrum";
272
273
  Blockchain["Arbitrum_Sepolia"] = "Arbitrum_Sepolia";
273
274
  Blockchain["Avalanche"] = "Avalanche";
@@ -488,6 +489,48 @@ const BRIDGE_CONTRACT_EVM_TESTNET = '0xC5567a5E3370d4DBfB0540025078e283e36A363d'
488
489
  */
489
490
  const BRIDGE_CONTRACT_EVM_MAINNET = '0xB3FA262d0fB521cc93bE83d87b322b8A23DAf3F0';
490
491
 
492
+ /**
493
+ * Arc Testnet chain definition
494
+ * @remarks
495
+ * This represents the test network for the Arc blockchain,
496
+ * Circle's EVM-compatible Layer-1 designed for stablecoin finance
497
+ * and asset tokenization. Arc uses USDC as the native gas token and
498
+ * features the Malachite Byzantine Fault Tolerant (BFT) consensus
499
+ * engine for sub-second finality.
500
+ */
501
+ const ArcTestnet = defineChain({
502
+ type: 'evm',
503
+ chain: exports.Blockchain.Arc_Testnet,
504
+ name: 'Arc Testnet',
505
+ title: 'ArcTestnet',
506
+ nativeCurrency: {
507
+ name: 'Arc',
508
+ symbol: 'Arc',
509
+ decimals: 18,
510
+ },
511
+ chainId: 5042002,
512
+ isTestnet: true,
513
+ explorerUrl: 'https://testnet.arcscan.app/tx/{hash}',
514
+ rpcEndpoints: ['https://rpc.testnet.arc.network/'],
515
+ eurcAddress: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
516
+ usdcAddress: '0x3600000000000000000000000000000000000000',
517
+ cctp: {
518
+ domain: 26,
519
+ contracts: {
520
+ v2: {
521
+ type: 'split',
522
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
523
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
524
+ confirmations: 1,
525
+ fastConfirmations: 1,
526
+ },
527
+ },
528
+ },
529
+ kitContracts: {
530
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
531
+ },
532
+ });
533
+
491
534
  /**
492
535
  * Arbitrum Mainnet chain definition
493
536
  * @remarks
@@ -1779,26 +1822,26 @@ const Sonic = defineChain({
1779
1822
  });
1780
1823
 
1781
1824
  /**
1782
- * Sonic Blaze Testnet chain definition
1825
+ * Sonic Testnet chain definition
1783
1826
  * @remarks
1784
1827
  * This represents the official test network for the Sonic blockchain.
1785
1828
  */
1786
1829
  const SonicTestnet = defineChain({
1787
1830
  type: 'evm',
1788
1831
  chain: exports.Blockchain.Sonic_Testnet,
1789
- name: 'Sonic Blaze Testnet',
1790
- title: 'Sonic Blaze Testnet',
1832
+ name: 'Sonic Testnet',
1833
+ title: 'Sonic Testnet',
1791
1834
  nativeCurrency: {
1792
1835
  name: 'Sonic',
1793
1836
  symbol: 'S',
1794
1837
  decimals: 18,
1795
1838
  },
1796
- chainId: 57054,
1839
+ chainId: 14601,
1797
1840
  isTestnet: true,
1798
1841
  explorerUrl: 'https://testnet.sonicscan.org/tx/{hash}',
1799
- rpcEndpoints: ['https://rpc.blaze.soniclabs.com'],
1842
+ rpcEndpoints: ['https://rpc.testnet.soniclabs.com'],
1800
1843
  eurcAddress: null,
1801
- usdcAddress: '0xA4879Fed32Ecbef99399e5cbC247E533421C4eC6',
1844
+ usdcAddress: '0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51',
1802
1845
  cctp: {
1803
1846
  domain: 13,
1804
1847
  contracts: {
@@ -2315,6 +2358,7 @@ var Blockchains = {
2315
2358
  AptosTestnet: AptosTestnet,
2316
2359
  Arbitrum: Arbitrum,
2317
2360
  ArbitrumSepolia: ArbitrumSepolia,
2361
+ ArcTestnet: ArcTestnet,
2318
2362
  Avalanche: Avalanche,
2319
2363
  AvalancheFuji: AvalancheFuji,
2320
2364
  Base: Base,
@@ -2992,6 +3036,10 @@ class KitError extends Error {
2992
3036
  }
2993
3037
  }
2994
3038
 
3039
+ /**
3040
+ * Minimum error code for INPUT type errors.
3041
+ * INPUT errors represent validation failures and invalid parameters.
3042
+ */
2995
3043
  /**
2996
3044
  * Standardized error definitions for INPUT type errors.
2997
3045
  *
@@ -8012,6 +8060,246 @@ class EvmAdapter extends Adapter {
8012
8060
  }
8013
8061
  }
8014
8062
 
8063
+ // --- Signature Parsing Constants ---
8064
+ const SIGNATURE_BYTE_LENGTH = 65; // Total bytes in an Ethereum signature
8065
+ const HEX_CHARS_PER_BYTE = 2;
8066
+ const SIGNATURE_HEX_LENGTH = SIGNATURE_BYTE_LENGTH * HEX_CHARS_PER_BYTE;
8067
+ const R_HEX_LENGTH = 32 * HEX_CHARS_PER_BYTE; // 32 bytes for 'r'
8068
+ const S_HEX_LENGTH = 32 * HEX_CHARS_PER_BYTE; // 32 bytes for 's'
8069
+ // --- Recovery ID Validation Constants ---
8070
+ /**
8071
+ * Legacy recovery IDs (pre-EIP-155 format):
8072
+ * - 0 or 1 (older clients) or 27, 28 (Ethereum before chain-id inclusion)
8073
+ */
8074
+ const LEGACY_V_VALUES = new Set([0, 1, 27, 28]);
8075
+ /**
8076
+ * Minimum v value indicating an EIP-155 style signature.
8077
+ * Calculated as 35 + (2 * chainId) + recoveryId (0 or 1).
8078
+ */
8079
+ const MIN_EIP155_V = 35;
8080
+ /**
8081
+ * parseSignature
8082
+ *
8083
+ * Parse a 65-byte ECDSA signature into its r, s, and v components, expressed in hex.
8084
+ *
8085
+ * Ethereum signatures are structured as:
8086
+ * signature = r (32 bytes) || s (32 bytes) || v (1 byte)
8087
+ *
8088
+ * - r, s: Big-endian hex values (32 bytes each)
8089
+ * - v: Recovery identifier, used by secp256k1 to reconstruct the signer’s public key
8090
+ *
8091
+ * @param signatureHex - Signature as a hex string, optionally prefixed with "0x".
8092
+ * @returns { r, s, v }
8093
+ * - r: Hex string of the R component.
8094
+ * - s: Hex string of the S component.
8095
+ * - v: Numeric recovery ID.
8096
+ *
8097
+ * @throws Error if:
8098
+ * - Input is not valid hex.
8099
+ * - Incorrect length (must be exactly 65 bytes / 130 hex chars).
8100
+ * - v is outside the supported range (Legacy or EIP-155 formula).
8101
+ *
8102
+ * Notes on EIP-155 overload:
8103
+ * EIP-155 repurposes the v field so that:
8104
+ * v = 35 + (2 * chainId) + recoveryId(0 or 1)
8105
+ * Any v value >= 35 indicates the chain-id is encoded, preventing cross-chain replay.
8106
+ * r and s values remain unchanged.
8107
+ *
8108
+ * Example:
8109
+ * ```typescript
8110
+ * const rawSig = '0x6c1b...f02b'
8111
+ * try {
8112
+ * const { r, s, v } = parseSignature(rawSig)
8113
+ * console.log('R:', r)
8114
+ * console.log('S:', s)
8115
+ * console.log('Recovery ID (v):', v)
8116
+ * } catch (err) {
8117
+ * console.error('Signature parse error:', err.message)
8118
+ * }
8119
+ * ```
8120
+ */
8121
+ function parseSignature(signatureHex) {
8122
+ // 1) Remove optional '0x' prefix
8123
+ const hex = signatureHex.startsWith('0x')
8124
+ ? signatureHex.slice(2)
8125
+ : signatureHex;
8126
+ // 2) Validate hex format and length
8127
+ const VALID_HEX_REGEX = /^[0-9a-fA-F]+$/;
8128
+ if (!VALID_HEX_REGEX.test(hex)) {
8129
+ throw new Error('Signature must be a valid hex string (0-9, a-f, A-F).');
8130
+ }
8131
+ if (hex.length !== SIGNATURE_HEX_LENGTH) {
8132
+ throw new Error(`Invalid length: expected ${SIGNATURE_HEX_LENGTH.toString()} hex chars (65 bytes), got ${hex.length.toString()}`);
8133
+ }
8134
+ // 3) Extract components
8135
+ const r = `0x${hex.slice(0, R_HEX_LENGTH)}`;
8136
+ const s = `0x${hex.slice(R_HEX_LENGTH, R_HEX_LENGTH + S_HEX_LENGTH)}`;
8137
+ const vValue = parseInt(hex.slice(R_HEX_LENGTH + S_HEX_LENGTH), 16);
8138
+ // 4) Ensure v is within supported values
8139
+ const isLegacy = LEGACY_V_VALUES.has(vValue);
8140
+ const isEIP155 = vValue >= MIN_EIP155_V;
8141
+ if (!isLegacy && !isEIP155) {
8142
+ throw new Error(`Unsupported v value: ${vValue.toString()}. Must be one of [${[
8143
+ ...LEGACY_V_VALUES,
8144
+ ].join(', ')}] or >=${MIN_EIP155_V.toString()}`);
8145
+ }
8146
+ return { r: r, s: s, v: vValue };
8147
+ }
8148
+ /**
8149
+ * buildTypedData
8150
+ *
8151
+ * Create an EIP-712 compliant "TypedData" object, encapsulating:
8152
+ * - Domain separator (application/contract identity, chain ID)
8153
+ * - Explicit type definitions (field names and types)
8154
+ * - Primary type name identifying the root object
8155
+ * - Message payload matching the defined schema
8156
+ *
8157
+ * This enforces structured, non-ambiguous signing, and guards against signature replay.
8158
+ *
8159
+ * @typeParam Types - Map of type names to arrays of { name, type } definitions.
8160
+ * @typeParam Msg - Object whose shape matches the primaryType fields.
8161
+ *
8162
+ * @param domain - EIP712Domain, e.g. { name, version, chainId, verifyingContract }.
8163
+ * @param types - Type definitions, e.g.:
8164
+ * {
8165
+ * Mail: [ { name: 'from', type: 'string' }, { name: 'contents', type: 'string' } ],
8166
+ * }
8167
+ * @param primaryType - Root key in types, e.g. 'Mail'.
8168
+ * @param message - The actual values, e.g. { from: 'Alice', contents: 'Hello' }.
8169
+ *
8170
+ * @returns TypedData object ready for use with signing libraries.
8171
+ *
8172
+ * Example:
8173
+ * ```typescript
8174
+ * const typedData = buildTypedData(
8175
+ * { name: 'MyDApp', version: '1', chainId: 1, verifyingContract: '0xabc...' },
8176
+ * { Message: [{ name: 'text', type: 'string' }] },
8177
+ * 'Message',
8178
+ * { text: 'Hello, EIP-712!' }
8179
+ * )
8180
+ * // Pass typedData to ethers.js signer._signTypedData(domain, types, message)
8181
+ * ```
8182
+ */
8183
+ function buildTypedData(domain, types, primaryType, message) {
8184
+ return { domain, types, primaryType, message };
8185
+ }
8186
+
8187
+ /**
8188
+ * Compute default deadline for permit signatures.
8189
+ *
8190
+ * Returns a timestamp 1 hour (3600 seconds) from the current time,
8191
+ * converted to Unix timestamp format as a bigint. This is commonly
8192
+ * used as the default expiration time for EIP-2612 permit signatures.
8193
+ *
8194
+ * @returns Unix timestamp (in seconds) 1 hour from now as a bigint
8195
+ *
8196
+ * @example
8197
+ * ```typescript
8198
+ * import { computeDefaultDeadline } from '@core/adapter-evm'
8199
+ *
8200
+ * const deadline = computeDefaultDeadline()
8201
+ * console.log(`Permit expires at: ${deadline}`)
8202
+ * // Output: Permit expires at: 1640998800
8203
+ * ```
8204
+ */
8205
+ function computeDefaultDeadline() {
8206
+ return BigInt(Math.floor(Date.now() / 1000) + 3600);
8207
+ }
8208
+
8209
+ /**
8210
+ * EIP-2612 permit type definition.
8211
+ * Defines the structure for permit signatures according to EIP-2612 specification.
8212
+ *
8213
+ * @see {@link https://eips.ethereum.org/EIPS/eip-2612 | EIP-2612 Specification}
8214
+ */
8215
+ const EIP2612_TYPES = {
8216
+ Permit: [
8217
+ { name: 'owner', type: 'address' },
8218
+ { name: 'spender', type: 'address' },
8219
+ { name: 'value', type: 'uint256' },
8220
+ { name: 'nonce', type: 'uint256' },
8221
+ { name: 'deadline', type: 'uint256' },
8222
+ ],
8223
+ };
8224
+
8225
+ /**
8226
+ * Build EIP-2612 typed data for permit signing.
8227
+ *
8228
+ * This function creates the complete EIP-712 typed data structure required
8229
+ * for EIP-2612 permit signatures, including automatic nonce fetching using
8230
+ * the adapter's built-in nonce fetching capability.
8231
+ *
8232
+ * **Address Formatting**: All addresses are automatically formatted with proper
8233
+ * EIP-55 checksumming using the `convertAddress` utility, ensuring compatibility
8234
+ * with strict validation libraries like viem.
8235
+ *
8236
+ * **Nonce Handling**: The nonce can be provided explicitly or will be fetched
8237
+ * automatically using the adapter's `fetchEIP2612Nonce` method, which queries
8238
+ * the token contract's `nonces(owner)` function.
8239
+ *
8240
+ * **Deadline Calculation**: If no deadline is provided, it defaults to 24 hours
8241
+ * from the current time (computed using `computeDefaultDeadline`).
8242
+ *
8243
+ * @param meta - Domain metadata for the token contract
8244
+ * @param adapter - Adapter instance with nonce-fetching capability
8245
+ * @param opts - EIP-2612 permit options including owner, spender, value
8246
+ * @returns Complete EIP-712 typed data ready for signing
8247
+ *
8248
+ * @example
8249
+ * ```typescript
8250
+ * import { buildEIP2612TypedData } from '@core/adapter-evm'
8251
+ *
8252
+ * const typedData = await buildEIP2612TypedData(
8253
+ * {
8254
+ * name: 'USD Coin',
8255
+ * version: '2',
8256
+ * chainId: 1,
8257
+ * verifyingContract: '0xa0b86a33e6441e4d178bb0c14ce0e9ce9c83bdd8'
8258
+ * },
8259
+ * adapter,
8260
+ * {
8261
+ * owner: '0x742d35cc6639c0532fbe9002b3a2265ca4c878f8e',
8262
+ * spender: '0x1234567890123456789012345678901234567890',
8263
+ * value: 1000000n
8264
+ * }
8265
+ * )
8266
+ *
8267
+ * const signature = await adapter.signTypedData(typedData)
8268
+ * ```
8269
+ */
8270
+ async function buildEIP2612TypedData(meta, adapter, opts, ctx) {
8271
+ // Format addresses to ensure proper EIP-55 checksumming
8272
+ const formattedOwner = convertAddress(opts.owner, 'evm');
8273
+ const formattedSpender = convertAddress(opts.spender, 'evm');
8274
+ const formattedContract = convertAddress(meta.verifyingContract, 'evm');
8275
+ // Fetch nonce if not provided - adapter handles the contract interaction
8276
+ const nonce = opts.nonce ??
8277
+ (await adapter.fetchEIP2612Nonce(formattedContract, formattedOwner, ctx));
8278
+ /*
8279
+ * Calculate deadline if not provided (24 hours from now)
8280
+ * EIP-2612 deadline is a uint256 in the permit struct, so we use bigint for full compatibility
8281
+ * with on-chain values and to avoid overflow/precision issues with large timestamps.
8282
+ * Most real-world deadlines are within JS safe integer range, but using bigint is safest.
8283
+ */
8284
+ const now = BigInt(Math.floor(Date.now() / 1000));
8285
+ const deadline = opts.deadline !== undefined
8286
+ ? BigInt(opts.deadline)
8287
+ : computeDefaultDeadline();
8288
+ if (deadline <= now) {
8289
+ throw new Error(`EIP-2612 deadline must be in the future (got ${deadline.toString()}, now ${now.toString()})`);
8290
+ }
8291
+ // Build the message with properly formatted addresses
8292
+ const message = {
8293
+ owner: formattedOwner,
8294
+ spender: formattedSpender,
8295
+ value: opts.value,
8296
+ nonce,
8297
+ deadline,
8298
+ };
8299
+ // Return complete typed data structure with formatted contract address
8300
+ return buildTypedData({ ...meta, verifyingContract: formattedContract }, EIP2612_TYPES, 'Permit', message);
8301
+ }
8302
+
8015
8303
  /**
8016
8304
  * Checks if a function in an ABI is read-only (view or pure).
8017
8305
  *
@@ -9986,6 +10274,11 @@ class EthersAdapter extends EvmAdapter {
9986
10274
  * ```
9987
10275
  */
9988
10276
  async getAddress(chain) {
10277
+ // Prevent calling getAddress on developer-controlled adapters
10278
+ if (this.capabilities?.addressContext === 'developer-controlled') {
10279
+ throw new Error('Cannot call getAddress() on developer-controlled adapters. ' +
10280
+ 'Address must be provided explicitly in the operation context.');
10281
+ }
9989
10282
  // Chain parameter should now be provided by resolveOperationContext
9990
10283
  if (!chain) {
9991
10284
  throw new Error('Chain parameter is required for address resolution. ' +
@@ -10535,8 +10828,12 @@ const createAdapterFromProvider = async (params) => {
10535
10828
  }, resolvedCapabilities);
10536
10829
  };
10537
10830
 
10831
+ exports.JsonRpcProvider = ethers.JsonRpcProvider;
10538
10832
  exports.EthersAdapter = EthersAdapter;
10833
+ exports.buildEIP2612TypedData = buildEIP2612TypedData;
10834
+ exports.computeDefaultDeadline = computeDefaultDeadline;
10539
10835
  exports.createAdapterFromPrivateKey = createAdapterFromPrivateKey;
10540
10836
  exports.createAdapterFromProvider = createAdapterFromProvider;
10837
+ exports.parseSignature = parseSignature;
10541
10838
  exports.validateAdapterCapabilities = validateAdapterCapabilities;
10542
10839
  //# sourceMappingURL=index.cjs.js.map
package/index.d.ts CHANGED
@@ -17,6 +17,7 @@
17
17
  */
18
18
 
19
19
  import { Provider, Signer as Signer$1, Eip1193Provider } from 'ethers';
20
+ export { JsonRpcProvider } from 'ethers';
20
21
  import { Abi } from 'abitype';
21
22
  import { TransactionInstruction, Signer } from '@solana/web3.js';
22
23
 
@@ -409,6 +410,7 @@ declare enum Blockchain {
409
410
  Algorand_Testnet = "Algorand_Testnet",
410
411
  Aptos = "Aptos",
411
412
  Aptos_Testnet = "Aptos_Testnet",
413
+ Arc_Testnet = "Arc_Testnet",
412
414
  Arbitrum = "Arbitrum",
413
415
  Arbitrum_Sepolia = "Arbitrum_Sepolia",
414
416
  Avalanche = "Avalanche",
@@ -2295,6 +2297,127 @@ interface TypedData<Types extends Record<string, TypedDataField[]>, Message exte
2295
2297
  /** The message payload to be signed */
2296
2298
  message: Message;
2297
2299
  }
2300
+ /**
2301
+ * Standard ECDSA signature format (r, s, v components).
2302
+ *
2303
+ * Used for all EIP-712 and permit/authorization signatures.
2304
+ *
2305
+ * @example
2306
+ * ```typescript
2307
+ * const sig: Signature = { v: 28, r: "0x...", s: "0x..." }
2308
+ * ```
2309
+ */
2310
+ interface Signature {
2311
+ /** Recovery identifier (27 or 28) */
2312
+ v: number;
2313
+ /** ECDSA signature r value (32-byte hex string) */
2314
+ r: `0x${string}`;
2315
+ /** ECDSA signature s value (32-byte hex string) */
2316
+ s: `0x${string}`;
2317
+ }
2318
+ declare const PERMIT_STANDARDS: readonly ["EIP-2612"];
2319
+ type KnownPermitStandard = (typeof PERMIT_STANDARDS)[number];
2320
+ /**
2321
+ * Supported permit/authorization standards for cross-chain USDC.
2322
+ *
2323
+ * Extend this union as new standards are supported.
2324
+ */
2325
+ type PermitStandardName = KnownPermitStandard
2326
+ /**
2327
+ * Branded string type to keep this union from collapsing into plain `string`.
2328
+ * This lets TS/your IDE still offer autocomplete for the known literals
2329
+ * while allowing any other string without casting.
2330
+ */
2331
+ | (string & {
2332
+ readonly __brand?: 'PermitStandardName';
2333
+ });
2334
+ /**
2335
+ * Metadata required to construct an EIP-712 domain.
2336
+ *
2337
+ * Used as input to typed data builders for various standards.
2338
+ *
2339
+ * @example
2340
+ * ```typescript
2341
+ * const meta: DomainMeta = {
2342
+ * name: "USD Coin",
2343
+ * version: "2",
2344
+ * chainId: 1,
2345
+ * verifyingContract: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
2346
+ * }
2347
+ * ```
2348
+ */
2349
+ interface DomainMeta {
2350
+ /** Human-readable name of the signing domain */
2351
+ name: string;
2352
+ /** Current major version of the signing domain */
2353
+ version: string;
2354
+ /** EVM chain ID */
2355
+ chainId: number | bigint;
2356
+ /** Address of the verifying contract */
2357
+ verifyingContract: `0x${string}`;
2358
+ }
2359
+
2360
+ /**
2361
+ * Utility module for handling ECDSA signatures and EIP-712 typed data.
2362
+ *
2363
+ * This file provides:
2364
+ * 1. parseSignature: Take a raw hexadecimal signature and split it into its three
2365
+ * ECDSA components: r, s, and v (recovery identifier). Used when verifying
2366
+ * Ethereum transactions and signed messages.
2367
+ *
2368
+ * 2. buildTypedData: Assemble a data structure compliant with EIP-712, which standardizes
2369
+ * how structured data is formatted and hashed for secure off-chain signing.
2370
+ *
2371
+ * Key concepts:
2372
+ * - Cryptographic signatures (r, s, v) ensure that only the holder of a private key can
2373
+ * authorize actions or sign messages. The 'v' component enables recovering the public key
2374
+ * from the signature, confirming the signer’s identity.
2375
+ * - EIP-712 typed data enforces a clear schema for signing, preventing ambiguous or replayable
2376
+ * signatures and simplifying integration with common wallet libraries.
2377
+ */
2378
+
2379
+ /**
2380
+ * parseSignature
2381
+ *
2382
+ * Parse a 65-byte ECDSA signature into its r, s, and v components, expressed in hex.
2383
+ *
2384
+ * Ethereum signatures are structured as:
2385
+ * signature = r (32 bytes) || s (32 bytes) || v (1 byte)
2386
+ *
2387
+ * - r, s: Big-endian hex values (32 bytes each)
2388
+ * - v: Recovery identifier, used by secp256k1 to reconstruct the signer’s public key
2389
+ *
2390
+ * @param signatureHex - Signature as a hex string, optionally prefixed with "0x".
2391
+ * @returns { r, s, v }
2392
+ * - r: Hex string of the R component.
2393
+ * - s: Hex string of the S component.
2394
+ * - v: Numeric recovery ID.
2395
+ *
2396
+ * @throws Error if:
2397
+ * - Input is not valid hex.
2398
+ * - Incorrect length (must be exactly 65 bytes / 130 hex chars).
2399
+ * - v is outside the supported range (Legacy or EIP-155 formula).
2400
+ *
2401
+ * Notes on EIP-155 overload:
2402
+ * EIP-155 repurposes the v field so that:
2403
+ * v = 35 + (2 * chainId) + recoveryId(0 or 1)
2404
+ * Any v value >= 35 indicates the chain-id is encoded, preventing cross-chain replay.
2405
+ * r and s values remain unchanged.
2406
+ *
2407
+ * Example:
2408
+ * ```typescript
2409
+ * const rawSig = '0x6c1b...f02b'
2410
+ * try {
2411
+ * const { r, s, v } = parseSignature(rawSig)
2412
+ * console.log('R:', r)
2413
+ * console.log('S:', s)
2414
+ * console.log('Recovery ID (v):', v)
2415
+ * } catch (err) {
2416
+ * console.error('Signature parse error:', err.message)
2417
+ * }
2418
+ * ```
2419
+ */
2420
+ declare function parseSignature(signatureHex: string): Signature;
2298
2421
 
2299
2422
  /**
2300
2423
  * Core type definitions for EVM-compatible blockchain transaction execution
@@ -2475,6 +2598,129 @@ declare abstract class EvmAdapter<TAdapterCapabilities extends AdapterCapabiliti
2475
2598
  calculateTransactionFee(baseComputeUnits: bigint, bufferBasisPoints: bigint | undefined, chain: EVMChainDefinition): Promise<EstimatedGas>;
2476
2599
  }
2477
2600
 
2601
+ /**
2602
+ * EIP-2612 permit type definition.
2603
+ * Defines the structure for permit signatures according to EIP-2612 specification.
2604
+ *
2605
+ * @see {@link https://eips.ethereum.org/EIPS/eip-2612 | EIP-2612 Specification}
2606
+ */
2607
+ declare const EIP2612_TYPES: {
2608
+ readonly Permit: TypedDataField[];
2609
+ };
2610
+ /**
2611
+ * EIP-2612 permit message structure.
2612
+ * This is the exact data that gets signed according to EIP-2612.
2613
+ */
2614
+ interface EIP2612Message extends Record<string, unknown> {
2615
+ /** Token owner address */
2616
+ owner: `0x${string}`;
2617
+ /** Address permitted to spend tokens */
2618
+ spender: `0x${string}`;
2619
+ /** Amount of tokens permitted */
2620
+ value: bigint;
2621
+ /** Current nonce for the owner */
2622
+ nonce: bigint;
2623
+ /** Deadline timestamp (Unix timestamp in seconds) */
2624
+ deadline: bigint;
2625
+ }
2626
+ /**
2627
+ * Input options for building an EIP-2612 permit.
2628
+ * Nonce and deadline can be omitted and will be fetched/computed automatically.
2629
+ *
2630
+ * **Address Formatting**: Addresses are automatically formatted with proper EIP-55
2631
+ * checksumming, so you can provide addresses in any case format.
2632
+ */
2633
+ interface EIP2612Options extends Record<string, unknown> {
2634
+ /** Token owner address (automatically formatted with EIP-55 checksumming) */
2635
+ owner: `0x${string}`;
2636
+ /** Address that will be permitted to spend tokens (automatically formatted with EIP-55 checksumming) */
2637
+ spender: `0x${string}`;
2638
+ /** Amount of tokens to permit */
2639
+ value: bigint;
2640
+ /** Optional nonce - will be fetched from token contract if not provided */
2641
+ nonce?: bigint;
2642
+ /** Optional deadline - will default to 1 hour from now if not provided */
2643
+ deadline?: bigint;
2644
+ }
2645
+ /**
2646
+ * Function type for fetching nonces from EIP-2612 compatible tokens.
2647
+ *
2648
+ * @param owner - Token owner address
2649
+ * @param token - Token contract address
2650
+ * @returns Promise resolving to current nonce
2651
+ */
2652
+ type EIP2612NonceFetcher = (token: `0x${string}`, owner: `0x${string}`) => Promise<bigint>;
2653
+
2654
+ interface EIP2612Adapter {
2655
+ fetchEIP2612Nonce(tokenAddress: `0x${string}`, ownerAddress: `0x${string}`, ctx: OperationContext): Promise<bigint>;
2656
+ }
2657
+ /**
2658
+ * Build EIP-2612 typed data for permit signing.
2659
+ *
2660
+ * This function creates the complete EIP-712 typed data structure required
2661
+ * for EIP-2612 permit signatures, including automatic nonce fetching using
2662
+ * the adapter's built-in nonce fetching capability.
2663
+ *
2664
+ * **Address Formatting**: All addresses are automatically formatted with proper
2665
+ * EIP-55 checksumming using the `convertAddress` utility, ensuring compatibility
2666
+ * with strict validation libraries like viem.
2667
+ *
2668
+ * **Nonce Handling**: The nonce can be provided explicitly or will be fetched
2669
+ * automatically using the adapter's `fetchEIP2612Nonce` method, which queries
2670
+ * the token contract's `nonces(owner)` function.
2671
+ *
2672
+ * **Deadline Calculation**: If no deadline is provided, it defaults to 24 hours
2673
+ * from the current time (computed using `computeDefaultDeadline`).
2674
+ *
2675
+ * @param meta - Domain metadata for the token contract
2676
+ * @param adapter - Adapter instance with nonce-fetching capability
2677
+ * @param opts - EIP-2612 permit options including owner, spender, value
2678
+ * @returns Complete EIP-712 typed data ready for signing
2679
+ *
2680
+ * @example
2681
+ * ```typescript
2682
+ * import { buildEIP2612TypedData } from '@core/adapter-evm'
2683
+ *
2684
+ * const typedData = await buildEIP2612TypedData(
2685
+ * {
2686
+ * name: 'USD Coin',
2687
+ * version: '2',
2688
+ * chainId: 1,
2689
+ * verifyingContract: '0xa0b86a33e6441e4d178bb0c14ce0e9ce9c83bdd8'
2690
+ * },
2691
+ * adapter,
2692
+ * {
2693
+ * owner: '0x742d35cc6639c0532fbe9002b3a2265ca4c878f8e',
2694
+ * spender: '0x1234567890123456789012345678901234567890',
2695
+ * value: 1000000n
2696
+ * }
2697
+ * )
2698
+ *
2699
+ * const signature = await adapter.signTypedData(typedData)
2700
+ * ```
2701
+ */
2702
+ declare function buildEIP2612TypedData(meta: DomainMeta, adapter: EIP2612Adapter, opts: EIP2612Options, ctx: OperationContext): Promise<TypedData<typeof EIP2612_TYPES, EIP2612Message>>;
2703
+
2704
+ /**
2705
+ * Compute default deadline for permit signatures.
2706
+ *
2707
+ * Returns a timestamp 1 hour (3600 seconds) from the current time,
2708
+ * converted to Unix timestamp format as a bigint. This is commonly
2709
+ * used as the default expiration time for EIP-2612 permit signatures.
2710
+ *
2711
+ * @returns Unix timestamp (in seconds) 1 hour from now as a bigint
2712
+ *
2713
+ * @example
2714
+ * ```typescript
2715
+ * import { computeDefaultDeadline } from '@core/adapter-evm'
2716
+ *
2717
+ * const deadline = computeDefaultDeadline()
2718
+ * console.log(`Permit expires at: ${deadline}`)
2719
+ * // Output: Permit expires at: 1640998800
2720
+ * ```
2721
+ */
2722
+ declare function computeDefaultDeadline(): bigint;
2723
+
2478
2724
  /**
2479
2725
  * @packageDocumentation
2480
2726
  * @module EthersAdapter
@@ -3404,5 +3650,5 @@ declare const createAdapterFromProvider: (params: CreateAdapterFromProviderParam
3404
3650
  */
3405
3651
  declare function validateAdapterCapabilities(capabilities: AdapterCapabilities): void;
3406
3652
 
3407
- export { Blockchain, EthersAdapter, createAdapterFromPrivateKey, createAdapterFromProvider, validateAdapterCapabilities };
3408
- export type { ChainIdentifier, CreateAdapterFromPrivateKeyParams, CreateAdapterFromProviderParams, EthersAdapterOptions };
3653
+ export { Blockchain, EthersAdapter, buildEIP2612TypedData, computeDefaultDeadline, createAdapterFromPrivateKey, createAdapterFromProvider, parseSignature, validateAdapterCapabilities };
3654
+ export type { ChainIdentifier, CreateAdapterFromPrivateKeyParams, CreateAdapterFromProviderParams, EIP2612Message, EIP2612NonceFetcher, EIP2612Options, EthersAdapterOptions, PermitStandardName, Signature, TypedData, TypedDataField };
package/index.mjs CHANGED
@@ -17,6 +17,7 @@
17
17
  */
18
18
 
19
19
  import { JsonRpcProvider, Wallet, BrowserProvider, Interface, Contract } from 'ethers';
20
+ export { JsonRpcProvider } from 'ethers';
20
21
  import { z } from 'zod';
21
22
  import { hexlify, hexZeroPad } from '@ethersproject/bytes';
22
23
  import { getAddress } from '@ethersproject/address';
@@ -262,6 +263,7 @@ var Blockchain;
262
263
  Blockchain["Algorand_Testnet"] = "Algorand_Testnet";
263
264
  Blockchain["Aptos"] = "Aptos";
264
265
  Blockchain["Aptos_Testnet"] = "Aptos_Testnet";
266
+ Blockchain["Arc_Testnet"] = "Arc_Testnet";
265
267
  Blockchain["Arbitrum"] = "Arbitrum";
266
268
  Blockchain["Arbitrum_Sepolia"] = "Arbitrum_Sepolia";
267
269
  Blockchain["Avalanche"] = "Avalanche";
@@ -482,6 +484,48 @@ const BRIDGE_CONTRACT_EVM_TESTNET = '0xC5567a5E3370d4DBfB0540025078e283e36A363d'
482
484
  */
483
485
  const BRIDGE_CONTRACT_EVM_MAINNET = '0xB3FA262d0fB521cc93bE83d87b322b8A23DAf3F0';
484
486
 
487
+ /**
488
+ * Arc Testnet chain definition
489
+ * @remarks
490
+ * This represents the test network for the Arc blockchain,
491
+ * Circle's EVM-compatible Layer-1 designed for stablecoin finance
492
+ * and asset tokenization. Arc uses USDC as the native gas token and
493
+ * features the Malachite Byzantine Fault Tolerant (BFT) consensus
494
+ * engine for sub-second finality.
495
+ */
496
+ const ArcTestnet = defineChain({
497
+ type: 'evm',
498
+ chain: Blockchain.Arc_Testnet,
499
+ name: 'Arc Testnet',
500
+ title: 'ArcTestnet',
501
+ nativeCurrency: {
502
+ name: 'Arc',
503
+ symbol: 'Arc',
504
+ decimals: 18,
505
+ },
506
+ chainId: 5042002,
507
+ isTestnet: true,
508
+ explorerUrl: 'https://testnet.arcscan.app/tx/{hash}',
509
+ rpcEndpoints: ['https://rpc.testnet.arc.network/'],
510
+ eurcAddress: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
511
+ usdcAddress: '0x3600000000000000000000000000000000000000',
512
+ cctp: {
513
+ domain: 26,
514
+ contracts: {
515
+ v2: {
516
+ type: 'split',
517
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
518
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
519
+ confirmations: 1,
520
+ fastConfirmations: 1,
521
+ },
522
+ },
523
+ },
524
+ kitContracts: {
525
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
526
+ },
527
+ });
528
+
485
529
  /**
486
530
  * Arbitrum Mainnet chain definition
487
531
  * @remarks
@@ -1773,26 +1817,26 @@ const Sonic = defineChain({
1773
1817
  });
1774
1818
 
1775
1819
  /**
1776
- * Sonic Blaze Testnet chain definition
1820
+ * Sonic Testnet chain definition
1777
1821
  * @remarks
1778
1822
  * This represents the official test network for the Sonic blockchain.
1779
1823
  */
1780
1824
  const SonicTestnet = defineChain({
1781
1825
  type: 'evm',
1782
1826
  chain: Blockchain.Sonic_Testnet,
1783
- name: 'Sonic Blaze Testnet',
1784
- title: 'Sonic Blaze Testnet',
1827
+ name: 'Sonic Testnet',
1828
+ title: 'Sonic Testnet',
1785
1829
  nativeCurrency: {
1786
1830
  name: 'Sonic',
1787
1831
  symbol: 'S',
1788
1832
  decimals: 18,
1789
1833
  },
1790
- chainId: 57054,
1834
+ chainId: 14601,
1791
1835
  isTestnet: true,
1792
1836
  explorerUrl: 'https://testnet.sonicscan.org/tx/{hash}',
1793
- rpcEndpoints: ['https://rpc.blaze.soniclabs.com'],
1837
+ rpcEndpoints: ['https://rpc.testnet.soniclabs.com'],
1794
1838
  eurcAddress: null,
1795
- usdcAddress: '0xA4879Fed32Ecbef99399e5cbC247E533421C4eC6',
1839
+ usdcAddress: '0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51',
1796
1840
  cctp: {
1797
1841
  domain: 13,
1798
1842
  contracts: {
@@ -2309,6 +2353,7 @@ var Blockchains = /*#__PURE__*/Object.freeze({
2309
2353
  AptosTestnet: AptosTestnet,
2310
2354
  Arbitrum: Arbitrum,
2311
2355
  ArbitrumSepolia: ArbitrumSepolia,
2356
+ ArcTestnet: ArcTestnet,
2312
2357
  Avalanche: Avalanche,
2313
2358
  AvalancheFuji: AvalancheFuji,
2314
2359
  Base: Base,
@@ -2986,6 +3031,10 @@ class KitError extends Error {
2986
3031
  }
2987
3032
  }
2988
3033
 
3034
+ /**
3035
+ * Minimum error code for INPUT type errors.
3036
+ * INPUT errors represent validation failures and invalid parameters.
3037
+ */
2989
3038
  /**
2990
3039
  * Standardized error definitions for INPUT type errors.
2991
3040
  *
@@ -8006,6 +8055,246 @@ class EvmAdapter extends Adapter {
8006
8055
  }
8007
8056
  }
8008
8057
 
8058
+ // --- Signature Parsing Constants ---
8059
+ const SIGNATURE_BYTE_LENGTH = 65; // Total bytes in an Ethereum signature
8060
+ const HEX_CHARS_PER_BYTE = 2;
8061
+ const SIGNATURE_HEX_LENGTH = SIGNATURE_BYTE_LENGTH * HEX_CHARS_PER_BYTE;
8062
+ const R_HEX_LENGTH = 32 * HEX_CHARS_PER_BYTE; // 32 bytes for 'r'
8063
+ const S_HEX_LENGTH = 32 * HEX_CHARS_PER_BYTE; // 32 bytes for 's'
8064
+ // --- Recovery ID Validation Constants ---
8065
+ /**
8066
+ * Legacy recovery IDs (pre-EIP-155 format):
8067
+ * - 0 or 1 (older clients) or 27, 28 (Ethereum before chain-id inclusion)
8068
+ */
8069
+ const LEGACY_V_VALUES = new Set([0, 1, 27, 28]);
8070
+ /**
8071
+ * Minimum v value indicating an EIP-155 style signature.
8072
+ * Calculated as 35 + (2 * chainId) + recoveryId (0 or 1).
8073
+ */
8074
+ const MIN_EIP155_V = 35;
8075
+ /**
8076
+ * parseSignature
8077
+ *
8078
+ * Parse a 65-byte ECDSA signature into its r, s, and v components, expressed in hex.
8079
+ *
8080
+ * Ethereum signatures are structured as:
8081
+ * signature = r (32 bytes) || s (32 bytes) || v (1 byte)
8082
+ *
8083
+ * - r, s: Big-endian hex values (32 bytes each)
8084
+ * - v: Recovery identifier, used by secp256k1 to reconstruct the signer’s public key
8085
+ *
8086
+ * @param signatureHex - Signature as a hex string, optionally prefixed with "0x".
8087
+ * @returns { r, s, v }
8088
+ * - r: Hex string of the R component.
8089
+ * - s: Hex string of the S component.
8090
+ * - v: Numeric recovery ID.
8091
+ *
8092
+ * @throws Error if:
8093
+ * - Input is not valid hex.
8094
+ * - Incorrect length (must be exactly 65 bytes / 130 hex chars).
8095
+ * - v is outside the supported range (Legacy or EIP-155 formula).
8096
+ *
8097
+ * Notes on EIP-155 overload:
8098
+ * EIP-155 repurposes the v field so that:
8099
+ * v = 35 + (2 * chainId) + recoveryId(0 or 1)
8100
+ * Any v value >= 35 indicates the chain-id is encoded, preventing cross-chain replay.
8101
+ * r and s values remain unchanged.
8102
+ *
8103
+ * Example:
8104
+ * ```typescript
8105
+ * const rawSig = '0x6c1b...f02b'
8106
+ * try {
8107
+ * const { r, s, v } = parseSignature(rawSig)
8108
+ * console.log('R:', r)
8109
+ * console.log('S:', s)
8110
+ * console.log('Recovery ID (v):', v)
8111
+ * } catch (err) {
8112
+ * console.error('Signature parse error:', err.message)
8113
+ * }
8114
+ * ```
8115
+ */
8116
+ function parseSignature(signatureHex) {
8117
+ // 1) Remove optional '0x' prefix
8118
+ const hex = signatureHex.startsWith('0x')
8119
+ ? signatureHex.slice(2)
8120
+ : signatureHex;
8121
+ // 2) Validate hex format and length
8122
+ const VALID_HEX_REGEX = /^[0-9a-fA-F]+$/;
8123
+ if (!VALID_HEX_REGEX.test(hex)) {
8124
+ throw new Error('Signature must be a valid hex string (0-9, a-f, A-F).');
8125
+ }
8126
+ if (hex.length !== SIGNATURE_HEX_LENGTH) {
8127
+ throw new Error(`Invalid length: expected ${SIGNATURE_HEX_LENGTH.toString()} hex chars (65 bytes), got ${hex.length.toString()}`);
8128
+ }
8129
+ // 3) Extract components
8130
+ const r = `0x${hex.slice(0, R_HEX_LENGTH)}`;
8131
+ const s = `0x${hex.slice(R_HEX_LENGTH, R_HEX_LENGTH + S_HEX_LENGTH)}`;
8132
+ const vValue = parseInt(hex.slice(R_HEX_LENGTH + S_HEX_LENGTH), 16);
8133
+ // 4) Ensure v is within supported values
8134
+ const isLegacy = LEGACY_V_VALUES.has(vValue);
8135
+ const isEIP155 = vValue >= MIN_EIP155_V;
8136
+ if (!isLegacy && !isEIP155) {
8137
+ throw new Error(`Unsupported v value: ${vValue.toString()}. Must be one of [${[
8138
+ ...LEGACY_V_VALUES,
8139
+ ].join(', ')}] or >=${MIN_EIP155_V.toString()}`);
8140
+ }
8141
+ return { r: r, s: s, v: vValue };
8142
+ }
8143
+ /**
8144
+ * buildTypedData
8145
+ *
8146
+ * Create an EIP-712 compliant "TypedData" object, encapsulating:
8147
+ * - Domain separator (application/contract identity, chain ID)
8148
+ * - Explicit type definitions (field names and types)
8149
+ * - Primary type name identifying the root object
8150
+ * - Message payload matching the defined schema
8151
+ *
8152
+ * This enforces structured, non-ambiguous signing, and guards against signature replay.
8153
+ *
8154
+ * @typeParam Types - Map of type names to arrays of { name, type } definitions.
8155
+ * @typeParam Msg - Object whose shape matches the primaryType fields.
8156
+ *
8157
+ * @param domain - EIP712Domain, e.g. { name, version, chainId, verifyingContract }.
8158
+ * @param types - Type definitions, e.g.:
8159
+ * {
8160
+ * Mail: [ { name: 'from', type: 'string' }, { name: 'contents', type: 'string' } ],
8161
+ * }
8162
+ * @param primaryType - Root key in types, e.g. 'Mail'.
8163
+ * @param message - The actual values, e.g. { from: 'Alice', contents: 'Hello' }.
8164
+ *
8165
+ * @returns TypedData object ready for use with signing libraries.
8166
+ *
8167
+ * Example:
8168
+ * ```typescript
8169
+ * const typedData = buildTypedData(
8170
+ * { name: 'MyDApp', version: '1', chainId: 1, verifyingContract: '0xabc...' },
8171
+ * { Message: [{ name: 'text', type: 'string' }] },
8172
+ * 'Message',
8173
+ * { text: 'Hello, EIP-712!' }
8174
+ * )
8175
+ * // Pass typedData to ethers.js signer._signTypedData(domain, types, message)
8176
+ * ```
8177
+ */
8178
+ function buildTypedData(domain, types, primaryType, message) {
8179
+ return { domain, types, primaryType, message };
8180
+ }
8181
+
8182
+ /**
8183
+ * Compute default deadline for permit signatures.
8184
+ *
8185
+ * Returns a timestamp 1 hour (3600 seconds) from the current time,
8186
+ * converted to Unix timestamp format as a bigint. This is commonly
8187
+ * used as the default expiration time for EIP-2612 permit signatures.
8188
+ *
8189
+ * @returns Unix timestamp (in seconds) 1 hour from now as a bigint
8190
+ *
8191
+ * @example
8192
+ * ```typescript
8193
+ * import { computeDefaultDeadline } from '@core/adapter-evm'
8194
+ *
8195
+ * const deadline = computeDefaultDeadline()
8196
+ * console.log(`Permit expires at: ${deadline}`)
8197
+ * // Output: Permit expires at: 1640998800
8198
+ * ```
8199
+ */
8200
+ function computeDefaultDeadline() {
8201
+ return BigInt(Math.floor(Date.now() / 1000) + 3600);
8202
+ }
8203
+
8204
+ /**
8205
+ * EIP-2612 permit type definition.
8206
+ * Defines the structure for permit signatures according to EIP-2612 specification.
8207
+ *
8208
+ * @see {@link https://eips.ethereum.org/EIPS/eip-2612 | EIP-2612 Specification}
8209
+ */
8210
+ const EIP2612_TYPES = {
8211
+ Permit: [
8212
+ { name: 'owner', type: 'address' },
8213
+ { name: 'spender', type: 'address' },
8214
+ { name: 'value', type: 'uint256' },
8215
+ { name: 'nonce', type: 'uint256' },
8216
+ { name: 'deadline', type: 'uint256' },
8217
+ ],
8218
+ };
8219
+
8220
+ /**
8221
+ * Build EIP-2612 typed data for permit signing.
8222
+ *
8223
+ * This function creates the complete EIP-712 typed data structure required
8224
+ * for EIP-2612 permit signatures, including automatic nonce fetching using
8225
+ * the adapter's built-in nonce fetching capability.
8226
+ *
8227
+ * **Address Formatting**: All addresses are automatically formatted with proper
8228
+ * EIP-55 checksumming using the `convertAddress` utility, ensuring compatibility
8229
+ * with strict validation libraries like viem.
8230
+ *
8231
+ * **Nonce Handling**: The nonce can be provided explicitly or will be fetched
8232
+ * automatically using the adapter's `fetchEIP2612Nonce` method, which queries
8233
+ * the token contract's `nonces(owner)` function.
8234
+ *
8235
+ * **Deadline Calculation**: If no deadline is provided, it defaults to 24 hours
8236
+ * from the current time (computed using `computeDefaultDeadline`).
8237
+ *
8238
+ * @param meta - Domain metadata for the token contract
8239
+ * @param adapter - Adapter instance with nonce-fetching capability
8240
+ * @param opts - EIP-2612 permit options including owner, spender, value
8241
+ * @returns Complete EIP-712 typed data ready for signing
8242
+ *
8243
+ * @example
8244
+ * ```typescript
8245
+ * import { buildEIP2612TypedData } from '@core/adapter-evm'
8246
+ *
8247
+ * const typedData = await buildEIP2612TypedData(
8248
+ * {
8249
+ * name: 'USD Coin',
8250
+ * version: '2',
8251
+ * chainId: 1,
8252
+ * verifyingContract: '0xa0b86a33e6441e4d178bb0c14ce0e9ce9c83bdd8'
8253
+ * },
8254
+ * adapter,
8255
+ * {
8256
+ * owner: '0x742d35cc6639c0532fbe9002b3a2265ca4c878f8e',
8257
+ * spender: '0x1234567890123456789012345678901234567890',
8258
+ * value: 1000000n
8259
+ * }
8260
+ * )
8261
+ *
8262
+ * const signature = await adapter.signTypedData(typedData)
8263
+ * ```
8264
+ */
8265
+ async function buildEIP2612TypedData(meta, adapter, opts, ctx) {
8266
+ // Format addresses to ensure proper EIP-55 checksumming
8267
+ const formattedOwner = convertAddress(opts.owner, 'evm');
8268
+ const formattedSpender = convertAddress(opts.spender, 'evm');
8269
+ const formattedContract = convertAddress(meta.verifyingContract, 'evm');
8270
+ // Fetch nonce if not provided - adapter handles the contract interaction
8271
+ const nonce = opts.nonce ??
8272
+ (await adapter.fetchEIP2612Nonce(formattedContract, formattedOwner, ctx));
8273
+ /*
8274
+ * Calculate deadline if not provided (24 hours from now)
8275
+ * EIP-2612 deadline is a uint256 in the permit struct, so we use bigint for full compatibility
8276
+ * with on-chain values and to avoid overflow/precision issues with large timestamps.
8277
+ * Most real-world deadlines are within JS safe integer range, but using bigint is safest.
8278
+ */
8279
+ const now = BigInt(Math.floor(Date.now() / 1000));
8280
+ const deadline = opts.deadline !== undefined
8281
+ ? BigInt(opts.deadline)
8282
+ : computeDefaultDeadline();
8283
+ if (deadline <= now) {
8284
+ throw new Error(`EIP-2612 deadline must be in the future (got ${deadline.toString()}, now ${now.toString()})`);
8285
+ }
8286
+ // Build the message with properly formatted addresses
8287
+ const message = {
8288
+ owner: formattedOwner,
8289
+ spender: formattedSpender,
8290
+ value: opts.value,
8291
+ nonce,
8292
+ deadline,
8293
+ };
8294
+ // Return complete typed data structure with formatted contract address
8295
+ return buildTypedData({ ...meta, verifyingContract: formattedContract }, EIP2612_TYPES, 'Permit', message);
8296
+ }
8297
+
8009
8298
  /**
8010
8299
  * Checks if a function in an ABI is read-only (view or pure).
8011
8300
  *
@@ -9980,6 +10269,11 @@ class EthersAdapter extends EvmAdapter {
9980
10269
  * ```
9981
10270
  */
9982
10271
  async getAddress(chain) {
10272
+ // Prevent calling getAddress on developer-controlled adapters
10273
+ if (this.capabilities?.addressContext === 'developer-controlled') {
10274
+ throw new Error('Cannot call getAddress() on developer-controlled adapters. ' +
10275
+ 'Address must be provided explicitly in the operation context.');
10276
+ }
9983
10277
  // Chain parameter should now be provided by resolveOperationContext
9984
10278
  if (!chain) {
9985
10279
  throw new Error('Chain parameter is required for address resolution. ' +
@@ -10529,5 +10823,5 @@ const createAdapterFromProvider = async (params) => {
10529
10823
  }, resolvedCapabilities);
10530
10824
  };
10531
10825
 
10532
- export { Blockchain, EthersAdapter, createAdapterFromPrivateKey, createAdapterFromProvider, validateAdapterCapabilities };
10826
+ export { Blockchain, EthersAdapter, buildEIP2612TypedData, computeDefaultDeadline, createAdapterFromPrivateKey, createAdapterFromProvider, parseSignature, validateAdapterCapabilities };
10533
10827
  //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@circle-fin/adapter-ethers-v6",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
+ "description": "EVM blockchain adapter powered by Ethers v6",
4
5
  "main": "./index.cjs.js",
5
6
  "module": "./index.mjs",
6
7
  "types": "./index.d.ts",
@@ -31,6 +32,7 @@
31
32
  "package.json"
32
33
  ],
33
34
  "publishConfig": {
35
+ "access": "public",
34
36
  "directory": "../../dist/adapters/ethers.v6"
35
37
  }
36
38
  }