@circle-fin/app-kit 1.0.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/README.md +640 -0
- package/package.json +77 -0
package/README.md
ADDED
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
# App Kit
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/js/@circle-fin%2Fapp-kit)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
8
|
+
[](https://discord.com/invite/buildoncircle)
|
|
9
|
+
|
|
10
|
+
**A unified, strongly-typed SDK for seamless stablecoin operations across chains**
|
|
11
|
+
|
|
12
|
+
_Making cross-chain transfers and same-chain swaps as simple as a single function call_
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
## Table of Contents
|
|
17
|
+
|
|
18
|
+
- [App Kit](#app-kit)
|
|
19
|
+
- [Table of Contents](#table-of-contents)
|
|
20
|
+
- [Overview](#overview)
|
|
21
|
+
- [Why App Kit?](#why-app-kit)
|
|
22
|
+
- [Architecture Flow](#architecture-flow)
|
|
23
|
+
- [Installation](#installation)
|
|
24
|
+
- [Adapters](#adapters)
|
|
25
|
+
- [Quick Start](#quick-start)
|
|
26
|
+
- [🌉 Cross-Chain Bridge](#-cross-chain-bridge)
|
|
27
|
+
- [🔄 Same-Chain Swap](#-same-chain-swap)
|
|
28
|
+
- [📊 Estimate Operations](#-estimate-operations)
|
|
29
|
+
- [🔍 Query Supported Chains](#-query-supported-chains)
|
|
30
|
+
- [Configuration](#configuration)
|
|
31
|
+
- [Send Parameters](#send-parameters)
|
|
32
|
+
- [Swap Parameters](#swap-parameters)
|
|
33
|
+
- [Bridge Parameters](#bridge-parameters)
|
|
34
|
+
- [Custom Fees](#custom-fees)
|
|
35
|
+
- [Operation-Level Custom Fees](#operation-level-custom-fees)
|
|
36
|
+
- [Kit-Level Fee Policies](#kit-level-fee-policies)
|
|
37
|
+
- [Error Handling](#error-handling)
|
|
38
|
+
- [Examples](#examples)
|
|
39
|
+
- [Basic Cross-Chain Transfer](#basic-cross-chain-transfer)
|
|
40
|
+
- [Same-Chain Token Swap](#same-chain-token-swap)
|
|
41
|
+
- [Send to Different Address](#send-to-different-address)
|
|
42
|
+
- [Event Monitoring](#event-monitoring)
|
|
43
|
+
- [API Reference](#api-reference)
|
|
44
|
+
- [Core Methods](#core-methods)
|
|
45
|
+
- [Development](#development)
|
|
46
|
+
- [Building](#building)
|
|
47
|
+
- [Testing](#testing)
|
|
48
|
+
- [Local Development](#local-development)
|
|
49
|
+
- [Community \& Support](#community--support)
|
|
50
|
+
- [License](#license)
|
|
51
|
+
|
|
52
|
+
## Overview
|
|
53
|
+
|
|
54
|
+
The App Kit ecosystem is Circle's open-source effort to streamline stablecoin development with SDKs that are easy to use correctly and hard to misuse. Kits are cross-framework (viem, ethers, @solana/web3) and integrate cleanly into any stack. They're opinionated with sensible defaults, but offer escape hatches for full control. A pluggable architecture makes implementation flexible, and all kits are interoperable, so they can be composed to suit a wide range of use cases.
|
|
55
|
+
|
|
56
|
+
The App Kit provides a **unified interface** for both cross-chain transfers and same-chain swaps, abstracting away the complexity of choosing between bridging and swapping operations. It combines the power of Bridge Kit and Swap Kit into a single, cohesive API.
|
|
57
|
+
|
|
58
|
+
### Why App Kit?
|
|
59
|
+
|
|
60
|
+
- **🎯 Unified interface**: Single API for both bridge and swap operations
|
|
61
|
+
- **🤖 Smart routing**: Automatically selects the appropriate operation (bridge vs swap)
|
|
62
|
+
- **⚡ Zero-config defaults**: Built-in reliable RPC endpoints - start building right away
|
|
63
|
+
- **🔧 Bring your own infrastructure**: Seamlessly integrate with your existing setup when needed
|
|
64
|
+
- **🔒 Production-ready security**: Leverages Circle's CCTPv2 for bridging and trusted swap providers
|
|
65
|
+
- **🚀 Developer experience**: Complete TypeScript support, comprehensive validation, and instant connectivity
|
|
66
|
+
- **🌍 Multi-chain support**: Bridge across **35 chains** with **578 total bridge routes** through Circle's CCTPv2
|
|
67
|
+
- **Mainnet (17 chains)**: Arbitrum, Avalanche, Base, Codex, Ethereum, HyperEVM, Ink, Linea, OP Mainnet, Plume, Polygon PoS, Sei, Solana, Sonic, Unichain, World Chain, XDC
|
|
68
|
+
- **Testnet (18 chains)**: Arc Testnet, Arbitrum Sepolia, Avalanche Fuji, Base Sepolia, Codex Testnet, Ethereum Sepolia, HyperEVM Testnet, Ink Testnet, Linea Sepolia, OP Sepolia, Plume Testnet, Polygon PoS Amoy, Sei Testnet, Solana Devnet, Sonic Testnet, Unichain Sepolia, World Chain Sepolia, XDC Apothem
|
|
69
|
+
- **🔄 Swap support**: Same-chain token swaps powered by Circle's Stablecoin Service
|
|
70
|
+
- **🎯 Flexible adapters**: Supporting EVM (Viem, Ethers) and Solana (@solana/web3)
|
|
71
|
+
- **📡 Real-time event monitoring**: Track progress throughout the operation lifecycle
|
|
72
|
+
- **🛡️ Robust error handling**: Graceful partial success recovery
|
|
73
|
+
- **✈️ Pre-flight validation**: Verify operations with cost estimation before execution
|
|
74
|
+
|
|
75
|
+
## Architecture Flow
|
|
76
|
+
|
|
77
|
+
The App Kit follows a composable architecture that integrates Bridge Kit and Swap Kit:
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
81
|
+
│ App Kit │
|
|
82
|
+
│ (Unified Interface) │
|
|
83
|
+
└──────────────────────┬──────────────────────────────────────┘
|
|
84
|
+
│
|
|
85
|
+
┌─────────────┴─────────────┐
|
|
86
|
+
│ │
|
|
87
|
+
▼ ▼
|
|
88
|
+
┌─────────────────┐ ┌─────────────────┐
|
|
89
|
+
│ Bridge Kit │ │ Swap Kit │
|
|
90
|
+
│ (Cross-Chain) │ │ (Same-Chain) │
|
|
91
|
+
└────────┬────────┘ └────────┬────────┘
|
|
92
|
+
│ │
|
|
93
|
+
▼ ▼
|
|
94
|
+
┌─────────────────┐ ┌─────────────────┐
|
|
95
|
+
│ Provider │ │ Provider │
|
|
96
|
+
│ (CCTPv2) │ │ (Service) │
|
|
97
|
+
└────────┬────────┘ └────────┬────────┘
|
|
98
|
+
│ │
|
|
99
|
+
└─────────────┬─────────────┘
|
|
100
|
+
▼
|
|
101
|
+
┌───────────────┐
|
|
102
|
+
│ Adapter │
|
|
103
|
+
│ (Blockchain) │
|
|
104
|
+
└───────────────┘
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
1. **Adapter**: Handles blockchain-specific operations (wallets, transactions, gas)
|
|
108
|
+
2. **Provider**: Implements protocols (CCTPv2 for bridging, Circle Service for swaps)
|
|
109
|
+
3. **Bridge/Swap Kit**: Specialized kits for each operation type
|
|
110
|
+
4. **App Kit**: Unified orchestration layer providing a consistent API
|
|
111
|
+
|
|
112
|
+
This separation ensures that each component has a single responsibility while maintaining seamless integration across the entire stablecoin operation lifecycle.
|
|
113
|
+
|
|
114
|
+
## Installation
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npm install @circle-fin/app-kit
|
|
118
|
+
# or
|
|
119
|
+
yarn add @circle-fin/app-kit
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Adapters
|
|
123
|
+
|
|
124
|
+
Choose the appropriate adapter for your target chains:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# For EVM chains (Ethereum, Base, Arbitrum, etc.)
|
|
128
|
+
npm install @circle-fin/adapter-viem-v2 viem
|
|
129
|
+
# or
|
|
130
|
+
yarn add @circle-fin/adapter-viem-v2 viem
|
|
131
|
+
|
|
132
|
+
# For EVM chains using Ethers.js
|
|
133
|
+
npm install @circle-fin/adapter-ethers-v6
|
|
134
|
+
# or
|
|
135
|
+
yarn add @circle-fin/adapter-ethers-v6
|
|
136
|
+
|
|
137
|
+
# For Solana
|
|
138
|
+
npm install @circle-fin/adapter-solana @solana/web3.js @solana/spl-token
|
|
139
|
+
# or
|
|
140
|
+
yarn add @circle-fin/adapter-solana @solana/web3.js @solana/spl-token
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Chain Definitions
|
|
144
|
+
|
|
145
|
+
Import chain definitions directly from the kit:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { Ethereum, Base, Polygon, Solana } from '@circle-fin/app-kit/chains'
|
|
149
|
+
|
|
150
|
+
// Use with adapter context
|
|
151
|
+
const result = await kit.bridge({
|
|
152
|
+
from: { adapter, chain: Ethereum },
|
|
153
|
+
to: { adapter, chain: Base },
|
|
154
|
+
amount: '10.50',
|
|
155
|
+
})
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
All 35 CCTPv2-supported chains are available for import.
|
|
159
|
+
|
|
160
|
+
## Quick Start
|
|
161
|
+
|
|
162
|
+
### 🌉 Cross-Chain Bridge
|
|
163
|
+
|
|
164
|
+
Transfer USDC from one chain to another:
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
import { AppKit } from '@circle-fin/app-kit'
|
|
168
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
169
|
+
|
|
170
|
+
// Initialize the kit
|
|
171
|
+
const kit = new AppKit()
|
|
172
|
+
|
|
173
|
+
// Create ONE adapter that works across all chains
|
|
174
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
175
|
+
privateKey: process.env.PRIVATE_KEY as string,
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
// Bridge from Ethereum to Base
|
|
179
|
+
const result = await kit.bridge({
|
|
180
|
+
from: { adapter, chain: 'Ethereum' },
|
|
181
|
+
to: { adapter, chain: 'Base' },
|
|
182
|
+
amount: '10.50',
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 🔄 Same-Chain Swap
|
|
187
|
+
|
|
188
|
+
Swap tokens on the same chain with support for multiple stablecoins:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Swap USDC to USDT on Ethereum
|
|
192
|
+
const result = await kit.swap({
|
|
193
|
+
from: { adapter, chain: 'Ethereum' },
|
|
194
|
+
tokenIn: 'USDC',
|
|
195
|
+
tokenOut: 'USDT',
|
|
196
|
+
amountIn: '100.0',
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
// Swap DAI to USDC (18 decimals handled automatically)
|
|
200
|
+
const daiSwap = await kit.swap({
|
|
201
|
+
from: { adapter, chain: 'Ethereum' },
|
|
202
|
+
tokenIn: 'DAI',
|
|
203
|
+
tokenOut: 'USDC',
|
|
204
|
+
amountIn: '500.0',
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
// Swap native ETH to stablecoin
|
|
208
|
+
const nativeSwap = await kit.swap({
|
|
209
|
+
from: { adapter, chain: 'Ethereum' },
|
|
210
|
+
tokenIn: 'NATIVE', // ETH on Ethereum
|
|
211
|
+
tokenOut: 'USDC',
|
|
212
|
+
amountIn: '1.5',
|
|
213
|
+
})
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 📊 Estimate Operations
|
|
217
|
+
|
|
218
|
+
Get cost estimates before executing operations:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// Estimate bridge operation
|
|
222
|
+
const bridgeEstimate = await kit.estimateBridge({
|
|
223
|
+
from: { adapter, chain: 'Ethereum' },
|
|
224
|
+
to: { adapter, chain: 'Base' },
|
|
225
|
+
amount: '10.50',
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
// Estimate swap operation
|
|
229
|
+
const swapEstimate = await kit.estimateSwap({
|
|
230
|
+
from: { adapter, chain: 'Ethereum' },
|
|
231
|
+
tokenIn: 'USDC',
|
|
232
|
+
tokenOut: 'USDT',
|
|
233
|
+
amountIn: '100.0',
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
console.log('Bridge fees:', bridgeEstimate.fees)
|
|
237
|
+
console.log(
|
|
238
|
+
'Swap estimated output:',
|
|
239
|
+
swapEstimate.estimatedOutput?.amount,
|
|
240
|
+
swapEstimate.estimatedOutput?.token,
|
|
241
|
+
)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### 🔍 Query Supported Chains
|
|
245
|
+
|
|
246
|
+
Check which chains support specific operations:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
// Get all chains that support bridging
|
|
250
|
+
const bridgeChains = kit.getSupportedChains('bridge')
|
|
251
|
+
|
|
252
|
+
// Get all chains that support swapping
|
|
253
|
+
const swapChains = kit.getSupportedChains('swap')
|
|
254
|
+
|
|
255
|
+
// Get all chains (both bridge and swap)
|
|
256
|
+
const allChains = kit.getSupportedChains()
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Configuration
|
|
260
|
+
|
|
261
|
+
### Send Parameters
|
|
262
|
+
|
|
263
|
+
The `send()` method provides a unified interface for transferring tokens. It automatically determines whether to use bridging or swapping based on the source and destination chains:
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
interface SendParams {
|
|
267
|
+
from: AdapterContext // Source wallet and chain
|
|
268
|
+
to: Adapter | string // Destination wallet or address
|
|
269
|
+
amount: string // Amount to transfer (e.g., '10.50')
|
|
270
|
+
token?: TokenAlias | string // Optional, defaults to 'USDC'
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Token Support**
|
|
275
|
+
|
|
276
|
+
The `token` field accepts both known aliases and custom token contract addresses:
|
|
277
|
+
|
|
278
|
+
**Known aliases (for send operations):**
|
|
279
|
+
|
|
280
|
+
- `'USDC'` - USD Coin (6 decimals)
|
|
281
|
+
- `'USDT'` - Tether USD (6 decimals)
|
|
282
|
+
- `'NATIVE'` - Chain's native currency (ETH, SOL, etc.)
|
|
283
|
+
|
|
284
|
+
**Custom addresses:**
|
|
285
|
+
|
|
286
|
+
- EVM contract addresses (e.g., `0x6B175474E89094C44Da98b954EedeAC495271d0F`)
|
|
287
|
+
- Solana SPL mint addresses
|
|
288
|
+
|
|
289
|
+
**Note**: For swap operations, additional stablecoins (EURC, DAI, USDE, PYUSD) are supported. See [Swap Parameters](#swap-parameters) section below.
|
|
290
|
+
|
|
291
|
+
**Examples**
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// Using USDC (default)
|
|
295
|
+
await kit.send({
|
|
296
|
+
from: { adapter, chain: 'Ethereum' },
|
|
297
|
+
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
|
|
298
|
+
amount: '10.0',
|
|
299
|
+
token: 'USDC',
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
// Using USDT
|
|
303
|
+
await kit.send({
|
|
304
|
+
from: { adapter, chain: 'Ethereum' },
|
|
305
|
+
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
|
|
306
|
+
amount: '100.0',
|
|
307
|
+
token: 'USDT',
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
// Using NATIVE
|
|
311
|
+
await kit.send({
|
|
312
|
+
from: { adapter, chain: 'Ethereum' },
|
|
313
|
+
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
|
|
314
|
+
amount: '1.5',
|
|
315
|
+
token: 'NATIVE', // ETH on Ethereum
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
// Using a custom ERC-20 token address (e.g., DAI)
|
|
319
|
+
await kit.send({
|
|
320
|
+
from: { adapter, chain: 'Ethereum' },
|
|
321
|
+
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
|
|
322
|
+
amount: '50.0',
|
|
323
|
+
token: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI contract address
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
// Using a custom SPL token on Solana
|
|
327
|
+
await kit.send({
|
|
328
|
+
from: { adapter, chain: 'Solana' },
|
|
329
|
+
to: 'DhzPkKCLJGHBZbs1AzmK2tRNLZkV8J3yWF3LuWMuKJpN',
|
|
330
|
+
amount: '50.0',
|
|
331
|
+
token: 'So11111111111111111111111111111111111111112', // Wrapped SOL
|
|
332
|
+
})
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Swap Parameters
|
|
336
|
+
|
|
337
|
+
For explicit same-chain swaps:
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
interface SwapParams {
|
|
341
|
+
from: AdapterContext // Source wallet and chain
|
|
342
|
+
tokenIn: SupportedToken // Input token
|
|
343
|
+
tokenOut: SupportedToken // Output token
|
|
344
|
+
amountIn: string // Amount to swap
|
|
345
|
+
to?: string // Optional recipient address
|
|
346
|
+
config?: SwapConfig // Optional swap configuration
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// SupportedToken includes:
|
|
350
|
+
// - Stablecoins (6 decimals): 'USDC', 'EURC', 'USDT', 'PYUSD'
|
|
351
|
+
// - Stablecoins (18 decimals): 'DAI', 'USDE'
|
|
352
|
+
// - Native: 'NATIVE' (chain's native currency)
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Swap Examples with Different Tokens**
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
// Swap between 6-decimal stablecoins
|
|
359
|
+
await kit.swap({
|
|
360
|
+
from: { adapter, chain: 'Ethereum' },
|
|
361
|
+
tokenIn: 'EURC',
|
|
362
|
+
tokenOut: 'USDC',
|
|
363
|
+
amountIn: '100.0',
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
// Swap DAI (18 decimals) to USDT
|
|
367
|
+
await kit.swap({
|
|
368
|
+
from: { adapter, chain: 'Ethereum' },
|
|
369
|
+
tokenIn: 'DAI',
|
|
370
|
+
tokenOut: 'USDT',
|
|
371
|
+
amountIn: '500.0', // SDK automatically handles 18-decimal precision
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
// Swap native token to stablecoin
|
|
375
|
+
await kit.swap({
|
|
376
|
+
from: { adapter, chain: 'Ethereum' },
|
|
377
|
+
tokenIn: 'NATIVE', // ETH on Ethereum
|
|
378
|
+
tokenOut: 'USDC',
|
|
379
|
+
amountIn: '2.5',
|
|
380
|
+
})
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Bridge Parameters
|
|
384
|
+
|
|
385
|
+
For explicit cross-chain bridges:
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
interface BridgeParams {
|
|
389
|
+
from: AdapterContext // Source wallet and chain
|
|
390
|
+
to: BridgeDestination // Destination wallet/address and chain
|
|
391
|
+
amount: string // Amount to transfer (e.g., '10.50')
|
|
392
|
+
token?: 'USDC' // Optional, defaults to 'USDC'
|
|
393
|
+
config?: BridgeConfig // Optional bridge configuration
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## Custom Fees
|
|
398
|
+
|
|
399
|
+
App Kit supports custom developer fees on both bridge and swap operations.
|
|
400
|
+
|
|
401
|
+
### Operation-Level Custom Fees
|
|
402
|
+
|
|
403
|
+
Apply custom fees to individual operations:
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
// Bridge with custom fee
|
|
407
|
+
await kit.bridge({
|
|
408
|
+
from: { adapter, chain: 'Ethereum' },
|
|
409
|
+
to: { adapter, chain: 'Base' },
|
|
410
|
+
amount: '1000',
|
|
411
|
+
config: {
|
|
412
|
+
customFee: {
|
|
413
|
+
value: '10', // 10 USDC fee
|
|
414
|
+
recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0',
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
// Swap with custom fee
|
|
420
|
+
await kit.swap({
|
|
421
|
+
from: { adapter, chain: 'Ethereum' },
|
|
422
|
+
tokenIn: 'USDC',
|
|
423
|
+
tokenOut: 'USDT',
|
|
424
|
+
amountIn: '1000',
|
|
425
|
+
config: {
|
|
426
|
+
customFee: {
|
|
427
|
+
value: '10',
|
|
428
|
+
recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0',
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
})
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Kit-Level Fee Policies
|
|
435
|
+
|
|
436
|
+
For dynamic fee calculation across all operations, use kit-level policies:
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
import { AppKit } from '@circle-fin/app-kit'
|
|
440
|
+
import { formatUnits } from 'viem'
|
|
441
|
+
|
|
442
|
+
const kit = new AppKit()
|
|
443
|
+
|
|
444
|
+
kit.setCustomFeePolicy({
|
|
445
|
+
calculateFee: (params) => {
|
|
446
|
+
// Calculate fee based on operation type and parameters
|
|
447
|
+
const amount = Number(formatUnits(BigInt(params.amount), 6))
|
|
448
|
+
const feePercentage = type === 'bridge' ? 0.01 : 0.005 // 1% for bridge, 0.5% for swap
|
|
449
|
+
|
|
450
|
+
return (amount * feePercentage).toFixed(6)
|
|
451
|
+
},
|
|
452
|
+
resolveFeeRecipientAddress: (type, info) => {
|
|
453
|
+
// Return appropriate address based on operation type and chain
|
|
454
|
+
return info.chain.type === 'solana'
|
|
455
|
+
? 'SolanaAddressBase58...'
|
|
456
|
+
: '0xEvmAddress...'
|
|
457
|
+
},
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
// All subsequent operations will use this policy
|
|
461
|
+
await kit.bridge({
|
|
462
|
+
from: { adapter, chain: 'Ethereum' },
|
|
463
|
+
to: { adapter, chain: 'Base' },
|
|
464
|
+
amount: '1000', // Custom fee calculated automatically
|
|
465
|
+
})
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
## Error Handling
|
|
469
|
+
|
|
470
|
+
The kit uses a thoughtful error handling approach:
|
|
471
|
+
|
|
472
|
+
- **Hard errors** (thrown): Validation, configuration, and authentication errors
|
|
473
|
+
- **Soft errors** (returned): Recoverable issues like insufficient balance or network errors
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
import { AppKit } from '@circle-fin/app-kit'
|
|
477
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
478
|
+
|
|
479
|
+
const kit = new AppKit()
|
|
480
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
481
|
+
privateKey: process.env.PRIVATE_KEY as string,
|
|
482
|
+
})
|
|
483
|
+
|
|
484
|
+
const result = await kit.bridge({
|
|
485
|
+
from: { adapter, chain: 'Ethereum' },
|
|
486
|
+
to: { adapter, chain: 'Base' },
|
|
487
|
+
amount: '100.0',
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
if (result.state === 'success') {
|
|
491
|
+
console.log('Operation successful!')
|
|
492
|
+
} else {
|
|
493
|
+
// Handle partial completion with recovery information
|
|
494
|
+
console.log(
|
|
495
|
+
'Successful steps:',
|
|
496
|
+
result.steps.filter((s) => s.state === 'success'),
|
|
497
|
+
)
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
## Examples
|
|
502
|
+
|
|
503
|
+
### Basic Cross-Chain Transfer
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
import { AppKit } from '@circle-fin/app-kit'
|
|
507
|
+
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
508
|
+
|
|
509
|
+
const kit = new AppKit()
|
|
510
|
+
const adapter = createViemAdapterFromPrivateKey({
|
|
511
|
+
privateKey: process.env.PRIVATE_KEY as string,
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
// Bridge USDC from Ethereum to Base
|
|
515
|
+
const result = await kit.bridge({
|
|
516
|
+
from: { adapter, chain: 'Ethereum' },
|
|
517
|
+
to: { adapter, chain: 'Base' },
|
|
518
|
+
amount: '50.0',
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
console.log('Bridge result:', result)
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### Same-Chain Token Swap
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
// Swap USDC to USDT on Ethereum
|
|
528
|
+
const result = await kit.swap({
|
|
529
|
+
from: { adapter, chain: 'Ethereum' },
|
|
530
|
+
tokenIn: 'USDC',
|
|
531
|
+
tokenOut: 'USDT',
|
|
532
|
+
amountIn: '100.0',
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
console.log('Swap result:', result)
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Send to Different Address
|
|
539
|
+
|
|
540
|
+
```typescript
|
|
541
|
+
// Send to a different address (automatically chooses bridge or swap)
|
|
542
|
+
const result = await kit.send({
|
|
543
|
+
from: { adapter, chain: 'Ethereum' },
|
|
544
|
+
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
|
|
545
|
+
amount: '10.50',
|
|
546
|
+
token: 'USDC',
|
|
547
|
+
})
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### Event Monitoring
|
|
551
|
+
|
|
552
|
+
```typescript
|
|
553
|
+
// Listen to bridge events
|
|
554
|
+
kit.on('bridge.approve', (payload) => {
|
|
555
|
+
console.log('Approval transaction:', payload.values.txHash)
|
|
556
|
+
})
|
|
557
|
+
|
|
558
|
+
kit.on('bridge.burn', (payload) => {
|
|
559
|
+
console.log('Burn transaction:', payload.values.txHash)
|
|
560
|
+
})
|
|
561
|
+
|
|
562
|
+
// Listen to all events
|
|
563
|
+
kit.on('*', (payload) => {
|
|
564
|
+
console.log('Action:', payload.method)
|
|
565
|
+
})
|
|
566
|
+
|
|
567
|
+
// Execute operation
|
|
568
|
+
await kit.bridge({
|
|
569
|
+
from: { adapter, chain: 'Ethereum' },
|
|
570
|
+
to: { adapter, chain: 'Base' },
|
|
571
|
+
amount: '10.0',
|
|
572
|
+
})
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
## API Reference
|
|
576
|
+
|
|
577
|
+
### Core Methods
|
|
578
|
+
|
|
579
|
+
- `kit.send(params)` - Unified interface for transfers (auto-routes to bridge or swap)
|
|
580
|
+
- `kit.bridge(params)` - Execute cross-chain bridge operation
|
|
581
|
+
- `kit.swap(params)` - Execute same-chain swap operation
|
|
582
|
+
- `kit.estimateBridge(params)` - Get cost estimates for bridging
|
|
583
|
+
- `kit.estimateSwap(params)` - Get cost estimates for swapping
|
|
584
|
+
- `kit.estimateSend(params)` - Get cost estimates for send operation
|
|
585
|
+
- `kit.getSupportedChains(operationType?)` - Query supported chains by operation type
|
|
586
|
+
- `kit.setCustomFeePolicy(policy)` - Set kit-level custom fee policy
|
|
587
|
+
- `kit.on(event, handler)` - Listen to operation events
|
|
588
|
+
- `kit.off(event, handler)` - Remove event listener
|
|
589
|
+
|
|
590
|
+
## Development
|
|
591
|
+
|
|
592
|
+
### Building
|
|
593
|
+
|
|
594
|
+
```bash
|
|
595
|
+
# From the root of the monorepo
|
|
596
|
+
nx build @circle-fin/app-kit
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### Testing
|
|
600
|
+
|
|
601
|
+
```bash
|
|
602
|
+
# From the root of the monorepo
|
|
603
|
+
nx test @circle-fin/app-kit
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
### Local Development
|
|
607
|
+
|
|
608
|
+
```bash
|
|
609
|
+
# Install dependencies
|
|
610
|
+
yarn install
|
|
611
|
+
|
|
612
|
+
# Build all packages
|
|
613
|
+
yarn build
|
|
614
|
+
|
|
615
|
+
# Build the app-kit specifically
|
|
616
|
+
nx build @circle-fin/app-kit
|
|
617
|
+
|
|
618
|
+
# Run tests
|
|
619
|
+
nx test @circle-fin/app-kit
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
## Community & Support
|
|
623
|
+
|
|
624
|
+
- **💬 Discord**: [Join our community](https://discord.com/invite/buildoncircle)
|
|
625
|
+
|
|
626
|
+
## License
|
|
627
|
+
|
|
628
|
+
This project is licensed under the Apache 2.0 License. Contact [support](https://help.circle.com/s/submit-ticket) for details.
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
<div align="center">
|
|
633
|
+
|
|
634
|
+
**Ready to start building?**
|
|
635
|
+
|
|
636
|
+
[Join Discord](https://discord.com/invite/buildoncircle)
|
|
637
|
+
|
|
638
|
+
_Built with ❤️ by Circle_
|
|
639
|
+
|
|
640
|
+
</div>
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@circle-fin/app-kit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=16.0.0"
|
|
7
|
+
},
|
|
8
|
+
"main": "./index.cjs",
|
|
9
|
+
"module": "./index.mjs",
|
|
10
|
+
"types": "./index.d.ts",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@core/chains": "0.3.0",
|
|
13
|
+
"@circle-fin/bridge-kit": "1.6.1",
|
|
14
|
+
"@circle-fin/swap-kit": "1.0.0",
|
|
15
|
+
"@core/provider": "0.0.9",
|
|
16
|
+
"@core/utils": "0.2.3",
|
|
17
|
+
"@core/adapter": "0.2.0",
|
|
18
|
+
"@core/errors": "0.2.3",
|
|
19
|
+
"@core/tokens": "0.0.4"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@core/adapter": "0.1.1",
|
|
23
|
+
"@core/chains": "0.2.0",
|
|
24
|
+
"@core/provider": "0.0.8",
|
|
25
|
+
"@core/utils": "0.2.2",
|
|
26
|
+
"@nx/vite": "21.1.2",
|
|
27
|
+
"vitest": "^3.0.0"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
"./package.json": "./package.json",
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./index.d.ts",
|
|
33
|
+
"import": "./index.mjs",
|
|
34
|
+
"require": "./index.cjs",
|
|
35
|
+
"default": "./index.cjs"
|
|
36
|
+
},
|
|
37
|
+
"./chains": {
|
|
38
|
+
"types": "./chains.d.ts",
|
|
39
|
+
"import": "./chains.mjs",
|
|
40
|
+
"require": "./chains.cjs",
|
|
41
|
+
"default": "./chains.cjs"
|
|
42
|
+
},
|
|
43
|
+
"./context": {
|
|
44
|
+
"types": "./kit/context/context.d.ts",
|
|
45
|
+
"import": "./kit/context/context.mjs",
|
|
46
|
+
"require": "./kit/context/context.cjs"
|
|
47
|
+
},
|
|
48
|
+
"./bridge": {
|
|
49
|
+
"types": "./kit/operations/bridge/bridge.d.ts",
|
|
50
|
+
"import": "./kit/operations/bridge/bridge.mjs",
|
|
51
|
+
"require": "./kit/operations/bridge/bridge.cjs"
|
|
52
|
+
},
|
|
53
|
+
"./estimateBridge": {
|
|
54
|
+
"types": "./kit/operations/estimateBridge/estimateBridge.d.ts",
|
|
55
|
+
"import": "./kit/operations/estimateBridge/estimateBridge.mjs",
|
|
56
|
+
"require": "./kit/operations/estimateBridge/estimateBridge.cjs"
|
|
57
|
+
},
|
|
58
|
+
"./swap": {
|
|
59
|
+
"types": "./kit/operations/swap/swap.d.ts",
|
|
60
|
+
"import": "./kit/operations/swap/swap.mjs",
|
|
61
|
+
"require": "./kit/operations/swap/swap.cjs"
|
|
62
|
+
},
|
|
63
|
+
"./estimateSwap": {
|
|
64
|
+
"types": "./kit/operations/estimateSwap/estimateSwap.d.ts",
|
|
65
|
+
"import": "./kit/operations/estimateSwap/estimateSwap.mjs",
|
|
66
|
+
"require": "./kit/operations/estimateSwap/estimateSwap.cjs"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"files": [
|
|
70
|
+
"chains.*",
|
|
71
|
+
"index.*",
|
|
72
|
+
"README.md",
|
|
73
|
+
"QUICKSTART.md",
|
|
74
|
+
"package.json",
|
|
75
|
+
"LICENSE"
|
|
76
|
+
]
|
|
77
|
+
}
|