@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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @circle-fin/adapter-ethers-v6
2
2
 
3
+ ## 1.8.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Enable Earn deposit, withdrawal, and claim rewards execution when paired with `@circle-fin/earn-kit`.
8
+
9
+ ## 1.8.0
10
+
11
+ ### Minor Changes
12
+
13
+ - Improves the error you get when attempting a Gateway spend with a smart-contract account (SCA) as the signer. The failure previously surfaced as a confusing `invalid integer value <nil>/<nil> for type uint256` response from the Circle Wallets backend; you now get a clear `INPUT_UNSUPPORTED_ACTION` error up front that points at the delegate workflow (`kit.unifiedBalance.addDelegate` + `kit.unifiedBalance.spend` with the delegate EOA as `from.address` and the SCA as `from.sourceAccount`).
14
+
15
+ ### Patch Changes
16
+
17
+ - Fix excessive MetaMask mobile app-switching prompts during bridge estimation and support chain switching for WalletConnect/Dynamic mobile wallet providers
18
+
3
19
  ## 1.7.0
4
20
 
5
21
  ### Minor Changes
package/index.cjs CHANGED
@@ -408,6 +408,12 @@ const InputError = {
408
408
  name: 'INPUT_INVALID_AMOUNT',
409
409
  type: 'INPUT',
410
410
  },
411
+ /** Unsupported or invalid bridge route configuration */
412
+ UNSUPPORTED_ROUTE: {
413
+ code: 1003,
414
+ name: 'INPUT_UNSUPPORTED_ROUTE',
415
+ type: 'INPUT',
416
+ },
411
417
  /** Invalid or unsupported chain identifier */
412
418
  INVALID_CHAIN: {
413
419
  code: 1005,
@@ -564,6 +570,35 @@ const NetworkError = {
564
570
  type: 'NETWORK',
565
571
  }};
566
572
 
573
+ /**
574
+ * Creates error for unsupported earn route.
575
+ *
576
+ * This error is thrown when no available earn provider supports the requested
577
+ * operation on the specified chain.
578
+ *
579
+ * @param operation - Earn operation name
580
+ * @param chain - Chain name where the earn operation was attempted
581
+ * @returns KitError with specific earn route details
582
+ *
583
+ * @example
584
+ * ```typescript
585
+ * import { createUnsupportedEarnRouteError } from '@core/errors'
586
+ *
587
+ * throw createUnsupportedEarnRouteError('deposit', 'Ethereum')
588
+ * // Message: "Earn deposit on Ethereum is not supported by any available provider."
589
+ * ```
590
+ */
591
+ function createUnsupportedEarnRouteError(operation, chain) {
592
+ const errorDetails = {
593
+ ...InputError.UNSUPPORTED_ROUTE,
594
+ recoverability: 'FATAL',
595
+ message: `Earn ${operation} on ${chain} is not supported by any available provider.`,
596
+ cause: {
597
+ trace: { operation, chain },
598
+ },
599
+ };
600
+ return new KitError(errorDetails);
601
+ }
567
602
  /**
568
603
  * Creates error for unsupported token on chain.
569
604
  *
@@ -2052,23 +2087,21 @@ var UnifiedBalanceChain;
2052
2087
  * Enumeration of blockchains that support earn (vault deposit/withdraw)
2053
2088
  * operations through the Earn Kit.
2054
2089
  *
2055
- * Currently only Ethereum mainnet is supported. Additional chains
2056
- * will be added as vault protocol support expands.
2057
- *
2058
2090
  * @example
2059
2091
  * ```typescript
2060
2092
  * import { EarnChain } from '@core/chains'
2061
2093
  *
2062
2094
  * const result = await earnKit.deposit({
2063
- * from: { adapter, chain: EarnChain.Ethereum },
2095
+ * from: { adapter, chain: EarnChain.Arc_Testnet },
2064
2096
  * vaultAddress: '0x...',
2065
2097
  * amount: '100',
2066
2098
  * })
2067
2099
  * ```
2068
2100
  */
2101
+ // Values must match Blockchain enum members exactly.
2069
2102
  var EarnChain;
