@circle-fin/adapter-ethers-v6 1.7.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/index.cjs +338 -124
  3. package/index.d.ts +125 -4
  4. package/index.mjs +338 -124
  5. package/package.json +1 -1
package/index.mjs CHANGED
@@ -403,6 +403,12 @@ const InputError = {
403
403
  name: 'INPUT_INVALID_AMOUNT',
404
404
  type: 'INPUT',
405
405
  },
406
+ /** Unsupported or invalid bridge route configuration */
407
+ UNSUPPORTED_ROUTE: {
408
+ code: 1003,
409
+ name: 'INPUT_UNSUPPORTED_ROUTE',
410
+ type: 'INPUT',
411
+ },
406
412
  /** Invalid or unsupported chain identifier */
407
413
  INVALID_CHAIN: {
408
414
  code: 1005,
@@ -559,6 +565,35 @@ const NetworkError = {
559
565
  type: 'NETWORK',
560
566
  }};
561
567
 
568
+ /**
569
+ * Creates error for unsupported earn route.
570
+ *
571
+ * This error is thrown when no available earn provider supports the requested
572
+ * operation on the specified chain.
573
+ *
574
+ * @param operation - Earn operation name
575
+ * @param chain - Chain name where the earn operation was attempted
576
+ * @returns KitError with specific earn route details
577
+ *
578
+ * @example
579
+ * ```typescript
580
+ * import { createUnsupportedEarnRouteError } from '@core/errors'
581
+ *
582
+ * throw createUnsupportedEarnRouteError('deposit', 'Ethereum')
583
+ * // Message: "Earn deposit on Ethereum is not supported by any available provider."
584
+ * ```
585
+ */
586
+ function createUnsupportedEarnRouteError(operation, chain) {
587
+ const errorDetails = {
588
+ ...InputError.UNSUPPORTED_ROUTE,
589
+ recoverability: 'FATAL',
590
+ message: `Earn ${operation} on ${chain} is not supported by any available provider.`,
591
+ cause: {
592
+ trace: { operation, chain },
593
+ },
594
+ };
595
+ return new KitError(errorDetails);
596
+ }
562
597
  /**
563
598
  * Creates error for unsupported token on chain.
564
599
  *
@@ -2047,23 +2082,21 @@ var UnifiedBalanceChain;
2047
2082
  * Enumeration of blockchains that support earn (vault deposit/withdraw)
2048
2083
  * operations through the Earn Kit.
2049
2084
  *
2050
- * Currently only Ethereum mainnet is supported. Additional chains
2051
- * will be added as vault protocol support expands.
2052
- *
2053
2085
  * @example
2054
2086
  * ```typescript
2055
2087
  * import { EarnChain } from '@core/chains'
2056
2088
  *
2057
2089
  * const result = await earnKit.deposit({
2058
- * from: { adapter, chain: EarnChain.Ethereum },
2090
+ * from: { adapter, chain: EarnChain.Arc_Testnet },
2059
2091
  * vaultAddress: '0x...',
2060
2092
  * amount: '100',
2061
2093
  * })
2062
2094
  * ```
2063
2095
  */
2096
+ // Values must match Blockchain enum members exactly.
2064
2097
  var EarnChain;
2065
2098
  (function (EarnChain) {
2066
- EarnChain["Ethereum"] = "Ethereum";
2099
+ EarnChain["Arc_Testnet"] = "Arc_Testnet";
2067
2100
  })(EarnChain || (EarnChain = {}));
2068
2101
 
