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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @circle-fin/adapter-ethers-v6
2
2
 
3
+ ## 1.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 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`).
8
+
9
+ ### Patch Changes
10
+
11
+ - Fix excessive MetaMask mobile app-switching prompts during bridge estimation and support chain switching for WalletConnect/Dynamic mobile wallet providers
12
+
3
13
  ## 1.7.0
4
14
 
5
15
  ### Minor Changes
package/index.cjs CHANGED
@@ -17102,41 +17102,37 @@ function isBrowserWithEthereum() {
17102
17102
  return (typeof window !== 'undefined' && !!window.ethereum);
17103
17103
  }
17104
17104
  /**
17105
- * Attempts to switch the connected chain in a browser wallet, or adds the chain if it is not recognized.
17105
+ * Switch or add a chain via an EIP-1193 provider, then return a fresh signer.
17106
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.
17114
- *
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.
17107
+ * @param provider - An EIP-1193 compatible provider with a `send` method.
17108
+ * @param chain - The target EVM chain definition.
17109
+ * @returns A promise resolving to a new Signer connected to the target chain.
17110
+ * @throws When the user rejects the switch or the provider fails.
17117
17111
  */
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
- }
17112
+ async function switchOrAddViaProvider(provider, chain) {
17124
17113
  try {
17125
- await signerWithProvider.provider.send('wallet_switchEthereumChain', [
17114
+ await provider.send('wallet_switchEthereumChain', [
17126
17115
  { chainId: `0x${chain.chainId.toString(16)}` },
17127
17116
  ]);
17128
- return await getBrowserSigner(win);
17129
17117
  }
17130
17118
  catch (error) {
17131
17119
  if (typeof error === 'object' &&
17132
17120
  error !== null &&
17133
17121
  'code' in error &&
17134
17122
  error.code === UNRECOGNIZED_CHAIN_ID_ERROR_CODE) {
17135
- await addChainInBrowser(signerWithProvider, chain);
17136
- return await getBrowserSigner(win);
17123
+ await addChainInBrowser({ provider }, chain);
17124
+ await provider.send('wallet_switchEthereumChain', [
17125
+ { chainId: `0x${chain.chainId.toString(16)}` },
17126
+ ]);
17127
+ }
17128
+ else {
17129
+ throw error;
17137
17130
  }
17138
- throw error;
17139
17131
  }
17132
+ const browserProvider = provider instanceof ethers.BrowserProvider
17133
+ ? provider
17134
+ : new ethers.BrowserProvider(provider);
17135
+ return await browserProvider.getSigner();
17140
17136
  }
17141
17137
  /**
17142
17138
  * Adds a new EVM chain to the user's browser wallet using the EIP-3085 `wallet_addEthereumChain` method.
@@ -17151,9 +17147,9 @@ async function switchOrAddChainInBrowser(signer, chain) {
17151
17147
  *
17152
17148
  * @throws {Error} If the provider does not support the `send` method or if the request fails.
17153
17149
  */
17154
- async function addChainInBrowser(signerWithProvider, chain) {
17150
+ async function addChainInBrowser(providerLike, chain) {
17155
17151
  const rpcUrl = chain.rpcEndpoints[0];
17156
- await signerWithProvider.provider.send('wallet_addEthereumChain', [
17152
+ await providerLike.provider.send('wallet_addEthereumChain', [
17157
17153
  {
17158
17154
  chainId: `0x${chain.chainId.toString(16)}`,
17159
17155
  chainName: chain.name,
@@ -17165,24 +17161,9 @@ async function addChainInBrowser(signerWithProvider, chain) {
17165
17161
  },
17166
17162
  ]);
17167
17163
  }
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
17164
  /**
17184
17165
  * Switches the chain for the given signer and returns a new signer for the target chain.
17185
- * Handles both browser and server-side environments.
17166
+ * Handles browser (injected + WalletConnect/Dynamic) and server-side environments.
17186
17167
  *
17187
17168
  * @param signer - The current signer instance
17188
17169
  * @param chain - The target chain definition
@@ -17190,8 +17171,21 @@ async function getBrowserSigner(win) {
17190
17171
  * @throws If switching fails or the environment is unsupported
17191
17172
  */
17192
17173
  async function switchChain(signer, chain) {
17174
+ // Injected browser wallet (window.ethereum present)
17193
17175
  if (isBrowserWithEthereum()) {
17194
- return switchOrAddChainInBrowser(signer, chain);
17176
+ const signerWithProvider = signer;
17177
+ if (!signerWithProvider?.provider?.send) {
17178
+ throw new Error('Browser provider does not support chain switching');
17179
+ }
17180
+ return switchOrAddViaProvider(signerWithProvider.provider, chain);
17181
+ }
17182
+ // Browser without window.ethereum (e.g., WalletConnect/Dynamic mobile wallets)
17183
+ const signerWithProvider = signer;
17184
+ const signerProvider = signerWithProvider.provider;
17185
+ if (globalThis.window !== undefined &&
17186
+ signerProvider &&
17187
+ 'send' in signerProvider) {
17188
+ return switchOrAddViaProvider(signerProvider, chain);
17195
17189
  }
17196
17190
  // Server-side: recreate the provider and signer
17197
17191
  const rpcUrl = chain.rpcEndpoints[0];
@@ -18167,6 +18161,36 @@ class EthersAdapter extends EvmAdapter {
18167
18161
  });
18168
18162
  }
18169
18163
  }
18164
+ /**
18165
+ * Reads the on-chain bytecode for a given address via ethers' `getCode`.
18166
+ *
18167
+ * Returns the hex-encoded bytecode when the address is a contract, or
18168
+ * `'0x'` when the address has no code (EOA). Used by Gateway burn-intent
18169
+ * signing to detect smart-contract accounts, which are not supported by
18170
+ * Gateway's ecrecover-based verification path.
18171
+ */
18172
+ async readBytecode(address, chain) {
18173
+ const provider = await this.getProvider(chain);
18174
+ try {
18175
+ const code = await provider.getCode(address);
18176
+ return code;
18177
+ }
18178
+ catch (error) {
18179
+ const errorMessage = error instanceof Error ? error.message : String(error);
18180
+ throw new KitError({
18181
+ ...RpcError.ENDPOINT_ERROR,
18182
+ recoverability: 'RETRYABLE',
18183
+ message: `Failed to read bytecode for ${address}: ${errorMessage}`,
18184
+ cause: {
18185
+ trace: {
18186
+ operation: 'readBytecode',
18187
+ address,
18188
+ chain: chain.name,
18189
+ },
18190
+ },
18191
+ });
18192
+ }
18193
+ }
18170
18194
  /**
18171
18195
  * Signs EIP-712 typed data using the configured signer with OperationContext.
18172
18196
  *
package/index.d.ts CHANGED
@@ -3974,6 +3974,37 @@ declare abstract class EvmAdapter<TAdapterCapabilities extends AdapterCapabiliti
3974
3974
  * @throws Error when balance retrieval fails.
3975
3975
  */
3976
3976
  abstract readNativeBalance(address: string, chain: EVMChainDefinition): Promise<bigint>;
3977
+ /**
3978
+ * Reads the on-chain bytecode for a given address.
3979
+ *
3980
+ * Returns the deployed contract bytecode as a hex string (`0x`-prefixed),
3981
+ * or `'0x'` when the address has no code (i.e. is a plain EOA).
3982
+ * Implementations MUST normalize a "no code" RPC response to `'0x'` so
3983
+ * callers can compare against a single sentinel.
3984
+ *
3985
+ * Useful for detecting smart-contract accounts before attempting an
3986
+ * operation that only supports EOA signing (e.g. Gateway burn intents).
3987
+ *
3988
+ * @param address - The address to read bytecode for.
3989
+ * @param chain - The chain definition to read from.
3990
+ * @returns Promise resolving to `'0x'` for an EOA, or the hex-encoded
3991
+ * deployed bytecode otherwise.
3992
+ * @throws Error when the underlying RPC call fails.
3993
+ *
3994
+ * @example
3995
+ * ```typescript
3996
+ * import { ViemAdapter } from '@circle-fin/adapter-viem-v2'
3997
+ * import { Ethereum } from '@core/chains'
3998
+ *
3999
+ * const adapter = new ViemAdapter({ publicClient, walletClient })
4000
+ * const code = await adapter.readBytecode('0xabc...', Ethereum)
4001
+ *
4002
+ * if (code !== '0x') {
4003
+ * throw new Error('signer has on-chain bytecode')
4004
+ * }
4005
+ * ```
4006
+ */
4007
+ abstract readBytecode(address: string, chain: EVMChainDefinition): Promise<`0x${string}`>;
3977
4008
  /**
3978
4009
  * Calculate the total transaction fee including compute cost and buffer for the configured chain.
3979
4010
  *
@@ -4573,6 +4604,15 @@ declare class EthersAdapter<TAdapterCapabilities extends AdapterCapabilities = A
4573
4604
  * ```
4574
4605
  */
4575
4606
  readNativeBalance(address: string, chain: EVMChainDefinition): Promise<bigint>;
4607
+ /**
4608
+ * Reads the on-chain bytecode for a given address via ethers' `getCode`.
4609
+ *
4610
+ * Returns the hex-encoded bytecode when the address is a contract, or
4611
+ * `'0x'` when the address has no code (EOA). Used by Gateway burn-intent
4612
+ * signing to detect smart-contract accounts, which are not supported by
4613
+ * Gateway's ecrecover-based verification path.
4614
+ */
4615
+ readBytecode(address: string, chain: EVMChainDefinition): Promise<`0x${string}`>;
4576
4616
  /**
4577
4617
  * Signs EIP-712 typed data using the configured signer with OperationContext.
4578
4618
  *
package/index.mjs CHANGED
@@ -17097,41 +17097,37 @@ function isBrowserWithEthereum() {
17097
17097
  return (typeof window !== 'undefined' && !!window.ethereum);
17098
17098
  }
17099
17099
  /**
17100
- * Attempts to switch the connected chain in a browser wallet, or adds the chain if it is not recognized.
17100
+ * Switch or add a chain via an EIP-1193 provider, then return a fresh signer.
17101
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.
17109
- *
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.
17102
+ * @param provider - An EIP-1193 compatible provider with a `send` method.
17103
+ * @param chain - The target EVM chain definition.
17104
+ * @returns A promise resolving to a new Signer connected to the target chain.
17105
+ * @throws When the user rejects the switch or the provider fails.
17112
17106
  */
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
- }
17107
+ async function switchOrAddViaProvider(provider, chain) {
17119
17108
  try {
17120
- await signerWithProvider.provider.send('wallet_switchEthereumChain', [
17109
+ await provider.send('wallet_switchEthereumChain', [
17121
17110
  { chainId: `0x${chain.chainId.toString(16)}` },
17122
17111
  ]);
17123
- return await getBrowserSigner(win);
17124
17112
  }
17125
17113
  catch (error) {
17126
17114
  if (typeof error === 'object' &&
17127
17115
  error !== null &&
17128
17116
  'code' in error &&
17129
17117
  error.code === UNRECOGNIZED_CHAIN_ID_ERROR_CODE) {
17130
- await addChainInBrowser(signerWithProvider, chain);
17131
- return await getBrowserSigner(win);
17118
+ await addChainInBrowser({ provider }, chain);
17119
+ await provider.send('wallet_switchEthereumChain', [
17120
+ { chainId: `0x${chain.chainId.toString(16)}` },
17121
+ ]);
17122
+ }
17123
+ else {
17124
+ throw error;
17132
17125
  }
17133
- throw error;
17134
17126
  }
17127
+ const browserProvider = provider instanceof BrowserProvider
17128
+ ? provider
17129
+ : new BrowserProvider(provider);
17130
+ return await browserProvider.getSigner();
17135
17131
  }
17136
17132
  /**
17137
17133
  * Adds a new EVM chain to the user's browser wallet using the EIP-3085 `wallet_addEthereumChain` method.
@@ -17146,9 +17142,9 @@ async function switchOrAddChainInBrowser(signer, chain) {
17146
17142
  *
17147
17143
  * @throws {Error} If the provider does not support the `send` method or if the request fails.
17148
17144
  */
17149
- async function addChainInBrowser(signerWithProvider, chain) {
17145
+ async function addChainInBrowser(providerLike, chain) {
17150
17146
  const rpcUrl = chain.rpcEndpoints[0];
17151
- await signerWithProvider.provider.send('wallet_addEthereumChain', [
17147
+ await providerLike.provider.send('wallet_addEthereumChain', [
17152
17148
  {
17153
17149
  chainId: `0x${chain.chainId.toString(16)}`,
17154
17150
  chainName: chain.name,
@@ -17160,24 +17156,9 @@ async function addChainInBrowser(signerWithProvider, chain) {
17160
17156
  },
17161
17157
  ]);
17162
17158
  }
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
17159
  /**
17179
17160
  * Switches the chain for the given signer and returns a new signer for the target chain.
17180
- * Handles both browser and server-side environments.
17161
+ * Handles browser (injected + WalletConnect/Dynamic) and server-side environments.
17181
17162
  *
17182
17163
  * @param signer - The current signer instance
17183
17164
  * @param chain - The target chain definition
@@ -17185,8 +17166,21 @@ async function getBrowserSigner(win) {
17185
17166
  * @throws If switching fails or the environment is unsupported
17186
17167
  */
17187
17168
  async function switchChain(signer, chain) {
17169
+ // Injected browser wallet (window.ethereum present)
17188
17170
  if (isBrowserWithEthereum()) {
17189
- return switchOrAddChainInBrowser(signer, chain);
17171
+ const signerWithProvider = signer;
17172
+ if (!signerWithProvider?.provider?.send) {
17173
+ throw new Error('Browser provider does not support chain switching');
17174
+ }
17175
+ return switchOrAddViaProvider(signerWithProvider.provider, chain);
17176
+ }
17177
+ // Browser without window.ethereum (e.g., WalletConnect/Dynamic mobile wallets)
17178
+ const signerWithProvider = signer;
17179
+ const signerProvider = signerWithProvider.provider;
17180
+ if (globalThis.window !== undefined &&
17181
+ signerProvider &&
17182
+ 'send' in signerProvider) {
17183
+ return switchOrAddViaProvider(signerProvider, chain);
17190
17184
  }
17191
17185
  // Server-side: recreate the provider and signer
17192
17186
  const rpcUrl = chain.rpcEndpoints[0];
@@ -18162,6 +18156,36 @@ class EthersAdapter extends EvmAdapter {
18162
18156
  });
18163
18157
  }
18164
18158
  }
18159
+ /**
18160
+ * Reads the on-chain bytecode for a given address via ethers' `getCode`.
18161
+ *
18162
+ * Returns the hex-encoded bytecode when the address is a contract, or
18163
+ * `'0x'` when the address has no code (EOA). Used by Gateway burn-intent
18164
+ * signing to detect smart-contract accounts, which are not supported by
18165
+ * Gateway's ecrecover-based verification path.
18166
+ */
18167
+ async readBytecode(address, chain) {
18168
+ const provider = await this.getProvider(chain);
18169
+ try {
18170
+ const code = await provider.getCode(address);
18171
+ return code;
18172
+ }
18173
+ catch (error) {
18174
+ const errorMessage = error instanceof Error ? error.message : String(error);
18175
+ throw new KitError({
18176
+ ...RpcError.ENDPOINT_ERROR,
18177
+ recoverability: 'RETRYABLE',
18178
+ message: `Failed to read bytecode for ${address}: ${errorMessage}`,
18179
+ cause: {
18180
+ trace: {
18181
+ operation: 'readBytecode',
18182
+ address,
18183
+ chain: chain.name,
18184
+ },
18185
+ },
18186
+ });
18187
+ }
18188
+ }
18165
18189
  /**
18166
18190
  * Signs EIP-712 typed data using the configured signer with OperationContext.
18167
18191
  *
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.0",
4
4
  "description": "EVM blockchain adapter powered by Ethers v6",
5
5
  "keywords": [
6
6
  "circle",