2070
2103
  (function (EarnChain) {
2071
- EarnChain["Ethereum"] = "Ethereum";
2104
+ EarnChain["Arc_Testnet"] = "Arc_Testnet";
2072
2105
  })(EarnChain || (EarnChain = {}));
2073
2106
 
2074
2107
  /**
@@ -5762,7 +5795,7 @@ zod.z.union([
5762
5795
  * Zod schema for validating earn-specific chain identifiers.
5763
5796
  *
5764
5797
  * Validate that the provided chain is supported for earn (vault
5765
- * deposit/withdraw) operations. Currently only Ethereum is
5798
+ * deposit/withdraw) operations. Currently only Arc Testnet is
5766
5799
  * supported.
5767
5800
  *
5768
5801
  * Accept an EarnChain enum value, a matching string literal, or
@@ -5771,12 +5804,12 @@ zod.z.union([
5771
5804
  * @example
5772
5805
  * ```typescript
5773
5806
  * import { earnChainIdentifierSchema } from '@core/chains'
5774
- * import { EarnChain, Ethereum } from '@core/chains'
5807
+ * import { ArcTestnet, EarnChain } from '@core/chains'
5775
5808
  *
5776
5809
  * // Valid
5777
- * earnChainIdentifierSchema.parse(EarnChain.Ethereum)
5778
- * earnChainIdentifierSchema.parse('Ethereum')
5779
- * earnChainIdentifierSchema.parse(Ethereum)
5810
+ * earnChainIdentifierSchema.parse(EarnChain.Arc_Testnet)
5811
+ * earnChainIdentifierSchema.parse('Arc_Testnet')
5812
+ * earnChainIdentifierSchema.parse(ArcTestnet)
5780
5813
  *
5781
5814
  * // Invalid (throws ZodError)
5782
5815
  * earnChainIdentifierSchema.parse('Solana')
@@ -7389,7 +7422,9 @@ const hexStringSchema = zod.z
7389
7422
  * console.log(result.success) // true
7390
7423
  * ```
7391
7424
  */
7392
- const evmAddressSchema = hexStringSchema.refine((value) => value.length === 42, 'EVM address must be exactly 42 characters long (0x + 40 hex characters)');
7425
+ const evmAddressSchema = hexStringSchema
7426
+ .refine((value) => value.length === 42, 'EVM address must be exactly 42 characters long (0x + 40 hex characters)')
7427
+ .transform((value) => value);
7393
7428
  /**
7394
7429
  * Schema for validating transaction hashes.
7395
7430
  *
@@ -7410,6 +7445,29 @@ const evmAddressSchema = hexStringSchema.refine((value) => value.length === 42,
7410
7445
  * ```
7411
7446
  */
7412
7447
  hexStringSchema.refine((value) => value.length === 66, 'Transaction hash must be exactly 66 characters long (0x + 64 hex characters)');