2069
2102
  /**
@@ -5757,7 +5790,7 @@ z.union([
5757
5790
  * Zod schema for validating earn-specific chain identifiers.
5758
5791
  *
5759
5792
  * Validate that the provided chain is supported for earn (vault
5760
- * deposit/withdraw) operations. Currently only Ethereum is
5793
+ * deposit/withdraw) operations. Currently only Arc Testnet is
5761
5794
  * supported.
5762
5795
  *
5763
5796
  * Accept an EarnChain enum value, a matching string literal, or
@@ -5766,12 +5799,12 @@ z.union([
5766
5799
  * @example
5767
5800
  * ```typescript
5768
5801
  * import { earnChainIdentifierSchema } from '@core/chains'
5769
- * import { EarnChain, Ethereum } from '@core/chains'
5802
+ * import { ArcTestnet, EarnChain } from '@core/chains'
5770
5803
  *
5771
5804
  * // Valid
5772
- * earnChainIdentifierSchema.parse(EarnChain.Ethereum)
5773
- * earnChainIdentifierSchema.parse('Ethereum')
5774
- * earnChainIdentifierSchema.parse(Ethereum)
5805
+ * earnChainIdentifierSchema.parse(EarnChain.Arc_Testnet)
5806
+ * earnChainIdentifierSchema.parse('Arc_Testnet')
5807
+ * earnChainIdentifierSchema.parse(ArcTestnet)
5775
5808
  *
5776
5809
  * // Invalid (throws ZodError)
5777
5810
  * earnChainIdentifierSchema.parse('Solana')
@@ -7384,7 +7417,9 @@ const hexStringSchema = z
7384
7417
  * console.log(result.success) // true
7385
7418
  * ```
7386
7419
  */
7387
- const evmAddressSchema = hexStringSchema.refine((value) => value.length === 42, 'EVM address must be exactly 42 characters long (0x + 40 hex characters)');
7420
+ const evmAddressSchema = hexStringSchema
7421
+ .refine((value) => value.length === 42, 'EVM address must be exactly 42 characters long (0x + 40 hex characters)')
7422
+ .transform((value) => value);
7388
7423
  /**
7389
7424
  * Schema for validating transaction hashes.
7390
7425
  *
@@ -7405,6 +7440,29 @@ const evmAddressSchema = hexStringSchema.refine((value) => value.length === 42,
7405
7440
  * ```
7406
7441
  */
7407
7442
  hexStringSchema.refine((value) => value.length === 66, 'Transaction hash must be exactly 66 characters long (0x + 64 hex characters)');
