@b3dotfun/sdk 0.0.40-alpha.4 → 0.0.40-alpha.6
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/dist/cjs/bondkit/bondkitToken.d.ts +36 -1
- package/dist/cjs/bondkit/bondkitToken.js +266 -0
- package/dist/cjs/bondkit/constants.d.ts +4 -0
- package/dist/cjs/bondkit/constants.js +6 -1
- package/dist/cjs/bondkit/index.d.ts +1 -0
- package/dist/cjs/bondkit/index.js +4 -1
- package/dist/cjs/bondkit/swapService.d.ts +43 -0
- package/dist/cjs/bondkit/swapService.js +373 -0
- package/dist/cjs/bondkit/types.d.ts +10 -4
- package/dist/cjs/bondkit/types.js +4 -5
- package/dist/cjs/global-account/react/components/LinkAccount/LinkAccount.js +63 -3
- package/dist/cjs/global-account/react/components/ManageAccount/ManageAccount.js +35 -2
- package/dist/esm/bondkit/bondkitToken.d.ts +36 -1
- package/dist/esm/bondkit/bondkitToken.js +266 -0
- package/dist/esm/bondkit/constants.d.ts +4 -0
- package/dist/esm/bondkit/constants.js +5 -0
- package/dist/esm/bondkit/index.d.ts +1 -0
- package/dist/esm/bondkit/index.js +2 -0
- package/dist/esm/bondkit/swapService.d.ts +43 -0
- package/dist/esm/bondkit/swapService.js +369 -0
- package/dist/esm/bondkit/types.d.ts +10 -4
- package/dist/esm/bondkit/types.js +4 -5
- package/dist/esm/global-account/react/components/LinkAccount/LinkAccount.js +65 -5
- package/dist/esm/global-account/react/components/ManageAccount/ManageAccount.js +35 -2
- package/dist/styles/index.css +1 -1
- package/dist/types/bondkit/bondkitToken.d.ts +36 -1
- package/dist/types/bondkit/constants.d.ts +4 -0
- package/dist/types/bondkit/index.d.ts +1 -0
- package/dist/types/bondkit/swapService.d.ts +43 -0
- package/dist/types/bondkit/types.d.ts +10 -4
- package/package.json +1 -1
- package/src/bondkit/bondkitToken.ts +321 -1
- package/src/bondkit/constants.ts +7 -0
- package/src/bondkit/index.ts +3 -0
- package/src/bondkit/swapService.ts +461 -0
- package/src/bondkit/types.ts +12 -5
- package/src/global-account/react/components/LinkAccount/LinkAccount.tsx +106 -32
- package/src/global-account/react/components/ManageAccount/ManageAccount.tsx +60 -5
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import type { Address, Hex, WalletClient, PublicClient } from "viem";
|
|
2
|
+
import {
|
|
3
|
+
parseUnits,
|
|
4
|
+
formatUnits,
|
|
5
|
+
encodeAbiParameters,
|
|
6
|
+
parseAbiParameters,
|
|
7
|
+
getContract,
|
|
8
|
+
createPublicClient,
|
|
9
|
+
http,
|
|
10
|
+
} from "viem";
|
|
11
|
+
import { base } from "viem/chains";
|
|
12
|
+
import { UniversalRouterAddress, QuoterAddress, Permit2Address, BaseMainnetRpcUrl } from "./constants";
|
|
13
|
+
import type { SwapQuote } from "./types";
|
|
14
|
+
|
|
15
|
+
// Minimal ABIs needed for swap functionality
|
|
16
|
+
const UNIVERSAL_ROUTER_ABI = [
|
|
17
|
+
{
|
|
18
|
+
inputs: [
|
|
19
|
+
{ name: "commands", type: "bytes" },
|
|
20
|
+
{ name: "inputs", type: "bytes[]" },
|
|
21
|
+
{ name: "deadline", type: "uint256" },
|
|
22
|
+
],
|
|
23
|
+
name: "execute",
|
|
24
|
+
outputs: [],
|
|
25
|
+
stateMutability: "payable",
|
|
26
|
+
type: "function",
|
|
27
|
+
},
|
|
28
|
+
] as const;
|
|
29
|
+
|
|
30
|
+
const QUOTER_ABI = [
|
|
31
|
+
{
|
|
32
|
+
inputs: [
|
|
33
|
+
{
|
|
34
|
+
components: [
|
|
35
|
+
{
|
|
36
|
+
components: [
|
|
37
|
+
{ internalType: "Currency", name: "currency0", type: "address" },
|
|
38
|
+
{ internalType: "Currency", name: "currency1", type: "address" },
|
|
39
|
+
{ internalType: "uint24", name: "fee", type: "uint24" },
|
|
40
|
+
{ internalType: "int24", name: "tickSpacing", type: "int24" },
|
|
41
|
+
{ internalType: "contract IHooks", name: "hooks", type: "address" },
|
|
42
|
+
],
|
|
43
|
+
internalType: "struct PoolKey",
|
|
44
|
+
name: "poolKey",
|
|
45
|
+
type: "tuple",
|
|
46
|
+
},
|
|
47
|
+
{ internalType: "bool", name: "zeroForOne", type: "bool" },
|
|
48
|
+
{ internalType: "uint128", name: "exactAmount", type: "uint128" },
|
|
49
|
+
{ internalType: "bytes", name: "hookData", type: "bytes" },
|
|
50
|
+
],
|
|
51
|
+
internalType: "struct IV4Quoter.QuoteExactSingleParams",
|
|
52
|
+
name: "params",
|
|
53
|
+
type: "tuple",
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
name: "quoteExactInputSingle",
|
|
57
|
+
outputs: [
|
|
58
|
+
{ internalType: "uint256", name: "amountOut", type: "uint256" },
|
|
59
|
+
{ internalType: "uint256", name: "gasEstimate", type: "uint256" },
|
|
60
|
+
],
|
|
61
|
+
stateMutability: "nonpayable",
|
|
62
|
+
type: "function",
|
|
63
|
+
},
|
|
64
|
+
] as const;
|
|
65
|
+
|
|
66
|
+
const ERC20_ABI = [
|
|
67
|
+
{
|
|
68
|
+
inputs: [
|
|
69
|
+
{ name: "spender", type: "address" },
|
|
70
|
+
{ name: "amount", type: "uint256" },
|
|
71
|
+
],
|
|
72
|
+
name: "approve",
|
|
73
|
+
outputs: [{ name: "", type: "bool" }],
|
|
74
|
+
stateMutability: "nonpayable",
|
|
75
|
+
type: "function",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
inputs: [
|
|
79
|
+
{ name: "owner", type: "address" },
|
|
80
|
+
{ name: "spender", type: "address" },
|
|
81
|
+
],
|
|
82
|
+
name: "allowance",
|
|
83
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
84
|
+
stateMutability: "view",
|
|
85
|
+
type: "function",
|
|
86
|
+
},
|
|
87
|
+
] as const;
|
|
88
|
+
|
|
89
|
+
const PERMIT2_ABI = [
|
|
90
|
+
{
|
|
91
|
+
inputs: [
|
|
92
|
+
{ name: "token", type: "address" },
|
|
93
|
+
{ name: "spender", type: "address" },
|
|
94
|
+
{ name: "amount", type: "uint160" },
|
|
95
|
+
{ name: "expiration", type: "uint48" },
|
|
96
|
+
],
|
|
97
|
+
name: "approve",
|
|
98
|
+
outputs: [],
|
|
99
|
+
stateMutability: "nonpayable",
|
|
100
|
+
type: "function",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
inputs: [
|
|
104
|
+
{ name: "owner", type: "address" },
|
|
105
|
+
{ name: "token", type: "address" },
|
|
106
|
+
{ name: "spender", type: "address" },
|
|
107
|
+
],
|
|
108
|
+
name: "allowance",
|
|
109
|
+
outputs: [
|
|
110
|
+
{ name: "amount", type: "uint160" },
|
|
111
|
+
{ name: "expiration", type: "uint48" },
|
|
112
|
+
{ name: "nonce", type: "uint48" },
|
|
113
|
+
],
|
|
114
|
+
stateMutability: "view",
|
|
115
|
+
type: "function",
|
|
116
|
+
},
|
|
117
|
+
] as const;
|
|
118
|
+
|
|
119
|
+
const TOKEN_V4_CONFIG_ABI = [
|
|
120
|
+
{
|
|
121
|
+
inputs: [],
|
|
122
|
+
name: "v4Hook",
|
|
123
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
124
|
+
stateMutability: "view",
|
|
125
|
+
type: "function",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
inputs: [],
|
|
129
|
+
name: "v4PoolFee",
|
|
130
|
+
outputs: [{ internalType: "uint24", name: "", type: "uint24" }],
|
|
131
|
+
stateMutability: "view",
|
|
132
|
+
type: "function",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
inputs: [],
|
|
136
|
+
name: "v4TickSpacing",
|
|
137
|
+
outputs: [{ internalType: "int24", name: "", type: "int24" }],
|
|
138
|
+
stateMutability: "view",
|
|
139
|
+
type: "function",
|
|
140
|
+
},
|
|
141
|
+
] as const;
|
|
142
|
+
|
|
143
|
+
// Command and action constants
|
|
144
|
+
const COMMANDS = {
|
|
145
|
+
V4_SWAP: "0x10",
|
|
146
|
+
} as const;
|
|
147
|
+
|
|
148
|
+
const V4_ACTIONS = {
|
|
149
|
+
SWAP_EXACT_IN_SINGLE: 6,
|
|
150
|
+
TAKE_ALL: 15,
|
|
151
|
+
SETTLE_ALL: 12,
|
|
152
|
+
} as const;
|
|
153
|
+
|
|
154
|
+
interface V4PoolConfig {
|
|
155
|
+
hook: string;
|
|
156
|
+
fee: number;
|
|
157
|
+
tickSpacing: number;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface SwapParams {
|
|
161
|
+
tokenIn: Address;
|
|
162
|
+
tokenOut: Address;
|
|
163
|
+
amountIn: string;
|
|
164
|
+
tokenInDecimals: number;
|
|
165
|
+
tokenOutDecimals: number;
|
|
166
|
+
slippageTolerance: number;
|
|
167
|
+
recipient: Address;
|
|
168
|
+
deadline?: number;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Internal swap service for handling Uniswap V4 swaps between trading token and bondkit token
|
|
173
|
+
*/
|
|
174
|
+
export class BondkitSwapService {
|
|
175
|
+
private v4Config: V4PoolConfig | null = null;
|
|
176
|
+
private configInitialized = false;
|
|
177
|
+
private readonly bondkitTokenAddress: Address;
|
|
178
|
+
private readonly publicClient: PublicClient;
|
|
179
|
+
|
|
180
|
+
constructor(bondkitTokenAddress: Address) {
|
|
181
|
+
this.bondkitTokenAddress = bondkitTokenAddress;
|
|
182
|
+
this.publicClient = createPublicClient({
|
|
183
|
+
chain: base,
|
|
184
|
+
transport: http(BaseMainnetRpcUrl),
|
|
185
|
+
}) as PublicClient;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Initialize V4 pool configuration from bondkit token contract
|
|
190
|
+
*/
|
|
191
|
+
private async initializeV4Config(): Promise<void> {
|
|
192
|
+
if (this.configInitialized) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const tokenContract = getContract({
|
|
198
|
+
address: this.bondkitTokenAddress,
|
|
199
|
+
abi: TOKEN_V4_CONFIG_ABI,
|
|
200
|
+
client: this.publicClient,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const [hook, fee, tickSpacing] = await Promise.all([
|
|
204
|
+
tokenContract.read.v4Hook(),
|
|
205
|
+
tokenContract.read.v4PoolFee(),
|
|
206
|
+
tokenContract.read.v4TickSpacing(),
|
|
207
|
+
]);
|
|
208
|
+
|
|
209
|
+
this.v4Config = {
|
|
210
|
+
hook: hook as string,
|
|
211
|
+
fee: Number(fee),
|
|
212
|
+
tickSpacing: Number(tickSpacing),
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
this.configInitialized = true;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.warn("Failed to initialize V4 configuration:", error);
|
|
218
|
+
// Use fallback configuration
|
|
219
|
+
this.v4Config = {
|
|
220
|
+
hook: "0xB36f4A2FB18b745ef8eD31452781a463d2B3f0cC",
|
|
221
|
+
fee: 30000,
|
|
222
|
+
tickSpacing: 60,
|
|
223
|
+
};
|
|
224
|
+
this.configInitialized = true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get V4 pool configuration
|
|
230
|
+
*/
|
|
231
|
+
private async getV4Config(): Promise<V4PoolConfig> {
|
|
232
|
+
await this.initializeV4Config();
|
|
233
|
+
return this.v4Config!;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Handle token approvals for swap
|
|
238
|
+
*/
|
|
239
|
+
private async handleTokenApprovals(
|
|
240
|
+
tokenAddress: Address,
|
|
241
|
+
amountIn: string,
|
|
242
|
+
walletClient: WalletClient,
|
|
243
|
+
deadline: number,
|
|
244
|
+
): Promise<void> {
|
|
245
|
+
// Skip approvals for ETH
|
|
246
|
+
if (tokenAddress === "0x0000000000000000000000000000000000000000") {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const userAddress = walletClient.account?.address;
|
|
251
|
+
if (!userAddress) {
|
|
252
|
+
throw new Error("No user address found");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const erc20Contract = getContract({
|
|
256
|
+
address: tokenAddress,
|
|
257
|
+
abi: ERC20_ABI,
|
|
258
|
+
client: walletClient,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const permit2Contract = getContract({
|
|
262
|
+
address: Permit2Address,
|
|
263
|
+
abi: PERMIT2_ABI,
|
|
264
|
+
client: walletClient,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Check ERC20 allowance to Permit2
|
|
268
|
+
const currentAllowance = (await erc20Contract.read.allowance([userAddress, Permit2Address])) as bigint;
|
|
269
|
+
|
|
270
|
+
const requiredAmount = BigInt(amountIn);
|
|
271
|
+
|
|
272
|
+
if (currentAllowance < requiredAmount) {
|
|
273
|
+
await erc20Contract.write.approve(
|
|
274
|
+
[Permit2Address, BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")],
|
|
275
|
+
{
|
|
276
|
+
account: userAddress,
|
|
277
|
+
chain: base,
|
|
278
|
+
},
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Check Permit2 allowance for Universal Router
|
|
283
|
+
const permit2Allowance = (await permit2Contract.read.allowance([
|
|
284
|
+
userAddress,
|
|
285
|
+
tokenAddress,
|
|
286
|
+
UniversalRouterAddress,
|
|
287
|
+
])) as [bigint, number, number];
|
|
288
|
+
|
|
289
|
+
const [currentPermit2Amount, expiration] = permit2Allowance;
|
|
290
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
291
|
+
const isExpired = expiration <= currentTime;
|
|
292
|
+
|
|
293
|
+
if (currentPermit2Amount < requiredAmount || isExpired) {
|
|
294
|
+
await permit2Contract.write.approve(
|
|
295
|
+
[tokenAddress, UniversalRouterAddress, BigInt("0xffffffffffffffffffffffffffffffffffffff"), Number(deadline)],
|
|
296
|
+
{
|
|
297
|
+
account: userAddress,
|
|
298
|
+
chain: base,
|
|
299
|
+
},
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Get swap quote
|
|
306
|
+
*/
|
|
307
|
+
async getSwapQuote(params: SwapParams): Promise<SwapQuote | null> {
|
|
308
|
+
try {
|
|
309
|
+
const { tokenIn, tokenOut, amountIn, tokenInDecimals, tokenOutDecimals, slippageTolerance } = params;
|
|
310
|
+
|
|
311
|
+
const v4Config = await this.getV4Config();
|
|
312
|
+
const amountInWei = parseUnits(amountIn, tokenInDecimals);
|
|
313
|
+
|
|
314
|
+
// Determine token order for pool
|
|
315
|
+
const currency0 = tokenIn.toLowerCase() < tokenOut.toLowerCase() ? tokenIn : tokenOut;
|
|
316
|
+
const currency1 = tokenIn.toLowerCase() < tokenOut.toLowerCase() ? tokenOut : tokenIn;
|
|
317
|
+
const zeroForOne = tokenIn.toLowerCase() === currency0.toLowerCase();
|
|
318
|
+
|
|
319
|
+
const poolKey = {
|
|
320
|
+
currency0: currency0 as Address,
|
|
321
|
+
currency1: currency1 as Address,
|
|
322
|
+
fee: v4Config.fee,
|
|
323
|
+
tickSpacing: v4Config.tickSpacing,
|
|
324
|
+
hooks: v4Config.hook as Address,
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const quoteParams = {
|
|
328
|
+
poolKey,
|
|
329
|
+
zeroForOne,
|
|
330
|
+
exactAmount: BigInt(amountInWei.toString()),
|
|
331
|
+
hookData: "0x" as Hex,
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
const { result } = await this.publicClient.simulateContract({
|
|
335
|
+
address: QuoterAddress,
|
|
336
|
+
abi: QUOTER_ABI,
|
|
337
|
+
functionName: "quoteExactInputSingle",
|
|
338
|
+
args: [quoteParams],
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const [amountOut] = result;
|
|
342
|
+
const amountOutRaw = formatUnits(amountOut, tokenOutDecimals);
|
|
343
|
+
const amountOutFormatted = parseFloat(amountOutRaw).toFixed(Math.min(6, tokenOutDecimals));
|
|
344
|
+
|
|
345
|
+
// Calculate minimum amount out with slippage
|
|
346
|
+
const slippageMultiplier = (100 - slippageTolerance) / 100;
|
|
347
|
+
const amountOutMinRaw = parseFloat(amountOutFormatted) * slippageMultiplier;
|
|
348
|
+
const amountOutMin = amountOutMinRaw.toFixed(tokenOutDecimals);
|
|
349
|
+
|
|
350
|
+
// Simple execution price calculation
|
|
351
|
+
const rate = parseFloat(amountOutFormatted) / parseFloat(amountIn);
|
|
352
|
+
const executionPrice = `1 = ${rate.toFixed(6)}`;
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
amountOut: amountOutFormatted,
|
|
356
|
+
amountOutMin,
|
|
357
|
+
priceImpact: "0.0", // Simplified
|
|
358
|
+
executionPrice,
|
|
359
|
+
fee: (v4Config.fee / 10000).toString(),
|
|
360
|
+
};
|
|
361
|
+
} catch (error) {
|
|
362
|
+
console.warn("Error getting swap quote:", error);
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Execute swap transaction
|
|
369
|
+
*/
|
|
370
|
+
async executeSwap(params: SwapParams, walletClient: WalletClient): Promise<string | null> {
|
|
371
|
+
try {
|
|
372
|
+
const { tokenIn, tokenOut, amountIn, tokenInDecimals, tokenOutDecimals, deadline } = params;
|
|
373
|
+
|
|
374
|
+
const swapDeadline = deadline || Math.floor(Date.now() / 1000) + 3600;
|
|
375
|
+
|
|
376
|
+
if (!walletClient.account) {
|
|
377
|
+
throw new Error("Wallet client must have an account");
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const amountInWei = parseUnits(amountIn, tokenInDecimals);
|
|
381
|
+
|
|
382
|
+
// Handle token approvals
|
|
383
|
+
await this.handleTokenApprovals(tokenIn, amountInWei.toString(), walletClient, swapDeadline);
|
|
384
|
+
|
|
385
|
+
// Get quote for minimum amount out
|
|
386
|
+
const quote = await this.getSwapQuote(params);
|
|
387
|
+
if (!quote) {
|
|
388
|
+
throw new Error("Unable to get swap quote");
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const amountOutMinimum = parseUnits(quote.amountOutMin, tokenOutDecimals);
|
|
392
|
+
const v4Config = await this.getV4Config();
|
|
393
|
+
|
|
394
|
+
// Determine token order
|
|
395
|
+
const currency0 = tokenIn.toLowerCase() < tokenOut.toLowerCase() ? tokenIn : tokenOut;
|
|
396
|
+
const currency1 = tokenIn.toLowerCase() < tokenOut.toLowerCase() ? tokenOut : tokenIn;
|
|
397
|
+
const zeroForOne = tokenIn.toLowerCase() === currency0.toLowerCase();
|
|
398
|
+
|
|
399
|
+
const poolKey = [currency0, currency1, v4Config.fee, v4Config.tickSpacing, v4Config.hook];
|
|
400
|
+
|
|
401
|
+
// Encode V4 actions
|
|
402
|
+
const actions = [
|
|
403
|
+
{
|
|
404
|
+
type: V4_ACTIONS.SWAP_EXACT_IN_SINGLE,
|
|
405
|
+
params: [poolKey, zeroForOne, amountInWei, amountOutMinimum, "0x" as `0x${string}`],
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
type: V4_ACTIONS.TAKE_ALL,
|
|
409
|
+
params: [(zeroForOne ? currency1 : currency0) as Address, BigInt(0)],
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
type: V4_ACTIONS.SETTLE_ALL,
|
|
413
|
+
params: [(zeroForOne ? currency0 : currency1) as Address, amountInWei],
|
|
414
|
+
},
|
|
415
|
+
];
|
|
416
|
+
|
|
417
|
+
// Encode actions
|
|
418
|
+
const actionTypes = actions.map(action => action.type);
|
|
419
|
+
const actionsBytes = ("0x" + actionTypes.map(type => type.toString(16).padStart(2, "0")).join("")) as Hex;
|
|
420
|
+
|
|
421
|
+
const actionParams = actions.map(action => {
|
|
422
|
+
switch (action.type) {
|
|
423
|
+
case V4_ACTIONS.SWAP_EXACT_IN_SINGLE:
|
|
424
|
+
return encodeAbiParameters(
|
|
425
|
+
parseAbiParameters("((address,address,uint24,int24,address),bool,uint128,uint128,bytes)"),
|
|
426
|
+
[action.params as any],
|
|
427
|
+
);
|
|
428
|
+
case V4_ACTIONS.TAKE_ALL:
|
|
429
|
+
return encodeAbiParameters(parseAbiParameters("address,uint256"), action.params as [Address, bigint]);
|
|
430
|
+
case V4_ACTIONS.SETTLE_ALL:
|
|
431
|
+
return encodeAbiParameters(parseAbiParameters("address,uint256"), action.params as [Address, bigint]);
|
|
432
|
+
default:
|
|
433
|
+
return "0x00" as Hex;
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
const v4SwapInput = encodeAbiParameters(parseAbiParameters("bytes,bytes[]"), [actionsBytes, actionParams]);
|
|
438
|
+
|
|
439
|
+
const commands = COMMANDS.V4_SWAP;
|
|
440
|
+
const inputs = [v4SwapInput];
|
|
441
|
+
|
|
442
|
+
// Execute swap
|
|
443
|
+
const universalRouter = getContract({
|
|
444
|
+
address: UniversalRouterAddress,
|
|
445
|
+
abi: UNIVERSAL_ROUTER_ABI,
|
|
446
|
+
client: walletClient,
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const txHash = await universalRouter.write.execute([commands, inputs, BigInt(swapDeadline)], {
|
|
450
|
+
account: walletClient.account,
|
|
451
|
+
chain: base,
|
|
452
|
+
value: tokenIn === "0x0000000000000000000000000000000000000000" ? amountInWei : BigInt(0),
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
return txHash;
|
|
456
|
+
} catch (error) {
|
|
457
|
+
console.warn("Error executing swap:", error);
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
package/src/bondkit/types.ts
CHANGED
|
@@ -64,12 +64,11 @@ export type DexMigrationEventArgs = {
|
|
|
64
64
|
ethForFeeRecipient: bigint;
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
// Enum for Status (
|
|
67
|
+
// Enum for Status (matches contract Status enum exactly)
|
|
68
68
|
export enum TokenStatus {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
Migrated = 3,
|
|
69
|
+
Uninitialized = 0,
|
|
70
|
+
Bonding = 1,
|
|
71
|
+
Dex = 2,
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
export interface GetTransactionHistoryOptions {
|
|
@@ -101,3 +100,11 @@ export interface TransactionResponse {
|
|
|
101
100
|
skip: number;
|
|
102
101
|
data: Transaction[];
|
|
103
102
|
}
|
|
103
|
+
|
|
104
|
+
export interface SwapQuote {
|
|
105
|
+
amountOut: string;
|
|
106
|
+
amountOutMin: string;
|
|
107
|
+
priceImpact: string;
|
|
108
|
+
executionPrice: string;
|
|
109
|
+
fee: string;
|
|
110
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import app from "@b3dotfun/sdk/global-account/app";
|
|
2
2
|
import { ecosystemWalletId } from "@b3dotfun/sdk/shared/constants";
|
|
3
|
+
import { thirdwebB3Mainnet } from "@b3dotfun/sdk/shared/constants/chains/b3Chain";
|
|
3
4
|
import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
|
|
4
|
-
import { Loader2, Mail, Phone } from "lucide-react";
|
|
5
|
+
import { Loader2, Mail, Phone, WalletIcon } from "lucide-react";
|
|
5
6
|
import { useCallback, useEffect, useState } from "react";
|
|
6
7
|
import { toast } from "sonner";
|
|
7
8
|
import { useLinkProfile, useProfiles } from "thirdweb/react";
|
|
8
|
-
import { preAuthenticate } from "thirdweb/wallets";
|
|
9
|
+
import { createWallet, preAuthenticate, WalletId } from "thirdweb/wallets";
|
|
10
|
+
import { WalletRow } from "../..";
|
|
9
11
|
import { LinkAccountModalProps, useModalStore } from "../../stores/useModalStore";
|
|
10
12
|
import { getProfileDisplayInfo } from "../../utils/profileDisplay";
|
|
11
13
|
import { useB3 } from "../B3Provider/useB3";
|
|
@@ -17,13 +19,14 @@ import { XIcon } from "../icons/XIcon";
|
|
|
17
19
|
import { Button } from "../ui/button";
|
|
18
20
|
type OTPStrategy = "email" | "phone";
|
|
19
21
|
type SocialStrategy = "google" | "x" | "discord" | "apple" | "farcaster";
|
|
20
|
-
type Strategy = OTPStrategy | SocialStrategy;
|
|
22
|
+
type Strategy = OTPStrategy | SocialStrategy | "wallet";
|
|
21
23
|
|
|
22
24
|
interface AuthMethod {
|
|
23
25
|
id: Strategy;
|
|
24
26
|
label: string;
|
|
25
27
|
enabled: boolean;
|
|
26
28
|
icon: React.ReactNode;
|
|
29
|
+
walletType?: WalletId;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
const AUTH_METHODS: AuthMethod[] = [
|
|
@@ -41,6 +44,39 @@ const AUTH_METHODS: AuthMethod[] = [
|
|
|
41
44
|
},
|
|
42
45
|
];
|
|
43
46
|
|
|
47
|
+
const WALLET_METHODS: AuthMethod[] = [
|
|
48
|
+
{
|
|
49
|
+
id: "wallet",
|
|
50
|
+
label: "Wallet",
|
|
51
|
+
enabled: true,
|
|
52
|
+
icon: <WalletIcon className="size-6" />,
|
|
53
|
+
walletType: "com.coinbase.wallet",
|
|
54
|
+
},
|
|
55
|
+
{ id: "wallet", label: "Wallet", enabled: true, icon: <WalletIcon className="size-6" />, walletType: "io.metamask" },
|
|
56
|
+
{
|
|
57
|
+
id: "wallet",
|
|
58
|
+
label: "Wallet",
|
|
59
|
+
enabled: true,
|
|
60
|
+
icon: <WalletIcon className="size-6" />,
|
|
61
|
+
walletType: "me.rainbow",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "wallet",
|
|
65
|
+
label: "Wallet",
|
|
66
|
+
enabled: true,
|
|
67
|
+
icon: <WalletIcon className="size-6" />,
|
|
68
|
+
walletType: "app.phantom",
|
|
69
|
+
},
|
|
70
|
+
{ id: "wallet", label: "Wallet", enabled: true, icon: <WalletIcon className="size-6" />, walletType: "io.rabby" },
|
|
71
|
+
{
|
|
72
|
+
id: "wallet",
|
|
73
|
+
label: "Wallet",
|
|
74
|
+
enabled: true,
|
|
75
|
+
icon: <WalletIcon className="size-6" />,
|
|
76
|
+
walletType: "walletConnect",
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
44
80
|
export function LinkAccount({
|
|
45
81
|
onSuccess: onSuccessCallback,
|
|
46
82
|
onError,
|
|
@@ -60,7 +96,7 @@ export function LinkAccount({
|
|
|
60
96
|
|
|
61
97
|
// Get connected auth methods
|
|
62
98
|
const connectedAuthMethods = profilesRaw
|
|
63
|
-
.filter((profile: any) => !["custom_auth_endpoint"
|
|
99
|
+
.filter((profile: any) => !["custom_auth_endpoint"].includes(profile.type))
|
|
64
100
|
.map((profile: any) => profile.type as Strategy);
|
|
65
101
|
|
|
66
102
|
// Filter available auth methods
|
|
@@ -69,7 +105,7 @@ export function LinkAccount({
|
|
|
69
105
|
);
|
|
70
106
|
|
|
71
107
|
const profiles = profilesRaw
|
|
72
|
-
.filter((profile: any) => !["custom_auth_endpoint"
|
|
108
|
+
.filter((profile: any) => !["custom_auth_endpoint"].includes(profile.type))
|
|
73
109
|
.map((profile: any) => ({
|
|
74
110
|
...getProfileDisplayInfo(profile),
|
|
75
111
|
originalProfile: profile,
|
|
@@ -211,6 +247,29 @@ export function LinkAccount({
|
|
|
211
247
|
}
|
|
212
248
|
};
|
|
213
249
|
|
|
250
|
+
const handleLinkWallet = async (walletType: WalletId) => {
|
|
251
|
+
setLinkingState(true, "wallet");
|
|
252
|
+
console.log("selectedMethod", walletType);
|
|
253
|
+
try {
|
|
254
|
+
if (!walletType) {
|
|
255
|
+
throw new Error("Wallet type not found");
|
|
256
|
+
}
|
|
257
|
+
await linkProfile(
|
|
258
|
+
{
|
|
259
|
+
client,
|
|
260
|
+
strategy: "wallet",
|
|
261
|
+
wallet: createWallet(walletType),
|
|
262
|
+
chain: thirdwebB3Mainnet,
|
|
263
|
+
},
|
|
264
|
+
mutationOptions,
|
|
265
|
+
);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error("Error linking account:", error);
|
|
268
|
+
setError(error instanceof Error ? error.message : "Failed to link account");
|
|
269
|
+
onError?.(error as Error);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
214
273
|
const handleSocialLink = async (strategy: SocialStrategy) => {
|
|
215
274
|
try {
|
|
216
275
|
console.log("handleSocialLink", strategy);
|
|
@@ -338,6 +397,20 @@ export function LinkAccount({
|
|
|
338
397
|
)}
|
|
339
398
|
</Button>
|
|
340
399
|
))}
|
|
400
|
+
{WALLET_METHODS.map(method => {
|
|
401
|
+
if (!method.walletType) {
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return (
|
|
406
|
+
<WalletRow
|
|
407
|
+
key={method.walletType}
|
|
408
|
+
walletId={method.walletType as WalletId}
|
|
409
|
+
onClick={() => handleLinkWallet(method.walletType as WalletId)}
|
|
410
|
+
isLoading={isLinking}
|
|
411
|
+
/>
|
|
412
|
+
);
|
|
413
|
+
})}
|
|
341
414
|
{availableAuthMethods.length === 0 && (
|
|
342
415
|
<div className="text-b3-foreground-muted py-8 text-center">
|
|
343
416
|
All available authentication methods have been connected
|
|
@@ -379,38 +452,39 @@ export function LinkAccount({
|
|
|
379
452
|
|
|
380
453
|
{error && <div className="text-b3-negative font-neue-montreal-medium py-2 text-sm">{error}</div>}
|
|
381
454
|
|
|
382
|
-
{
|
|
383
|
-
|
|
384
|
-
<div className="space-y-
|
|
385
|
-
<
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
455
|
+
{(selectedMethod === "email" || selectedMethod === "phone") &&
|
|
456
|
+
(otpSent ? (
|
|
457
|
+
<div className="space-y-4">
|
|
458
|
+
<div className="space-y-2">
|
|
459
|
+
<label className="text-b3-grey font-neue-montreal-medium text-sm">Verification Code</label>
|
|
460
|
+
<input
|
|
461
|
+
type="text"
|
|
462
|
+
placeholder="Enter verification code"
|
|
463
|
+
className="bg-b3-line text-b3-grey font-neue-montreal-medium focus:ring-b3-primary-blue/20 w-full rounded-xl p-4 focus:outline-none focus:ring-2"
|
|
464
|
+
value={otp}
|
|
465
|
+
onChange={e => setOtp(e.target.value)}
|
|
466
|
+
/>
|
|
467
|
+
</div>
|
|
468
|
+
<Button
|
|
469
|
+
className="bg-b3-primary-blue hover:bg-b3-primary-blue/90 font-neue-montreal-semibold h-12 w-full text-white"
|
|
470
|
+
onClick={handleLinkAccount}
|
|
471
|
+
>
|
|
472
|
+
Link Account
|
|
473
|
+
</Button>
|
|
393
474
|
</div>
|
|
475
|
+
) : (
|
|
394
476
|
<Button
|
|
395
477
|
className="bg-b3-primary-blue hover:bg-b3-primary-blue/90 font-neue-montreal-semibold h-12 w-full text-white"
|
|
396
|
-
onClick={
|
|
478
|
+
onClick={handleSendOTP}
|
|
479
|
+
disabled={(!email && !phone) || (isLinking && linkingMethod === selectedMethod)}
|
|
397
480
|
>
|
|
398
|
-
|
|
481
|
+
{isLinking && linkingMethod === selectedMethod ? (
|
|
482
|
+
<Loader2 className="animate-spin" />
|
|
483
|
+
) : (
|
|
484
|
+
"Send Verification Code"
|
|
485
|
+
)}
|
|
399
486
|
</Button>
|
|
400
|
-
|
|
401
|
-
) : (
|
|
402
|
-
<Button
|
|
403
|
-
className="bg-b3-primary-blue hover:bg-b3-primary-blue/90 font-neue-montreal-semibold h-12 w-full text-white"
|
|
404
|
-
onClick={handleSendOTP}
|
|
405
|
-
disabled={(!email && !phone) || (isLinking && linkingMethod === selectedMethod)}
|
|
406
|
-
>
|
|
407
|
-
{isLinking && linkingMethod === selectedMethod ? (
|
|
408
|
-
<Loader2 className="animate-spin" />
|
|
409
|
-
) : (
|
|
410
|
-
"Send Verification Code"
|
|
411
|
-
)}
|
|
412
|
-
</Button>
|
|
413
|
-
)}
|
|
487
|
+
))}
|
|
414
488
|
</div>
|
|
415
489
|
)}
|
|
416
490
|
</div>
|