7448
+ /**
7449
+ * Schema for validating EVM signatures.
7450
+ *
7451
+ * This schema validates that a string is a properly formatted 65-byte EVM
7452
+ * signature:
7453
+ * - Must be a valid hex string with '0x' prefix
7454
+ * - Must be exactly 132 characters long (0x + 130 hex characters)
7455
+ *
7456
+ * @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
7457
+ *
7458
+ * @example
7459
+ * ```typescript
7460
+ * import { evmSignatureSchema } from '@core/adapter'
7461
+ *
7462
+ * const validSignature = `0x${'ab'.repeat(65)}`
7463
+ *
7464
+ * const result = evmSignatureSchema.safeParse(validSignature)
7465
+ * console.log(result.success) // true
7466
+ * ```
7467
+ */
7468
+ const evmSignatureSchema = hexStringSchema
7469
+ .refine((value) => value.length === 132, 'EVM signature must be exactly 132 characters long (0x + 130 hex characters)')
7470
+ .transform((value) => value);
7413
7471
  /**
7414
7472
  * Schema for validating base58-encoded strings.
7415
7473
  *
@@ -7644,6 +7702,79 @@ function getSupportedChains(ecosystem) {
7644
7702
  }
7645
7703
  }
7646
7704
 
7705
+ /**
7706
+ * IAdapter contract ABI.
7707
+ *
7708
+ * Shared ABI for the on-chain Adapter contract used by multiple kits
7709
+ * (swap, earn) for executing signed instruction sets. The `execute()`
7710
+ * function accepts EIP-712 signed execution parameters, token inputs,
7711
+ * and a signature, then executes the corresponding on-chain
7712
+ * instructions.
7713
+ */
7714
+ const adapterContractAbi = [
7715
+ {
7716
+ type: 'function',
7717
+ name: 'execute',
7718
+ inputs: [
7719
+ {
7720
+ name: 'params',
7721
+ type: 'tuple',
7722
+ internalType: 'struct IAdapter.ExecutionParams',
7723
+ components: [
7724
+ {
7725
+ name: 'instructions',
7726
+ type: 'tuple[]',
7727
+ internalType: 'struct IAdapter.Instruction[]',
7728
+ components: [
7729
+ { name: 'target', type: 'address', internalType: 'address' },
7730
+ { name: 'data', type: 'bytes', internalType: 'bytes' },
7731
+ { name: 'value', type: 'uint256', internalType: 'uint256' },
7732
+ { name: 'tokenIn', type: 'address', internalType: 'address' },
7733
+ {
7734
+ name: 'amountToApprove',
7735
+ type: 'uint256',
7736
+ internalType: 'uint256',
7737
+ },
7738
+ { name: 'tokenOut', type: 'address', internalType: 'address' },
7739
+ { name: 'minTokenOut', type: 'uint256', internalType: 'uint256' },
7740
+ ],
7741
+ },
7742
+ {
7743
+ name: 'tokens',
7744
+ type: 'tuple[]',
7745
+ internalType: 'struct IAdapter.TokenRecipient[]',
7746
+ components: [
7747
+ { name: 'token', type: 'address', internalType: 'address' },
7748
+ { name: 'beneficiary', type: 'address', internalType: 'address' },
7749
+ ],
7750
+ },
7751
+ { name: 'execId', type: 'uint256', internalType: 'uint256' },
7752
+ { name: 'deadline', type: 'uint256', internalType: 'uint256' },
7753
+ { name: 'metadata', type: 'bytes', internalType: 'bytes' },
7754
+ ],
7755
+ },
7756
+ {
7757
+ name: 'tokenInputs',
7758
+ type: 'tuple[]',
7759
+ internalType: 'struct IAdapter.TokenInput[]',
7760
+ components: [
7761
+ {
7762
+ name: 'permitType',
7763
+ type: 'uint8',
7764
+ internalType: 'enum IAdapter.PermitType',
7765
+ },
7766
+ { name: 'token', type: 'address', internalType: 'address' },
7767
+ { name: 'amount', type: 'uint256', internalType: 'uint256' },
7768
+ { name: 'permitCalldata', type: 'bytes', internalType: 'bytes' },
7769
+ ],
7770
+ },
7771
+ { name: 'signature', type: 'bytes', internalType: 'bytes' },
7772
+ ],
7773
+ outputs: [],
7774
+ stateMutability: 'payable',
7775
+ },
7776
+ ];
7777
+
7647
7778
  /**
7648
7779
  * ERC20 ABI
7649
7780
  *
@@ -11009,78 +11140,6 @@ const bridgeContractAbi = [
11009
11140
  },
11010
11141
  ];
11011
11142
 
11012
- /**
11013
- * Adapter Contract ABI
11014
- *
11015
- * This ABI is used to interact with the Adapter smart contract that handles
11016
- * gasless token approvals via permits and atomic swap execution.
11017
- *
11018
- * The execute() function is the primary entry point for swap operations.
11019
- */
11020
- const adapterContractAbi = [
11021
- {
11022
- type: 'function',
11023
- name: 'execute',
11024
- inputs: [
11025
- {
11026
- name: 'params',
11027
- type: 'tuple',
11028
- internalType: 'struct IAdapter.ExecutionParams',
11029
- components: [
11030
- {
11031
- name: 'instructions',
11032
- type: 'tuple[]',
11033
- internalType: 'struct IAdapter.Instruction[]',
11034
- components: [
11035
- { name: 'target', type: 'address', internalType: 'address' },
11036
- { name: 'data', type: 'bytes', internalType: 'bytes' },
11037
- { name: 'value', type: 'uint256', internalType: 'uint256' },
11038
- { name: 'tokenIn', type: 'address', internalType: 'address' },
11039
- {
11040
- name: 'amountToApprove',
11041
- type: 'uint256',
11042
- internalType: 'uint256',
11043
- },
11044
- { name: 'tokenOut', type: 'address', internalType: 'address' },
11045
- { name: 'minTokenOut', type: 'uint256', internalType: 'uint256' },
11046
- ],
11047
- },
11048
- {
11049
- name: 'tokens',
11050
- type: 'tuple[]',
11051
- internalType: 'struct IAdapter.TokenRecipient[]',
11052
- components: [
11053
- { name: 'token', type: 'address', internalType: 'address' },
11054
- { name: 'beneficiary', type: 'address', internalType: 'address' },
11055
- ],
11056
- },
11057
- { name: 'execId', type: 'uint256', internalType: 'uint256' },
11058
- { name: 'deadline', type: 'uint256', internalType: 'uint256' },
11059
- { name: 'metadata', type: 'bytes', internalType: 'bytes' },
11060
- ],
11061
- },
11062
- {
11063
- name: 'tokenInputs',
11064
- type: 'tuple[]',
11065
- internalType: 'struct IAdapter.TokenInput[]',
11066
- components: [
11067
- {
11068
- name: 'permitType',
11069
- type: 'uint8',
11070
- internalType: 'enum IAdapter.PermitType',
11071
- },
11072
- { name: 'token', type: 'address', internalType: 'address' },
11073
- { name: 'amount', type: 'uint256', internalType: 'uint256' },
11074
- { name: 'permitCalldata', type: 'bytes', internalType: 'bytes' },
11075
- ],
11076
- },
11077
- { name: 'signature', type: 'bytes', internalType: 'bytes' },
11078
- ],
11079
- outputs: [],
11080
- stateMutability: 'payable',
11081
- },
11082
- ];
11083
-
11084
11143
  /**
11085
11144
  * ABI for the Circle Gateway Wallet contract.
11086
11145
  *
@@ -15502,6 +15561,107 @@ const balanceOf$2 = async (params, adapter, context) => {
15502
15561
  };
15503
15562
  };
15504
15563
 
15564
+ const hexBytesSchema = zod.z
15565
+ .string()
15566
+ .regex(/^0x([0-9a-fA-F]{2})*$/, {
15567
+ message: 'value must be a 0x-prefixed even-length hex string',
15568
+ })
15569
+ .transform((value) => value);
15570
+ const earnExecuteParamsSchema = zod.z
15571
+ .object({
15572
+ executeParams: zod.z.object({}).passthrough(),
15573
+ signature: evmSignatureSchema,
15574
+ tokenInputs: zod.z.array(zod.z.object({
15575
+ permitType: zod.z.nativeEnum(PermitType),
15576
+ token: evmAddressSchema,
15577
+ amount: zod.z.bigint().refine((value) => value >= 0n, {
15578
+ message: 'amount must be a non-negative bigint',
15579
+ }),
15580
+ permitCalldata: hexBytesSchema,
15581
+ })),
15582
+ })
15583
+ .passthrough();
15584
+ function validateAdapterContractAddress(chainName, value) {
15585
+ const result = evmAddressSchema.safeParse(value);
15586
+ if (!result.success) {
15587
+ throw createValidationFailedError('chain.kitContracts.adapter', value, `Adapter contract for chain ${chainName} must be a 0x-prefixed 20-byte hex address.`);
15588
+ }
15589
+ return result.data;
15590
+ }
15591
+ function validateEarnExecuteParams(params) {
15592
+ const result = earnExecuteParamsSchema.safeParse(params);
15593
+ if (!result.success) {
15594
+ throw createValidationErrorFromZod(result.error, 'earn execute params');
15595
+ }
15596
+ return {
15597
+ executeParams: result.data.executeParams,
15598
+ signature: result.data.signature,
15599
+ tokenInputs: result.data.tokenInputs,
15600
+ __isActionParams: true,
15601
+ };
15602
+ }
15603
+ /**
15604
+ * Prepare an earn operation against the adapter contract on EVM chains.
15605
+ *
15606
+ * Forward the service-signed `executeParams` and `signature` to the adapter
15607
+ * contract's `execute` function. Token approvals are handled upstream by the
15608
+ * provider (today via a separate `token.approve` transaction; future: permit
15609
+ * entries in `tokenInputs`). This function performs runtime validation for
15610
+ * plain-JS callers, prepares calldata only, performs no network I/O, and does
15611
+ * not submit the transaction.
15612
+ *
15613
+ * @param params - Service-signed earn payload.
15614
+ * @param adapter - EVM adapter used to prepare the transaction.
15615
+ * @param context - Resolved operation context (chain + address).
15616
+ * @returns Prepared chain request ready to `estimate()` or `execute()`.
15617
+ * @throws {@link KitError} If the chain is not EVM-compatible.
15618
+ * @throws {@link KitError} If the chain has no adapter contract configured.
15619
+ * @throws {@link KitError} If required fields are missing or invalid.
15620
+ *
15621
+ * @example
15622
+ * ```typescript
15623
+ * const prepared = await evmAdapter.prepareAction(
15624
+ * 'earn.deposit',
15625
+ * {
15626
+ * executeParams: {
15627
+ * instructions: [],
15628
+ * tokens: [],
15629
+ * execId: 1n,
15630
+ * deadline: 0n,
15631
+ * metadata: '0x',
15632
+ * },
15633
+ * tokenInputs: [],
15634
+ * signature: '0x...',
15635
+ * },
15636
+ * { chain, address },
15637
+ * )
15638
+ * const txHash = await prepared.execute()
15639
+ * ```
15640
+ */
15641
+ const prepareEarnExecute = async (params, adapter, context) => {
15642
+ const { chain } = context;
15643
+ if (chain.type !== 'evm') {
15644
+ throw createInvalidChainError(chain.name, `Expected EVM chain, but received chain type: ${String(chain.type)}`);
15645
+ }
15646
+ const adapterContractAddress = chain.kitContracts?.adapter;
15647
+ if (adapterContractAddress === undefined) {
15648
+ throw createUnsupportedEarnRouteError('execute', chain.name);
15649
+ }
15650
+ const validatedAdapterAddress = validateAdapterContractAddress(chain.name, adapterContractAddress);
15651
+ const validatedParams = validateEarnExecuteParams(params);
15652
+ return adapter.prepare({
15653
+ type: 'evm',
15654
+ abi: adapterContractAbi,
15655
+ address: validatedAdapterAddress,
15656
+ functionName: 'execute',
15657
+ args: [
15658
+ validatedParams.executeParams,
15659
+ validatedParams.tokenInputs,
15660
+ validatedParams.signature,
15661
+ ],
15662
+ }, context);
15663
+ };
15664
+
15505
15665
  /**
15506
15666
  * Type guard to check if params is ExecuteSwapEVMParams.
15507
15667
  *
@@ -16351,6 +16511,36 @@ const getHandlers = (adapter) => {
16351
16511
  'swap.execute': async (params, context) => {
16352
16512
  return executeSwap(params, adapter, context);
16353
16513
  },
16514
+ /**
16515
+ * Handler for earn deposit execution on EVM chains.
16516
+ *
16517
+ * Forwards the earn service's signed `executeParams` and `signature` to the
16518
+ * on-chain adapter contract's `execute` function. Token approvals are handled
16519
+ * by the earn provider via separate `token.approve` transactions.
16520
+ */
16521
+ 'earn.deposit': async (params, context) => {
16522
+ return prepareEarnExecute(params, adapter, context);
16523
+ },
16524
+ /**
16525
+ * Handler for earn withdraw execution on EVM chains.
16526
+ *
16527
+ * Forwards the earn service's signed `executeParams` and `signature` to the
16528
+ * on-chain adapter contract's `execute` function. Token approvals are handled
16529
+ * by the earn provider via separate `token.approve` transactions.
16530
+ */
16531
+ 'earn.withdraw': async (params, context) => {
16532
+ return prepareEarnExecute(params, adapter, context);
16533
+ },
16534
+ /**
16535
+ * Handler for earn claim rewards execution on EVM chains.
16536
+ *
16537
+ * Forwards the earn service's signed `executeParams` and `signature` to the
16538
+ * on-chain adapter contract's `execute` function. Claim rewards does not
16539
+ * require approvals because the adapter does not pull user tokens.
16540
+ */
16541
+ 'earn.claimRewards': async (params, context) => {
16542
+ return prepareEarnExecute(params, adapter, context);
16543
+ },
16354
16544
  /**
16355
16545
  * Handler for CCTP v2 custom burn with hook operations on EVM chains.
16356
16546
  *
@@ -17102,41 +17292,37 @@ function isBrowserWithEthereum() {
17102
17292
  return (typeof window !== 'undefined' && !!window.ethereum);
17103
17293
  }
17104
17294
  /**
17105
- * Attempts to switch the connected chain in a browser wallet, or adds the chain if it is not recognized.
17106
- *
17107
- * @param signer - The current Ethers.js Signer instance, expected to be connected to a browser provider.
17108
- * @param chain - The EVM chain definition to switch to.
17109
- * @returns A promise resolving to the new Signer instance connected to the target chain.
17110
- *
17111
- * @remarks
17112
- * This function uses the EIP-3326 `wallet_switchEthereumChain` and EIP-3085 `wallet_addEthereumChain` methods
17113
- * to prompt the user to switch or add the specified chain in their browser wallet.
17295
+ * Switch or add a chain via an EIP-1193 provider, then return a fresh signer.
17114
17296
  *
17115
- * @throws {Error} If the provider does not support chain switching, or if the user rejects the request,
17116
- * or if the operation fails for any other reason.
17297
+ * @param provider - An EIP-1193 compatible provider with a `send` method.
17298
+ * @param chain - The target EVM chain definition.
17299
+ * @returns A promise resolving to a new Signer connected to the target chain.
17300
+ * @throws When the user rejects the switch or the provider fails.
17117
17301
  */
