@hongming-wang/usdc-bridge-widget 0.1.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 +272 -0
- package/dist/chunk-6JW37N76.mjs +211 -0
- package/dist/chunk-GJBJYQCU.mjs +218 -0
- package/dist/chunk-JHG7XCWW.mjs +218 -0
- package/dist/index.d.mts +765 -0
- package/dist/index.d.ts +765 -0
- package/dist/index.js +2356 -0
- package/dist/index.mjs +2295 -0
- package/dist/useBridge-LDEXWLEC.mjs +10 -0
- package/dist/useBridge-VGN5DMO6.mjs +10 -0
- package/dist/useBridge-WJA4XLLR.mjs +10 -0
- package/package.json +63 -0
- package/src/BridgeWidget.tsx +1133 -0
- package/src/__tests__/BridgeWidget.test.tsx +310 -0
- package/src/__tests__/chains.test.ts +131 -0
- package/src/__tests__/constants.test.ts +77 -0
- package/src/__tests__/hooks.test.ts +127 -0
- package/src/__tests__/icons.test.tsx +159 -0
- package/src/__tests__/setup.ts +8 -0
- package/src/__tests__/theme.test.ts +148 -0
- package/src/__tests__/useBridge.test.ts +133 -0
- package/src/__tests__/utils.test.ts +255 -0
- package/src/chains.ts +209 -0
- package/src/constants.ts +97 -0
- package/src/hooks.ts +349 -0
- package/src/icons.tsx +228 -0
- package/src/index.tsx +111 -0
- package/src/theme.ts +131 -0
- package/src/types.ts +160 -0
- package/src/useBridge.ts +424 -0
- package/src/utils.ts +239 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// src/useBridge.ts
|
|
2
|
+
import { useState, useCallback, useEffect, useRef } from "react";
|
|
3
|
+
import { useConnectorClient } from "wagmi";
|
|
4
|
+
var CHAIN_NAME_MAP = {
|
|
5
|
+
1: "Ethereum",
|
|
6
|
+
42161: "Arbitrum",
|
|
7
|
+
43114: "Avalanche",
|
|
8
|
+
8453: "Base",
|
|
9
|
+
10: "OP_Mainnet",
|
|
10
|
+
137: "Polygon",
|
|
11
|
+
59144: "Linea",
|
|
12
|
+
130: "Unichain",
|
|
13
|
+
146: "Sonic",
|
|
14
|
+
480: "World_Chain",
|
|
15
|
+
143: "Monad",
|
|
16
|
+
1329: "Sei",
|
|
17
|
+
50: "XDC",
|
|
18
|
+
999: "HyperEVM",
|
|
19
|
+
57073: "Ink",
|
|
20
|
+
98866: "Plume",
|
|
21
|
+
81224: "Codex"
|
|
22
|
+
};
|
|
23
|
+
function getChainName(chainId) {
|
|
24
|
+
return CHAIN_NAME_MAP[chainId] || `Chain_${chainId}`;
|
|
25
|
+
}
|
|
26
|
+
function useBridge() {
|
|
27
|
+
const { data: connectorClient } = useConnectorClient();
|
|
28
|
+
const [state, setState] = useState({
|
|
29
|
+
status: "idle",
|
|
30
|
+
events: []
|
|
31
|
+
});
|
|
32
|
+
const isMountedRef = useRef(true);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
isMountedRef.current = true;
|
|
35
|
+
return () => {
|
|
36
|
+
isMountedRef.current = false;
|
|
37
|
+
};
|
|
38
|
+
}, []);
|
|
39
|
+
const addEvent = useCallback((type, data) => {
|
|
40
|
+
if (!isMountedRef.current) return;
|
|
41
|
+
setState((prev) => ({
|
|
42
|
+
...prev,
|
|
43
|
+
events: [...prev.events, { type, timestamp: Date.now(), data }]
|
|
44
|
+
}));
|
|
45
|
+
}, []);
|
|
46
|
+
const reset = useCallback(() => {
|
|
47
|
+
setState({ status: "idle", events: [] });
|
|
48
|
+
}, []);
|
|
49
|
+
const bridge = useCallback(
|
|
50
|
+
async (params) => {
|
|
51
|
+
const { sourceChainConfig, destChainConfig, amount, recipientAddress } = params;
|
|
52
|
+
if (!connectorClient) {
|
|
53
|
+
setState({
|
|
54
|
+
status: "error",
|
|
55
|
+
error: new Error("Wallet not connected"),
|
|
56
|
+
events: []
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
setState({ status: "loading", events: [] });
|
|
61
|
+
addEvent("start", { amount, sourceChain: sourceChainConfig.chain.id, destChain: destChainConfig.chain.id });
|
|
62
|
+
try {
|
|
63
|
+
let BridgeKit;
|
|
64
|
+
let createAdapterFromProvider;
|
|
65
|
+
try {
|
|
66
|
+
const bridgingKitPath = ["@circle-fin", "bridge-kit"].join("/");
|
|
67
|
+
const adapterPath = ["@circle-fin", "adapter-viem-v2"].join("/");
|
|
68
|
+
const dynamicImport = Function("m", "return import(m)");
|
|
69
|
+
const [bridgingKitModule, adapterModule] = await Promise.all([
|
|
70
|
+
dynamicImport(bridgingKitPath),
|
|
71
|
+
dynamicImport(adapterPath)
|
|
72
|
+
]);
|
|
73
|
+
BridgeKit = bridgingKitModule.BridgeKit;
|
|
74
|
+
createAdapterFromProvider = adapterModule.createAdapterFromProvider;
|
|
75
|
+
} catch {
|
|
76
|
+
throw new Error(
|
|
77
|
+
"Circle Bridge Kit packages not installed. Run: npm install @circle-fin/bridge-kit @circle-fin/adapter-viem-v2"
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
const clientWithTransport = connectorClient;
|
|
81
|
+
const provider = clientWithTransport?.transport?.value?.provider;
|
|
82
|
+
if (!provider) {
|
|
83
|
+
throw new Error("Could not get wallet provider from connector");
|
|
84
|
+
}
|
|
85
|
+
const adapter = await createAdapterFromProvider({ provider });
|
|
86
|
+
const kit = new BridgeKit();
|
|
87
|
+
kit.on("approve", (event) => {
|
|
88
|
+
addEvent("approve", event);
|
|
89
|
+
if (isMountedRef.current) {
|
|
90
|
+
setState((prev) => ({
|
|
91
|
+
...prev,
|
|
92
|
+
status: "approving",
|
|
93
|
+
txHash: event.values?.txHash
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
kit.on("burn", (event) => {
|
|
98
|
+
addEvent("burn", event);
|
|
99
|
+
if (isMountedRef.current) {
|
|
100
|
+
setState((prev) => ({
|
|
101
|
+
...prev,
|
|
102
|
+
status: "burning",
|
|
103
|
+
txHash: event.values?.txHash
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
kit.on("mint", (event) => {
|
|
108
|
+
addEvent("mint", event);
|
|
109
|
+
if (isMountedRef.current) {
|
|
110
|
+
setState((prev) => ({
|
|
111
|
+
...prev,
|
|
112
|
+
status: "minting",
|
|
113
|
+
txHash: event.values?.txHash
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
const sourceChainName = getChainName(sourceChainConfig.chain.id);
|
|
118
|
+
const destChainName = getChainName(destChainConfig.chain.id);
|
|
119
|
+
const bridgeParams = {
|
|
120
|
+
from: { adapter, chain: sourceChainName },
|
|
121
|
+
to: { adapter, chain: destChainName },
|
|
122
|
+
amount
|
|
123
|
+
};
|
|
124
|
+
if (recipientAddress) {
|
|
125
|
+
bridgeParams.recipientAddress = recipientAddress;
|
|
126
|
+
}
|
|
127
|
+
const result = await kit.bridge(bridgeParams);
|
|
128
|
+
addEvent("complete", result);
|
|
129
|
+
if (isMountedRef.current) {
|
|
130
|
+
setState((prev) => ({
|
|
131
|
+
...prev,
|
|
132
|
+
status: "success",
|
|
133
|
+
txHash: result?.txHash
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
addEvent("error", error);
|
|
138
|
+
if (isMountedRef.current) {
|
|
139
|
+
setState((prev) => ({
|
|
140
|
+
...prev,
|
|
141
|
+
status: "error",
|
|
142
|
+
error: error instanceof Error ? error : new Error("Bridge transfer failed")
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
[connectorClient, addEvent]
|
|
149
|
+
);
|
|
150
|
+
return { bridge, state, reset };
|
|
151
|
+
}
|
|
152
|
+
function useBridgeQuote(sourceChainId, destChainId, amount) {
|
|
153
|
+
const [quote, setQuote] = useState(null);
|
|
154
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
155
|
+
const [error, setError] = useState(null);
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
if (!sourceChainId || !destChainId || !amount || parseFloat(amount) <= 0) {
|
|
158
|
+
setQuote(null);
|
|
159
|
+
setError(null);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const fetchQuote = async () => {
|
|
163
|
+
setIsLoading(true);
|
|
164
|
+
setError(null);
|
|
165
|
+
try {
|
|
166
|
+
let BridgeKit;
|
|
167
|
+
try {
|
|
168
|
+
const modulePath = ["@circle-fin", "bridge-kit"].join("/");
|
|
169
|
+
const dynamicImport = Function("m", "return import(m)");
|
|
170
|
+
const bridgingKitModule = await dynamicImport(modulePath);
|
|
171
|
+
BridgeKit = bridgingKitModule.BridgeKit;
|
|
172
|
+
} catch {
|
|
173
|
+
setError(new Error("Bridge Kit not installed. Run: npm install @circle-fin/bridge-kit"));
|
|
174
|
+
setQuote(null);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const kit = new BridgeKit();
|
|
178
|
+
const sourceChainName = getChainName(sourceChainId);
|
|
179
|
+
const destChainName = getChainName(destChainId);
|
|
180
|
+
if (typeof kit.getQuote === "function") {
|
|
181
|
+
const result = await kit.getQuote({
|
|
182
|
+
from: { chain: sourceChainName },
|
|
183
|
+
to: { chain: destChainName },
|
|
184
|
+
amount
|
|
185
|
+
});
|
|
186
|
+
setQuote({
|
|
187
|
+
estimatedGasFee: result.estimatedGasFee || "0.00",
|
|
188
|
+
bridgeFee: result.bridgeFee || "0.00",
|
|
189
|
+
totalFee: result.totalFee || result.estimatedGasFee || "0.00",
|
|
190
|
+
estimatedTime: result.estimatedTime || "~15-20 minutes",
|
|
191
|
+
expiresAt: result.expiresAt
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
setQuote({
|
|
195
|
+
estimatedGasFee: "Estimated by wallet",
|
|
196
|
+
bridgeFee: "0.00",
|
|
197
|
+
totalFee: "Gas only",
|
|
198
|
+
estimatedTime: "~15-20 minutes"
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
} catch (err) {
|
|
202
|
+
setError(err instanceof Error ? err : new Error("Failed to get quote"));
|
|
203
|
+
setQuote(null);
|
|
204
|
+
} finally {
|
|
205
|
+
setIsLoading(false);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
const debounceTimer = setTimeout(fetchQuote, 500);
|
|
209
|
+
return () => clearTimeout(debounceTimer);
|
|
210
|
+
}, [sourceChainId, destChainId, amount]);
|
|
211
|
+
return { quote, isLoading, error };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export {
|
|
215
|
+
getChainName,
|
|
216
|
+
useBridge,
|
|
217
|
+
useBridgeQuote
|
|
218
|
+
};
|