@circle-fin/bridge-kit 1.1.2 → 1.3.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 +35 -0
- package/QUICKSTART.md +144 -28
- package/README.md +127 -11
- package/chains.cjs +136 -3
- package/chains.d.ts +93 -4
- package/chains.mjs +137 -4
- package/index.cjs +3869 -3022
- package/index.d.ts +705 -85
- package/index.mjs +3856 -3019
- package/package.json +14 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# @circle-fin/bridge-kit
|
|
2
2
|
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- **Enhanced bridge estimate response**: `EstimateResult` now includes `token`, `amount`, `source`, `destination` fields to provide complete transfer information alongside cost estimates.
|
|
8
|
+
- Export error handling utilities and constants.
|
|
9
|
+
|
|
10
|
+
New exports include:
|
|
11
|
+
|
|
12
|
+
- Error type guards: isKitError, isBalanceError, isOnchainError, isRpcError, isNetworkError, isRetryableError, isFatalError
|
|
13
|
+
- Error constants: BalanceError, OnchainError, RpcError, NetworkError, InputError
|
|
14
|
+
- Utility: getErrorCode, getErrorMessage
|
|
15
|
+
|
|
16
|
+
## 1.2.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- Add computeFee function to CustomFeePolicy that receives human-readable amounts (e.g., '100' for 100 USDC), deprecate calculateFee which receives smallest-unit amounts
|
|
21
|
+
- Enhanced decimal format validation to standardize on strict dot (.) decimal separators. Amount, maxFee, and customFee fields now use a unified dot-decimal format. Supported examples include "1000.50" and "1.5"
|
|
22
|
+
- Introduce the `BridgeChain` enum and `BridgeChainIdentifier` type to restrict chain selection to only CCTPv2-supported chains. This provides IDE autocomplete limited to valid bridge chains and ensures that passing an unsupported chain triggers a compile-time error.
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Updated BridgeKit to return `BridgeResults` in human readable units for `bridge()` and `retry()`. For example, when printing out the results of a `kit.bridge()` call with 1 USDC, the result's `amount` attribute will now show `'1.0'` instead of `'1000000'`.
|
|
27
|
+
- Validation errors now use standardized error codes for consistent error handling. All validation failures return the same `KitError` structure as other SDK errors.
|
|
28
|
+
|
|
29
|
+
**Migration:** If you're catching validation errors, update your error handling:
|
|
30
|
+
- Check `error.code === 1098` instead of `instanceof ValidationError`
|
|
31
|
+
- Access details via `error.cause?.trace?.validationErrors` instead of `error.errors`
|
|
32
|
+
|
|
33
|
+
The legacy `ValidationError` class remains available for backward compatibility.
|
|
34
|
+
|
|
35
|
+
- Documented the exact custom-fee flow across Bridge Kit’s README and JSDoc.
|
|
36
|
+
Clarified that kit-level and per-transfer custom fees are added to the transfer debit before CCTPv2 runs.
|
|
37
|
+
|
|
3
38
|
## 1.1.2
|
|
4
39
|
|
|
5
40
|
### Patch Changes
|
package/QUICKSTART.md
CHANGED
|
@@ -56,7 +56,7 @@ Before diving into the examples, it's important to understand the two address co
|
|
|
56
56
|
|
|
57
57
|
```typescript
|
|
58
58
|
// User-controlled example - address resolved automatically
|
|
59
|
-
const adapter =
|
|
59
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
60
60
|
privateKey: process.env.PRIVATE_KEY,
|
|
61
61
|
// addressContext: 'user-controlled' is the default
|
|
62
62
|
})
|
|
@@ -84,7 +84,7 @@ await kit.bridge({
|
|
|
84
84
|
|
|
85
85
|
```typescript
|
|
86
86
|
// Developer-controlled example - explicit address control
|
|
87
|
-
const adapter =
|
|
87
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
88
88
|
privateKey: process.env.PRIVATE_KEY,
|
|
89
89
|
capabilities: {
|
|
90
90
|
addressContext: 'developer-controlled',
|
|
@@ -190,14 +190,14 @@ The factory methods make getting started incredibly simple. Plus, you can create
|
|
|
190
190
|
|
|
191
191
|
```typescript
|
|
192
192
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
193
|
-
import {
|
|
193
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
194
194
|
|
|
195
195
|
// Initialize the kit (CCTPv2 provider included by default)
|
|
196
196
|
const kit = new BridgeKit()
|
|
197
197
|
|
|
198
198
|
// Create ONE adapter that can work across chains!
|
|
199
199
|
// Uses 'user-controlled' by default - addresses resolved automatically
|
|
200
|
-
const adapter =
|
|
200
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
201
201
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
202
202
|
})
|
|
203
203
|
|
|
@@ -222,12 +222,12 @@ const result = await kit.bridge({
|
|
|
222
222
|
|
|
223
223
|
```typescript
|
|
224
224
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
225
|
-
import {
|
|
225
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
226
226
|
|
|
227
227
|
const kit = new BridgeKit()
|
|
228
228
|
|
|
229
229
|
// Create adapter with explicit address control
|
|
230
|
-
const adapter =
|
|
230
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
231
231
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
232
232
|
capabilities: {
|
|
233
233
|
addressContext: 'developer-controlled', // Explicit address control
|
|
@@ -257,13 +257,13 @@ const result = await kit.bridge({
|
|
|
257
257
|
|
|
258
258
|
```typescript
|
|
259
259
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
260
|
-
import {
|
|
260
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
261
261
|
import { createPublicClient, http } from 'viem'
|
|
262
262
|
|
|
263
263
|
const kit = new BridgeKit()
|
|
264
264
|
|
|
265
265
|
// Production-ready setup with custom RPC endpoints
|
|
266
|
-
const adapter =
|
|
266
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
267
267
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
268
268
|
capabilities: {
|
|
269
269
|
addressContext: 'user-controlled', // Explicit for clarity in production
|
|
@@ -296,13 +296,13 @@ For browser environments with wallet providers like MetaMask:
|
|
|
296
296
|
|
|
297
297
|
```typescript
|
|
298
298
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
299
|
-
import {
|
|
299
|
+
import { createViemAdapterFromProvider } from '@circle-fin/adapter-viem-v2'
|
|
300
300
|
|
|
301
301
|
const kit = new BridgeKit()
|
|
302
302
|
|
|
303
303
|
// Create adapters from browser wallet providers
|
|
304
304
|
// Browser wallets are typically user-controlled (one connected address)
|
|
305
|
-
const adapter = await
|
|
305
|
+
const adapter = await createViemAdapterFromProvider({
|
|
306
306
|
provider: window.ethereum,
|
|
307
307
|
capabilities: {
|
|
308
308
|
addressContext: 'user-controlled', // Browser wallets = user-controlled
|
|
@@ -412,7 +412,7 @@ type AdapterContext = { adapter: Adapter; chain: ChainIdentifier }
|
|
|
412
412
|
**Chain-agnostic adapter creation**:
|
|
413
413
|
|
|
414
414
|
```typescript
|
|
415
|
-
const adapter =
|
|
415
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
416
416
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
417
417
|
chain: 'Ethereum',
|
|
418
418
|
})
|
|
@@ -527,6 +527,122 @@ interface BridgeConfig {
|
|
|
527
527
|
- **FAST**: Optimized for speed with potentially higher fees
|
|
528
528
|
- **SLOW**: Optimized for cost with zero transfer fees but longer confirmation times
|
|
529
529
|
|
|
530
|
+
## Custom Fees
|
|
531
|
+
|
|
532
|
+
### Understanding How Custom Fees Work
|
|
533
|
+
|
|
534
|
+
Custom fees allow you to charge developer fees on cross-chain USDC transfers. **The custom fee is added on top of the transfer amount.** The wallet signs for `amount + customFee`, the custom fee is split (10% to Circle, 90% to your `recipientAddress`) on the source chain, and the entire transfer amount continues through CCTPv2 unchanged.
|
|
535
|
+
|
|
536
|
+
### Step-by-Step Example: 1,000 USDC Transfer with 10 USDC Custom Fee
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
540
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
541
|
+
|
|
542
|
+
const kit = new BridgeKit()
|
|
543
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
544
|
+
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
545
|
+
})
|
|
546
|
+
|
|
547
|
+
// User wants to transfer 1,000 USDC
|
|
548
|
+
// You want to charge a 10 USDC custom fee
|
|
549
|
+
await kit.bridge({
|
|
550
|
+
from: { adapter, chain: 'Ethereum' },
|
|
551
|
+
to: { adapter, chain: 'Base' },
|
|
552
|
+
amount: '1000', // ← Amount forwarded to CCTPv2
|
|
553
|
+
config: {
|
|
554
|
+
customFee: {
|
|
555
|
+
value: '10', // ← Additional debit on top of the transfer amount
|
|
556
|
+
recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0',
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
})
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
#### Breakdown of What Happens:
|
|
563
|
+
|
|
564
|
+
| Stage | Amount | Description |
|
|
565
|
+
| ------------------------------------- | ---------- | -------------------------------------------------- |
|
|
566
|
+
| **Wallet debit** | 1,010 USDC | User signs for transfer (1,000) + custom fee (10) |
|
|
567
|
+
| **Custom fee → Circle (10%)** | 1 USDC | Automatically routed to Circle on the source chain |
|
|
568
|
+
| **Custom fee → Your recipient (90%)** | 9 USDC | Sent to `0x742d35Cc...bEb0` |
|
|
569
|
+
| **Forwarded to CCTPv2** | 1,000 USDC | Transfer amount proceeds unchanged |
|
|
570
|
+
| **CCTPv2 fee (FAST 1 bps example)** | -0.1 USDC | Protocol fee taken from the transfer amount |
|
|
571
|
+
| **Destination receives** | 999.9 USDC | Amount minted on Base |
|
|
572
|
+
|
|
573
|
+
**Summary:**
|
|
574
|
+
|
|
575
|
+
- **Circle receives:** 10% of the custom fee (1 USDC in this example).
|
|
576
|
+
- **You receive:** 90% of the custom fee (9 USDC).
|
|
577
|
+
- **User receives:** Transfer amount minus the CCTPv2 protocol fee (999.9 USDC).
|
|
578
|
+
- **Important:** Circle only participates in the 10% share when a custom fee is present. If you do not configure a custom fee for a transfer, Circle does not collect any additional amount.
|
|
579
|
+
- **Total user debit:** 1,010.1 USDC (1,000 transfer + 10 custom fee + 0.1 protocol fee).
|
|
580
|
+
|
|
581
|
+
### Per-Transfer Custom Fee
|
|
582
|
+
|
|
583
|
+
Add a one-off fee directly inside `bridge` parameters:
|
|
584
|
+
|
|
585
|
+
```typescript
|
|
586
|
+
await kit.bridge({
|
|
587
|
+
from: { adapter, chain: 'Ethereum' },
|
|
588
|
+
to: { adapter, chain: 'Base' },
|
|
589
|
+
amount: '100',
|
|
590
|
+
config: {
|
|
591
|
+
customFee: {
|
|
592
|
+
value: '1', // 1 USDC fee
|
|
593
|
+
recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0',
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
})
|
|
597
|
+
|
|
598
|
+
// Result (Fast Transfer):
|
|
599
|
+
// - Wallet signs for 101 USDC (100 transfer + 1 custom fee)
|
|
600
|
+
// - Custom fee split: 0.1 USDC to Circle, 0.9 USDC to your recipientAddress wallet
|
|
601
|
+
// - 100 USDC forwarded to CCTPv2
|
|
602
|
+
// - CCTPv2 fee: 0.01 USDC (1 bps)
|
|
603
|
+
// - User receives: 99.99 USDC
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
### Kit-Level Fee Policy
|
|
607
|
+
|
|
608
|
+
Set a global fee policy for all transfers:
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
612
|
+
|
|
613
|
+
const kit = new BridgeKit()
|
|
614
|
+
|
|
615
|
+
kit.setCustomFeePolicy({
|
|
616
|
+
computeFee: (params) => {
|
|
617
|
+
const amount = parseFloat(params.amount)
|
|
618
|
+
|
|
619
|
+
// Example: Charge 1% fee
|
|
620
|
+
const feePercentage = 0.01
|
|
621
|
+
const fee = amount * feePercentage
|
|
622
|
+
|
|
623
|
+
// Return human-readable fee (e.g., '10' for 10 USDC)
|
|
624
|
+
return fee.toFixed(6)
|
|
625
|
+
},
|
|
626
|
+
resolveFeeRecipientAddress: (feePayoutChain) => {
|
|
627
|
+
// Return address for the source chain
|
|
628
|
+
if (feePayoutChain.type === 'solana') {
|
|
629
|
+
return 'YourSolanaAddressBase58Encoded...'
|
|
630
|
+
}
|
|
631
|
+
return '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0'
|
|
632
|
+
},
|
|
633
|
+
})
|
|
634
|
+
|
|
635
|
+
// Now all transfers automatically include the custom fee
|
|
636
|
+
await kit.bridge({
|
|
637
|
+
from: { adapter, chain: 'Ethereum' },
|
|
638
|
+
to: { adapter, chain: 'Base' },
|
|
639
|
+
amount: '1000',
|
|
640
|
+
})
|
|
641
|
+
// Custom fee (10 USDC) calculated and charged automatically
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
> **Note**: The `calculateFee` function is deprecated. Use `computeFee` instead, which receives human-readable amounts (e.g., `'100'` for 100 USDC) rather than smallest-unit amounts.
|
|
645
|
+
|
|
530
646
|
## Examples
|
|
531
647
|
|
|
532
648
|
### Example 1: EVM to EVM Transfer (Ethereum → Base)
|
|
@@ -535,13 +651,13 @@ interface BridgeConfig {
|
|
|
535
651
|
|
|
536
652
|
```typescript
|
|
537
653
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
538
|
-
import {
|
|
654
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
539
655
|
|
|
540
656
|
const kit = new BridgeKit()
|
|
541
657
|
|
|
542
658
|
// Create ONE adapter that can work across chains
|
|
543
659
|
// Uses user-controlled by default - addresses resolved automatically
|
|
544
|
-
const adapter =
|
|
660
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
545
661
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
546
662
|
})
|
|
547
663
|
|
|
@@ -588,12 +704,12 @@ async function evmToEvmTransfer() {
|
|
|
588
704
|
|
|
589
705
|
```typescript
|
|
590
706
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
591
|
-
import {
|
|
707
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
592
708
|
|
|
593
709
|
const kit = new BridgeKit()
|
|
594
710
|
|
|
595
711
|
// Create adapter with explicit address control for enterprise use
|
|
596
|
-
const adapter =
|
|
712
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
597
713
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
598
714
|
capabilities: {
|
|
599
715
|
addressContext: 'developer-controlled', // Enterprise/multi-address mode
|
|
@@ -632,7 +748,7 @@ async function enterpriseEvmToEvmTransfer() {
|
|
|
632
748
|
|
|
633
749
|
```typescript
|
|
634
750
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
635
|
-
import {
|
|
751
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
636
752
|
import { SolanaAdapter } from '@circle-fin/adapter-solana'
|
|
637
753
|
import { Connection, Keypair } from '@solana/web3.js'
|
|
638
754
|
import bs58 from 'bs58'
|
|
@@ -640,7 +756,7 @@ import bs58 from 'bs58'
|
|
|
640
756
|
const kit = new BridgeKit()
|
|
641
757
|
|
|
642
758
|
// Create EVM adapter (Ethereum) using factory method
|
|
643
|
-
const ethereumAdapter =
|
|
759
|
+
const ethereumAdapter = createViemAdapterFromPrivateKey({
|
|
644
760
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
645
761
|
chain: 'Ethereum',
|
|
646
762
|
})
|
|
@@ -686,12 +802,12 @@ async function evmToSolanaTransfer() {
|
|
|
686
802
|
|
|
687
803
|
```typescript
|
|
688
804
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
689
|
-
import {
|
|
805
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
690
806
|
|
|
691
807
|
const kit = new BridgeKit()
|
|
692
808
|
|
|
693
809
|
// Create ONE adapter that can work across chains
|
|
694
|
-
const adapter =
|
|
810
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
695
811
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
696
812
|
chain: 'Ethereum',
|
|
697
813
|
})
|
|
@@ -821,10 +937,10 @@ retry<
|
|
|
821
937
|
|
|
822
938
|
```typescript
|
|
823
939
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
824
|
-
import {
|
|
940
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
825
941
|
|
|
826
942
|
const kit = new BridgeKit()
|
|
827
|
-
const adapter =
|
|
943
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
828
944
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
829
945
|
})
|
|
830
946
|
|
|
@@ -850,14 +966,14 @@ if (result.state === 'error') {
|
|
|
850
966
|
|
|
851
967
|
```typescript
|
|
852
968
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
853
|
-
import {
|
|
854
|
-
import {
|
|
969
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
970
|
+
import { createSolanaAdapterFromPrivateKey } from '@circle-fin/adapter-solana'
|
|
855
971
|
|
|
856
972
|
const kit = new BridgeKit()
|
|
857
|
-
const evm =
|
|
973
|
+
const evm = createViemAdapterFromPrivateKey({
|
|
858
974
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
859
975
|
})
|
|
860
|
-
const sol =
|
|
976
|
+
const sol = createSolanaAdapterFromPrivateKey({
|
|
861
977
|
privateKey: process.env.SOL_PRIVATE_KEY as string,
|
|
862
978
|
})
|
|
863
979
|
|
|
@@ -889,10 +1005,10 @@ if (result.state === 'error') {
|
|
|
889
1005
|
|
|
890
1006
|
```typescript
|
|
891
1007
|
import { BridgeKit, BridgeResult } from '@circle-fin/bridge-kit'
|
|
892
|
-
import {
|
|
1008
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
893
1009
|
|
|
894
1010
|
const kit = new BridgeKit()
|
|
895
|
-
const adapter =
|
|
1011
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
896
1012
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
897
1013
|
})
|
|
898
1014
|
|
|
@@ -941,7 +1057,7 @@ if (result.state === 'error') {
|
|
|
941
1057
|
const mintStep = result.steps.find((s) => s.name === 'mint')
|
|
942
1058
|
if (result.state === 'error' && mintStep?.state === 'error') {
|
|
943
1059
|
// Recreate destination adapter with updated gas settings as needed
|
|
944
|
-
const dest =
|
|
1060
|
+
const dest = createViemAdapterFromPrivateKey({
|
|
945
1061
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
946
1062
|
// Provide a higher-priority RPC or adjust client gas policies here if supported by your setup
|
|
947
1063
|
})
|
package/README.md
CHANGED
|
@@ -36,6 +36,10 @@ _Making cross-chain stablecoin (USDC, and soon more tokens) transfers as simple
|
|
|
36
36
|
- [3. **BridgeConfig** - Transfer Settings](#3-bridgeconfig---transfer-settings)
|
|
37
37
|
- [Complete Example with All Options](#complete-example-with-all-options)
|
|
38
38
|
- [Bridge Speed Configuration](#bridge-speed-configuration)
|
|
39
|
+
- [Custom Fees](#custom-fees)
|
|
40
|
+
- [How Custom Fees Work](#how-custom-fees-work)
|
|
41
|
+
- [1000 USDC Transfer Example](#1000-usdc-transfer-example)
|
|
42
|
+
- [Kit-Level Fee Policies](#kit-level-fee-policies)
|
|
39
43
|
- [Error Handling](#error-handling)
|
|
40
44
|
- [Retrying Failed Transfers](#retrying-failed-transfers)
|
|
41
45
|
- [Examples](#examples)
|
|
@@ -133,13 +137,13 @@ The factory methods make it incredibly easy to get started with **built-in relia
|
|
|
133
137
|
|
|
134
138
|
```typescript
|
|
135
139
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
136
|
-
import {
|
|
140
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
137
141
|
|
|
138
142
|
// Initialize the kit
|
|
139
143
|
const kit = new BridgeKit()
|
|
140
144
|
|
|
141
145
|
// Create ONE adapter that works across all chains!
|
|
142
|
-
const adapter =
|
|
146
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
143
147
|
privateKey: process.env.PRIVATE_KEY as string,
|
|
144
148
|
})
|
|
145
149
|
|
|
@@ -171,7 +175,7 @@ const result = await kit.bridge({
|
|
|
171
175
|
})
|
|
172
176
|
|
|
173
177
|
// Or use a different adapter for the destination chain
|
|
174
|
-
const baseAdapter =
|
|
178
|
+
const baseAdapter = createViemAdapterFromPrivateKey({
|
|
175
179
|
privateKey: process.env.PRIVATE_KEY as string,
|
|
176
180
|
})
|
|
177
181
|
|
|
@@ -194,7 +198,7 @@ const resultWithDifferentAdapter = await kit.bridge({
|
|
|
194
198
|
import { createPublicClient, http } from 'viem'
|
|
195
199
|
|
|
196
200
|
// Production-ready setup with custom RPC endpoints
|
|
197
|
-
const adapter =
|
|
201
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
198
202
|
privateKey: process.env.PRIVATE_KEY as string,
|
|
199
203
|
getPublicClient: ({ chain }) =>
|
|
200
204
|
createPublicClient({
|
|
@@ -222,10 +226,10 @@ const result = await kit.bridge({
|
|
|
222
226
|
**Best for**: Browser applications, wallet integrations, user-controlled transactions
|
|
223
227
|
|
|
224
228
|
```typescript
|
|
225
|
-
import {
|
|
229
|
+
import { createViemAdapterFromProvider } from '@circle-fin/adapter-viem-v2'
|
|
226
230
|
|
|
227
231
|
// Create adapters from browser wallet providers
|
|
228
|
-
const adapter = await
|
|
232
|
+
const adapter = await createViemAdapterFromProvider({
|
|
229
233
|
provider: window.ethereum,
|
|
230
234
|
})
|
|
231
235
|
|
|
@@ -312,7 +316,7 @@ The Bridge Kit supports different configuration patterns to match your use case:
|
|
|
312
316
|
|
|
313
317
|
```typescript
|
|
314
318
|
// Create chain-agnostic adapter
|
|
315
|
-
const adapter =
|
|
319
|
+
const adapter = createViemAdapterFromPrivateKey({...})
|
|
316
320
|
|
|
317
321
|
// Always specify chain explicitly for clarity
|
|
318
322
|
const adapterContext = { adapter, chain: 'Ethereum' }
|
|
@@ -360,6 +364,118 @@ const result = await kit.bridge({
|
|
|
360
364
|
})
|
|
361
365
|
```
|
|
362
366
|
|
|
367
|
+
## Custom Fees
|
|
368
|
+
|
|
369
|
+
Bridge Kit allows you to charge custom developer fees on cross-chain USDC transfers. Understanding how these fees interact with wallet debits and CCTPv2 protocol fees is crucial for correct implementation.
|
|
370
|
+
|
|
371
|
+
### How Custom Fees Work
|
|
372
|
+
|
|
373
|
+
Custom fees are **added on top of the transfer amount**, not taken out of it. The wallet signs for `transfer amount + custom fee`, so the user must have enough balance for both values. The entire transfer amount continues through CCTPv2 unchanged, while the custom fee is split on the source chain:
|
|
374
|
+
|
|
375
|
+
- **10%** of the custom fee is automatically routed to Circle.
|
|
376
|
+
- **90%** is sent to your `recipientAddress`.
|
|
377
|
+
- **Important:** Circle only takes the 10% share when a custom fee is actually charged. If you omit a custom fee, Circle does not collect anything beyond the protocol fee.
|
|
378
|
+
|
|
379
|
+
After the custom fee is collected, the transfer amount (e.g., 1,000 USDC) proceeds through CCTPv2, where the protocol applies its own fee (1–14 bps in FAST mode, 0% in STANDARD).
|
|
380
|
+
|
|
381
|
+
**Fee Flow (1,000 USDC transfer + 10 USDC custom fee):**
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
┌───────────────────────────────────────────────────────────────┐
|
|
385
|
+
│ User signs: 1,000 USDC transfer + 10 USDC custom fee = 1,010 │
|
|
386
|
+
└──────────────────────┬────────────────────────────────────────┘
|
|
387
|
+
│
|
|
388
|
+
▼
|
|
389
|
+
┌───────────────────────────────────────────────────────────────┐
|
|
390
|
+
│ Custom fee distribution (source chain) │
|
|
391
|
+
│ - 1 USDC (10%) → Circle │
|
|
392
|
+
│ - 9 USDC (90%) → Your fee recipient │
|
|
393
|
+
└──────────────────────┬────────────────────────────────────────┘
|
|
394
|
+
│
|
|
395
|
+
▼
|
|
396
|
+
┌───────────────────────────────────────────────────────────────┐
|
|
397
|
+
│ CCTPv2 processes full transfer amount (1,000 USDC) │
|
|
398
|
+
│ - Protocol fee example (FAST 1 bps): 0.1 USDC │
|
|
399
|
+
│ - Destination receives: 999.9 USDC │
|
|
400
|
+
└───────────────────────────────────────────────────────────────┘
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### 1,000 USDC Transfer Example
|
|
404
|
+
|
|
405
|
+
**Scenario:** Transfer 1,000 USDC from Ethereum to Base with a 10 USDC custom fee.
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
409
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
410
|
+
|
|
411
|
+
const kit = new BridgeKit()
|
|
412
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
413
|
+
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
await kit.bridge({
|
|
417
|
+
from: { adapter, chain: 'Ethereum' },
|
|
418
|
+
to: { adapter, chain: 'Base' },
|
|
419
|
+
amount: '1000', // Transfer amount forwarded to CCTPv2
|
|
420
|
+
config: {
|
|
421
|
+
customFee: {
|
|
422
|
+
value: '10', // Additional debit charged on top of the transfer amount
|
|
423
|
+
recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0',
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
})
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**What happens on-chain:**
|
|
430
|
+
|
|
431
|
+
| Stage | Amount | Description |
|
|
432
|
+
| ----------------------------- | ---------- | ------------------------------------------------------ |
|
|
433
|
+
| **Transfer amount** | 1,000 USDC | Forwarded to CCTPv2 without reduction |
|
|
434
|
+
| **Custom fee debit** | +10 USDC | Wallet signs for 1,010 USDC total |
|
|
435
|
+
| **Custom fee → Circle (10%)** | 1 USDC | Automatically routed to Circle |
|
|
436
|
+
| **Custom fee → You (90%)** | 9 USDC | Sent to `0x742d35Cc...bEb0` (your fee recipient) |
|
|
437
|
+
| **CCTPv2 fee (FAST 1 bps)\*** | -0.1 USDC | Protocol fee taken from the 1,000 USDC transfer amount |
|
|
438
|
+
| **Destination receives** | 999.9 USDC | Amount minted on Base after protocol fee |
|
|
439
|
+
|
|
440
|
+
\* \_CCTPv2 FAST transfers charge 1–14 bps depending on the route; STANDARD transfers charge 0 bps.
|
|
441
|
+
|
|
442
|
+
### Kit-Level Fee Policies
|
|
443
|
+
|
|
444
|
+
For dynamic fee calculation across all transfers, use kit-level policies:
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
448
|
+
|
|
449
|
+
const kit = new BridgeKit()
|
|
450
|
+
|
|
451
|
+
kit.setCustomFeePolicy({
|
|
452
|
+
computeFee: (params) => {
|
|
453
|
+
const amount = parseFloat(params.amount)
|
|
454
|
+
|
|
455
|
+
const feePercentage = 0.01 // 1%
|
|
456
|
+
const calculatedFee = amount * feePercentage
|
|
457
|
+
|
|
458
|
+
// Return human-readable fee (e.g., '10' for 10 USDC)
|
|
459
|
+
return calculatedFee.toFixed(6)
|
|
460
|
+
},
|
|
461
|
+
resolveFeeRecipientAddress: (feePayoutChain) => {
|
|
462
|
+
// Return appropriate address for source chain
|
|
463
|
+
return feePayoutChain.type === 'solana'
|
|
464
|
+
? 'SolanaAddressBase58...'
|
|
465
|
+
: '0xEvmAddress...'
|
|
466
|
+
},
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
// All subsequent bridges will use this policy
|
|
470
|
+
await kit.bridge({
|
|
471
|
+
from: { adapter, chain: 'Ethereum' },
|
|
472
|
+
to: { adapter, chain: 'Base' },
|
|
473
|
+
amount: '1000', // Custom fee calculated automatically
|
|
474
|
+
})
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
> **Note**: The `calculateFee` function is deprecated. Use `computeFee` instead, which receives human-readable amounts (e.g., `'100'` for 100 USDC) rather than smallest-unit amounts.
|
|
478
|
+
|
|
363
479
|
## Error Handling
|
|
364
480
|
|
|
365
481
|
The kit uses a thoughtful error handling approach:
|
|
@@ -369,10 +485,10 @@ The kit uses a thoughtful error handling approach:
|
|
|
369
485
|
|
|
370
486
|
```typescript
|
|
371
487
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
372
|
-
import {
|
|
488
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
373
489
|
|
|
374
490
|
const kit = new BridgeKit()
|
|
375
|
-
const adapter =
|
|
491
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
376
492
|
privateKey: process.env.PRIVATE_KEY as string,
|
|
377
493
|
})
|
|
378
494
|
|
|
@@ -415,10 +531,10 @@ retry<
|
|
|
415
531
|
|
|
416
532
|
```typescript
|
|
417
533
|
import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
418
|
-
import {
|
|
534
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
419
535
|
|
|
420
536
|
const kit = new BridgeKit()
|
|
421
|
-
const adapter =
|
|
537
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
422
538
|
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
423
539
|
})
|
|
424
540
|
|