@circle-fin/bridge-kit 1.0.0 → 1.1.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/CHANGELOG.md +109 -0
- package/QUICKSTART.md +175 -11
- package/README.md +79 -3
- package/{chains.cjs.js → chains.cjs} +71 -17
- package/chains.cjs.map +1 -0
- package/chains.d.ts +444 -7
- package/chains.mjs +60 -17
- package/{index.cjs.js → index.cjs} +3592 -3254
- package/index.cjs.map +1 -0
- package/index.d.ts +299 -23
- package/index.mjs +3580 -3249
- package/package.json +10 -7
- package/chains.cjs.js.map +0 -1
- package/index.cjs.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# @circle-fin/bridge-kit
|
|
2
|
+
|
|
3
|
+
## 1.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fix CommonJS compatibility by correcting file extensions and package exports. This resolves a "ReferenceError: require is not defined" error that occurred when using packages in CommonJS projects with ts-node.
|
|
8
|
+
|
|
9
|
+
- Fixes a bug where the `recipientAddress` field in the `to` parameter was not being properly propagated to the underlying CCTP v2 provider. This caused custom recipient addresses to be ignored during cross-chain USDC transfers, resulting in funds being minted to the signer's address instead of the intended recipient.
|
|
10
|
+
|
|
11
|
+
With this fix, when you specify a `recipientAddress` in the `to` parameter, the Bridge Kit now correctly passes it through to the provider, ensuring funds are minted to the correct address.
|
|
12
|
+
|
|
13
|
+
**Example usage** (no changes needed if already using this pattern):
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
await kit.bridge({
|
|
17
|
+
from: { adapter: sourceAdapter, chain: 'Ethereum' },
|
|
18
|
+
to: {
|
|
19
|
+
adapter: destAdapter,
|
|
20
|
+
chain: 'Base',
|
|
21
|
+
recipientAddress: '0x...', // Now properly respected
|
|
22
|
+
},
|
|
23
|
+
amount: '10',
|
|
24
|
+
token: 'USDC',
|
|
25
|
+
})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**No breaking changes** - existing code without `recipientAddress` continues to work unchanged. This fix only affects transfers where you explicitly provide a custom recipient address.
|
|
29
|
+
|
|
30
|
+
## 1.1.0
|
|
31
|
+
|
|
32
|
+
### Minor Changes
|
|
33
|
+
|
|
34
|
+
- Add automatic HTTP request tracking for analytics and debugging. The kit now automatically registers itself when the module loads, enabling Circle to track kit usage and identify issues. Applications can optionally set an external prefix using the new `setExternalPrefix` function to identify themselves in request analytics. All HTTP requests from the kit will include user agent information in the format: `[app/version] bridge-kit/version (runtime)`. This feature requires no code changes for existing users.
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- Standardize `maxFee` parameter to accept human-readable values. The `maxFee` parameter in `BridgeConfig` now correctly accepts human-readable token amounts (e.g., `"1"` for 1 USDC, `"0.5"` for 0.5 USDC), matching the behavior of `customFee.value`. This resolves an undocumented inconsistency in the API. If you were previously passing values in smallest units, update to human-readable format: use `"1"` instead of `"1000000"` for 1 USDC.
|
|
39
|
+
- Complete CCTP v2 chain support exports
|
|
40
|
+
|
|
41
|
+
Ensures all 35 chains with CCTP v2 support are properly exported from the `chains` entry point. This fix adds previously missing chain definitions including Codex, HyperEVM, Ink, Plume, Sei, Sonic, Unichain, WorldChain, and XDC networks (both mainnet and testnet variants where applicable).
|
|
42
|
+
|
|
43
|
+
- Add support for Arc Testnet chain definition. Arc is Circle's EVM-compatible Layer-1 blockchain designed for stablecoin finance and asset
|
|
44
|
+
tokenization, featuring USDC as the native gas token and sub-second finality via the Malachite BFT consensus engine.
|
|
45
|
+
- Fix support for developer-controlled address context. Bridge operations now correctly accept an explicit `address` field in the context, allowing developer-controlled adapters to specify which address to use for operations. Previously, this field was incorrectly rejected at runtime.
|
|
46
|
+
- Update Sonic Testnet chain definition to canonical network. The `SonicTestnet` chain definition now points to the official Sonic Testnet (chainId: 14601) instead of the deprecated Sonic Blaze Testnet (chainId: 57054). The RPC endpoint has been updated to `https://rpc.testnet.soniclabs.com`, the display name simplified to "Sonic Testnet", and the USDC contract address updated to the new deployment.
|
|
47
|
+
|
|
48
|
+
**Breaking Changes:**
|
|
49
|
+
- **Chain ID:** 57054 → 14601
|
|
50
|
+
- **RPC Endpoint:** `https://rpc.blaze.soniclabs.com` → `https://rpc.testnet.soniclabs.com`
|
|
51
|
+
- **USDC Address:** `0xA4879Fed32Ecbef99399e5cbC247E533421C4eC6` → `0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51`
|
|
52
|
+
|
|
53
|
+
**Migration:** If you were using `SonicTestnet`, your application will automatically connect to the new network upon upgrading. Any accounts, contracts, or transactions on the old Blaze testnet (chainId: 57054) will need to be recreated on the new testnet.
|
|
54
|
+
|
|
55
|
+
## 1.0.0
|
|
56
|
+
|
|
57
|
+
### Major Changes
|
|
58
|
+
|
|
59
|
+
- # Bridge Kit 1.0.0 Release 🎉
|
|
60
|
+
|
|
61
|
+
The core orchestration library for cross-chain USDC transfers - providing a unified, type-safe interface for bridging USDC between heterogeneous blockchain networks.
|
|
62
|
+
|
|
63
|
+
## 🚀 Core Features
|
|
64
|
+
- **Complete Cross-chain Orchestration**: High-level API for USDC transfers between any supported chains
|
|
65
|
+
- **Type-safe APIs**: Exhaustive runtime validation with strict TypeScript support
|
|
66
|
+
- **Deterministic Operations**: Pre-flight simulations and predictable quote generation
|
|
67
|
+
- **Comprehensive Finality Tracking**: Monitor transfer progress across all bridge steps
|
|
68
|
+
|
|
69
|
+
## 🔄 Intelligent Retry System
|
|
70
|
+
|
|
71
|
+
Sophisticated retry mechanism that automatically handles failed or incomplete transfers:
|
|
72
|
+
- **Automatic Recovery**: Resume transfers from the exact point of failure
|
|
73
|
+
- **Step Analysis**: Intelligent detection of which operations completed successfully
|
|
74
|
+
- **Network Resilience**: Handle temporary connectivity issues and gas estimation failures
|
|
75
|
+
- **Multi-step Flow Support**: Retry complex bridge operations involving multiple blockchain transactions
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// Retry a failed transfer with fresh adapter instances
|
|
79
|
+
const retryResult = await kit.retry(failedResult, {
|
|
80
|
+
from: sourceAdapter,
|
|
81
|
+
to: destAdapter,
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 💰 Flexible Fee Management
|
|
86
|
+
|
|
87
|
+
Comprehensive fee system supporting both protocol fees and custom integrator fees:
|
|
88
|
+
- **Transfer Speed Options**: Choose between FAST (with fees) and SLOW (fee-free) transfers
|
|
89
|
+
- **Dynamic Fee Calculation**: Automatic fee estimation based on transfer amount and network conditions
|
|
90
|
+
- **Custom Fee Policies**: Implement your own fee structures with absolute amounts
|
|
91
|
+
- **Multi-chain Fee Support**: Different fee configurations per source chain
|
|
92
|
+
- **Fee Recipient Control**: Specify where fees are sent on the source chain
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Set custom fee policy
|
|
96
|
+
kit.setCustomFeePolicy({
|
|
97
|
+
calculateFee: (params) =>
|
|
98
|
+
params.from.chain.type === 'solana' ? '0.1' : '0.2',
|
|
99
|
+
resolveFeeRecipientAddress: (chain) => getFeeRecipientForChain(chain),
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 🎯 Use Cases
|
|
104
|
+
- **Multi-chain dApp Integration**: Single SDK for all cross-chain USDC needs
|
|
105
|
+
- **Wallet Integration**: Seamless cross-chain transfers for end users
|
|
106
|
+
- **Exchange Integration**: Institutional-grade cross-chain USDC movement
|
|
107
|
+
- **DeFi Protocol Integration**: Bridge USDC liquidity across ecosystems
|
|
108
|
+
|
|
109
|
+
This release provides the foundational orchestration layer for cross-chain USDC applications with production-ready reliability and comprehensive developer tooling.
|
package/QUICKSTART.md
CHANGED
|
@@ -13,6 +13,7 @@ Welcome to the Bridge Kit! This guide will help you understand the ecosystem and
|
|
|
13
13
|
- [Event Handling](#event-handling)
|
|
14
14
|
- [Supported Chains](#supported-chains)
|
|
15
15
|
- [Error Handling](#error-handling)
|
|
16
|
+
- [Retry Guide: Resuming Failed Transfers](#retry-guide-resuming-failed-transfers)
|
|
16
17
|
- [Troubleshooting](#troubleshooting)
|
|
17
18
|
- [Best Practices](#best-practices)
|
|
18
19
|
- [Next Steps](#next-steps)
|
|
@@ -514,11 +515,12 @@ Customize your transfer behavior with the `config` parameter:
|
|
|
514
515
|
interface BridgeConfig {
|
|
515
516
|
transferSpeed: 'FAST' | 'SLOW' // Default: 'FAST'
|
|
516
517
|
/**
|
|
517
|
-
* The maximum bridging fee you're willing to pay
|
|
518
|
+
* The maximum bridging fee you're willing to pay in human-readable format.
|
|
519
|
+
* For example: "1" for 1 USDC, "0.5" for 0.5 USDC.
|
|
518
520
|
* You should only set this parameter if speed is set to FAST.
|
|
519
521
|
* If this value ends up being less than the protocol fee, the bridge flow will be executed as a SLOW transfer.
|
|
520
522
|
*/
|
|
521
|
-
maxFee?: string // Optional: maximum fee
|
|
523
|
+
maxFee?: string // Optional: maximum fee (e.g., "1" for 1 USDC)
|
|
522
524
|
}
|
|
523
525
|
```
|
|
524
526
|
|
|
@@ -714,7 +716,7 @@ const result2 = await kit.bridge({
|
|
|
714
716
|
recipientAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
|
|
715
717
|
},
|
|
716
718
|
amount: '100.0',
|
|
717
|
-
config: { transferSpeed: 'FAST', maxFee: '
|
|
719
|
+
config: { transferSpeed: 'FAST', maxFee: '1' }, // 1 USDC maximum fee
|
|
718
720
|
})
|
|
719
721
|
```
|
|
720
722
|
|
|
@@ -722,7 +724,7 @@ const result2 = await kit.bridge({
|
|
|
722
724
|
|
|
723
725
|
## Event Handling
|
|
724
726
|
|
|
725
|
-
Events allow you to subscribe to different parts of the briding lifecycle and respond to them however you want. Events match up 1-1 to actions that are taken by the Bridge Kit: **'approve', 'burn', '
|
|
727
|
+
Events allow you to subscribe to different parts of the briding lifecycle and respond to them however you want. Events match up 1-1 to actions that are taken by the Bridge Kit: **'approve', 'burn', 'fetchAttestation', and 'mint'** are the events you can subscribe to, or you can use **'\*'** to subscribe to all events.
|
|
726
728
|
|
|
727
729
|
Each event can also be subscribed to multiple times with different callbacks.
|
|
728
730
|
|
|
@@ -740,19 +742,19 @@ kit.on('*', (event) => {
|
|
|
740
742
|
|
|
741
743
|
// Listen to specific events
|
|
742
744
|
kit.on('approve', (event) => {
|
|
743
|
-
console.log('Approval completed:', event.txHash)
|
|
745
|
+
console.log('Approval completed:', event.values.txHash)
|
|
744
746
|
})
|
|
745
747
|
|
|
746
748
|
kit.on('burn', (event) => {
|
|
747
|
-
console.log('Burn completed:', event.txHash)
|
|
749
|
+
console.log('Burn completed:', event.values.txHash)
|
|
748
750
|
})
|
|
749
751
|
|
|
750
|
-
kit.on('
|
|
751
|
-
console.log('Attestation received:', event.
|
|
752
|
+
kit.on('fetchAttestation', (event) => {
|
|
753
|
+
console.log('Attestation received:', event.values.data)
|
|
752
754
|
})
|
|
753
755
|
|
|
754
756
|
kit.on('mint', (event) => {
|
|
755
|
-
console.log('Mint completed:', event.txHash)
|
|
757
|
+
console.log('Mint completed:', event.values.txHash)
|
|
756
758
|
})
|
|
757
759
|
```
|
|
758
760
|
|
|
@@ -792,7 +794,169 @@ For recoverable issues during transfer execution, the kit returns a result objec
|
|
|
792
794
|
|
|
793
795
|
This approach gives you full control over recovery scenarios while preventing unexpected crashes.
|
|
794
796
|
|
|
795
|
-
> **Note**:
|
|
797
|
+
> **Note**: You can resume actionable failures using `BridgeKit.retry(result, context)`. See the Retry Guide below.
|
|
798
|
+
|
|
799
|
+
## Retry Guide: Resuming Failed Transfers
|
|
800
|
+
|
|
801
|
+
Use `kit.retry(result, context)` to resume failed or incomplete transfers when the failure is actionable (e.g., transient network issues, dropped/repriced transactions, or a failed step in a multi-step flow). The kit delegates retry to the original provider (CCTPv2 supports actionable retries) and continues from the correct step.
|
|
802
|
+
|
|
803
|
+
### Method signature
|
|
804
|
+
|
|
805
|
+
```typescript
|
|
806
|
+
retry<
|
|
807
|
+
TFromAdapterCapabilities extends AdapterCapabilities,
|
|
808
|
+
TToAdapterCapabilities extends AdapterCapabilities
|
|
809
|
+
>(
|
|
810
|
+
result: BridgeResult,
|
|
811
|
+
context: RetryContext<TFromAdapterCapabilities, TToAdapterCapabilities>
|
|
812
|
+
): Promise<BridgeResult>
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
### When to retry vs manual intervention
|
|
816
|
+
|
|
817
|
+
- Retry: transient RPC/network errors; gas repricing; step failed but prior steps succeeded.
|
|
818
|
+
- Manual: insufficient funds; wrong recipient; unsupported route; errors indicating non-actionable state.
|
|
819
|
+
|
|
820
|
+
### Example A: EVM → EVM retry (Ethereum Sepolia → Base Sepolia)
|
|
821
|
+
|
|
822
|
+
```typescript
|
|
823
|
+
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
824
|
+
import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
825
|
+
|
|
826
|
+
const kit = new BridgeKit()
|
|
827
|
+
const adapter = createAdapterFromPrivateKey({
|
|
828
|
+
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
829
|
+
})
|
|
830
|
+
|
|
831
|
+
// Start a transfer that may fail
|
|
832
|
+
const result = await kit.bridge({
|
|
833
|
+
from: { adapter, chain: 'Ethereum_Sepolia' },
|
|
834
|
+
to: { adapter, chain: 'Base_Sepolia' },
|
|
835
|
+
amount: '1',
|
|
836
|
+
})
|
|
837
|
+
|
|
838
|
+
if (result.state === 'error') {
|
|
839
|
+
try {
|
|
840
|
+
const retryResult = await kit.retry(result, { from: adapter, to: adapter })
|
|
841
|
+
console.log('Retry state:', retryResult.state)
|
|
842
|
+
console.log('Steps:', retryResult.steps)
|
|
843
|
+
} catch (error) {
|
|
844
|
+
console.error('Retry failed:', error)
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
### Example B: EVM → Solana retry (Ethereum → Solana Devnet)
|
|
850
|
+
|
|
851
|
+
```typescript
|
|
852
|
+
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
853
|
+
import { createAdapterFromPrivateKey as createEvmAdapter } from '@circle-fin/adapter-viem-v2'
|
|
854
|
+
import { createAdapterFromPrivateKey as createSolAdapter } from '@circle-fin/adapter-solana'
|
|
855
|
+
|
|
856
|
+
const kit = new BridgeKit()
|
|
857
|
+
const evm = createEvmAdapter({
|
|
858
|
+
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
859
|
+
})
|
|
860
|
+
const sol = createSolAdapter({
|
|
861
|
+
privateKey: process.env.SOL_PRIVATE_KEY as string,
|
|
862
|
+
})
|
|
863
|
+
|
|
864
|
+
const result = await kit.bridge({
|
|
865
|
+
from: { adapter: evm, chain: 'Ethereum' },
|
|
866
|
+
to: { adapter: sol, chain: 'Solana' },
|
|
867
|
+
amount: '1',
|
|
868
|
+
})
|
|
869
|
+
|
|
870
|
+
// Check if the bridge failed and retry if needed
|
|
871
|
+
if (result.state === 'error') {
|
|
872
|
+
try {
|
|
873
|
+
const retryResult = await kit.retry(result, { from: evm, to: sol })
|
|
874
|
+
console.log('Retry state:', retryResult.state)
|
|
875
|
+
} catch (error) {
|
|
876
|
+
console.error('Retry failed:', error)
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
### Limitations
|
|
882
|
+
|
|
883
|
+
- Only actionable failures can be retried; some failures require user action first.
|
|
884
|
+
- Provide valid adapters for both `from` and `to` contexts.
|
|
885
|
+
|
|
886
|
+
### Common Retry Scenarios
|
|
887
|
+
|
|
888
|
+
#### 1) Transient RPC/network timeout (use backoff)
|
|
889
|
+
|
|
890
|
+
```typescript
|
|
891
|
+
import { BridgeKit, BridgeResult } from '@circle-fin/bridge-kit'
|
|
892
|
+
import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
893
|
+
|
|
894
|
+
const kit = new BridgeKit()
|
|
895
|
+
const adapter = createAdapterFromPrivateKey({
|
|
896
|
+
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
897
|
+
})
|
|
898
|
+
|
|
899
|
+
async function retryWithBackoff(result: BridgeResult) {
|
|
900
|
+
let attempt = 0
|
|
901
|
+
const maxAttempts = 5
|
|
902
|
+
const baseDelayMs = 1000
|
|
903
|
+
|
|
904
|
+
while (attempt < maxAttempts) {
|
|
905
|
+
try {
|
|
906
|
+
return await kit.retry(result, { from: adapter, to: adapter })
|
|
907
|
+
} catch (error) {
|
|
908
|
+
attempt++
|
|
909
|
+
if (attempt >= maxAttempts) throw error
|
|
910
|
+
const delay = baseDelayMs * 2 ** (attempt - 1)
|
|
911
|
+
await new Promise((r) => setTimeout(r, delay))
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
#### 2) Burn succeeded, attestation pending (wait, then retry)
|
|
918
|
+
|
|
919
|
+
```typescript
|
|
920
|
+
// If failure indicates attestation delay, wait/poll before retrying
|
|
921
|
+
if (result.state === 'error') {
|
|
922
|
+
const burnStep = result.steps.find((s) => s.name === 'burn')
|
|
923
|
+
const attestationStep = result.steps.find(
|
|
924
|
+
(s) => s.name === 'fetchAttestation',
|
|
925
|
+
)
|
|
926
|
+
|
|
927
|
+
if (burnStep?.state === 'success' && attestationStep?.state !== 'success') {
|
|
928
|
+
// Wait a bit for attestation availability (example: 30 seconds)
|
|
929
|
+
await new Promise((r) => setTimeout(r, 30_000))
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
const retryResult = await kit.retry(result, { from: adapter, to: adapter })
|
|
933
|
+
console.log('Retry state:', retryResult.state)
|
|
934
|
+
}
|
|
935
|
+
```
|
|
936
|
+
|
|
937
|
+
#### 3) Mint failed due to gas (EVM gas repricing), then retry
|
|
938
|
+
|
|
939
|
+
```typescript
|
|
940
|
+
// If the mint step failed on destination chain (EVM), reprice and retry
|
|
941
|
+
const mintStep = result.steps.find((s) => s.name === 'mint')
|
|
942
|
+
if (result.state === 'error' && mintStep?.state === 'error') {
|
|
943
|
+
// Recreate destination adapter with updated gas settings as needed
|
|
944
|
+
const dest = createAdapterFromPrivateKey({
|
|
945
|
+
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
946
|
+
// Provide a higher-priority RPC or adjust client gas policies here if supported by your setup
|
|
947
|
+
})
|
|
948
|
+
|
|
949
|
+
const retryResult = await kit.retry(result, { from: adapter, to: dest })
|
|
950
|
+
console.log('Retry state:', retryResult.state)
|
|
951
|
+
}
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
### Performance and best practices
|
|
955
|
+
|
|
956
|
+
- Backoff on transient failures (2s, 4s, 8s...).
|
|
957
|
+
- Reprice gas sensibly when congestion is high.
|
|
958
|
+
- Persist `result.steps` and transaction hashes for observability.
|
|
959
|
+
- Log and monitor both source and destination tx hashes.
|
|
796
960
|
|
|
797
961
|
## Troubleshooting
|
|
798
962
|
|
|
@@ -889,7 +1053,7 @@ const publicClient = createPublicClient({
|
|
|
889
1053
|
|
|
890
1054
|
When transfers encounter soft errors (network congestion, insufficient gas, RPC timeouts), you can recover by using the CCTPv2BridgingProvider directly to complete the remaining steps. The BridgeKit's transfer result contains enough information to resume from any point.
|
|
891
1055
|
|
|
892
|
-
>
|
|
1056
|
+
> **Note**: You can resume actionable failures using `BridgeKit.retry(result, context)` to automatically continue from the correct step. For non-actionable cases, use the manual recovery patterns below.
|
|
893
1057
|
|
|
894
1058
|
#### Understanding Transfer State
|
|
895
1059
|
|
package/README.md
CHANGED
|
@@ -37,6 +37,7 @@ _Making cross-chain stablecoin (USDC, and soon more tokens) transfers as simple
|
|
|
37
37
|
- [Complete Example with All Options](#complete-example-with-all-options)
|
|
38
38
|
- [Bridge Speed Configuration](#bridge-speed-configuration)
|
|
39
39
|
- [Error Handling](#error-handling)
|
|
40
|
+
- [Retrying Failed Transfers](#retrying-failed-transfers)
|
|
40
41
|
- [Examples](#examples)
|
|
41
42
|
- [Basic Bridge Operation](#basic-bridge-operation)
|
|
42
43
|
- [EVM to Non-EVM Bridge (EVM ↔ Solana)](#evm-to-non-evm-bridge-evm--solana)
|
|
@@ -394,12 +395,80 @@ if (result.state === 'success') {
|
|
|
394
395
|
}
|
|
395
396
|
```
|
|
396
397
|
|
|
398
|
+
## Retrying Failed Transfers
|
|
399
|
+
|
|
400
|
+
Use `BridgeKit.retry` to resume failed or incomplete bridge operations when the failure is actionable (e.g., transient RPC issues, dropped transactions, or a failed step in a multi-step flow). The kit delegates retry to the original provider (CCTPv2 supports actionable retries) and continues from the appropriate step.
|
|
401
|
+
|
|
402
|
+
### Method signature
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
retry<
|
|
406
|
+
TFromAdapterCapabilities extends AdapterCapabilities,
|
|
407
|
+
TToAdapterCapabilities extends AdapterCapabilities
|
|
408
|
+
>(
|
|
409
|
+
result: BridgeResult,
|
|
410
|
+
context: RetryContext<TFromAdapterCapabilities, TToAdapterCapabilities>
|
|
411
|
+
): Promise<BridgeResult>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Basic usage (EVM → EVM)
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
418
|
+
import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
419
|
+
|
|
420
|
+
const kit = new BridgeKit()
|
|
421
|
+
const adapter = createAdapterFromPrivateKey({
|
|
422
|
+
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
const result = await kit.bridge({
|
|
426
|
+
from: { adapter, chain: 'Ethereum_Sepolia' },
|
|
427
|
+
to: { adapter, chain: 'Base_Sepolia' },
|
|
428
|
+
amount: '1',
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
if (result.state === 'error') {
|
|
432
|
+
try {
|
|
433
|
+
const retryResult = await kit.retry(result, { from: adapter, to: adapter })
|
|
434
|
+
console.log('Retry state:', retryResult.state)
|
|
435
|
+
} catch (error) {
|
|
436
|
+
console.error('Retry failed:', error)
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### When to retry vs manual intervention
|
|
442
|
+
|
|
443
|
+
- Retry: transient network/RPC errors, gas repricing/dropped txs, step failure with progress recorded.
|
|
444
|
+
- Manual: insufficient funds, incorrect recipient, unsupported route, or errors indicating non-actionable state.
|
|
445
|
+
|
|
446
|
+
### Limitations
|
|
447
|
+
|
|
448
|
+
- Only actionable failures can be retried; some failures require user action first.
|
|
449
|
+
- Source and destination chains must still be supported by the provider (CCTPv2).
|
|
450
|
+
- Provide valid adapters for both `from` and `to` contexts.
|
|
451
|
+
|
|
452
|
+
### Performance and best practices
|
|
453
|
+
|
|
454
|
+
- Use exponential backoff on transient failures; avoid rapid replay.
|
|
455
|
+
- Reprice gas sensibly on congested networks.
|
|
456
|
+
- Persist `result.steps` and tx hashes to aid observability and support.
|
|
457
|
+
|
|
458
|
+
### Troubleshooting
|
|
459
|
+
|
|
460
|
+
- "Retry not supported for this result, requires user action": fix balances/addresses/attestation issues and try again.
|
|
461
|
+
- "Provider not found": ensure the same provider (e.g., CCTPv2) is present in `BridgeKit` configuration.
|
|
462
|
+
|
|
463
|
+
> See a runnable example at `examples/basic-usdc-transfer/src/retry.ts` (script: `yarn start:retry`).
|
|
464
|
+
|
|
397
465
|
## API Reference
|
|
398
466
|
|
|
399
467
|
### Core Methods
|
|
400
468
|
|
|
401
469
|
- `kit.bridge(params)` - Execute cross-chain bridge operation
|
|
402
470
|
- `kit.estimate(params)` - Get cost estimates before bridging
|
|
471
|
+
- `kit.retry(result, context)` - Resume actionable failed/partial transfers
|
|
403
472
|
- `kit.supportsRoute(source, destination, token)` - Check route support
|
|
404
473
|
- `kit.on(event, handler)` - Listen to bridge events
|
|
405
474
|
- `kit.off(event, handler)` - Removes the listener from bridge events
|
|
@@ -415,8 +484,12 @@ interface BridgeParams {
|
|
|
415
484
|
config?: BridgeConfig // Optional bridge configuration (e.g., transfer speed). If omitted, defaults will be used
|
|
416
485
|
}
|
|
417
486
|
|
|
418
|
-
// AdapterContext: Your blockchain connection
|
|
419
|
-
type AdapterContext = {
|
|
487
|
+
// AdapterContext: Your blockchain connection
|
|
488
|
+
type AdapterContext = {
|
|
489
|
+
adapter: Adapter
|
|
490
|
+
chain: ChainIdentifier
|
|
491
|
+
address?: string // Required for developer-controlled adapters; forbidden for user-controlled
|
|
492
|
+
}
|
|
420
493
|
|
|
421
494
|
// BridgeDestination: Where funds go
|
|
422
495
|
type BridgeDestination =
|
|
@@ -450,7 +523,10 @@ nx test @circle-fin/bridge-kit
|
|
|
450
523
|
# Install dependencies
|
|
451
524
|
yarn install
|
|
452
525
|
|
|
453
|
-
# Build
|
|
526
|
+
# Build all packages
|
|
527
|
+
yarn build
|
|
528
|
+
|
|
529
|
+
# Build the bridge-kit specifically
|
|
454
530
|
nx build @circle-fin/bridge-kit
|
|
455
531
|
|
|
456
532
|
# Run tests
|
|
@@ -35,6 +35,7 @@ var Blockchain;
|
|
|
35
35
|
Blockchain["Algorand_Testnet"] = "Algorand_Testnet";
|
|
36
36
|
Blockchain["Aptos"] = "Aptos";
|
|
37
37
|
Blockchain["Aptos_Testnet"] = "Aptos_Testnet";
|
|
38
|
+
Blockchain["Arc_Testnet"] = "Arc_Testnet";
|
|
38
39
|
Blockchain["Arbitrum"] = "Arbitrum";
|
|
39
40
|
Blockchain["Arbitrum_Sepolia"] = "Arbitrum_Sepolia";
|
|
40
41
|
Blockchain["Avalanche"] = "Avalanche";
|
|
@@ -255,6 +256,48 @@ const BRIDGE_CONTRACT_EVM_TESTNET = '0xC5567a5E3370d4DBfB0540025078e283e36A363d'
|
|
|
255
256
|
*/
|
|
256
257
|
const BRIDGE_CONTRACT_EVM_MAINNET = '0xB3FA262d0fB521cc93bE83d87b322b8A23DAf3F0';
|
|
257
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Arc Testnet chain definition
|
|
261
|
+
* @remarks
|
|
262
|
+
* This represents the test network for the Arc blockchain,
|
|
263
|
+
* Circle's EVM-compatible Layer-1 designed for stablecoin finance
|
|
264
|
+
* and asset tokenization. Arc uses USDC as the native gas token and
|
|
265
|
+
* features the Malachite Byzantine Fault Tolerant (BFT) consensus
|
|
266
|
+
* engine for sub-second finality.
|
|
267
|
+
*/
|
|
268
|
+
const ArcTestnet = defineChain({
|
|
269
|
+
type: 'evm',
|
|
270
|
+
chain: Blockchain.Arc_Testnet,
|
|
271
|
+
name: 'Arc Testnet',
|
|
272
|
+
title: 'ArcTestnet',
|
|
273
|
+
nativeCurrency: {
|
|
274
|
+
name: 'Arc',
|
|
275
|
+
symbol: 'Arc',
|
|
276
|
+
decimals: 18,
|
|
277
|
+
},
|
|
278
|
+
chainId: 5042002,
|
|
279
|
+
isTestnet: true,
|
|
280
|
+
explorerUrl: 'https://testnet.arcscan.app/tx/{hash}',
|
|
281
|
+
rpcEndpoints: ['https://rpc.testnet.arc.network/'],
|
|
282
|
+
eurcAddress: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
|
|
283
|
+
usdcAddress: '0x3600000000000000000000000000000000000000',
|
|
284
|
+
cctp: {
|
|
285
|
+
domain: 26,
|
|
286
|
+
contracts: {
|
|
287
|
+
v2: {
|
|
288
|
+
type: 'split',
|
|
289
|
+
tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
|
|
290
|
+
messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
|
|
291
|
+
confirmations: 1,
|
|
292
|
+
fastConfirmations: 1,
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
kitContracts: {
|
|
297
|
+
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
|
|
258
301
|
/**
|
|
259
302
|
* Arbitrum Mainnet chain definition
|
|
260
303
|
* @remarks
|
|
@@ -784,7 +827,7 @@ defineChain({
|
|
|
784
827
|
* HyperEVM is a Layer 1 blockchain specialized for DeFi and trading applications
|
|
785
828
|
* with native orderbook and matching engine.
|
|
786
829
|
*/
|
|
787
|
-
defineChain({
|
|
830
|
+
const HyperEVM = defineChain({
|
|
788
831
|
type: 'evm',
|
|
789
832
|
chain: Blockchain.HyperEVM,
|
|
790
833
|
name: 'HyperEVM',
|
|
@@ -823,7 +866,7 @@ defineChain({
|
|
|
823
866
|
* This represents the official testnet for the HyperEVM blockchain.
|
|
824
867
|
* Used for development and testing purposes before deploying to mainnet.
|
|
825
868
|
*/
|
|
826
|
-
defineChain({
|
|
869
|
+
const HyperEVMTestnet = defineChain({
|
|
827
870
|
type: 'evm',
|
|
828
871
|
chain: Blockchain.HyperEVM_Testnet,
|
|
829
872
|
name: 'HyperEVM Testnet',
|
|
@@ -863,7 +906,7 @@ defineChain({
|
|
|
863
906
|
* Ink is a Layer 1 blockchain specialized for DeFi and trading applications
|
|
864
907
|
* with native orderbook and matching engine.
|
|
865
908
|
*/
|
|
866
|
-
defineChain({
|
|
909
|
+
const Ink = defineChain({
|
|
867
910
|
type: 'evm',
|
|
868
911
|
chain: Blockchain.Ink,
|
|
869
912
|
name: 'Ink',
|
|
@@ -905,7 +948,7 @@ defineChain({
|
|
|
905
948
|
* This represents the official testnet for the Ink blockchain.
|
|
906
949
|
* Used for development and testing purposes before deploying to mainnet.
|
|
907
950
|
*/
|
|
908
|
-
defineChain({
|
|
951
|
+
const InkTestnet = defineChain({
|
|
909
952
|
type: 'evm',
|
|
910
953
|
chain: Blockchain.Ink_Testnet,
|
|
911
954
|
name: 'Ink Sepolia',
|
|
@@ -1222,7 +1265,7 @@ const OptimismSepolia = defineChain({
|
|
|
1222
1265
|
* Plume is a Layer 1 blockchain specialized for DeFi and trading applications
|
|
1223
1266
|
* with native orderbook and matching engine.
|
|
1224
1267
|
*/
|
|
1225
|
-
defineChain({
|
|
1268
|
+
const Plume = defineChain({
|
|
1226
1269
|
type: 'evm',
|
|
1227
1270
|
chain: Blockchain.Plume,
|
|
1228
1271
|
name: 'Plume',
|
|
@@ -1261,7 +1304,7 @@ defineChain({
|
|
|
1261
1304
|
* This represents the official testnet for the Plume blockchain.
|
|
1262
1305
|
* Used for development and testing purposes before deploying to mainnet.
|
|
1263
1306
|
*/
|
|
1264
|
-
defineChain({
|
|
1307
|
+
const PlumeTestnet = defineChain({
|
|
1265
1308
|
type: 'evm',
|
|
1266
1309
|
chain: Blockchain.Plume_Testnet,
|
|
1267
1310
|
name: 'Plume Testnet',
|
|
@@ -1435,7 +1478,7 @@ const PolygonAmoy = defineChain({
|
|
|
1435
1478
|
* Sei is a Layer 1 blockchain specialized for DeFi and trading applications
|
|
1436
1479
|
* with native orderbook and matching engine.
|
|
1437
1480
|
*/
|
|
1438
|
-
defineChain({
|
|
1481
|
+
const Sei = defineChain({
|
|
1439
1482
|
type: 'evm',
|
|
1440
1483
|
chain: Blockchain.Sei,
|
|
1441
1484
|
name: 'Sei',
|
|
@@ -1474,7 +1517,7 @@ defineChain({
|
|
|
1474
1517
|
* This represents the official testnet for the Sei blockchain.
|
|
1475
1518
|
* Used for development and testing purposes before deploying to mainnet.
|
|
1476
1519
|
*/
|
|
1477
|
-
defineChain({
|
|
1520
|
+
const SeiTestnet = defineChain({
|
|
1478
1521
|
type: 'evm',
|
|
1479
1522
|
chain: Blockchain.Sei_Testnet,
|
|
1480
1523
|
name: 'Sei Testnet',
|
|
@@ -1546,26 +1589,26 @@ const Sonic = defineChain({
|
|
|
1546
1589
|
});
|
|
1547
1590
|
|
|
1548
1591
|
/**
|
|
1549
|
-
* Sonic
|
|
1592
|
+
* Sonic Testnet chain definition
|
|
1550
1593
|
* @remarks
|
|
1551
1594
|
* This represents the official test network for the Sonic blockchain.
|
|
1552
1595
|
*/
|
|
1553
1596
|
const SonicTestnet = defineChain({
|
|
1554
1597
|
type: 'evm',
|
|
1555
1598
|
chain: Blockchain.Sonic_Testnet,
|
|
1556
|
-
name: 'Sonic
|
|
1557
|
-
title: 'Sonic
|
|
1599
|
+
name: 'Sonic Testnet',
|
|
1600
|
+
title: 'Sonic Testnet',
|
|
1558
1601
|
nativeCurrency: {
|
|
1559
1602
|
name: 'Sonic',
|
|
1560
1603
|
symbol: 'S',
|
|
1561
1604
|
decimals: 18,
|
|
1562
1605
|
},
|
|
1563
|
-
chainId:
|
|
1606
|
+
chainId: 14601,
|
|
1564
1607
|
isTestnet: true,
|
|
1565
1608
|
explorerUrl: 'https://testnet.sonicscan.org/tx/{hash}',
|
|
1566
|
-
rpcEndpoints: ['https://rpc.
|
|
1609
|
+
rpcEndpoints: ['https://rpc.testnet.soniclabs.com'],
|
|
1567
1610
|
eurcAddress: null,
|
|
1568
|
-
usdcAddress: '
|
|
1611
|
+
usdcAddress: '0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51',
|
|
1569
1612
|
cctp: {
|
|
1570
1613
|
domain: 13,
|
|
1571
1614
|
contracts: {
|
|
@@ -1955,7 +1998,7 @@ const WorldChainSepolia = defineChain({
|
|
|
1955
1998
|
* XDC is a Layer 1 blockchain specialized for DeFi and trading applications
|
|
1956
1999
|
* with native orderbook and matching engine.
|
|
1957
2000
|
*/
|
|
1958
|
-
defineChain({
|
|
2001
|
+
const XDC = defineChain({
|
|
1959
2002
|
type: 'evm',
|
|
1960
2003
|
chain: Blockchain.XDC,
|
|
1961
2004
|
name: 'XDC',
|
|
@@ -1993,7 +2036,7 @@ defineChain({
|
|
|
1993
2036
|
* @remarks
|
|
1994
2037
|
* This represents the official test network for the XDC Network, known as Apothem.
|
|
1995
2038
|
*/
|
|
1996
|
-
defineChain({
|
|
2039
|
+
const XDCApothem = defineChain({
|
|
1997
2040
|
type: 'evm',
|
|
1998
2041
|
chain: Blockchain.XDC_Apothem,
|
|
1999
2042
|
name: 'Apothem Network',
|
|
@@ -2225,6 +2268,7 @@ zod.z.union([
|
|
|
2225
2268
|
|
|
2226
2269
|
exports.Arbitrum = Arbitrum;
|
|
2227
2270
|
exports.ArbitrumSepolia = ArbitrumSepolia;
|
|
2271
|
+
exports.ArcTestnet = ArcTestnet;
|
|
2228
2272
|
exports.Avalanche = Avalanche;
|
|
2229
2273
|
exports.AvalancheFuji = AvalancheFuji;
|
|
2230
2274
|
exports.Base = Base;
|
|
@@ -2233,12 +2277,20 @@ exports.Codex = Codex;
|
|
|
2233
2277
|
exports.CodexTestnet = CodexTestnet;
|
|
2234
2278
|
exports.Ethereum = Ethereum;
|
|
2235
2279
|
exports.EthereumSepolia = EthereumSepolia;
|
|
2280
|
+
exports.HyperEVM = HyperEVM;
|
|
2281
|
+
exports.HyperEVMTestnet = HyperEVMTestnet;
|
|
2282
|
+
exports.Ink = Ink;
|
|
2283
|
+
exports.InkTestnet = InkTestnet;
|
|
2236
2284
|
exports.Linea = Linea;
|
|
2237
2285
|
exports.LineaSepolia = LineaSepolia;
|
|
2238
2286
|
exports.Optimism = Optimism;
|
|
2239
2287
|
exports.OptimismSepolia = OptimismSepolia;
|
|
2288
|
+
exports.Plume = Plume;
|
|
2289
|
+
exports.PlumeTestnet = PlumeTestnet;
|
|
2240
2290
|
exports.Polygon = Polygon;
|
|
2241
2291
|
exports.PolygonAmoy = PolygonAmoy;
|
|
2292
|
+
exports.Sei = Sei;
|
|
2293
|
+
exports.SeiTestnet = SeiTestnet;
|
|
2242
2294
|
exports.Solana = Solana;
|
|
2243
2295
|
exports.SolanaDevnet = SolanaDevnet;
|
|
2244
2296
|
exports.Sonic = Sonic;
|
|
@@ -2247,4 +2299,6 @@ exports.Unichain = Unichain;
|
|
|
2247
2299
|
exports.UnichainSepolia = UnichainSepolia;
|
|
2248
2300
|
exports.WorldChain = WorldChain;
|
|
2249
2301
|
exports.WorldChainSepolia = WorldChainSepolia;
|
|
2250
|
-
|
|
2302
|
+
exports.XDC = XDC;
|
|
2303
|
+
exports.XDCApothem = XDCApothem;
|
|
2304
|
+
//# sourceMappingURL=chains.cjs.map
|
package/chains.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chains.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|