liquid-sdk 1.4.0 → 1.5.1
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/index.d.mts +48 -2
- package/dist/index.d.ts +48 -2
- package/dist/index.js +95 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +95 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
- package/skills/README.md +51 -0
- package/skills/bid-in-auction.md +296 -0
- package/skills/deploy-token.md +260 -0
- package/skills/index-tokens.md +501 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "liquid-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "TypeScript SDK to deploy ERC-20 tokens with Uniswap V4 liquidity on Base — zero API keys, one dependency (viem)",
|
|
5
5
|
"author": "Liquid Protocol",
|
|
6
6
|
"homepage": "https://github.com/craigbots/liquid-sdk#readme",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"dist",
|
|
22
|
+
"skills",
|
|
22
23
|
"README.md",
|
|
23
24
|
"AGENT_README.md",
|
|
24
25
|
"CHANGELOG.md",
|
|
@@ -66,6 +67,8 @@
|
|
|
66
67
|
"web3",
|
|
67
68
|
"smart-contract",
|
|
68
69
|
"liquidity",
|
|
69
|
-
"mev-protection"
|
|
70
|
+
"mev-protection",
|
|
71
|
+
"ai-agent",
|
|
72
|
+
"llm-skills"
|
|
70
73
|
]
|
|
71
74
|
}
|
package/skills/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Liquid SDK — Agent Skills
|
|
2
|
+
|
|
3
|
+
Drop these files into your AI agent's context to give it the ability to interact with Liquid Protocol on Base.
|
|
4
|
+
|
|
5
|
+
## Available Skills
|
|
6
|
+
|
|
7
|
+
| Skill | File | Description |
|
|
8
|
+
|-------|------|-------------|
|
|
9
|
+
| **Deploy Token** | [`deploy-token.md`](./deploy-token.md) | Deploy ERC-20 tokens with Uniswap V4 liquidity, custom fees, dev buys, and reward splits |
|
|
10
|
+
| **Bid in Auction** | [`bid-in-auction.md`](./bid-in-auction.md) | Participate in sniper auctions for early access to newly launched tokens |
|
|
11
|
+
| **Index Tokens** | [`index-tokens.md`](./index-tokens.md) | Discover, index, and monitor all Liquid Protocol token deployments |
|
|
12
|
+
|
|
13
|
+
## How to Use
|
|
14
|
+
|
|
15
|
+
These are standalone markdown files designed to be loaded into an AI agent's system prompt or context window. Each skill contains:
|
|
16
|
+
|
|
17
|
+
- Complete setup instructions
|
|
18
|
+
- Step-by-step workflows with code examples
|
|
19
|
+
- Type definitions and parameter references
|
|
20
|
+
- Full working examples
|
|
21
|
+
- Error handling and edge cases
|
|
22
|
+
|
|
23
|
+
### With Claude Code
|
|
24
|
+
|
|
25
|
+
Place a skill file in your project and reference it in your `CLAUDE.md`:
|
|
26
|
+
|
|
27
|
+
```markdown
|
|
28
|
+
For token deployment, follow the instructions in skills/deploy-token.md
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### With Other AI Agents
|
|
32
|
+
|
|
33
|
+
Load the skill file contents into your agent's system prompt or tool description:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
with open("skills/deploy-token.md") as f:
|
|
37
|
+
skill = f.read()
|
|
38
|
+
|
|
39
|
+
agent.system_prompt += f"\n\n{skill}"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### With MCP Servers
|
|
43
|
+
|
|
44
|
+
Serve skill files as resources through an MCP server for on-demand agent access.
|
|
45
|
+
|
|
46
|
+
## Requirements
|
|
47
|
+
|
|
48
|
+
All skills require:
|
|
49
|
+
- `liquid-sdk` and `viem` npm packages
|
|
50
|
+
- An RPC endpoint for Base mainnet (chain ID 8453)
|
|
51
|
+
- For write operations: a private key with ETH on Base
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# Skill: Bid in a Sniper Auction (MEV)
|
|
2
|
+
|
|
3
|
+
You are an AI agent that participates in Liquid Protocol's sniper auction system. This skill teaches you how to bid for early access to newly launched tokens through the MEV auction mechanism on Base.
|
|
4
|
+
|
|
5
|
+
## How the Sniper Auction Works
|
|
6
|
+
|
|
7
|
+
When a new token is deployed on Liquid Protocol, a **sniper auction** activates to price early trading activity and capture MEV. Here's the mechanism:
|
|
8
|
+
|
|
9
|
+
1. **Fee decay**: The auction starts with an **80% fee** on swaps and decays linearly to **40% over 32 seconds**
|
|
10
|
+
2. **Gas price bidding**: Bidders compete by setting their transaction gas price **above the pool's gas peg**. The difference between your gas price and the gas peg determines your bid amount
|
|
11
|
+
3. **Rounds**: The auction runs in discrete rounds, each lasting a configurable number of blocks. Each round has its own auction window
|
|
12
|
+
4. **Winner takes the swap**: The highest gas-price transaction in each block wins the right to swap at the current fee rate
|
|
13
|
+
5. **Revenue distribution**: Auction revenue (bid amounts) flows to the protocol and LP holders
|
|
14
|
+
|
|
15
|
+
The auction is **not** a separate step from trading — it's a modified swap where your gas price encodes your bid.
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install liquid-sdk viem
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
You need:
|
|
24
|
+
- A **private key** with ETH on Base
|
|
25
|
+
- The **token address** or **pool ID** of a recently launched token
|
|
26
|
+
- An **RPC endpoint** for Base mainnet
|
|
27
|
+
|
|
28
|
+
## Setup
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { createPublicClient, createWalletClient, http, parseEther } from "viem";
|
|
32
|
+
import { base } from "viem/chains";
|
|
33
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
34
|
+
import { LiquidSDK } from "liquid-sdk";
|
|
35
|
+
|
|
36
|
+
const account = privateKeyToAccount(PRIVATE_KEY);
|
|
37
|
+
const publicClient = createPublicClient({ chain: base, transport: http(RPC_URL) });
|
|
38
|
+
const walletClient = createWalletClient({ account, chain: base, transport: http(RPC_URL) });
|
|
39
|
+
const sdk = new LiquidSDK({ publicClient, walletClient });
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Step-by-Step: Bidding in an Auction
|
|
43
|
+
|
|
44
|
+
### Step 1: Get the Auction State
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// You need the pool ID — get it from deployment or token lookup
|
|
48
|
+
const tokenEvent = await sdk.getTokenEvent(tokenAddress);
|
|
49
|
+
const poolId = tokenEvent.poolId;
|
|
50
|
+
|
|
51
|
+
// Query current auction state
|
|
52
|
+
const auction = await sdk.getAuctionState(poolId);
|
|
53
|
+
|
|
54
|
+
console.log("Next auction block:", auction.nextAuctionBlock);
|
|
55
|
+
console.log("Current round:", auction.round);
|
|
56
|
+
console.log("Gas peg:", auction.gasPeg);
|
|
57
|
+
console.log("Current fee:", auction.currentFee); // in uniBps (800000 = 80%)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Key fields:**
|
|
61
|
+
| Field | Type | Description |
|
|
62
|
+
|-------|------|-------------|
|
|
63
|
+
| `nextAuctionBlock` | `bigint` | Block number when next auction round starts |
|
|
64
|
+
| `round` | `bigint` | Current round number (must match when bidding) |
|
|
65
|
+
| `gasPeg` | `bigint` | Base gas price reference — you bid by exceeding this |
|
|
66
|
+
| `currentFee` | `number` | Current MEV fee in uniBps (decays from 800000→400000) |
|
|
67
|
+
|
|
68
|
+
### Step 2: Check Auction Fee Config
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const feeConfig = await sdk.getAuctionFeeConfig(poolId);
|
|
72
|
+
|
|
73
|
+
console.log("Starting fee:", feeConfig.startingFee); // 800000 (80%)
|
|
74
|
+
console.log("Ending fee:", feeConfig.endingFee); // 400000 (40%)
|
|
75
|
+
console.log("Seconds to decay:", feeConfig.secondsToDecay); // 32n
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Step 3: Check Timing
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// When did fee decay start?
|
|
82
|
+
const decayStart = await sdk.getAuctionDecayStartTime(poolId);
|
|
83
|
+
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
84
|
+
const elapsed = now - decayStart;
|
|
85
|
+
|
|
86
|
+
console.log("Seconds since decay started:", elapsed);
|
|
87
|
+
// If elapsed > secondsToDecay, fee is at the floor (endingFee)
|
|
88
|
+
|
|
89
|
+
// How many rounds total?
|
|
90
|
+
const maxRounds = await sdk.getAuctionMaxRounds();
|
|
91
|
+
console.log("Max auction rounds:", maxRounds);
|
|
92
|
+
|
|
93
|
+
// Is this round still active?
|
|
94
|
+
const currentBlock = await publicClient.getBlockNumber();
|
|
95
|
+
console.log("Current block:", currentBlock);
|
|
96
|
+
console.log("Next auction block:", auction.nextAuctionBlock);
|
|
97
|
+
// If currentBlock < nextAuctionBlock, the current round is still active
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Step 4: Calculate Gas Price for Your Bid
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
const desiredBidAmount = parseEther("0.01"); // How much ETH you want to bid
|
|
104
|
+
|
|
105
|
+
// SDK calculates the exact gas price needed
|
|
106
|
+
const requiredGasPrice = await sdk.getAuctionGasPriceForBid(
|
|
107
|
+
auction.gasPeg,
|
|
108
|
+
desiredBidAmount,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
console.log("Required gas price:", requiredGasPrice);
|
|
112
|
+
// This is the maxFeePerGas you must set on your transaction
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**The formula:** `bidAmount = (txGasPrice - gasPeg) * paymentPerGasUnit`. The utility contract solves for `txGasPrice` given your desired `bidAmount`.
|
|
116
|
+
|
|
117
|
+
### Step 5: Get the Pool Key
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// The pool key identifies the Uniswap V4 pool for the swap
|
|
121
|
+
const rewards = await sdk.getTokenRewards(tokenAddress);
|
|
122
|
+
const poolKey = rewards.poolKey;
|
|
123
|
+
|
|
124
|
+
// poolKey contains:
|
|
125
|
+
// .currency0 — lower-sorted token (WETH or the Liquid token)
|
|
126
|
+
// .currency1 — higher-sorted token
|
|
127
|
+
// .fee — fee tier
|
|
128
|
+
// .tickSpacing — tick spacing (200)
|
|
129
|
+
// .hooks — hook contract address
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Step 6: Execute the Bid
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
const result = await sdk.bidInAuction(
|
|
136
|
+
{
|
|
137
|
+
poolKey: rewards.poolKey,
|
|
138
|
+
zeroForOne: true, // true = buying tokens with ETH
|
|
139
|
+
amountIn: parseEther("0.1"), // amount of ETH to swap
|
|
140
|
+
amountOutMinimum: 0n, // set slippage protection (0 = no minimum)
|
|
141
|
+
round: auction.round, // must match current on-chain round
|
|
142
|
+
bidAmount: desiredBidAmount, // ETH bid (sent as msg.value)
|
|
143
|
+
},
|
|
144
|
+
requiredGasPrice, // calculated gas price from step 4
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
console.log("Bid tx:", result.txHash);
|
|
148
|
+
|
|
149
|
+
// Wait for confirmation
|
|
150
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: result.txHash });
|
|
151
|
+
console.log("Status:", receipt.status); // "success" or "reverted"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Complete Example: Automated Auction Sniper
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { createPublicClient, createWalletClient, http, parseEther, formatEther } from "viem";
|
|
158
|
+
import { base } from "viem/chains";
|
|
159
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
160
|
+
import { LiquidSDK } from "liquid-sdk";
|
|
161
|
+
|
|
162
|
+
async function snipeToken(tokenAddress: `0x${string}`, bidETH: string, swapETH: string) {
|
|
163
|
+
const account = privateKeyToAccount(PRIVATE_KEY);
|
|
164
|
+
const publicClient = createPublicClient({ chain: base, transport: http(RPC_URL) });
|
|
165
|
+
const walletClient = createWalletClient({ account, chain: base, transport: http(RPC_URL) });
|
|
166
|
+
const sdk = new LiquidSDK({ publicClient, walletClient });
|
|
167
|
+
|
|
168
|
+
// 1. Look up the token
|
|
169
|
+
const tokenEvent = await sdk.getTokenEvent(tokenAddress);
|
|
170
|
+
if (!tokenEvent) throw new Error("Token not found");
|
|
171
|
+
|
|
172
|
+
console.log(`Sniping ${tokenEvent.tokenName} (${tokenEvent.tokenSymbol})`);
|
|
173
|
+
console.log(`Pool ID: ${tokenEvent.poolId}`);
|
|
174
|
+
|
|
175
|
+
// 2. Check auction state
|
|
176
|
+
const auction = await sdk.getAuctionState(tokenEvent.poolId);
|
|
177
|
+
console.log(`Auction round: ${auction.round}, Fee: ${Number(auction.currentFee) / 10000}%`);
|
|
178
|
+
|
|
179
|
+
// 3. Check if auction is still active
|
|
180
|
+
const maxRounds = await sdk.getAuctionMaxRounds();
|
|
181
|
+
if (auction.round >= maxRounds) {
|
|
182
|
+
console.log("Auction ended — trading at normal fees now");
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 4. Get pool key for the swap
|
|
187
|
+
const rewards = await sdk.getTokenRewards(tokenAddress);
|
|
188
|
+
|
|
189
|
+
// 5. Calculate gas price for desired bid
|
|
190
|
+
const bidAmount = parseEther(bidETH);
|
|
191
|
+
const gasPrice = await sdk.getAuctionGasPriceForBid(auction.gasPeg, bidAmount);
|
|
192
|
+
|
|
193
|
+
console.log(`Bid amount: ${formatEther(bidAmount)} ETH`);
|
|
194
|
+
console.log(`Required gas price: ${gasPrice}`);
|
|
195
|
+
|
|
196
|
+
// 6. Execute the bid
|
|
197
|
+
const result = await sdk.bidInAuction(
|
|
198
|
+
{
|
|
199
|
+
poolKey: rewards.poolKey,
|
|
200
|
+
zeroForOne: true,
|
|
201
|
+
amountIn: parseEther(swapETH),
|
|
202
|
+
amountOutMinimum: 0n, // In production, calculate proper slippage!
|
|
203
|
+
round: auction.round,
|
|
204
|
+
bidAmount,
|
|
205
|
+
},
|
|
206
|
+
gasPrice,
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: result.txHash });
|
|
210
|
+
console.log(`Bid ${receipt.status === "success" ? "WON" : "FAILED"}: ${result.txHash}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Usage
|
|
214
|
+
await snipeToken("0x...", "0.005", "0.1"); // bid 0.005 ETH, swap 0.1 ETH
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## BidInAuctionParams Reference
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
interface BidInAuctionParams {
|
|
221
|
+
poolKey: PoolKey; // The Uniswap V4 pool key (get from getTokenRewards)
|
|
222
|
+
zeroForOne: boolean; // true = ETH→token, false = token→ETH
|
|
223
|
+
amountIn: bigint; // Amount of input token to swap (in wei)
|
|
224
|
+
amountOutMinimum: bigint;// Minimum output (slippage protection)
|
|
225
|
+
round: bigint; // Must match current on-chain auction round
|
|
226
|
+
bidAmount: bigint; // ETH bid amount (sent as msg.value)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
interface BidInAuctionResult {
|
|
230
|
+
txHash: Hash;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Auction Parameters (Defaults)
|
|
235
|
+
|
|
236
|
+
| Parameter | Value | Description |
|
|
237
|
+
|-----------|-------|-------------|
|
|
238
|
+
| Starting fee | 800,000 (80%) | Fee at auction start |
|
|
239
|
+
| Ending fee | 400,000 (40%) | Fee after decay completes |
|
|
240
|
+
| Decay period | 32 seconds | Time for fee to decay from start to end |
|
|
241
|
+
| Gas peg | Dynamic | Base gas price, updated per round |
|
|
242
|
+
| Max rounds | Contract-defined | Total auction rounds per token |
|
|
243
|
+
|
|
244
|
+
## Timing Strategy
|
|
245
|
+
|
|
246
|
+
The auction fee **decays over time**, so there's a tradeoff:
|
|
247
|
+
|
|
248
|
+
- **Bid early** (high fee): You pay up to 80% of the swap as a fee, but you get the tokens before others. Useful if you expect rapid price appreciation.
|
|
249
|
+
- **Bid late** (lower fee): The fee decays to 40% over 32 seconds. You pay less in fees but risk being outbid or missing the auction window.
|
|
250
|
+
- **Wait for auction to end**: After all rounds complete, trading is at normal pool fees (typically 1%). No auction mechanics apply.
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// Check current fee percentage
|
|
254
|
+
const feePercent = auction.currentFee / 10000; // e.g., 80.0, 60.5, 40.0
|
|
255
|
+
console.log(`Current fee: ${feePercent}%`);
|
|
256
|
+
|
|
257
|
+
// Check fee decay progress
|
|
258
|
+
const feeConfig = await sdk.getAuctionFeeConfig(poolId);
|
|
259
|
+
const decayStart = await sdk.getAuctionDecayStartTime(poolId);
|
|
260
|
+
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
261
|
+
const decayProgress = Number(now - decayStart) / Number(feeConfig.secondsToDecay);
|
|
262
|
+
console.log(`Decay progress: ${Math.min(decayProgress * 100, 100).toFixed(1)}%`);
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Important Notes
|
|
266
|
+
|
|
267
|
+
1. **`round` must be current**: If you submit a bid with a stale round number, the transaction will revert. Always fetch `getAuctionState()` right before bidding.
|
|
268
|
+
|
|
269
|
+
2. **Gas price is the bid**: The auction uses `tx.gasprice` as the bidding mechanism. The SDK's `bidInAuction()` sets `maxFeePerGas` to the calculated value. On Base (L2), gas prices are low, so even small bids produce manageable gas costs.
|
|
270
|
+
|
|
271
|
+
3. **Slippage protection**: Set `amountOutMinimum` to a non-zero value in production. Calculate it based on the current pool price and your acceptable slippage tolerance.
|
|
272
|
+
|
|
273
|
+
4. **The bid amount is sent as `msg.value`**: This ETH goes to the auction contract, not to the swap. The `amountIn` is the separate ETH amount for the actual token swap.
|
|
274
|
+
|
|
275
|
+
5. **`zeroForOne` direction**: Almost always `true` for sniping (buying tokens with ETH). Only set to `false` if selling tokens back through the auction.
|
|
276
|
+
|
|
277
|
+
## Read-Only Auction Queries (No Wallet Needed)
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
const sdk = new LiquidSDK({ publicClient }); // read-only
|
|
281
|
+
|
|
282
|
+
const auction = await sdk.getAuctionState(poolId);
|
|
283
|
+
const feeConfig = await sdk.getAuctionFeeConfig(poolId);
|
|
284
|
+
const decayStart = await sdk.getAuctionDecayStartTime(poolId);
|
|
285
|
+
const maxRounds = await sdk.getAuctionMaxRounds();
|
|
286
|
+
const gasPrice = await sdk.getAuctionGasPriceForBid(auction.gasPeg, parseEther("0.01"));
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Contract Addresses
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { ADDRESSES } from "liquid-sdk";
|
|
293
|
+
|
|
294
|
+
ADDRESSES.SNIPER_AUCTION_V2 // Auction state contract
|
|
295
|
+
ADDRESSES.SNIPER_UTIL_V2 // Bid execution contract (called by bidInAuction)
|
|
296
|
+
```
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# Skill: Deploy a Token with Liquid Protocol
|
|
2
|
+
|
|
3
|
+
You are an AI agent that deploys ERC-20 tokens on Base using the `liquid-sdk`. This skill gives you everything you need to deploy tokens autonomously — from minimal launches to fully customized deployments with dev buys, custom fee structures, and multi-tranche liquidity positions.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install liquid-sdk viem
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
You need:
|
|
12
|
+
- A **private key** with ETH on Base (for gas + optional dev buy)
|
|
13
|
+
- An **RPC endpoint** for Base mainnet (chain ID 8453)
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createPublicClient, createWalletClient, http, parseEther } from "viem";
|
|
19
|
+
import { base } from "viem/chains";
|
|
20
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
21
|
+
import { LiquidSDK } from "liquid-sdk";
|
|
22
|
+
|
|
23
|
+
const account = privateKeyToAccount(PRIVATE_KEY);
|
|
24
|
+
const publicClient = createPublicClient({ chain: base, transport: http(RPC_URL) });
|
|
25
|
+
const walletClient = createWalletClient({ account, chain: base, transport: http(RPC_URL) });
|
|
26
|
+
const sdk = new LiquidSDK({ publicClient, walletClient });
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Deploying a Token
|
|
30
|
+
|
|
31
|
+
### Minimal Deploy (2 required fields)
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
const result = await sdk.deployToken({
|
|
35
|
+
name: "My Token",
|
|
36
|
+
symbol: "MTK",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// result.tokenAddress → 0x... (the deployed ERC-20)
|
|
40
|
+
// result.txHash → 0x... (the transaction hash)
|
|
41
|
+
// result.event.poolId → 0x... (the Uniswap V4 pool ID)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This creates a token with 100 billion supply, a Uniswap V4 pool, locked liquidity, 1% buy fee, and sniper auction MEV protection — all with sensible defaults.
|
|
45
|
+
|
|
46
|
+
### Deploy with Dev Buy (buy tokens at launch)
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
const result = await sdk.deployToken({
|
|
50
|
+
name: "My Token",
|
|
51
|
+
symbol: "MTK",
|
|
52
|
+
image: "https://example.com/logo.png",
|
|
53
|
+
metadata: JSON.stringify({ description: "Launched by an AI agent" }),
|
|
54
|
+
devBuy: {
|
|
55
|
+
ethAmount: parseEther("0.01"), // ETH to spend buying tokens at launch
|
|
56
|
+
recipient: account.address, // who receives the purchased tokens
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The dev buy happens atomically in the same transaction as deployment. The ETH is swapped through the Uniswap V4 pool. The `msg.value` of the transaction equals the `ethAmount`.
|
|
62
|
+
|
|
63
|
+
### Deploy with Custom Fees
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { encodeStaticFeePoolData, encodeDynamicFeePoolData, ADDRESSES } from "liquid-sdk";
|
|
67
|
+
|
|
68
|
+
// Static fees: 0% sell fee, 2% buy fee
|
|
69
|
+
const result = await sdk.deployToken({
|
|
70
|
+
name: "High Fee Token",
|
|
71
|
+
symbol: "HFT",
|
|
72
|
+
poolData: encodeStaticFeePoolData(0, 200), // (liquidFeeBps, pairedFeeBps)
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Dynamic fees (volatility-responsive)
|
|
76
|
+
const result2 = await sdk.deployToken({
|
|
77
|
+
name: "Dynamic Fee Token",
|
|
78
|
+
symbol: "DFT",
|
|
79
|
+
hook: ADDRESSES.HOOK_DYNAMIC_FEE_V2,
|
|
80
|
+
poolData: encodeDynamicFeePoolData({
|
|
81
|
+
baseFeeBps: 30, // 0.3% base
|
|
82
|
+
maxFeeBps: 500, // 5% max
|
|
83
|
+
referenceTickFilterPeriod: 300,
|
|
84
|
+
resetPeriod: 86400,
|
|
85
|
+
resetTickFilter: 600,
|
|
86
|
+
feeControlNumerator: 50000n,
|
|
87
|
+
decayFilterBps: 500,
|
|
88
|
+
}),
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Deploy with Custom Liquidity Positions
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { createPositionsUSD, createDefaultPositions } from "liquid-sdk";
|
|
96
|
+
|
|
97
|
+
// Option A: Default 3-tranche positions based on current ETH price
|
|
98
|
+
const positions = createDefaultPositions(20_000, 2500); // $20K starting cap, ETH=$2500
|
|
99
|
+
const result = await sdk.deployToken({
|
|
100
|
+
name: "Positioned Token",
|
|
101
|
+
symbol: "POS",
|
|
102
|
+
tickIfToken0IsLiquid: positions.tickIfToken0IsLiquid,
|
|
103
|
+
tickLower: positions.tickLower,
|
|
104
|
+
tickUpper: positions.tickUpper,
|
|
105
|
+
positionBps: positions.positionBps,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Option B: Custom tranches
|
|
109
|
+
const custom = createPositionsUSD(20_000, 2500, [
|
|
110
|
+
{ upperMarketCapUSD: 100_000, supplyPct: 30 },
|
|
111
|
+
{ upperMarketCapUSD: 1_000_000, supplyPct: 40 },
|
|
112
|
+
{ upperMarketCapUSD: 100_000_000, supplyPct: 30 },
|
|
113
|
+
]);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Deploy with Custom Reward Splits
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const result = await sdk.deployToken({
|
|
120
|
+
name: "Split Token",
|
|
121
|
+
symbol: "SPLIT",
|
|
122
|
+
rewardAdmins: [walletA, walletB],
|
|
123
|
+
rewardRecipients: [walletA, walletB],
|
|
124
|
+
rewardBps: [7000, 3000], // 70% / 30% split
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Deploy with Context (attribution/tracking)
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { buildContext, buildMetadata } from "liquid-sdk";
|
|
132
|
+
|
|
133
|
+
const result = await sdk.deployToken({
|
|
134
|
+
name: "Social Token",
|
|
135
|
+
symbol: "SOC",
|
|
136
|
+
context: buildContext({
|
|
137
|
+
interface: "My Agent",
|
|
138
|
+
platform: "Farcaster",
|
|
139
|
+
messageId: "0x123abc",
|
|
140
|
+
}),
|
|
141
|
+
metadata: buildMetadata({
|
|
142
|
+
description: "A token launched from a Farcaster cast",
|
|
143
|
+
socialMediaUrls: [
|
|
144
|
+
{ platform: "Twitter", url: "https://twitter.com/example" },
|
|
145
|
+
],
|
|
146
|
+
}),
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Complete DeployTokenParams Reference
|
|
151
|
+
|
|
152
|
+
| Parameter | Type | Default | Description |
|
|
153
|
+
|-----------|------|---------|-------------|
|
|
154
|
+
| `name` | `string` | **required** | Token name |
|
|
155
|
+
| `symbol` | `string` | **required** | Token symbol |
|
|
156
|
+
| `image` | `string` | `""` | Image URL |
|
|
157
|
+
| `metadata` | `string` | `""` | JSON metadata string |
|
|
158
|
+
| `context` | `string` | `'{"interface":"SDK"}'` | JSON context for attribution |
|
|
159
|
+
| `tokenAdmin` | `Address` | wallet address | Can update image/metadata |
|
|
160
|
+
| `salt` | `Hex` | auto-generated | CREATE2 salt (unique per deploy) |
|
|
161
|
+
| `hook` | `Address` | Static Fee V2 | Fee logic hook contract |
|
|
162
|
+
| `pairedToken` | `Address` | WETH | Quote token |
|
|
163
|
+
| `tickIfToken0IsLiquid` | `number` | `-230400` | Starting tick (~10 ETH market cap) |
|
|
164
|
+
| `tickSpacing` | `number` | `200` | Uniswap V4 tick spacing |
|
|
165
|
+
| `poolData` | `Hex` | 0% sell / 1% buy | Encoded fee configuration |
|
|
166
|
+
| `locker` | `Address` | LP Locker Fee Conversion | LP locker contract |
|
|
167
|
+
| `rewardAdmins` | `Address[]` | `[wallet]` | Reward admin per recipient |
|
|
168
|
+
| `rewardRecipients` | `Address[]` | `[wallet]` | Fee recipients |
|
|
169
|
+
| `rewardBps` | `number[]` | `[10000]` | BPS per recipient (sum = 10000) |
|
|
170
|
+
| `tickLower` | `number[]` | 5-tranche Liquid default | Position lower bounds |
|
|
171
|
+
| `tickUpper` | `number[]` | 5-tranche Liquid default | Position upper bounds |
|
|
172
|
+
| `positionBps` | `number[]` | 5-tranche Liquid default | Supply % per position (sum = 10000) |
|
|
173
|
+
| `lockerData` | `Hex` | auto-encoded | Locker init data |
|
|
174
|
+
| `mevModule` | `Address` | Sniper Auction V2 | MEV protection module |
|
|
175
|
+
| `mevModuleData` | `Hex` | 80%→40% over 32s | Encoded auction config |
|
|
176
|
+
| `extensions` | `ExtensionConfig[]` | `[]` | Additional extensions |
|
|
177
|
+
| `devBuy` | `DevBuyParams` | none | Buy tokens at launch |
|
|
178
|
+
|
|
179
|
+
## DeployTokenResult
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
interface DeployTokenResult {
|
|
183
|
+
tokenAddress: Address; // The deployed ERC-20 contract
|
|
184
|
+
txHash: Hash; // Transaction hash
|
|
185
|
+
event: TokenCreatedEvent; // Full on-chain event data
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// event contains:
|
|
189
|
+
// .poolId — Uniswap V4 pool ID (bytes32)
|
|
190
|
+
// .poolHook — Hook contract used
|
|
191
|
+
// .locker — LP locker address
|
|
192
|
+
// .mevModule — MEV module address
|
|
193
|
+
// .extensions — Extension addresses
|
|
194
|
+
// .tokenAdmin — Admin address
|
|
195
|
+
// .startingTick — Initial tick
|
|
196
|
+
// .pairedToken — Quote token (WETH)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## What Happens On-Chain
|
|
200
|
+
|
|
201
|
+
When `deployToken()` executes, a single transaction:
|
|
202
|
+
|
|
203
|
+
1. **Creates the ERC-20 token** with 100 billion supply (18 decimals)
|
|
204
|
+
2. **Initializes a Uniswap V4 pool** paired with WETH
|
|
205
|
+
3. **Locks all LP** in the LP Locker (non-ruggable)
|
|
206
|
+
4. **Configures reward splits** for fee distribution
|
|
207
|
+
5. **Activates MEV protection** (sniper auction: 80%→40% fee decay over 32 seconds)
|
|
208
|
+
6. **Executes dev buy** if specified (swaps ETH→tokens in same tx)
|
|
209
|
+
7. **Emits `TokenCreated` event** with all deployment data
|
|
210
|
+
|
|
211
|
+
## Validation Rules
|
|
212
|
+
|
|
213
|
+
The SDK validates before sending the transaction:
|
|
214
|
+
- 1–7 positions allowed, BPS must sum to 10000
|
|
215
|
+
- All ticks must be aligned to `tickSpacing`
|
|
216
|
+
- All `tickLower` must be `≥ tickIfToken0IsLiquid`
|
|
217
|
+
- At least one position must have `tickLower == tickIfToken0IsLiquid`
|
|
218
|
+
- 1+ reward recipients, BPS must sum to 10000
|
|
219
|
+
- Max 10 extensions, max 90% of supply to extensions total
|
|
220
|
+
|
|
221
|
+
## Post-Deploy Operations
|
|
222
|
+
|
|
223
|
+
After deployment, you can:
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
// Update token image or metadata (admin only)
|
|
227
|
+
await sdk.updateImage(result.tokenAddress, "https://new-image.png");
|
|
228
|
+
await sdk.updateMetadata(result.tokenAddress, '{"description":"Updated"}');
|
|
229
|
+
|
|
230
|
+
// Check deployment info
|
|
231
|
+
const info = await sdk.getDeploymentInfo(result.tokenAddress);
|
|
232
|
+
const tokenInfo = await sdk.getTokenInfo(result.tokenAddress);
|
|
233
|
+
|
|
234
|
+
// Check MEV lock status
|
|
235
|
+
const unlockTime = await sdk.getPoolUnlockTime(result.event.poolId);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Common Errors
|
|
239
|
+
|
|
240
|
+
| Error | Cause | Fix |
|
|
241
|
+
|-------|-------|-----|
|
|
242
|
+
| `TickRangeLowerThanStartingTick` | `tickLower < tickIfToken0IsLiquid` | Ensure all position ticks ≥ starting tick |
|
|
243
|
+
| Insufficient funds | Not enough ETH for gas + devBuy | Check balance covers gas + `devBuy.ethAmount` |
|
|
244
|
+
| `rewardBps must sum to 10000` | BPS array doesn't total 10000 | Fix the array values |
|
|
245
|
+
| `positions and bps arrays must be same length` | Mismatched arrays | Ensure tickLower, tickUpper, positionBps are same length |
|
|
246
|
+
|
|
247
|
+
## Contract Addresses (Base Mainnet)
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { ADDRESSES, EXTERNAL } from "liquid-sdk";
|
|
251
|
+
|
|
252
|
+
ADDRESSES.FACTORY // Token factory
|
|
253
|
+
ADDRESSES.HOOK_STATIC_FEE_V2 // Default hook (1% buy fee)
|
|
254
|
+
ADDRESSES.HOOK_DYNAMIC_FEE_V2 // Dynamic fee hook
|
|
255
|
+
ADDRESSES.LP_LOCKER_FEE_CONVERSION // Default locker (converts fees to ETH)
|
|
256
|
+
ADDRESSES.SNIPER_AUCTION_V2 // Default MEV module
|
|
257
|
+
ADDRESSES.UNIV4_ETH_DEV_BUY // Dev buy extension
|
|
258
|
+
EXTERNAL.WETH // Base WETH
|
|
259
|
+
EXTERNAL.POOL_MANAGER // Uniswap V4 Pool Manager
|
|
260
|
+
```
|