7443
+ /**
7444
+ * Schema for validating EVM signatures.
7445
+ *
7446
+ * This schema validates that a string is a properly formatted 65-byte EVM
7447
+ * signature:
7448
+ * - Must be a valid hex string with '0x' prefix
7449
+ * - Must be exactly 132 characters long (0x + 130 hex characters)
7450
+ *
7451
+ * @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
7452
+ *
7453
+ * @example
7454
+ * ```typescript
7455
+ * import { evmSignatureSchema } from '@core/adapter'
7456
+ *
7457
+ * const validSignature = `0x${'ab'.repeat(65)}`
7458
+ *
7459
+ * const result = evmSignatureSchema.safeParse(validSignature)
7460
+ * console.log(result.success) // true
7461
+ * ```
7462
+ */
7463
+ const evmSignatureSchema = hexStringSchema
7464
+ .refine((value) => value.length === 132, 'EVM signature must be exactly 132 characters long (0x + 130 hex characters)')
7465
+ .transform((value) => value);
7408
7466
  /**
7409
7467
  * Schema for validating base58-encoded strings.
7410
7468
  *
@@ -7639,6 +7697,79 @@ function getSupportedChains(ecosystem) {
7639
7697
  }
7640
7698
  }
7641
7699
 
7700
+ /**
7701
+ * IAdapter contract ABI.
7702
+ *
7703
+ * Shared ABI for the on-chain Adapter contract used by multiple kits
7704
+ * (swap, earn) for executing signed instruction sets. The `execute()`
7705
+ * function accepts EIP-712 signed execution parameters, token inputs,
7706
+ * and a signature, then executes the corresponding on-chain
7707
+ * instructions.
7708
+ */
7709
+ const adapterContractAbi = [
7710
+ {
7711
+ type: 'function',
7712
+ name: 'execute',
7713
+ inputs: [
7714
+ {
7715
+ name: 'params',
7716
+ type: 'tuple',
7717
+ internalType: 'struct IAdapter.ExecutionParams',
7718
+ components: [
7719
+ {
7720
+ name: 'instructions',
7721
+ type: 'tuple[]',
7722
+ internalType: 'struct IAdapter.Instruction[]',
7723
+ components: [
7724
+ { name: 'target', type: 'address', internalType: 'address' },
7725
+ { name: 'data', type: 'bytes', internalType: 'bytes' },
7726
+ { name: 'value', type: 'uint256', internalType: 'uint256' },
7727
+ { name: 'tokenIn', type: 'address', internalType: 'address' },
7728
+ {
7729
+ name: 'amountToApprove',
7730
+ type: 'uint256',
7731
+ internalType: 'uint256',
7732
+ },
7733
+ { name: 'tokenOut', type: 'address', internalType: 'address' },
7734
+ { name: 'minTokenOut', type: 'uint256', internalType: 'uint256' },
7735
+ ],
7736
+ },
7737
+ {
7738
+ name: 'tokens',
7739
+ type: 'tuple[]',
7740
+ internalType: 'struct IAdapter.TokenRecipient[]',
7741
+ components: [
7742
+ { name: 'token', type: 'address', internalType: 'address' },
7743
+ { name: 'beneficiary', type: 'address', internalType: 'address' },
7744
+ ],
7745
+ },
7746
+ { name: 'execId', type: 'uint256', internalType: 'uint256' },
7747
+ { name: 'deadline', type: 'uint256', internalType: 'uint256' },
7748
+ { name: 'metadata', type: 'bytes', internalType: 'bytes' },
7749
+ ],
7750
+ },
7751
+ {
7752
+ name: 'tokenInputs',
7753
+ type: 'tuple[]',
7754
+ internalType: 'struct IAdapter.TokenInput[]',
7755
+ components: [
7756
+ {
7757
+ name: 'permitType',
7758
+ type: 'uint8',
7759
+ internalType: 'enum IAdapter.PermitType',
7760
+ },
7761
+ { name: 'token', type: 'address', internalType: 'address' },
7762
+ { name: 'amount', type: 'uint256', internalType: 'uint256' },
7763
+ { name: 'permitCalldata', type: 'bytes', internalType: 'bytes' },
7764
+ ],
7765
+ },
7766
+ { name: 'signature', type: 'bytes', internalType: 'bytes' },
7767
+ ],
7768
+ outputs: [],
7769
+ stateMutability: 'payable',
7770
+ },
7771
+ ];
7772
+
7642
7773
  /**
7643
7774
  * ERC20 ABI
7644
7775
  *
@@ -11004,78 +11135,6 @@ const bridgeContractAbi = [
11004
11135
  },
11005
11136
  ];
11006
11137
 
11007
- /**
11008
- * Adapter Contract ABI
11009
- *
11010
- * This ABI is used to interact with the Adapter smart contract that handles
11011
- * gasless token approvals via permits and atomic swap execution.
11012
- *
11013
- * The execute() function is the primary entry point for swap operations.
11014
- */
11015
- const adapterContractAbi = [
11016
- {
11017
- type: 'function',
11018
- name: 'execute',
11019
- inputs: [
11020
- {
11021
- name: 'params',
11022
- type: 'tuple',
11023
- internalType: 'struct IAdapter.ExecutionParams',
11024
- components: [
11025
- {
11026
- name: 'instructions',
11027
- type: 'tuple[]',
11028
- internalType: 'struct IAdapter.Instruction[]',
11029
- components: [
11030
- { name: 'target', type: 'address', internalType: 'address' },
11031
- { name: 'data', type: 'bytes', internalType: 'bytes' },
11032
- { name: 'value', type: 'uint256', internalType: 'uint256' },
11033
- { name: 'tokenIn', type: 'address', internalType: 'address' },
11034
- {
11035
- name: 'amountToApprove',
11036
- type: 'uint256',
11037
- internalType: 'uint256',
11038
- },
11039
- { name: 'tokenOut', type: 'address', internalType: 'address' },
11040
- { name: 'minTokenOut', type: 'uint256', internalType: 'uint256' },
11041
- ],
11042
- },
11043
- {
11044
- name: 'tokens',
11045
- type: 'tuple[]',
11046
- internalType: 'struct IAdapter.TokenRecipient[]',
11047
- components: [
11048
- { name: 'token', type: 'address', internalType: 'address' },
11049
- { name: 'beneficiary', type: 'address', internalType: 'address' },
11050
- ],
11051
- },
11052
- { name: 'execId', type: 'uint256', internalType: 'uint256' },
11053
- { name: 'deadline', type: 'uint256', internalType: 'uint256' },
11054
- { name: 'metadata', type: 'bytes', internalType: 'bytes' },
11055
- ],
11056
- },
11057
- {
11058
- name: 'tokenInputs',
11059
- type: 'tuple[]',
11060
- internalType: 'struct IAdapter.TokenInput[]',
11061
- components: [
11062
- {
11063
- name: 'permitType',
11064
- type: 'uint8',
11065
- internalType: 'enum IAdapter.PermitType',
11066
- },
11067
- { name: 'token', type: 'address', internalType: 'address' },
11068
- { name: 'amount', type: 'uint256', internalType: 'uint256' },
11069
- { name: 'permitCalldata', type: 'bytes', internalType: 'bytes' },
11070
- ],
11071
- },
11072
- { name: 'signature', type: 'bytes', internalType: 'bytes' },
11073
- ],
11074
- outputs: [],
11075
- stateMutability: 'payable',
11076
- },
11077
- ];
11078
-
11079
11138
  /**
11080
11139
  * ABI for the Circle Gateway Wallet contract.
11081
11140
  *
@@ -15497,6 +15556,107 @@ const balanceOf$2 = async (params, adapter, context) => {
15497
15556
  };
15498
15557
  };
15499
15558
 
15559
+ const hexBytesSchema = z
15560
+ .string()
15561
+ .regex(/^0x([0-9a-fA-F]{2})*$/, {
15562
+ message: 'value must be a 0x-prefixed even-length hex string',
15563
+ })
15564
+ .transform((value) => value);
15565
+ const earnExecuteParamsSchema = z
15566
+ .object({
15567
+ executeParams: z.object({}).passthrough(),
15568
+ signature: evmSignatureSchema,
15569
+ tokenInputs: z.array(z.object({
15570
+ permitType: z.nativeEnum(PermitType),
15571
+ token: evmAddressSchema,
15572
+ amount: z.bigint().refine((value) => value >= 0n, {
15573
+ message: 'amount must be a non-negative bigint',
15574
+ }),
15575
+ permitCalldata: hexBytesSchema,
15576
+ })),
15577
+ })
15578
+ .passthrough();
15579
+ function validateAdapterContractAddress(chainName, value) {
15580
+ const result = evmAddressSchema.safeParse(value);
15581
+ if (!result.success) {
15582
+ throw createValidationFailedError('chain.kitContracts.adapter', value, `Adapter contract for chain ${chainName} must be a 0x-prefixed 20-byte hex address.`);
15583
+ }
15584
+ return result.data;
15585
+ }
15586
+ function validateEarnExecuteParams(params) {
15587
+ const result = earnExecuteParamsSchema.safeParse(params);
15588
+ if (!result.success) {
15589
+ throw createValidationErrorFromZod(result.error, 'earn execute params');
15590
+ }
15591
+ return {
15592
+ executeParams: result.data.executeParams,
15593
+ signature: result.data.signature,
15594
+ tokenInputs: result.data.tokenInputs,
15595
+ __isActionParams: true,
15596
+ };
15597
+ }
15598
+ /**
15599
+ * Prepare an earn operation against the adapter contract on EVM chains.
15600
+ *
15601
+ * Forward the service-signed `executeParams` and `signature` to the adapter
15602
+ * contract's `execute` function. Token approvals are handled upstream by the
15603
+ * provider (today via a separate `token.approve` transaction; future: permit
15604
+ * entries in `tokenInputs`). This function performs runtime validation for
15605
+ * plain-JS callers, prepares calldata only, performs no network I/O, and does
15606
+ * not submit the transaction.
15607
+ *
15608
+ * @param params - Service-signed earn payload.
15609
+ * @param adapter - EVM adapter used to prepare the transaction.
15610
+ * @param context - Resolved operation context (chain + address).
15611
+ * @returns Prepared chain request ready to `estimate()` or `execute()`.
15612
+ * @throws {@link KitError} If the chain is not EVM-compatible.
15613
+ * @throws {@link KitError} If the chain has no adapter contract configured.
15614
+ * @throws {@link KitError} If required fields are missing or invalid.
15615
+ *
15616
+ * @example
15617
+ * ```typescript
15618
+ * const prepared = await evmAdapter.prepareAction(
15619
+ * 'earn.deposit',
15620
+ * {
15621
+ * executeParams: {
15622
+ * instructions: [],
15623
+ * tokens: [],
15624
+ * execId: 1n,
15625
+ * deadline: 0n,
15626
+ * metadata: '0x',
15627
+ * },
15628
+ * tokenInputs: [],
15629
+ * signature: '0x...',
15630
+ * },
15631
+ * { chain, address },
15632
+ * )
15633
+ * const txHash = await prepared.execute()
15634
+ * ```
15635
+ */
15636
+ const prepareEarnExecute = async (params, adapter, context) => {
15637
+ const { chain } = context;
15638
+ if (chain.type !== 'evm') {
15639
+ throw createInvalidChainError(chain.name, `Expected EVM chain, but received chain type: ${String(chain.type)}`);
15640
+ }
15641
+ const adapterContractAddress = chain.kitContracts?.adapter;
15642
+ if (adapterContractAddress === undefined) {
15643
+ throw createUnsupportedEarnRouteError('execute', chain.name);
15644
+ }
15645
+ const validatedAdapterAddress = validateAdapterContractAddress(chain.name, adapterContractAddress);
15646
+ const validatedParams = validateEarnExecuteParams(params);
15647
+ return adapter.prepare({
15648
+ type: 'evm',
15649
+ abi: adapterContractAbi,
15650
+ address: validatedAdapterAddress,
15651
+ functionName: 'execute',
15652
+ args: [
15653
+ validatedParams.executeParams,
15654
+ validatedParams.tokenInputs,
15655
+ validatedParams.signature,
15656
+ ],
15657
+ }, context);
15658
+ };
15659
+
15500
15660
  /**
15501
15661
  * Type guard to check if params is ExecuteSwapEVMParams.
15502
15662
  *
@@ -16346,6 +16506,36 @@ const getHandlers = (adapter) => {
16346
16506
  'swap.execute': async (params, context) => {
16347
16507
  return executeSwap(params, adapter, context);
16348
16508
  },
16509
+ /**
16510
+ * Handler for earn deposit execution on EVM chains.
16511
+ *
16512
+ * Forwards the earn service's signed `executeParams` and `signature` to the
16513
+ * on-chain adapter contract's `execute` function. Token approvals are handled
16514
+ * by the earn provider via separate `token.approve` transactions.
16515
+ */
16516
+ 'earn.deposit': async (params, context) => {
16517
+ return prepareEarnExecute(params, adapter, context);
16518
+ },
16519
+ /**
16520
+ * Handler for earn withdraw execution on EVM chains.
16521
+ *
16522
+ * Forwards the earn service's signed `executeParams` and `signature` to the
16523
+ * on-chain adapter contract's `execute` function. Token approvals are handled
16524
+ * by the earn provider via separate `token.approve` transactions.
16525
+ */
16526
+ 'earn.withdraw': async (params, context) => {
16527
+ return prepareEarnExecute(params, adapter, context);
16528
+ },
16529
+ /**
16530
+ * Handler for earn claim rewards execution on EVM chains.
16531
+ *
16532
+ * Forwards the earn service's signed `executeParams` and `signature` to the
16533
+ * on-chain adapter contract's `execute` function. Claim rewards does not
16534
+ * require approvals because the adapter does not pull user tokens.
16535
+ */
16536
+ 'earn.claimRewards': async (params, context) => {
16537
+ return prepareEarnExecute(params, adapter, context);
16538
+ },
16349
16539
  /**
16350
16540
  * Handler for CCTP v2 custom burn with hook operations on EVM chains.
16351
16541
  *
@@ -17097,41 +17287,37 @@ function isBrowserWithEthereum() {
17097
17287
  return (typeof window !== 'undefined' && !!window.ethereum);
17098
17288
  }
17099
17289
  /**
17100
- * Attempts to switch the connected chain in a browser wallet, or adds the chain if it is not recognized.
17101
- *
17102
- * @param signer - The current Ethers.js Signer instance, expected to be connected to a browser provider.
17103
- * @param chain - The EVM chain definition to switch to.
17104
- * @returns A promise resolving to the new Signer instance connected to the target chain.
17105
- *
17106
- * @remarks
17107
- * This function uses the EIP-3326 `wallet_switchEthereumChain` and EIP-3085 `wallet_addEthereumChain` methods
17108
- * to prompt the user to switch or add the specified chain in their browser wallet.
17290
+ * Switch or add a chain via an EIP-1193 provider, then return a fresh signer.
17109
17291
  *
17110
- * @throws {Error} If the provider does not support chain switching, or if the user rejects the request,
17111
- * or if the operation fails for any other reason.
17292
+ * @param provider - An EIP-1193 compatible provider with a `send` method.
17293
+ * @param chain - The target EVM chain definition.
17294
+ * @returns A promise resolving to a new Signer connected to the target chain.
17295
+ * @throws When the user rejects the switch or the provider fails.
17112
17296
  */
