@circle-fin/bridge-kit 1.3.0 → 1.4.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,11 @@
1
1
  # @circle-fin/bridge-kit
2
2
 
3
+ ## 1.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add optional filtering to `getSupportedChains()` method to filter chains by type (EVM, Solana) and network (mainnet/testnet)
8
+
3
9
  ## 1.3.0
4
10
 
5
11
  ### Minor Changes
package/QUICKSTART.md CHANGED
@@ -1421,7 +1421,7 @@ const attestation = await retryWithBackoff(
1421
1421
 
1422
1422
  ## Next Steps
1423
1423
 
1424
- - **Explore Examples**: Check out the [examples directory](https://github.com/circlefin/stablecoin-kits-private/tree/main/examples) for more detailed implementations
1424
+ - **Explore Examples**: Check out the [examples directory](https://github.com/crcl-main/stablecoin-kits-private/tree/main/examples) for more detailed implementations
1425
1425
  - **Join the community**: Connect with other developers on [discord](https://discord.com/invite/buildoncircle) building on Circle's stablecoin infrastructure
1426
1426
 
1427
1427
  Ready to start bridging? Let's make cross-chain bridging as easy as a single function call! 🌉
package/README.md CHANGED
@@ -194,26 +194,50 @@ const resultWithDifferentAdapter = await kit.bridge({
194
194
 
195
195
  **Best for**: Production applications, better reliability, custom configuration
196
196
 
197
+ Bridging involves **two chains** (source and destination), and both require properly configured RPC endpoints. Use dynamic RPC mapping by chain ID to support multiple chains in a single adapter:
198
+
197
199
  ```typescript
198
- import { createPublicClient, http } from 'viem'
200
+ import 'dotenv/config'
201
+ import { BridgeKit } from '@circle-fin/bridge-kit'
202
+ import { Ethereum, Base } from '@circle-fin/bridge-kit/chains'
203
+ import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
204
+ import { createPublicClient, http, fallback } from 'viem'
205
+
206
+ // Define RPCs mapped by chain ID
207
+ const RPC_BY_CHAIN_ID: Record<number, string[]> = {
208
+ // The array allows providing multiple RPC URLs for fallback, e.g.,
209
+ // `[ "https://primary-rpc-url.com/...", "https://secondary-rpc-url.com/..." ]`
210
+ [Ethereum.chainId]: [
211
+ `https://eth-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`,
212
+ ],
213
+ [Base.chainId]: [
214
+ `https://base-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`,
215
+ ],
216
+ }
199
217
 
200
- // Production-ready setup with custom RPC endpoints
201
218
  const adapter = createViemAdapterFromPrivateKey({
202
- privateKey: process.env.PRIVATE_KEY as string,
203
- getPublicClient: ({ chain }) =>
204
- createPublicClient({
219
+ privateKey: process.env.PRIVATE_KEY as `0x${string}`,
220
+ getPublicClient: ({ chain }) => {
221
+ const rpcUrls = RPC_BY_CHAIN_ID[chain.id]
222
+ if (!rpcUrls) {
223
+ throw new Error(`No RPC configured for chainId=${chain.id}`)
224
+ }
225
+ return createPublicClient({
205
226
  chain,
206
- transport: http(
207
- `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
208
- {
209
- retryCount: 3,
210
- timeout: 10000,
211
- },
227
+ transport: fallback(
228
+ rpcUrls.map((url) =>
229
+ http(url, {
230
+ timeout: 10_000,
231
+ retryCount: 3,
232
+ }),
233
+ ),
212
234
  ),
213
- }),
235
+ })
236
+ },
214
237
  })
215
238
 
216
- // Same simple usage, but with production-grade RPC
239
+ const kit = new BridgeKit()
240
+
217
241
  const result = await kit.bridge({
218
242
  from: { adapter, chain: 'Ethereum' },
219
243
  to: { adapter, chain: 'Base' },
@@ -221,13 +245,22 @@ const result = await kit.bridge({
221
245
  })
222
246
  ```
223
247
 
248
+ **Best practices:**
249
+
250
+ - **Use paid RPC providers** (Alchemy, Infura, QuickNode) for improved reliability
251
+ - **Implement `fallback()` transport** for automatic failover between endpoints
252
+ - **Configure timeout and retry options** to handle network variability
253
+
224
254
  ### 🌐 Browser/Wallet Provider Support
225
255
 
226
256
  **Best for**: Browser applications, wallet integrations, user-controlled transactions
227
257
 
228
258
  ```typescript
259
+ import { BridgeKit } from '@circle-fin/bridge-kit'
229
260
  import { createViemAdapterFromProvider } from '@circle-fin/adapter-viem-v2'
230
261
 
262
+ const kit = new BridgeKit()
263
+
231
264
  // Create adapters from browser wallet providers
232
265
  const adapter = await createViemAdapterFromProvider({
233
266
  provider: window.ethereum,
@@ -254,10 +287,10 @@ import { privateKeyToAccount } from 'viem/accounts'
254
287
 
255
288
  const account = privateKeyToAccount(process.env.PRIVATE_KEY as string)
256
289
 
257
- // Chain-specific RPC URLs
258
- const rpcUrls = {
259
- [Ethereum.id]: 'https://eth-mainnet.alchemyapi.io/v2/YOUR_KEY',
260
- [Base.id]: 'https://base-mainnet.g.alchemy.com/v2/YOUR_KEY',
290
+ // Chain-specific RPC URLs mapped by chain ID
291
+ const rpcUrls: Record<number, string> = {
292
+ [Ethereum.chainId]: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
293
+ [Base.chainId]: 'https://base-mainnet.g.alchemy.com/v2/YOUR_KEY',
261
294
  }
262
295
 
263
296
  // Create one multi-chain adapter with chain-specific RPC configuration
@@ -445,8 +478,12 @@ For dynamic fee calculation across all transfers, use kit-level policies:
445
478
 
446
479
  ```typescript
447
480
  import { BridgeKit } from '@circle-fin/bridge-kit'
481
+ import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
448
482
 
449
483
  const kit = new BridgeKit()
484
+ const adapter = createViemAdapterFromPrivateKey({
485
+ privateKey: process.env.PRIVATE_KEY as `0x${string}`,
486
+ })
450
487
 
451
488
  kit.setCustomFeePolicy({
452
489
  computeFee: (params) => {
package/chains.cjs CHANGED
@@ -366,8 +366,11 @@ const ArcTestnet = defineChain({
366
366
  name: 'Arc Testnet',
367
367
  title: 'ArcTestnet',
368
368
  nativeCurrency: {
369
- name: 'Arc',
370
- symbol: 'Arc',
369
+ name: 'USDC',
370
+ symbol: 'USDC',
371
+ // Arc uses native USDC with 18 decimals for gas payments (EVM standard).
372
+ // Note: The ERC-20 USDC contract at usdcAddress uses 6 decimals.
373
+ // See: https://docs.arc.network/arc/references/contract-addresses
371
374
  decimals: 18,
372
375
  },
373
376
  chainId: 5042002,
package/chains.d.ts CHANGED
@@ -184,8 +184,8 @@ declare const ArcTestnet: {
184
184
  readonly name: "Arc Testnet";
185
185
  readonly title: "ArcTestnet";
186
186
  readonly nativeCurrency: {
187
- readonly name: "Arc";
188
- readonly symbol: "Arc";
187
+ readonly name: "USDC";
188
+ readonly symbol: "USDC";
189
189
  readonly decimals: 18;
190
190
  };
191
191
  readonly chainId: 5042002;
package/chains.mjs CHANGED
@@ -364,8 +364,11 @@ const ArcTestnet = defineChain({
364
364
  name: 'Arc Testnet',
365
365
  title: 'ArcTestnet',
366
366
  nativeCurrency: {
367
- name: 'Arc',
368
- symbol: 'Arc',
367
+ name: 'USDC',
368
+ symbol: 'USDC',
369
+ // Arc uses native USDC with 18 decimals for gas payments (EVM standard).
370
+ // Note: The ERC-20 USDC contract at usdcAddress uses 6 decimals.
371
+ // See: https://docs.arc.network/arc/references/contract-addresses
369
372
  decimals: 18,
370
373
  },
371
374
  chainId: 5042002,
package/index.cjs CHANGED
@@ -482,6 +482,12 @@ const InputError = {
482
482
  name: 'INPUT_INVALID_CHAIN',
483
483
  type: 'INPUT',
484
484
  },
485
+ /** Invalid or unknown token (symbol not found, missing decimals, etc.) */
486
+ INVALID_TOKEN: {
487
+ code: 1006,
488
+ name: 'INPUT_INVALID_TOKEN',
489
+ type: 'INPUT',
490
+ },
485
491
  /** General validation failure for complex validation rules */
486
492
  VALIDATION_FAILED: {
487
493
  code: 1098,
@@ -1264,8 +1270,11 @@ const ArcTestnet = defineChain({
1264
1270
  name: 'Arc Testnet',
1265
1271
  title: 'ArcTestnet',
1266
1272
  nativeCurrency: {
1267
- name: 'Arc',
1268
- symbol: 'Arc',
1273
+ name: 'USDC',
1274
+ symbol: 'USDC',
1275
+ // Arc uses native USDC with 18 decimals for gas payments (EVM standard).
1276
+ // Note: The ERC-20 USDC contract at usdcAddress uses 6 decimals.
1277
+ // See: https://docs.arc.network/arc/references/contract-addresses
1269
1278
  decimals: 18,
1270
1279
  },
1271
1280
  chainId: 5042002,
@@ -4630,7 +4639,7 @@ const parseAmount = (params) => {
4630
4639
  };
4631
4640
 
4632
4641
  var name = "@circle-fin/bridge-kit";
4633
- var version = "1.3.0";
4642
+ var version = "1.4.0";
4634
4643
  var pkg = {
4635
4644
  name: name,
4636
4645
  version: version};
@@ -5907,7 +5916,7 @@ class BridgeKit {
5907
5916
  return formatBridgeResult(await provider.estimate(finalResolvedParams), 'to-human-readable');
5908
5917
  }
5909
5918
  /**
5910
- * Get all chains supported by any provider in the kit.
5919
+ * Get all chains supported by any provider in the kit, with optional filtering.
5911
5920
  *
5912
5921
  * Aggregate and deduplicate the supported chains from all registered providers.
5913
5922
  * This provides a comprehensive list of chains that can be used as either source
@@ -5917,6 +5926,7 @@ class BridgeKit {
5917
5926
  * ensuring each chain appears only once in the result regardless of how many
5918
5927
  * providers support it.
5919
5928
  *
5929
+ * @param options - Optional filtering options to narrow down the returned chains
5920
5930
  * @returns Array of unique chain definitions supported by the registered providers
5921
5931
  *
5922
5932
  * @example
@@ -5924,19 +5934,56 @@ class BridgeKit {
5924
5934
  * import { BridgeKit } from '@circle-fin/bridge-kit'
5925
5935
  *
5926
5936
  * const kit = new BridgeKit()
5927
- * const chains = kit.getSupportedChains()
5937
+ *
5938
+ * // Get all supported chains (no filtering)
5939
+ * const allChains = kit.getSupportedChains()
5940
+ *
5941
+ * // Get only EVM chains
5942
+ * const evmChains = kit.getSupportedChains({ chainType: 'evm' })
5943
+ *
5944
+ * // Get EVM and Solana chains
5945
+ * const evmAndSolana = kit.getSupportedChains({ chainType: ['evm', 'solana'] })
5946
+ *
5947
+ * // Get only mainnet chains
5948
+ * const mainnets = kit.getSupportedChains({ isTestnet: false })
5949
+ *
5950
+ * // Get only EVM mainnet chains
5951
+ * const evmMainnets = kit.getSupportedChains({ chainType: 'evm', isTestnet: false })
5928
5952
  *
5929
5953
  * console.log('Supported chains:')
5930
- * chains.forEach(chain => {
5954
+ * allChains.forEach(chain => {
5931
5955
  * console.log(`- ${chain.name} (${chain.type})`)
5932
5956
  * })
5933
5957
  * ```
5934
5958
  */
5935
- getSupportedChains() {
5959
+ getSupportedChains(options) {
5936
5960
  const supportedChains = this.providers.flatMap((p) => p.supportedChains);
5937
5961
  // Deduplicate chains by using chain identifiers as object keys
5938
5962
  // Later duplicates will override earlier ones, keeping only the last occurrence
5939
- return Object.values(Object.fromEntries(supportedChains.map((chain) => [chain.chain, chain])));
5963
+ let chains = Object.values(Object.fromEntries(supportedChains.map((chain) => [chain.chain, chain])));
5964
+ // Apply chain type filter if provided
5965
+ if (options?.chainType !== undefined) {
5966
+ // Validate at runtime since JS consumers can bypass TypeScript's narrow type.
5967
+ const supportedChainTypes = ['evm', 'solana'];
5968
+ const chainTypeInput = options.chainType;
5969
+ const chainTypeValues = Array.isArray(chainTypeInput)
5970
+ ? chainTypeInput
5971
+ : [chainTypeInput];
5972
+ if (!chainTypeValues.every((chainType) => supportedChainTypes.includes(chainType))) {
5973
+ const listFormatter = new Intl.ListFormat('en', {
5974
+ style: 'long',
5975
+ type: 'conjunction',
5976
+ });
5977
+ throw createValidationFailedError$1('options.chainType', options.chainType, `Supported chain types include: ${listFormatter.format(supportedChainTypes)}`);
5978
+ }
5979
+ const chainTypes = new Set(chainTypeValues);
5980
+ chains = chains.filter((chain) => chainTypes.has(chain.type));
5981
+ }
5982
+ // Apply testnet filter if provided
5983
+ if (options?.isTestnet !== undefined) {
5984
+ chains = chains.filter((chain) => chain.isTestnet === options.isTestnet);
5985
+ }
5986
+ return chains;
5940
5987
  }
5941
5988
  /**
5942
5989
  * Validate that source and destination chains are on the same network type.