17118
- async function switchOrAddChainInBrowser(signer, chain) {
17119
- const win = window;
17120
- const signerWithProvider = signer;
17121
- if (!signerWithProvider?.provider?.send) {
17122
- throw new Error('Browser provider does not support chain switching');
17123
- }
17302
+ async function switchOrAddViaProvider(provider, chain) {
17124
17303
  try {
17125
- await signerWithProvider.provider.send('wallet_switchEthereumChain', [
17304
+ await provider.send('wallet_switchEthereumChain', [
17126
17305
  { chainId: `0x${chain.chainId.toString(16)}` },
17127
17306
  ]);
17128
- return await getBrowserSigner(win);
17129
17307
  }
17130
17308
  catch (error) {
17131
17309
  if (typeof error === 'object' &&
17132
17310
  error !== null &&
17133
17311
  'code' in error &&
17134
17312
  error.code === UNRECOGNIZED_CHAIN_ID_ERROR_CODE) {
17135
- await addChainInBrowser(signerWithProvider, chain);
17136
- return await getBrowserSigner(win);
17313
+ await addChainInBrowser({ provider }, chain);
17314
+ await provider.send('wallet_switchEthereumChain', [
17315
+ { chainId: `0x${chain.chainId.toString(16)}` },
17316
+ ]);
17317
+ }
17318
+ else {
17319
+ throw error;
17137
17320
  }
17138
- throw error;
17139
17321
  }
17322
+ const browserProvider = provider instanceof ethers.BrowserProvider
17323
+ ? provider
17324
+ : new ethers.BrowserProvider(provider);
17325
+ return await browserProvider.getSigner();
17140
17326
  }
17141
17327
  /**
17142
17328
  * Adds a new EVM chain to the user's browser wallet using the EIP-3085 `wallet_addEthereumChain` method.
@@ -17151,9 +17337,9 @@ async function switchOrAddChainInBrowser(signer, chain) {
17151
17337
  *
17152
17338
  * @throws {Error} If the provider does not support the `send` method or if the request fails.
17153
17339
  */
17154
- async function addChainInBrowser(signerWithProvider, chain) {
17340
+ async function addChainInBrowser(providerLike, chain) {
17155
17341
  const rpcUrl = chain.rpcEndpoints[0];
17156
- await signerWithProvider.provider.send('wallet_addEthereumChain', [
17342
+ await providerLike.provider.send('wallet_addEthereumChain', [
17157
17343
  {
17158
17344
  chainId: `0x${chain.chainId.toString(16)}`,
17159
17345
  chainName: chain.name,
@@ -17165,24 +17351,9 @@ async function addChainInBrowser(signerWithProvider, chain) {
17165
17351
  },
17166
17352
  ]);
17167
17353
  }
17168
- /**
17169
- * Returns a Signer instance from the browser's injected Ethereum provider.
17170
- *
17171
- * @param win - The browser window object with an injected `ethereum` provider.
17172
- * @returns A promise resolving Signer instance connected to the browser's provider.
17173
- *
17174
- * @remarks
17175
- * This function wraps the Ethers.js `BrowserProvider` to obtain a Signer for user interaction.
17176
- *
17177
- * @throws {Error} If the `ethereum` provider is not available or if the signer cannot be retrieved.
17178
- */
17179
- async function getBrowserSigner(win) {
17180
- const browserProvider = new ethers.BrowserProvider(win.ethereum);
17181
- return await browserProvider.getSigner();
17182
- }
17183
17354
  /**
17184
17355
  * Switches the chain for the given signer and returns a new signer for the target chain.
17185
- * Handles both browser and server-side environments.
17356
+ * Handles browser (injected + WalletConnect/Dynamic) and server-side environments.
17186
17357
  *
17187
17358
  * @param signer - The current signer instance
17188
17359
  * @param chain - The target chain definition
@@ -17190,8 +17361,21 @@ async function getBrowserSigner(win) {
17190
17361
  * @throws If switching fails or the environment is unsupported
17191
17362
  */
17192
17363
  async function switchChain(signer, chain) {
17364
+ // Injected browser wallet (window.ethereum present)
17193
17365
  if (isBrowserWithEthereum()) {
17194
- return switchOrAddChainInBrowser(signer, chain);
17366
+ const signerWithProvider = signer;
17367
+ if (!signerWithProvider?.provider?.send) {
17368
+ throw new Error('Browser provider does not support chain switching');
17369
+ }
17370
+ return switchOrAddViaProvider(signerWithProvider.provider, chain);
17371
+ }
17372
+ // Browser without window.ethereum (e.g., WalletConnect/Dynamic mobile wallets)
17373
+ const signerWithProvider = signer;
17374
+ const signerProvider = signerWithProvider.provider;
17375
+ if (globalThis.window !== undefined &&
17376
+ signerProvider &&
17377
+ 'send' in signerProvider) {
17378
+ return switchOrAddViaProvider(signerProvider, chain);
17195
17379
  }
17196
17380
  // Server-side: recreate the provider and signer
17197
17381
  const rpcUrl = chain.rpcEndpoints[0];
@@ -18167,6 +18351,36 @@ class EthersAdapter extends EvmAdapter {
18167
18351
  });
18168
18352
  }
18169
18353
  }
18354
+ /**
18355
+ * Reads the on-chain bytecode for a given address via ethers' `getCode`.
18356
+ *
18357
+ * Returns the hex-encoded bytecode when the address is a contract, or
18358
+ * `'0x'` when the address has no code (EOA). Used by Gateway burn-intent
18359
+ * signing to detect smart-contract accounts, which are not supported by
18360
+ * Gateway's ecrecover-based verification path.
18361
+ */
18362
+ async readBytecode(address, chain) {
18363
+ const provider = await this.getProvider(chain);
18364
+ try {
18365
+ const code = await provider.getCode(address);
18366
+ return code;
18367
+ }
18368
+ catch (error) {
18369
+ const errorMessage = error instanceof Error ? error.message : String(error);
18370
+ throw new KitError({
18371
+ ...RpcError.ENDPOINT_ERROR,
18372
+ recoverability: 'RETRYABLE',
18373
+ message: `Failed to read bytecode for ${address}: ${errorMessage}`,
18374
+ cause: {
18375
+ trace: {
18376
+ operation: 'readBytecode',
18377
+ address,
18378
+ chain: chain.name,
18379
+ },
18380
+ },
18381
+ });
18382
+ }
18383
+ }
18170
18384
  /**
18171
18385
  * Signs EIP-712 typed data using the configured signer with OperationContext.
18172
18386
  *