17113
- async function switchOrAddChainInBrowser(signer, chain) {
17114
- const win = window;
17115
- const signerWithProvider = signer;
17116
- if (!signerWithProvider?.provider?.send) {
17117
- throw new Error('Browser provider does not support chain switching');
17118
- }
17297
+ async function switchOrAddViaProvider(provider, chain) {
17119
17298
  try {
17120
- await signerWithProvider.provider.send('wallet_switchEthereumChain', [
17299
+ await provider.send('wallet_switchEthereumChain', [
17121
17300
  { chainId: `0x${chain.chainId.toString(16)}` },
17122
17301
  ]);
17123
- return await getBrowserSigner(win);
17124
17302
  }
17125
17303
  catch (error) {
17126
17304
  if (typeof error === 'object' &&
17127
17305
  error !== null &&
17128
17306
  'code' in error &&
17129
17307
  error.code === UNRECOGNIZED_CHAIN_ID_ERROR_CODE) {
17130
- await addChainInBrowser(signerWithProvider, chain);
17131
- return await getBrowserSigner(win);
17308
+ await addChainInBrowser({ provider }, chain);
17309
+ await provider.send('wallet_switchEthereumChain', [
17310
+ { chainId: `0x${chain.chainId.toString(16)}` },
17311
+ ]);
17312
+ }
17313
+ else {
17314
+ throw error;
17132
17315
  }
17133
- throw error;
17134
17316
  }
17317
+ const browserProvider = provider instanceof BrowserProvider
17318
+ ? provider
17319
+ : new BrowserProvider(provider);
17320
+ return await browserProvider.getSigner();
17135
17321
  }
17136
17322
  /**
17137
17323
  * Adds a new EVM chain to the user's browser wallet using the EIP-3085 `wallet_addEthereumChain` method.
@@ -17146,9 +17332,9 @@ async function switchOrAddChainInBrowser(signer, chain) {
17146
17332
  *
17147
17333
  * @throws {Error} If the provider does not support the `send` method or if the request fails.
17148
17334
  */
17149
- async function addChainInBrowser(signerWithProvider, chain) {
17335
+ async function addChainInBrowser(providerLike, chain) {
17150
17336
  const rpcUrl = chain.rpcEndpoints[0];
17151
- await signerWithProvider.provider.send('wallet_addEthereumChain', [
17337
+ await providerLike.provider.send('wallet_addEthereumChain', [
17152
17338
  {
17153
17339
  chainId: `0x${chain.chainId.toString(16)}`,
17154
17340
  chainName: chain.name,
@@ -17160,24 +17346,9 @@ async function addChainInBrowser(signerWithProvider, chain) {
17160
17346
  },
17161
17347
  ]);
17162
17348
  }
17163
- /**
17164
- * Returns a Signer instance from the browser's injected Ethereum provider.
17165
- *
17166
- * @param win - The browser window object with an injected `ethereum` provider.
17167
- * @returns A promise resolving Signer instance connected to the browser's provider.
17168
- *
17169
- * @remarks
17170
- * This function wraps the Ethers.js `BrowserProvider` to obtain a Signer for user interaction.
17171
- *
17172
- * @throws {Error} If the `ethereum` provider is not available or if the signer cannot be retrieved.
17173
- */
17174
- async function getBrowserSigner(win) {
17175
- const browserProvider = new BrowserProvider(win.ethereum);
17176
- return await browserProvider.getSigner();
17177
- }
17178
17349
  /**
17179
17350
  * Switches the chain for the given signer and returns a new signer for the target chain.
17180
- * Handles both browser and server-side environments.
17351
+ * Handles browser (injected + WalletConnect/Dynamic) and server-side environments.
17181
17352
  *
17182
17353
  * @param signer - The current signer instance
17183
17354
  * @param chain - The target chain definition
@@ -17185,8 +17356,21 @@ async function getBrowserSigner(win) {
17185
17356
  * @throws If switching fails or the environment is unsupported
17186
17357
  */
17187
17358
  async function switchChain(signer, chain) {
17359
+ // Injected browser wallet (window.ethereum present)
17188
17360
  if (isBrowserWithEthereum()) {
17189
- return switchOrAddChainInBrowser(signer, chain);
17361
+ const signerWithProvider = signer;
17362
+ if (!signerWithProvider?.provider?.send) {
17363
+ throw new Error('Browser provider does not support chain switching');
17364
+ }
17365
+ return switchOrAddViaProvider(signerWithProvider.provider, chain);
17366
+ }
17367
+ // Browser without window.ethereum (e.g., WalletConnect/Dynamic mobile wallets)
17368
+ const signerWithProvider = signer;
17369
+ const signerProvider = signerWithProvider.provider;
17370
+ if (globalThis.window !== undefined &&
17371
+ signerProvider &&
17372
+ 'send' in signerProvider) {
17373
+ return switchOrAddViaProvider(signerProvider, chain);
17190
17374
  }
17191
17375
  // Server-side: recreate the provider and signer
17192
17376
  const rpcUrl = chain.rpcEndpoints[0];
@@ -18162,6 +18346,36 @@ class EthersAdapter extends EvmAdapter {
18162
18346
  });
18163
18347
  }
18164
18348
  }
18349
+ /**
18350
+ * Reads the on-chain bytecode for a given address via ethers' `getCode`.
18351
+ *
18352
+ * Returns the hex-encoded bytecode when the address is a contract, or
18353
+ * `'0x'` when the address has no code (EOA). Used by Gateway burn-intent
18354
+ * signing to detect smart-contract accounts, which are not supported by
18355
+ * Gateway's ecrecover-based verification path.
18356
+ */
18357
+ async readBytecode(address, chain) {
18358
+ const provider = await this.getProvider(chain);
18359
+ try {
18360
+ const code = await provider.getCode(address);
18361
+ return code;
18362
+ }
18363
+ catch (error) {
18364
+ const errorMessage = error instanceof Error ? error.message : String(error);
18365
+ throw new KitError({
18366
+ ...RpcError.ENDPOINT_ERROR,
18367
+ recoverability: 'RETRYABLE',
18368
+ message: `Failed to read bytecode for ${address}: ${errorMessage}`,
18369
+ cause: {
18370
+ trace: {
18371
+ operation: 'readBytecode',
18372
+ address,
18373
+ chain: chain.name,
18374
+ },
18375
+ },
18376
+ });
18377
+ }
18378
+ }
18165
18379
  /**
18166
18380
  * Signs EIP-712 typed data using the configured signer with OperationContext.
18167
18381
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@circle-fin/adapter-ethers-v6",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "EVM blockchain adapter powered by Ethers v6",
5
5
  "keywords": [
6
6
  "circle",