@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 +303 -6
- package/index.d.ts +248 -2
- package/index.mjs +301 -7
- package/package.json +3 -1
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
|
|
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
|
|
1790
|
-
title: 'Sonic
|
|
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:
|
|
1839
|
+
chainId: 14601,
|
|
1797
1840
|
isTestnet: true,
|
|
1798
1841
|
explorerUrl: 'https://testnet.sonicscan.org/tx/{hash}',
|
|
1799
|
-
rpcEndpoints: ['https://rpc.
|
|
1842
|
+
rpcEndpoints: ['https://rpc.testnet.soniclabs.com'],
|
|
1800
1843
|
eurcAddress: null,
|
|
1801
|
-
usdcAddress: '
|
|
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
|
|
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
|
|
1784
|
-
title: 'Sonic
|
|
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:
|
|
1834
|
+
chainId: 14601,
|
|
1791
1835
|
isTestnet: true,
|
|
1792
1836
|
explorerUrl: 'https://testnet.sonicscan.org/tx/{hash}',
|
|
1793
|
-
rpcEndpoints: ['https://rpc.
|
|
1837
|
+
rpcEndpoints: ['https://rpc.testnet.soniclabs.com'],
|
|
1794
1838
|
eurcAddress: null,
|
|
1795
|
-
usdcAddress: '
|
|
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.
|
|
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
|
}
|