@b3dotfun/sdk 0.0.40-alpha.5 → 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.
@@ -0,0 +1,369 @@
1
+ import { parseUnits, formatUnits, encodeAbiParameters, parseAbiParameters, getContract, createPublicClient, http, } from "viem";
2
+ import { base } from "viem/chains";
3
+ import { UniversalRouterAddress, QuoterAddress, Permit2Address, BaseMainnetRpcUrl } from "./constants.js";
4
+ // Minimal ABIs needed for swap functionality
5
+ const UNIVERSAL_ROUTER_ABI = [
6
+ {
7
+ inputs: [
8
+ { name: "commands", type: "bytes" },
9
+ { name: "inputs", type: "bytes[]" },
10
+ { name: "deadline", type: "uint256" },
11
+ ],
12
+ name: "execute",
13
+ outputs: [],
14
+ stateMutability: "payable",
15
+ type: "function",
16
+ },
17
+ ];
18
+ const QUOTER_ABI = [
19
+ {
20
+ inputs: [
21
+ {
22
+ components: [
23
+ {
24
+ components: [
25
+ { internalType: "Currency", name: "currency0", type: "address" },
26
+ { internalType: "Currency", name: "currency1", type: "address" },
27
+ { internalType: "uint24", name: "fee", type: "uint24" },
28
+ { internalType: "int24", name: "tickSpacing", type: "int24" },
29
+ { internalType: "contract IHooks", name: "hooks", type: "address" },
30
+ ],
31
+ internalType: "struct PoolKey",
32
+ name: "poolKey",
33
+ type: "tuple",
34
+ },
35
+ { internalType: "bool", name: "zeroForOne", type: "bool" },
36
+ { internalType: "uint128", name: "exactAmount", type: "uint128" },
37
+ { internalType: "bytes", name: "hookData", type: "bytes" },
38
+ ],
39
+ internalType: "struct IV4Quoter.QuoteExactSingleParams",
40
+ name: "params",
41
+ type: "tuple",
42
+ },
43
+ ],
44
+ name: "quoteExactInputSingle",
45
+ outputs: [
46
+ { internalType: "uint256", name: "amountOut", type: "uint256" },
47
+ { internalType: "uint256", name: "gasEstimate", type: "uint256" },
48
+ ],
49
+ stateMutability: "nonpayable",
50
+ type: "function",
51
+ },
52
+ ];
53
+ const ERC20_ABI = [
54
+ {
55
+ inputs: [
56
+ { name: "spender", type: "address" },
57
+ { name: "amount", type: "uint256" },
58
+ ],
59
+ name: "approve",
60
+ outputs: [{ name: "", type: "bool" }],
61
+ stateMutability: "nonpayable",
62
+ type: "function",
63
+ },
64
+ {
65
+ inputs: [
66
+ { name: "owner", type: "address" },
67
+ { name: "spender", type: "address" },
68
+ ],
69
+ name: "allowance",
70
+ outputs: [{ name: "", type: "uint256" }],
71
+ stateMutability: "view",
72
+ type: "function",
73
+ },
74
+ ];
75
+ const PERMIT2_ABI = [
76
+ {
77
+ inputs: [
78
+ { name: "token", type: "address" },
79
+ { name: "spender", type: "address" },
80
+ { name: "amount", type: "uint160" },
81
+ { name: "expiration", type: "uint48" },
82
+ ],
83
+ name: "approve",
84
+ outputs: [],
85
+ stateMutability: "nonpayable",
86
+ type: "function",
87
+ },
88
+ {
89
+ inputs: [
90
+ { name: "owner", type: "address" },
91
+ { name: "token", type: "address" },
92
+ { name: "spender", type: "address" },
93
+ ],
94
+ name: "allowance",
95
+ outputs: [
96
+ { name: "amount", type: "uint160" },
97
+ { name: "expiration", type: "uint48" },
98
+ { name: "nonce", type: "uint48" },
99
+ ],
100
+ stateMutability: "view",
101
+ type: "function",
102
+ },
103
+ ];
104
+ const TOKEN_V4_CONFIG_ABI = [
105
+ {
106
+ inputs: [],
107
+ name: "v4Hook",
108
+ outputs: [{ internalType: "address", name: "", type: "address" }],
109
+ stateMutability: "view",
110
+ type: "function",
111
+ },
112
+ {
113
+ inputs: [],
114
+ name: "v4PoolFee",
115
+ outputs: [{ internalType: "uint24", name: "", type: "uint24" }],
116
+ stateMutability: "view",
117
+ type: "function",
118
+ },
119
+ {
120
+ inputs: [],
121
+ name: "v4TickSpacing",
122
+ outputs: [{ internalType: "int24", name: "", type: "int24" }],
123
+ stateMutability: "view",
124
+ type: "function",
125
+ },
126
+ ];
127
+ // Command and action constants
128
+ const COMMANDS = {
129
+ V4_SWAP: "0x10",
130
+ };
131
+ const V4_ACTIONS = {
132
+ SWAP_EXACT_IN_SINGLE: 6,
133
+ TAKE_ALL: 15,
134
+ SETTLE_ALL: 12,
135
+ };
136
+ /**
137
+ * Internal swap service for handling Uniswap V4 swaps between trading token and bondkit token
138
+ */
139
+ export class BondkitSwapService {
140
+ constructor(bondkitTokenAddress) {
141
+ this.v4Config = null;
142
+ this.configInitialized = false;
143
+ this.bondkitTokenAddress = bondkitTokenAddress;
144
+ this.publicClient = createPublicClient({
145
+ chain: base,
146
+ transport: http(BaseMainnetRpcUrl),
147
+ });
148
+ }
149
+ /**
150
+ * Initialize V4 pool configuration from bondkit token contract
151
+ */
152
+ async initializeV4Config() {
153
+ if (this.configInitialized) {
154
+ return;
155
+ }
156
+ try {
157
+ const tokenContract = getContract({
158
+ address: this.bondkitTokenAddress,
159
+ abi: TOKEN_V4_CONFIG_ABI,
160
+ client: this.publicClient,
161
+ });
162
+ const [hook, fee, tickSpacing] = await Promise.all([
163
+ tokenContract.read.v4Hook(),
164
+ tokenContract.read.v4PoolFee(),
165
+ tokenContract.read.v4TickSpacing(),
166
+ ]);
167
+ this.v4Config = {
168
+ hook: hook,
169
+ fee: Number(fee),
170
+ tickSpacing: Number(tickSpacing),
171
+ };
172
+ this.configInitialized = true;
173
+ }
174
+ catch (error) {
175
+ console.warn("Failed to initialize V4 configuration:", error);
176
+ // Use fallback configuration
177
+ this.v4Config = {
178
+ hook: "0xB36f4A2FB18b745ef8eD31452781a463d2B3f0cC",
179
+ fee: 30000,
180
+ tickSpacing: 60,
181
+ };
182
+ this.configInitialized = true;
183
+ }
184
+ }
185
+ /**
186
+ * Get V4 pool configuration
187
+ */
188
+ async getV4Config() {
189
+ await this.initializeV4Config();
190
+ return this.v4Config;
191
+ }
192
+ /**
193
+ * Handle token approvals for swap
194
+ */
195
+ async handleTokenApprovals(tokenAddress, amountIn, walletClient, deadline) {
196
+ // Skip approvals for ETH
197
+ if (tokenAddress === "0x0000000000000000000000000000000000000000") {
198
+ return;
199
+ }
200
+ const userAddress = walletClient.account?.address;
201
+ if (!userAddress) {
202
+ throw new Error("No user address found");
203
+ }
204
+ const erc20Contract = getContract({
205
+ address: tokenAddress,
206
+ abi: ERC20_ABI,
207
+ client: walletClient,
208
+ });
209
+ const permit2Contract = getContract({
210
+ address: Permit2Address,
211
+ abi: PERMIT2_ABI,
212
+ client: walletClient,
213
+ });
214
+ // Check ERC20 allowance to Permit2
215
+ const currentAllowance = (await erc20Contract.read.allowance([userAddress, Permit2Address]));
216
+ const requiredAmount = BigInt(amountIn);
217
+ if (currentAllowance < requiredAmount) {
218
+ await erc20Contract.write.approve([Permit2Address, BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")], {
219
+ account: userAddress,
220
+ chain: base,
221
+ });
222
+ }
223
+ // Check Permit2 allowance for Universal Router
224
+ const permit2Allowance = (await permit2Contract.read.allowance([
225
+ userAddress,
226
+ tokenAddress,
227
+ UniversalRouterAddress,
228
+ ]));
229
+ const [currentPermit2Amount, expiration] = permit2Allowance;
230
+ const currentTime = Math.floor(Date.now() / 1000);
231
+ const isExpired = expiration <= currentTime;
232
+ if (currentPermit2Amount < requiredAmount || isExpired) {
233
+ await permit2Contract.write.approve([tokenAddress, UniversalRouterAddress, BigInt("0xffffffffffffffffffffffffffffffffffffff"), Number(deadline)], {
234
+ account: userAddress,
235
+ chain: base,
236
+ });
237
+ }
238
+ }
239
+ /**
240
+ * Get swap quote
241
+ */
242
+ async getSwapQuote(params) {
243
+ try {
244
+ const { tokenIn, tokenOut, amountIn, tokenInDecimals, tokenOutDecimals, slippageTolerance } = params;
245
+ const v4Config = await this.getV4Config();
246
+ const amountInWei = parseUnits(amountIn, tokenInDecimals);
247
+ // Determine token order for pool
248
+ const currency0 = tokenIn.toLowerCase() < tokenOut.toLowerCase() ? tokenIn : tokenOut;
249
+ const currency1 = tokenIn.toLowerCase() < tokenOut.toLowerCase() ? tokenOut : tokenIn;
250
+ const zeroForOne = tokenIn.toLowerCase() === currency0.toLowerCase();
251
+ const poolKey = {
252
+ currency0: currency0,
253
+ currency1: currency1,
254
+ fee: v4Config.fee,
255
+ tickSpacing: v4Config.tickSpacing,
256
+ hooks: v4Config.hook,
257
+ };
258
+ const quoteParams = {
259
+ poolKey,
260
+ zeroForOne,
261
+ exactAmount: BigInt(amountInWei.toString()),
262
+ hookData: "0x",
263
+ };
264
+ const { result } = await this.publicClient.simulateContract({
265
+ address: QuoterAddress,
266
+ abi: QUOTER_ABI,
267
+ functionName: "quoteExactInputSingle",
268
+ args: [quoteParams],
269
+ });
270
+ const [amountOut] = result;
271
+ const amountOutRaw = formatUnits(amountOut, tokenOutDecimals);
272
+ const amountOutFormatted = parseFloat(amountOutRaw).toFixed(Math.min(6, tokenOutDecimals));
273
+ // Calculate minimum amount out with slippage
274
+ const slippageMultiplier = (100 - slippageTolerance) / 100;
275
+ const amountOutMinRaw = parseFloat(amountOutFormatted) * slippageMultiplier;
276
+ const amountOutMin = amountOutMinRaw.toFixed(tokenOutDecimals);
277
+ // Simple execution price calculation
278
+ const rate = parseFloat(amountOutFormatted) / parseFloat(amountIn);
279
+ const executionPrice = `1 = ${rate.toFixed(6)}`;
280
+ return {
281
+ amountOut: amountOutFormatted,
282
+ amountOutMin,
283
+ priceImpact: "0.0", // Simplified
284
+ executionPrice,
285
+ fee: (v4Config.fee / 10000).toString(),
286
+ };
287
+ }
288
+ catch (error) {
289
+ console.warn("Error getting swap quote:", error);
290
+ return null;
291
+ }
292
+ }
293
+ /**
294
+ * Execute swap transaction
295
+ */
296
+ async executeSwap(params, walletClient) {
297
+ try {
298
+ const { tokenIn, tokenOut, amountIn, tokenInDecimals, tokenOutDecimals, deadline } = params;
299
+ const swapDeadline = deadline || Math.floor(Date.now() / 1000) + 3600;
300
+ if (!walletClient.account) {
301
+ throw new Error("Wallet client must have an account");
302
+ }
303
+ const amountInWei = parseUnits(amountIn, tokenInDecimals);
304
+ // Handle token approvals
305
+ await this.handleTokenApprovals(tokenIn, amountInWei.toString(), walletClient, swapDeadline);
306
+ // Get quote for minimum amount out
307
+ const quote = await this.getSwapQuote(params);
308
+ if (!quote) {
309
+ throw new Error("Unable to get swap quote");
310
+ }
311
+ const amountOutMinimum = parseUnits(quote.amountOutMin, tokenOutDecimals);
312
+ const v4Config = await this.getV4Config();
313
+ // Determine token order
314
+ const currency0 = tokenIn.toLowerCase() < tokenOut.toLowerCase() ? tokenIn : tokenOut;
315
+ const currency1 = tokenIn.toLowerCase() < tokenOut.toLowerCase() ? tokenOut : tokenIn;
316
+ const zeroForOne = tokenIn.toLowerCase() === currency0.toLowerCase();
317
+ const poolKey = [currency0, currency1, v4Config.fee, v4Config.tickSpacing, v4Config.hook];
318
+ // Encode V4 actions
319
+ const actions = [
320
+ {
321
+ type: V4_ACTIONS.SWAP_EXACT_IN_SINGLE,
322
+ params: [poolKey, zeroForOne, amountInWei, amountOutMinimum, "0x"],
323
+ },
324
+ {
325
+ type: V4_ACTIONS.TAKE_ALL,
326
+ params: [(zeroForOne ? currency1 : currency0), BigInt(0)],
327
+ },
328
+ {
329
+ type: V4_ACTIONS.SETTLE_ALL,
330
+ params: [(zeroForOne ? currency0 : currency1), amountInWei],
331
+ },
332
+ ];
333
+ // Encode actions
334
+ const actionTypes = actions.map(action => action.type);
335
+ const actionsBytes = ("0x" + actionTypes.map(type => type.toString(16).padStart(2, "0")).join(""));
336
+ const actionParams = actions.map(action => {
337
+ switch (action.type) {
338
+ case V4_ACTIONS.SWAP_EXACT_IN_SINGLE:
339
+ return encodeAbiParameters(parseAbiParameters("((address,address,uint24,int24,address),bool,uint128,uint128,bytes)"), [action.params]);
340
+ case V4_ACTIONS.TAKE_ALL:
341
+ return encodeAbiParameters(parseAbiParameters("address,uint256"), action.params);
342
+ case V4_ACTIONS.SETTLE_ALL:
343
+ return encodeAbiParameters(parseAbiParameters("address,uint256"), action.params);
344
+ default:
345
+ return "0x00";
346
+ }
347
+ });
348
+ const v4SwapInput = encodeAbiParameters(parseAbiParameters("bytes,bytes[]"), [actionsBytes, actionParams]);
349
+ const commands = COMMANDS.V4_SWAP;
350
+ const inputs = [v4SwapInput];
351
+ // Execute swap
352
+ const universalRouter = getContract({
353
+ address: UniversalRouterAddress,
354
+ abi: UNIVERSAL_ROUTER_ABI,
355
+ client: walletClient,
356
+ });
357
+ const txHash = await universalRouter.write.execute([commands, inputs, BigInt(swapDeadline)], {
358
+ account: walletClient.account,
359
+ chain: base,
360
+ value: tokenIn === "0x0000000000000000000000000000000000000000" ? amountInWei : BigInt(0),
361
+ });
362
+ return txHash;
363
+ }
364
+ catch (error) {
365
+ console.warn("Error executing swap:", error);
366
+ return null;
367
+ }
368
+ }
369
+ }
@@ -49,10 +49,9 @@ export type DexMigrationEventArgs = {
49
49
  ethForFeeRecipient: bigint;
50
50
  };
51
51
  export declare enum TokenStatus {
52
- Inactive = 0,// Assuming mapping from ABI, verify actual enum values if specified elsewhere
53
- BondingPhase = 1,
54
- DexPhase = 2,
55
- Migrated = 3
52
+ Uninitialized = 0,
53
+ Bonding = 1,
54
+ Dex = 2
56
55
  }
57
56
  export interface GetTransactionHistoryOptions {
58
57
  userAddress?: Address;
@@ -80,3 +79,10 @@ export interface TransactionResponse {
80
79
  skip: number;
81
80
  data: Transaction[];
82
81
  }
82
+ export interface SwapQuote {
83
+ amountOut: string;
84
+ amountOutMin: string;
85
+ priceImpact: string;
86
+ executionPrice: string;
87
+ fee: string;
88
+ }
@@ -1,8 +1,7 @@
1
- // Enum for Status (used in BondkitToken ABI)
1
+ // Enum for Status (matches contract Status enum exactly)
2
2
  export var TokenStatus;
3
3
  (function (TokenStatus) {
4
- TokenStatus[TokenStatus["Inactive"] = 0] = "Inactive";
5
- TokenStatus[TokenStatus["BondingPhase"] = 1] = "BondingPhase";
6
- TokenStatus[TokenStatus["DexPhase"] = 2] = "DexPhase";
7
- TokenStatus[TokenStatus["Migrated"] = 3] = "Migrated";
4
+ TokenStatus[TokenStatus["Uninitialized"] = 0] = "Uninitialized";
5
+ TokenStatus[TokenStatus["Bonding"] = 1] = "Bonding";
6
+ TokenStatus[TokenStatus["Dex"] = 2] = "Dex";
8
7
  })(TokenStatus || (TokenStatus = {}));
@@ -1,6 +1,7 @@
1
1
  import type { Address, EIP1193Provider, GetContractReturnType, Hex, PublicClient, WalletClient } from "viem";
2
2
  import { BondkitTokenABI } from "./abis";
3
- import type { BondkitTokenInitializationConfig, GetTransactionHistoryOptions, TokenDetails, TokenStatus, TransactionResponse } from "./types";
3
+ import type { BondkitTokenInitializationConfig, GetTransactionHistoryOptions, SwapQuote, TokenDetails, TransactionResponse } from "./types";
4
+ import { TokenStatus } from "./types";
4
5
  type ExecuteWriteOptions = {
5
6
  value?: bigint;
6
7
  gas?: bigint;
@@ -18,6 +19,7 @@ export declare class BondkitToken {
18
19
  private walletClientInstance;
19
20
  private connectedProvider?;
20
21
  private tradingToken?;
22
+ private swapService?;
21
23
  constructor(contractAddress: string, walletKey?: string, rpcUrl?: string);
22
24
  connect(provider?: EIP1193Provider): boolean;
23
25
  /**
@@ -69,5 +71,38 @@ export declare class BondkitToken {
69
71
  migrateToDex(options?: ExecuteWriteOptions): Promise<Hex | undefined>;
70
72
  transferTokenOwnership(newOwner: Address, options?: ExecuteWriteOptions): Promise<Hex | undefined>;
71
73
  renounceTokenOwnership(options?: ExecuteWriteOptions): Promise<Hex | undefined>;
74
+ /**
75
+ * Get the swap service instance (lazy initialization)
76
+ */
77
+ private getSwapService;
78
+ /**
79
+ * Check if DEX swapping is available (token must be in Dex phase)
80
+ */
81
+ isSwapAvailable(): Promise<boolean | undefined>;
82
+ /**
83
+ * Get swap quote for trading token → bondkit token
84
+ */
85
+ getSwapQuoteForBondkitToken(amountTradingTokenIn: string, slippageTolerance?: number): Promise<SwapQuote | undefined>;
86
+ /**
87
+ * Get swap quote for bondkit token → trading token
88
+ */
89
+ getSwapQuoteForTradingToken(amountBondkitTokenIn: string, slippageTolerance?: number): Promise<SwapQuote | undefined>;
90
+ /**
91
+ * Swap trading token for bondkit token
92
+ */
93
+ swapTradingTokenForBondkitToken(amountTradingTokenIn: string, slippageTolerance?: number, options?: ExecuteWriteOptions): Promise<Hex | undefined>;
94
+ /**
95
+ * Swap bondkit token for trading token
96
+ */
97
+ swapBondkitTokenForTradingToken(amountBondkitTokenIn: string, slippageTolerance?: number, options?: ExecuteWriteOptions): Promise<Hex | undefined>;
98
+ /**
99
+ * Helper method to get trading token decimals
100
+ */
101
+ private getTradingTokenDecimals;
102
+ /**
103
+ * Get trading token symbol
104
+ * @param tradingTokenAddress Optional trading token address to avoid fetching it again
105
+ */
106
+ getTradingTokenSymbol(tradingTokenAddress?: Address): Promise<string | undefined>;
72
107
  }
73
108
  export {};
@@ -1,3 +1,7 @@
1
1
  import type { Address } from "viem";
2
2
  export declare const BaseBondkitTokenFactoryContractAddress: Address;
3
3
  export declare const BaseMainnetRpcUrl = "https://base-rpc.publicnode.com";
4
+ export declare const UniversalRouterAddress: Address;
5
+ export declare const QuoterAddress: Address;
6
+ export declare const Permit2Address: Address;
7
+ export declare const B3TokenAddress: Address;
@@ -4,4 +4,5 @@ export * from "./config";
4
4
  export * from "./constants";
5
5
  export * from "./types";
6
6
  export * from "./abis";
7
+ export { BondkitSwapService } from "./swapService";
7
8
  export { default as TradingView } from "./components/TradingView";
@@ -0,0 +1,43 @@
1
+ import type { Address, WalletClient } from "viem";
2
+ import type { SwapQuote } from "./types";
3
+ interface SwapParams {
4
+ tokenIn: Address;
5
+ tokenOut: Address;
6
+ amountIn: string;
7
+ tokenInDecimals: number;
8
+ tokenOutDecimals: number;
9
+ slippageTolerance: number;
10
+ recipient: Address;
11
+ deadline?: number;
12
+ }
13
+ /**
14
+ * Internal swap service for handling Uniswap V4 swaps between trading token and bondkit token
15
+ */
16
+ export declare class BondkitSwapService {
17
+ private v4Config;
18
+ private configInitialized;
19
+ private readonly bondkitTokenAddress;
20
+ private readonly publicClient;
21
+ constructor(bondkitTokenAddress: Address);
22
+ /**
23
+ * Initialize V4 pool configuration from bondkit token contract
24
+ */
25
+ private initializeV4Config;
26
+ /**
27
+ * Get V4 pool configuration
28
+ */
29
+ private getV4Config;
30
+ /**
31
+ * Handle token approvals for swap
32
+ */
33
+ private handleTokenApprovals;
34
+ /**
35
+ * Get swap quote
36
+ */
37
+ getSwapQuote(params: SwapParams): Promise<SwapQuote | null>;
38
+ /**
39
+ * Execute swap transaction
40
+ */
41
+ executeSwap(params: SwapParams, walletClient: WalletClient): Promise<string | null>;
42
+ }
43
+ export {};
@@ -49,10 +49,9 @@ export type DexMigrationEventArgs = {
49
49
  ethForFeeRecipient: bigint;
50
50
  };
51
51
  export declare enum TokenStatus {
52
- Inactive = 0,// Assuming mapping from ABI, verify actual enum values if specified elsewhere
53
- BondingPhase = 1,
54
- DexPhase = 2,
55
- Migrated = 3
52
+ Uninitialized = 0,
53
+ Bonding = 1,
54
+ Dex = 2
56
55
  }
57
56
  export interface GetTransactionHistoryOptions {
58
57
  userAddress?: Address;
@@ -80,3 +79,10 @@ export interface TransactionResponse {
80
79
  skip: number;
81
80
  data: Transaction[];
82
81
  }
82
+ export interface SwapQuote {
83
+ amountOut: string;
84
+ amountOutMin: string;
85
+ priceImpact: string;
86
+ executionPrice: string;
87
+ fee: string;
88
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b3dotfun/sdk",
3
- "version": "0.0.40-alpha.5",
3
+ "version": "0.0.40-alpha.6",
4
4
  "source": "src/index.ts",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "react-native": "./dist/cjs/index.native.js",