flipmeme-sdk 1.3.74 โ 1.3.75
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 +2190 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1 +1,2190 @@
|
|
|
1
|
-
SDK
|
|
1
|
+
# Flipmeme EVM SDK Documentation
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
1. [Overview](#overview)
|
|
6
|
+
2. [Installation](#installation)
|
|
7
|
+
3. [Quick Start](#quick-start)
|
|
8
|
+
4. [Architecture](#architecture)
|
|
9
|
+
5. [API Reference](#api-reference)
|
|
10
|
+
- [Initialization](#initialization)
|
|
11
|
+
- [Collection Management](#collection-management)
|
|
12
|
+
- [Trading Operations](#trading-operations)
|
|
13
|
+
- [Query Operations](#query-operations)
|
|
14
|
+
- [Utility Functions](#utility-functions)
|
|
15
|
+
6. [Types Reference](#types-reference)
|
|
16
|
+
7. [Constants & Configuration](#constants--configuration)
|
|
17
|
+
8. [Advanced Usage](#advanced-usage)
|
|
18
|
+
9. [Error Handling](#error-handling)
|
|
19
|
+
10. [Best Practices](#best-practices)
|
|
20
|
+
11. [Examples](#examples)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Overview
|
|
25
|
+
|
|
26
|
+
The Flipmeme EVM SDK provides a comprehensive TypeScript/JavaScript interface for interacting with the Flipmeme protocol on Ethereum-compatible blockchains. It enables developers to:
|
|
27
|
+
|
|
28
|
+
- **Create NFT Collections** with dynamic bonding curve pricing
|
|
29
|
+
- **Mint & Trade NFTs** with integrated liquidity pools
|
|
30
|
+
- **Manage Multiple Collections** with batch operations
|
|
31
|
+
- **Calculate Pricing** with built-in fee handling
|
|
32
|
+
- **Query On-chain Data** efficiently
|
|
33
|
+
|
|
34
|
+
### Key Features
|
|
35
|
+
|
|
36
|
+
- ๐ **Multi-chain Support**: Works on Ethereum, Base, and other EVM-compatible chains
|
|
37
|
+
- ๐ฐ **Bonding Curve Pricing**: Automated market-making with customizable price curves
|
|
38
|
+
- ๐ **Liquidity Pools**: Built-in AMM for instant NFT trading
|
|
39
|
+
- ๐ฆ **Batch Operations**: Gas-efficient multi-collection transactions
|
|
40
|
+
- ๐ก๏ธ **Type-Safe**: Full TypeScript support with comprehensive type definitions
|
|
41
|
+
- ๐งช **Well-Tested**: Production-ready with extensive test coverage
|
|
42
|
+
|
|
43
|
+
### Protocol Overview
|
|
44
|
+
|
|
45
|
+
The Flipmeme protocol implements a bonding curve NFT marketplace where:
|
|
46
|
+
|
|
47
|
+
1. **Collections** are created with a defined supply and price range
|
|
48
|
+
2. **NFTs** can be minted at dynamically calculated prices
|
|
49
|
+
3. **Liquidity pools** allow instant buying/selling
|
|
50
|
+
4. **Fees** are distributed to creators, LP providers, and the protocol
|
|
51
|
+
5. **Liquidity migration** occurs when collections sell out
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
### Prerequisites
|
|
58
|
+
|
|
59
|
+
- Node.js >= 16.x
|
|
60
|
+
- npm or yarn or pnpm
|
|
61
|
+
|
|
62
|
+
### Install Dependencies
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install flipmeme-sdk ethers@^5.7.0
|
|
66
|
+
# or
|
|
67
|
+
yarn add flipmeme-sdk ethers@^5.7.0
|
|
68
|
+
# or
|
|
69
|
+
pnpm add flipmeme-sdk ethers@^5.7.0
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
> **Note**: The SDK currently uses ethers v5. Compatibility with ethers v6 is planned for future releases.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Quick Start
|
|
77
|
+
|
|
78
|
+
### Basic Setup
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { ethers } from "ethers";
|
|
82
|
+
import { FlipmemeSDK, BlockchainType, PLATFORM } from "flipmeme-sdk";
|
|
83
|
+
|
|
84
|
+
// 1. Setup provider and signer
|
|
85
|
+
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
|
|
86
|
+
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
|
|
87
|
+
|
|
88
|
+
// 2. Initialize SDK
|
|
89
|
+
const sdk = new FlipmemeSDK(
|
|
90
|
+
BlockchainType.ETHEREUM,
|
|
91
|
+
PLATFORM.DEV, // or PLATFORM.PROD
|
|
92
|
+
{
|
|
93
|
+
signer,
|
|
94
|
+
chainId: 84532, // Base Sepolia
|
|
95
|
+
provider,
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// 3. Create a collection
|
|
100
|
+
const { collectionId, collectionAddress } = await sdk.createCollection({
|
|
101
|
+
name: "My NFT Collection",
|
|
102
|
+
symbol: "MNFT",
|
|
103
|
+
totalSupply: 100,
|
|
104
|
+
premint: 5,
|
|
105
|
+
sessionId: `session-${Date.now()}`,
|
|
106
|
+
tokenUri: "ipfs://QmYourCollectionMetadata/",
|
|
107
|
+
startPrice: ethers.utils.parseEther("0.001").toString(),
|
|
108
|
+
endPrice: ethers.utils.parseEther("0.1").toString(),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
console.log("Collection created at:", collectionAddress);
|
|
112
|
+
|
|
113
|
+
// 4. Buy NFTs
|
|
114
|
+
const result = await sdk.buy({
|
|
115
|
+
collectionAddress,
|
|
116
|
+
baseUri: "ipfs://QmYourTokenMetadata/",
|
|
117
|
+
amount: 3, // Mint 3 new NFTs
|
|
118
|
+
tokenIds: [], // No buying from pool
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
console.log(`Successfully minted ${result.successCount} NFTs`);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Example: Complete Trading Flow
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// Calculate price before buying
|
|
128
|
+
const totalPrice = await sdk.getTotalPrice(
|
|
129
|
+
PurcahseType.BUY,
|
|
130
|
+
collectionAddress,
|
|
131
|
+
3
|
|
132
|
+
);
|
|
133
|
+
console.log("Total cost:", ethers.utils.formatEther(totalPrice), "ETH");
|
|
134
|
+
|
|
135
|
+
// Buy 3 NFTs
|
|
136
|
+
await sdk.buy({
|
|
137
|
+
collectionAddress,
|
|
138
|
+
baseUri: "ipfs://tokens/",
|
|
139
|
+
amount: 3,
|
|
140
|
+
tokenIds: [],
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Check your balance
|
|
144
|
+
const balance = await sdk.balanceOf(
|
|
145
|
+
collectionAddress,
|
|
146
|
+
await signer.getAddress()
|
|
147
|
+
);
|
|
148
|
+
console.log("You own", balance, "NFTs");
|
|
149
|
+
|
|
150
|
+
// Sell 2 NFTs back
|
|
151
|
+
await sdk.flipSell({
|
|
152
|
+
collectionAddress,
|
|
153
|
+
tokenIds: [0, 1],
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Architecture
|
|
160
|
+
|
|
161
|
+
### Class Hierarchy
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
FlipmemeSDK (main entry point)
|
|
165
|
+
โโโ EthereumConnector (EVM implementation)
|
|
166
|
+
โโโ Factory & Collection Contracts
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Contract Architecture
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
Factory Contract
|
|
173
|
+
โโโ Creates Collections
|
|
174
|
+
โโโ Manages Global State
|
|
175
|
+
โโโ Handles Multi-collection Operations
|
|
176
|
+
|
|
177
|
+
Collection Contract
|
|
178
|
+
โโโ ERC721A (NFT Standard)
|
|
179
|
+
โโโ Bonding Curve Logic
|
|
180
|
+
โโโ Liquidity Pool Management
|
|
181
|
+
โโโ Fee Distribution
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Data Flow
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
User โ SDK โ EthereumConnector โ Smart Contracts โ Blockchain
|
|
188
|
+
โ โ โ โ
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## API Reference
|
|
194
|
+
|
|
195
|
+
### Initialization
|
|
196
|
+
|
|
197
|
+
#### `new FlipmemeSDK(chainType, platform, config)`
|
|
198
|
+
|
|
199
|
+
Creates a new instance of the Flipmeme SDK.
|
|
200
|
+
|
|
201
|
+
**Parameters:**
|
|
202
|
+
|
|
203
|
+
- `chainType`: `BlockchainType.ETHEREUM`
|
|
204
|
+
- `platform`: `PLATFORM.DEV | PLATFORM.STAGE | PLATFORM.PROD`
|
|
205
|
+
- `config`: `EthereumConfig`
|
|
206
|
+
|
|
207
|
+
**EthereumConfig:**
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
interface EthereumConfig {
|
|
211
|
+
signer?: ethers.Signer; // Optional: required for write operations
|
|
212
|
+
chainId: number; // Network chain ID
|
|
213
|
+
provider: ethers.providers.BaseProvider; // RPC provider
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Example:**
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
const sdk = new FlipmemeSDK(BlockchainType.ETHEREUM, PLATFORM.PROD, {
|
|
221
|
+
signer: wallet,
|
|
222
|
+
chainId: 8453, // Base Mainnet
|
|
223
|
+
provider: provider,
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Throws:**
|
|
228
|
+
|
|
229
|
+
- `Error("Invalid config for the given blockchain type")` if config doesn't match chain type
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### Collection Management
|
|
234
|
+
|
|
235
|
+
#### `createCollection(params)`
|
|
236
|
+
|
|
237
|
+
Creates a new NFT collection with bonding curve pricing.
|
|
238
|
+
|
|
239
|
+
**Parameters:**
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
interface CollectionParams {
|
|
243
|
+
sessionId: string; // Unique session identifier
|
|
244
|
+
name: string; // Collection name (e.g., "Cool Cats")
|
|
245
|
+
symbol?: string; // Token symbol (e.g., "COOL")
|
|
246
|
+
tokenUri: string; // Collection metadata URI
|
|
247
|
+
totalSupply: number; // Max number of NFTs
|
|
248
|
+
premint?: number; // NFTs to pre-mint for creator (default: 0)
|
|
249
|
+
startPrice?: string; // Starting price in wei (default: 10^15 = 0.001 ETH)
|
|
250
|
+
endPrice?: string; // Ending price in wei (default: 10^16 = 0.01 ETH)
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Returns:**
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
Promise<CreateCollectionResponse>;
|
|
258
|
+
|
|
259
|
+
interface CreateCollectionResponse {
|
|
260
|
+
collectionId: string; // On-chain collection ID
|
|
261
|
+
collectionAddress: string; // Contract address
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Example:**
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
const { collectionId, collectionAddress } = await sdk.createCollection({
|
|
269
|
+
name: "Degen Apes",
|
|
270
|
+
symbol: "DAPE",
|
|
271
|
+
totalSupply: 1000,
|
|
272
|
+
premint: 10,
|
|
273
|
+
sessionId: `create-${Date.now()}-${Math.random()}`,
|
|
274
|
+
tokenUri: "ipfs://QmYourCollectionHash/metadata.json",
|
|
275
|
+
startPrice: ethers.utils.parseEther("0.0001").toString(),
|
|
276
|
+
endPrice: ethers.utils.parseEther("0.5").toString(),
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
console.log("Collection ID:", collectionId);
|
|
280
|
+
console.log(
|
|
281
|
+
"View on explorer:",
|
|
282
|
+
`https://basescan.org/address/${collectionAddress}`
|
|
283
|
+
);
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**Events Emitted:**
|
|
287
|
+
|
|
288
|
+
- `CollectionCreated(id, collection, creator, totalSupply, premint)`
|
|
289
|
+
|
|
290
|
+
**Gas Cost:** ~3-5M gas (varies by network)
|
|
291
|
+
|
|
292
|
+
**Important Notes:**
|
|
293
|
+
|
|
294
|
+
- `sessionId` should be unique per collection to avoid conflicts
|
|
295
|
+
- Price curve is logarithmic between `startPrice` and `endPrice`
|
|
296
|
+
- Preminted NFTs are sent to the creator's address
|
|
297
|
+
- Symbol is optional but recommended for better UX
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
### Trading Operations
|
|
302
|
+
|
|
303
|
+
#### `buy(params)`
|
|
304
|
+
|
|
305
|
+
Mints new NFTs and/or buys NFTs from the liquidity pool.
|
|
306
|
+
|
|
307
|
+
**Parameters:**
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
interface EthBuyParams {
|
|
311
|
+
collectionAddress: string; // Collection contract address
|
|
312
|
+
baseUri?: string; // Token metadata base URI (required if amount > 0)
|
|
313
|
+
amount: number; // Number of new NFTs to mint
|
|
314
|
+
tokenIds: number[]; // Token IDs to buy from pool
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Returns:**
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
Promise<ConfirmResult>;
|
|
322
|
+
|
|
323
|
+
interface ConfirmResult {
|
|
324
|
+
successCount: number; // Total NFTs acquired
|
|
325
|
+
failedCount: number; // Failed transactions (always 0 on success)
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Examples:**
|
|
330
|
+
|
|
331
|
+
**Mint Only:**
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
// Mint 5 new NFTs
|
|
335
|
+
const result = await sdk.buy({
|
|
336
|
+
collectionAddress: "0x1234...",
|
|
337
|
+
baseUri: "ipfs://QmTokens/",
|
|
338
|
+
amount: 5,
|
|
339
|
+
tokenIds: [],
|
|
340
|
+
});
|
|
341
|
+
// successCount = 5
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Buy from Pool Only:**
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
// Buy 3 specific NFTs from liquidity pool
|
|
348
|
+
const result = await sdk.buy({
|
|
349
|
+
collectionAddress: "0x1234...",
|
|
350
|
+
baseUri: "", // Not needed for pool purchases
|
|
351
|
+
amount: 0,
|
|
352
|
+
tokenIds: [10, 15, 20],
|
|
353
|
+
});
|
|
354
|
+
// successCount = 3
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**Combined Mint + Buy:**
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
// Mint 2 new + buy 3 from pool
|
|
361
|
+
const result = await sdk.buy({
|
|
362
|
+
collectionAddress: "0x1234...",
|
|
363
|
+
baseUri: "ipfs://QmTokens/",
|
|
364
|
+
amount: 2,
|
|
365
|
+
tokenIds: [5, 8, 12],
|
|
366
|
+
});
|
|
367
|
+
// successCount = 5 (2 minted + 3 bought)
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Pricing Logic:**
|
|
371
|
+
|
|
372
|
+
- Price increases with each mint/buy according to bonding curve
|
|
373
|
+
- Pool purchases use current price for each NFT
|
|
374
|
+
- Total price includes 2.5% protocol fee (configurable)
|
|
375
|
+
- Use `getTotalPrice()` to calculate cost before buying
|
|
376
|
+
|
|
377
|
+
**Gas Cost:** ~100-300k per NFT (varies by operation)
|
|
378
|
+
|
|
379
|
+
**Throws:**
|
|
380
|
+
|
|
381
|
+
- `Error` if insufficient ETH sent
|
|
382
|
+
- `Error` if collection is sold out and amount > 0
|
|
383
|
+
- `Error` if tokenIds reference non-existent or unavailable tokens
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
#### `flipSell(params)`
|
|
388
|
+
|
|
389
|
+
Sells NFTs back to the collection's liquidity pool.
|
|
390
|
+
|
|
391
|
+
**Parameters:**
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
interface EthSellParams {
|
|
395
|
+
collectionAddress: string; // Collection contract address
|
|
396
|
+
tokenIds: number[]; // Token IDs to sell
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**Returns:**
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
Promise<ConfirmResult>;
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**Example:**
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
// Sell 3 NFTs back to pool
|
|
410
|
+
const result = await sdk.flipSell({
|
|
411
|
+
collectionAddress: "0x1234...",
|
|
412
|
+
tokenIds: [5, 10, 15],
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
console.log(`Sold ${result.successCount} NFTs`);
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Important Notes:**
|
|
419
|
+
|
|
420
|
+
- You must own the NFTs you're selling
|
|
421
|
+
- NFTs must be from the specified collection
|
|
422
|
+
- Price is based on current bonding curve position
|
|
423
|
+
- Seller receives ETH minus protocol fee
|
|
424
|
+
- Sold NFTs return to the liquidity pool
|
|
425
|
+
|
|
426
|
+
**Gas Cost:** ~80-150k per NFT
|
|
427
|
+
|
|
428
|
+
**Throws:**
|
|
429
|
+
|
|
430
|
+
- `Error` if you don't own the tokens
|
|
431
|
+
- `Error` if collection doesn't have enough liquidity
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
#### `profileSell(params)`
|
|
436
|
+
|
|
437
|
+
Sells NFTs from multiple collections in a single transaction (gas-efficient).
|
|
438
|
+
|
|
439
|
+
**Parameters:**
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
interface EthProfileSellParams {
|
|
443
|
+
data: Record<string, EthSellData>; // Map of collectionId to sell data
|
|
444
|
+
slippage: number; // Not used in EVM (reserved for future)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
interface EthSellData {
|
|
448
|
+
tokenIds: number[]; // Token IDs to sell
|
|
449
|
+
collectionId: string; // Collection ID (not address!)
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
**Returns:**
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
Promise<ConfirmResult>;
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Example:**
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
// Get collection IDs first
|
|
463
|
+
const info1 = await sdk.getCollectionInfo(collectionAddr1);
|
|
464
|
+
const collectionId1 = info1[1].toString();
|
|
465
|
+
|
|
466
|
+
const info2 = await sdk.getCollectionInfo(collectionAddr2);
|
|
467
|
+
const collectionId2 = info2[1].toString();
|
|
468
|
+
|
|
469
|
+
// Sell from multiple collections
|
|
470
|
+
const result = await sdk.profileSell({
|
|
471
|
+
data: {
|
|
472
|
+
[collectionId1]: {
|
|
473
|
+
tokenIds: [1, 2, 3],
|
|
474
|
+
collectionId: collectionId1,
|
|
475
|
+
},
|
|
476
|
+
[collectionId2]: {
|
|
477
|
+
tokenIds: [5, 7],
|
|
478
|
+
collectionId: collectionId2,
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
slippage: 0, // Not used currently
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
console.log(`Sold ${result.successCount} total NFTs across collections`);
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
**Gas Savings:**
|
|
488
|
+
|
|
489
|
+
- ~30-40% cheaper than individual sells
|
|
490
|
+
- More efficient with more collections
|
|
491
|
+
|
|
492
|
+
**Use Cases:**
|
|
493
|
+
|
|
494
|
+
- Portfolio liquidation
|
|
495
|
+
- Mass selling across collections
|
|
496
|
+
- Gas optimization for power users
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
### Exporting NFT Images & Metadata (Off-chain)
|
|
502
|
+
|
|
503
|
+
These functions allow you to export NFT images and metadata from the server, enabling deployment to other platforms. They do **not** deploy collections to the blockchain directly.
|
|
504
|
+
|
|
505
|
+
#### `getCollectionPriceByTotalSupply(totalSupply)`
|
|
506
|
+
|
|
507
|
+
Calculates the price for exporting a collection based on its total supply.
|
|
508
|
+
|
|
509
|
+
**Parameters:**
|
|
510
|
+
|
|
511
|
+
- `totalSupply`: `number` โ The total number of NFTs in the collection to export.
|
|
512
|
+
|
|
513
|
+
**Returns:**
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
string // Price in wei as a string
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Example:**
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
const price = sdk.getCollectionPriceByTotalSupply(1000);
|
|
523
|
+
console.log("Export price:", ethers.utils.formatEther(price), "ETH");
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
#### `exportCollectionByEth(sessionId, totalSupply, slippage?)`
|
|
530
|
+
|
|
531
|
+
Pays the price for exporting a collection's images and metadata, and emits an event for backend monitoring. This is used for off-chain export, not for on-chain deployment.
|
|
532
|
+
|
|
533
|
+
**Parameters:**
|
|
534
|
+
|
|
535
|
+
- `sessionId`: `string` โ Unique session identifier for the export operation
|
|
536
|
+
- `totalSupply`: `number` โ Total number of NFTs to export
|
|
537
|
+
- `slippage?`: `number` (optional) โ Slippage tolerance. If omitted, a default of **3%** is used.
|
|
538
|
+
|
|
539
|
+
**Returns:**
|
|
540
|
+
|
|
541
|
+
```typescript
|
|
542
|
+
Promise<any> // Resolves when export is successful
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
**Example:**
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
// Uses default 3% slippage
|
|
549
|
+
await sdk.exportCollectionByEth("session-abc-123", 1000);
|
|
550
|
+
|
|
551
|
+
// Specify custom slippage (e.g., 5%)
|
|
552
|
+
await sdk.exportCollectionByEth("session-abc-123", 1000, 5);
|
|
553
|
+
|
|
554
|
+
console.log("Export initiated. Monitor backend for completion event.");
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
### Query Operations
|
|
560
|
+
|
|
561
|
+
#### `getCollectionInfo(collectionAddress)`
|
|
562
|
+
|
|
563
|
+
Retrieves comprehensive information about a collection.
|
|
564
|
+
|
|
565
|
+
**Parameters:**
|
|
566
|
+
|
|
567
|
+
- `collectionAddress`: `string` - The collection's contract address
|
|
568
|
+
|
|
569
|
+
**Returns:**
|
|
570
|
+
|
|
571
|
+
```typescript
|
|
572
|
+
Promise<
|
|
573
|
+
[
|
|
574
|
+
string, // [0] Factory address
|
|
575
|
+
BigNumber, // [1] Collection ID
|
|
576
|
+
string, // [2] Creator address
|
|
577
|
+
BigNumber, // [3] Max supply
|
|
578
|
+
BigNumber, // [4] Current mint count
|
|
579
|
+
boolean, // [5] Is sold out
|
|
580
|
+
BigNumber, // [6] Protocol fee (basis points)
|
|
581
|
+
string, // [7] Collection URI
|
|
582
|
+
string // [8] Base URI
|
|
583
|
+
]
|
|
584
|
+
>;
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
**Example:**
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
const info = await sdk.getCollectionInfo("0x1234...");
|
|
591
|
+
|
|
592
|
+
console.log("Factory:", info[0]);
|
|
593
|
+
console.log("Collection ID:", info[1].toString());
|
|
594
|
+
console.log("Creator:", info[2]);
|
|
595
|
+
console.log("Max Supply:", info[3].toString());
|
|
596
|
+
console.log("Minted:", info[4].toString());
|
|
597
|
+
console.log("Sold Out:", info[5]);
|
|
598
|
+
console.log("Protocol Fee BPS:", info[6].toString()); // 250 = 2.5%
|
|
599
|
+
console.log("Collection URI:", info[7]);
|
|
600
|
+
console.log("Base URI:", info[8]);
|
|
601
|
+
|
|
602
|
+
// Check progress
|
|
603
|
+
const progress = (info[4].toNumber() / info[3].toNumber()) * 100;
|
|
604
|
+
console.log(`Collection is ${progress.toFixed(1)}% minted`);
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
**Gas Cost:** Free (view function)
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
#### `getStateInfo()` (getFactoryInfo)
|
|
612
|
+
|
|
613
|
+
Gets global factory state and configuration.
|
|
614
|
+
|
|
615
|
+
**Returns:**
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
Promise<FactoryInfo>;
|
|
619
|
+
// Exact return type depends on contract implementation
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
**Example:**
|
|
623
|
+
|
|
624
|
+
```typescript
|
|
625
|
+
const factoryInfo = await sdk.getStateInfo();
|
|
626
|
+
console.log("Factory configuration:", factoryInfo);
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
**Use Cases:**
|
|
630
|
+
|
|
631
|
+
- Debugging
|
|
632
|
+
- Monitoring protocol state
|
|
633
|
+
- Admin operations
|
|
634
|
+
|
|
635
|
+
---
|
|
636
|
+
|
|
637
|
+
#### `getTotalPrice(type, collectionAddress, amount)`
|
|
638
|
+
|
|
639
|
+
Calculates the total price (including fees) for buying or selling NFTs.
|
|
640
|
+
|
|
641
|
+
**Parameters:**
|
|
642
|
+
|
|
643
|
+
- `type`: `PurcahseType.BUY | PurcahseType.SELL`
|
|
644
|
+
- `collectionAddress`: `string`
|
|
645
|
+
- `amount`: `number` - Number of NFTs
|
|
646
|
+
|
|
647
|
+
**Returns:**
|
|
648
|
+
|
|
649
|
+
```typescript
|
|
650
|
+
Promise<string>; // Price in wei as string
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
**Examples:**
|
|
654
|
+
|
|
655
|
+
```typescript
|
|
656
|
+
import { PurcahseType } from "flipmeme-sdk";
|
|
657
|
+
|
|
658
|
+
// Calculate buy price for 5 NFTs
|
|
659
|
+
const buyPrice = await sdk.getTotalPrice(
|
|
660
|
+
PurcahseType.BUY,
|
|
661
|
+
collectionAddress,
|
|
662
|
+
5
|
|
663
|
+
);
|
|
664
|
+
console.log("Cost:", ethers.utils.formatEther(buyPrice), "ETH");
|
|
665
|
+
|
|
666
|
+
// Calculate sell price for 3 NFTs
|
|
667
|
+
const sellPrice = await sdk.getTotalPrice(
|
|
668
|
+
PurcahseType.SELL,
|
|
669
|
+
collectionAddress,
|
|
670
|
+
3
|
|
671
|
+
);
|
|
672
|
+
console.log("You'll receive:", ethers.utils.formatEther(sellPrice), "ETH");
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
**Price Calculation:**
|
|
676
|
+
|
|
677
|
+
- Uses current bonding curve position
|
|
678
|
+
- Accounts for pool state (nftCount, mintCount)
|
|
679
|
+
- Includes protocol fee (2.5% by default)
|
|
680
|
+
- Sequential pricing for each NFT
|
|
681
|
+
|
|
682
|
+
**Important:**
|
|
683
|
+
|
|
684
|
+
- Prices change with each transaction
|
|
685
|
+
- Use immediately before buying/selling
|
|
686
|
+
- Consider adding slippage tolerance in UI
|
|
687
|
+
- Returns 0 if amount exceeds available supply
|
|
688
|
+
|
|
689
|
+
---
|
|
690
|
+
|
|
691
|
+
#### `calculatePrice(collectionAddress)`
|
|
692
|
+
|
|
693
|
+
Gets the current spot price for the next NFT purchase.
|
|
694
|
+
|
|
695
|
+
**Parameters:**
|
|
696
|
+
|
|
697
|
+
- `collectionAddress`: `string`
|
|
698
|
+
|
|
699
|
+
**Returns:**
|
|
700
|
+
|
|
701
|
+
```typescript
|
|
702
|
+
Promise<string>; // Current price in wei
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
**Example:**
|
|
706
|
+
|
|
707
|
+
```typescript
|
|
708
|
+
const currentPrice = await sdk.calculatePrice(collectionAddress);
|
|
709
|
+
console.log("Current price:", ethers.utils.formatEther(currentPrice), "ETH");
|
|
710
|
+
|
|
711
|
+
// Monitor price changes
|
|
712
|
+
setInterval(async () => {
|
|
713
|
+
const price = await sdk.calculatePrice(collectionAddress);
|
|
714
|
+
console.log("Price update:", ethers.utils.formatEther(price), "ETH");
|
|
715
|
+
}, 10000); // Every 10 seconds
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
**Use Cases:**
|
|
719
|
+
|
|
720
|
+
- Real-time price displays
|
|
721
|
+
- Price tracking/charts
|
|
722
|
+
- Market monitoring
|
|
723
|
+
|
|
724
|
+
---
|
|
725
|
+
|
|
726
|
+
#### `calculateTradeFee(collectionAddress, price)`
|
|
727
|
+
|
|
728
|
+
Calculates the protocol fee for a given price.
|
|
729
|
+
|
|
730
|
+
**Parameters:**
|
|
731
|
+
|
|
732
|
+
- `collectionAddress`: `string`
|
|
733
|
+
- `price`: `string` - Price in wei
|
|
734
|
+
|
|
735
|
+
**Returns:**
|
|
736
|
+
|
|
737
|
+
```typescript
|
|
738
|
+
Promise<string>; // Fee amount in wei
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
**Example:**
|
|
742
|
+
|
|
743
|
+
```typescript
|
|
744
|
+
const price = await sdk.calculatePrice(collectionAddress);
|
|
745
|
+
const fee = await sdk.calculateTradeFee(collectionAddress, price);
|
|
746
|
+
|
|
747
|
+
console.log("Price:", ethers.utils.formatEther(price), "ETH");
|
|
748
|
+
console.log("Fee (2.5%):", ethers.utils.formatEther(fee), "ETH");
|
|
749
|
+
console.log(
|
|
750
|
+
"Total:",
|
|
751
|
+
ethers.utils.formatEther(BigNumber.from(price).add(fee)),
|
|
752
|
+
"ETH"
|
|
753
|
+
);
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
---
|
|
757
|
+
|
|
758
|
+
#### `balanceOf(collectionAddress, owner)`
|
|
759
|
+
|
|
760
|
+
Gets the number of NFTs owned by an address in a collection.
|
|
761
|
+
|
|
762
|
+
**Parameters:**
|
|
763
|
+
|
|
764
|
+
- `collectionAddress`: `string`
|
|
765
|
+
- `owner`: `string` - Wallet address
|
|
766
|
+
|
|
767
|
+
**Returns:**
|
|
768
|
+
|
|
769
|
+
```typescript
|
|
770
|
+
Promise<string>; // Number of NFTs owned
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
**Example:**
|
|
774
|
+
|
|
775
|
+
```typescript
|
|
776
|
+
const userAddress = await signer.getAddress();
|
|
777
|
+
const balance = await sdk.balanceOf(collectionAddress, userAddress);
|
|
778
|
+
|
|
779
|
+
console.log(`You own ${balance} NFTs from this collection`);
|
|
780
|
+
|
|
781
|
+
if (parseInt(balance) > 0) {
|
|
782
|
+
console.log("You're a holder! ๐");
|
|
783
|
+
}
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
---
|
|
787
|
+
|
|
788
|
+
#### `ownerOf(collectionAddress, tokenId)`
|
|
789
|
+
|
|
790
|
+
Gets the owner of a specific NFT.
|
|
791
|
+
|
|
792
|
+
**Parameters:**
|
|
793
|
+
|
|
794
|
+
- `collectionAddress`: `string`
|
|
795
|
+
- `tokenId`: `number`
|
|
796
|
+
|
|
797
|
+
**Returns:**
|
|
798
|
+
|
|
799
|
+
```typescript
|
|
800
|
+
Promise<string>; // Owner's address
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
**Example:**
|
|
804
|
+
|
|
805
|
+
```typescript
|
|
806
|
+
const owner = await sdk.ownerOf(collectionAddress, 42);
|
|
807
|
+
console.log(`Token #42 is owned by ${owner}`);
|
|
808
|
+
|
|
809
|
+
// Check if you own a token
|
|
810
|
+
const yourAddress = await signer.getAddress();
|
|
811
|
+
if (owner.toLowerCase() === yourAddress.toLowerCase()) {
|
|
812
|
+
console.log("You own this token!");
|
|
813
|
+
}
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
**Throws:**
|
|
817
|
+
|
|
818
|
+
- `Error` if token doesn't exist
|
|
819
|
+
- `Error` if token was burned
|
|
820
|
+
|
|
821
|
+
---
|
|
822
|
+
|
|
823
|
+
#### `getTokenURI(collectionAddress, tokenId)`
|
|
824
|
+
|
|
825
|
+
Gets the metadata URI for a specific NFT.
|
|
826
|
+
|
|
827
|
+
**Parameters:**
|
|
828
|
+
|
|
829
|
+
- `collectionAddress`: `string`
|
|
830
|
+
- `tokenId`: `number`
|
|
831
|
+
|
|
832
|
+
**Returns:**
|
|
833
|
+
|
|
834
|
+
```typescript
|
|
835
|
+
Promise<string>; // Token metadata URI
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
**Example:**
|
|
839
|
+
|
|
840
|
+
```typescript
|
|
841
|
+
const tokenUri = await sdk.getTokenURI(collectionAddress, 10);
|
|
842
|
+
console.log("Token URI:", tokenUri);
|
|
843
|
+
// e.g., "ipfs://QmHash/10.json"
|
|
844
|
+
|
|
845
|
+
// Fetch and display metadata
|
|
846
|
+
const response = await fetch(
|
|
847
|
+
tokenUri.replace("ipfs://", "https://ipfs.io/ipfs/")
|
|
848
|
+
);
|
|
849
|
+
const metadata = await response.json();
|
|
850
|
+
console.log("Name:", metadata.name);
|
|
851
|
+
console.log("Image:", metadata.image);
|
|
852
|
+
console.log("Attributes:", metadata.attributes);
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
---
|
|
856
|
+
|
|
857
|
+
#### `totalSupply(collectionAddress)`
|
|
858
|
+
|
|
859
|
+
Gets the total number of NFTs minted in a collection.
|
|
860
|
+
|
|
861
|
+
**Parameters:**
|
|
862
|
+
|
|
863
|
+
- `collectionAddress`: `string`
|
|
864
|
+
|
|
865
|
+
**Returns:**
|
|
866
|
+
|
|
867
|
+
```typescript
|
|
868
|
+
Promise<string>; // Total minted count
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
**Example:**
|
|
872
|
+
|
|
873
|
+
```typescript
|
|
874
|
+
const total = await sdk.totalSupply(collectionAddress);
|
|
875
|
+
const info = await sdk.getCollectionInfo(collectionAddress);
|
|
876
|
+
const maxSupply = info[3].toString();
|
|
877
|
+
|
|
878
|
+
console.log(`${total} / ${maxSupply} minted`);
|
|
879
|
+
|
|
880
|
+
const percentMinted = (parseInt(total) / parseInt(maxSupply)) * 100;
|
|
881
|
+
console.log(`${percentMinted.toFixed(1)}% of collection minted`);
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
---
|
|
885
|
+
|
|
886
|
+
### Utility Functions
|
|
887
|
+
|
|
888
|
+
#### `moveLiquidity(collectionAddress)`
|
|
889
|
+
|
|
890
|
+
Moves liquidity from the pool when a collection is sold out. Distributes funds to LP providers, creator, and sellout bonus.
|
|
891
|
+
|
|
892
|
+
**Parameters:**
|
|
893
|
+
|
|
894
|
+
- `collectionAddress`: `string`
|
|
895
|
+
|
|
896
|
+
**Returns:**
|
|
897
|
+
|
|
898
|
+
```typescript
|
|
899
|
+
Promise<void>;
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
**Example:**
|
|
903
|
+
|
|
904
|
+
```typescript
|
|
905
|
+
// Check if sold out first
|
|
906
|
+
const info = await sdk.getCollectionInfo(collectionAddress);
|
|
907
|
+
const isSoldOut = info[5];
|
|
908
|
+
|
|
909
|
+
if (isSoldOut) {
|
|
910
|
+
await sdk.moveLiquidity(collectionAddress);
|
|
911
|
+
console.log("โ
Liquidity moved successfully!");
|
|
912
|
+
console.log("Distribution:");
|
|
913
|
+
console.log(" - 85% โ LP Provider");
|
|
914
|
+
console.log(" - 10% โ LP Sellout Bonus");
|
|
915
|
+
console.log(" - 5% โ Creator");
|
|
916
|
+
} else {
|
|
917
|
+
console.log("โ Collection not sold out yet");
|
|
918
|
+
}
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
**Distribution:**
|
|
922
|
+
|
|
923
|
+
- **85%** to LP Provider
|
|
924
|
+
- **10%** to LP Sellout Bonus Pool
|
|
925
|
+
- **5%** to Collection Creator
|
|
926
|
+
|
|
927
|
+
**Important:**
|
|
928
|
+
|
|
929
|
+
- Can only be called once per collection
|
|
930
|
+
- Collection must be fully sold out
|
|
931
|
+
- Transaction will revert if called prematurely
|
|
932
|
+
- Emits `LiquidityMoved` event
|
|
933
|
+
|
|
934
|
+
**Gas Cost:** ~150-250k gas
|
|
935
|
+
|
|
936
|
+
---
|
|
937
|
+
|
|
938
|
+
#### `buyCredit(params)`
|
|
939
|
+
|
|
940
|
+
Purchases credits by sending ETH to the admin address. Credits can be used for platform features.
|
|
941
|
+
|
|
942
|
+
**Parameters:**
|
|
943
|
+
|
|
944
|
+
```typescript
|
|
945
|
+
interface EthTransferParams {
|
|
946
|
+
amount: string; // Amount in ETH (e.g., "0.1")
|
|
947
|
+
}
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
**Returns:**
|
|
951
|
+
|
|
952
|
+
```typescript
|
|
953
|
+
Promise<string>; // Transaction hash
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
**Example:**
|
|
957
|
+
|
|
958
|
+
```typescript
|
|
959
|
+
// Buy 0.5 ETH worth of credits
|
|
960
|
+
const txHash = await sdk.buyCredit({
|
|
961
|
+
amount: "0.5",
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
console.log("Transaction hash:", txHash);
|
|
965
|
+
console.log("View on explorer:", `https://basescan.org/tx/${txHash}`);
|
|
966
|
+
|
|
967
|
+
// Wait for confirmation
|
|
968
|
+
const receipt = await provider.waitForTransaction(txHash);
|
|
969
|
+
console.log("Credits purchased! Confirmations:", receipt.confirmations);
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
**Use Cases:**
|
|
973
|
+
|
|
974
|
+
- Premium features
|
|
975
|
+
- Gas-less transactions
|
|
976
|
+
- Platform credits
|
|
977
|
+
|
|
978
|
+
**Note:** The amount is automatically converted from ETH to wei internally.
|
|
979
|
+
|
|
980
|
+
---
|
|
981
|
+
|
|
982
|
+
#### `claimReferralReward(userAddress, amount)`
|
|
983
|
+
|
|
984
|
+
Claims referral rewards by transferring ETH from the reward wallet to a user. This method should be called by the backend/admin who has access to the reward wallet's private key.
|
|
985
|
+
|
|
986
|
+
**โ ๏ธ Important:** The SDK signer must be the reward wallet, not the user's wallet. This is typically used in a backend service.
|
|
987
|
+
|
|
988
|
+
**Parameters:**
|
|
989
|
+
|
|
990
|
+
```typescript
|
|
991
|
+
userAddress: string; // The address of the user claiming the reward
|
|
992
|
+
amount: string; // Amount in ETH (e.g., "0.1")
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
**Returns:**
|
|
996
|
+
|
|
997
|
+
```typescript
|
|
998
|
+
Promise<string>; // Transaction hash
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
**Example:**
|
|
1002
|
+
|
|
1003
|
+
```typescript
|
|
1004
|
+
// Backend/Admin setup with reward wallet
|
|
1005
|
+
const rewardWalletSigner = new ethers.Wallet(
|
|
1006
|
+
REWARD_WALLET_PRIVATE_KEY,
|
|
1007
|
+
provider
|
|
1008
|
+
);
|
|
1009
|
+
|
|
1010
|
+
const adminSDK = new FlipmemeSDK(BlockchainType.ETHEREUM, PLATFORM.PROD, {
|
|
1011
|
+
signer: rewardWalletSigner, // Reward wallet signer, not user's
|
|
1012
|
+
chainId: 8453,
|
|
1013
|
+
provider,
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
// Claim referral reward for a user
|
|
1017
|
+
const userAddress = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb";
|
|
1018
|
+
const rewardAmount = "0.05"; // 0.05 ETH
|
|
1019
|
+
|
|
1020
|
+
const txHash = await adminSDK.claimReferralReward(userAddress, rewardAmount);
|
|
1021
|
+
|
|
1022
|
+
console.log("Referral reward claimed!");
|
|
1023
|
+
console.log("Transaction hash:", txHash);
|
|
1024
|
+
console.log("View on BaseScan:", `https://basescan.org/tx/${txHash}`);
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
**Flow:**
|
|
1028
|
+
|
|
1029
|
+
1. **User earns referral rewards** - Backend calculates rewards based on trading fees
|
|
1030
|
+
2. **Team funds reward wallet** - Manually add ETH to the designated reward wallet
|
|
1031
|
+
3. **User requests claim** - User initiates claim through your application
|
|
1032
|
+
4. **Backend processes claim** - Your backend calls this method with the reward wallet signer
|
|
1033
|
+
5. **ETH transferred** - Reward wallet sends ETH directly to user's address
|
|
1034
|
+
|
|
1035
|
+
**Use Cases:**
|
|
1036
|
+
|
|
1037
|
+
- Referral commission payouts
|
|
1038
|
+
- Trading fee rebates
|
|
1039
|
+
- Affiliate rewards
|
|
1040
|
+
- Promotional incentives
|
|
1041
|
+
|
|
1042
|
+
**Security Considerations:**
|
|
1043
|
+
|
|
1044
|
+
- The reward wallet private key should be securely stored (e.g., AWS Secrets Manager, HashiCorp Vault)
|
|
1045
|
+
- Implement rate limiting and claim verification in your backend
|
|
1046
|
+
- Keep reward wallet funded but with limited balance to minimize risk
|
|
1047
|
+
- Log all claim transactions for audit purposes
|
|
1048
|
+
- Consider implementing multi-signature for large reward wallets
|
|
1049
|
+
|
|
1050
|
+
**Note:** This does NOT interact with smart contracts or treasury - it's a simple ETH transfer from a team-controlled wallet to the user, providing maximum flexibility and minimal risk.
|
|
1051
|
+
|
|
1052
|
+
---
|
|
1053
|
+
|
|
1054
|
+
## Types Reference
|
|
1055
|
+
|
|
1056
|
+
### Core Types
|
|
1057
|
+
|
|
1058
|
+
```typescript
|
|
1059
|
+
// Blockchain selection
|
|
1060
|
+
enum BlockchainType {
|
|
1061
|
+
SOLANA = "solana",
|
|
1062
|
+
ETHEREUM = "ethereum",
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// Platform environment
|
|
1066
|
+
enum PLATFORM {
|
|
1067
|
+
DEV = "Dev",
|
|
1068
|
+
STAGE = "Stage",
|
|
1069
|
+
PROD = "Prod",
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// Purchase type for pricing
|
|
1073
|
+
enum PurcahseType {
|
|
1074
|
+
BUY = "buy",
|
|
1075
|
+
SELL = "sell",
|
|
1076
|
+
}
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
### Configuration Types
|
|
1080
|
+
|
|
1081
|
+
```typescript
|
|
1082
|
+
interface EthereumConfig {
|
|
1083
|
+
signer?: ethers.Signer; // Wallet for signing transactions
|
|
1084
|
+
chainId: number; // Network chain ID (8453 for Base)
|
|
1085
|
+
provider: ethers.providers.BaseProvider; // RPC provider instance
|
|
1086
|
+
}
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
### Collection Types
|
|
1090
|
+
|
|
1091
|
+
```typescript
|
|
1092
|
+
interface CollectionParams {
|
|
1093
|
+
sessionId: string; // Unique identifier for this collection creation
|
|
1094
|
+
name: string; // Collection display name
|
|
1095
|
+
symbol?: string; // Token symbol (optional)
|
|
1096
|
+
tokenUri: string; // Collection metadata URI
|
|
1097
|
+
totalSupply: number; // Maximum number of NFTs
|
|
1098
|
+
premint?: number; // Number to pre-mint for creator (default: 0)
|
|
1099
|
+
startPrice?: string; // Starting price in wei (default: 10^15)
|
|
1100
|
+
endPrice?: string; // Ending price in wei (default: 10^16)
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
interface CreateCollectionResponse {
|
|
1104
|
+
collectionId: string; // Unique on-chain ID
|
|
1105
|
+
collectionAddress: string; // Deployed contract address
|
|
1106
|
+
}
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
### Trading Types
|
|
1110
|
+
|
|
1111
|
+
```typescript
|
|
1112
|
+
interface EthBuyParams {
|
|
1113
|
+
collectionAddress: string; // Collection contract address
|
|
1114
|
+
baseUri?: string; // Token metadata base URI (required if minting)
|
|
1115
|
+
amount: number; // Number of new NFTs to mint
|
|
1116
|
+
tokenIds: number[]; // Existing token IDs to buy from pool
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
interface EthSellParams {
|
|
1120
|
+
collectionAddress: string; // Collection contract address
|
|
1121
|
+
tokenIds: number[]; // Token IDs to sell
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
interface EthProfileSellParams {
|
|
1125
|
+
data: Record<string, EthSellData>; // Collection ID โ sell data mapping
|
|
1126
|
+
slippage: number; // Not currently used (reserved)
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
interface EthSellData {
|
|
1130
|
+
tokenIds: number[]; // Token IDs to sell
|
|
1131
|
+
collectionId: string; // Collection ID (not address)
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
interface ConfirmResult {
|
|
1135
|
+
successCount: number; // Number of successful operations
|
|
1136
|
+
failedCount: number; // Number of failed operations
|
|
1137
|
+
}
|
|
1138
|
+
```
|
|
1139
|
+
|
|
1140
|
+
### Utility Types
|
|
1141
|
+
|
|
1142
|
+
```typescript
|
|
1143
|
+
interface EthTransferParams {
|
|
1144
|
+
amount: string; // Amount in ETH (e.g., "0.1")
|
|
1145
|
+
}
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
---
|
|
1149
|
+
|
|
1150
|
+
## Constants & Configuration
|
|
1151
|
+
|
|
1152
|
+
### Network Addresses
|
|
1153
|
+
|
|
1154
|
+
```typescript
|
|
1155
|
+
interface ETHAddresses {
|
|
1156
|
+
TREASURY: string; // Protocol treasury
|
|
1157
|
+
LP_SELL_OUT: string; // LP sellout bonus pool
|
|
1158
|
+
LP_PROVIDER: string; // LP provider address
|
|
1159
|
+
ROYALTY: string; // Royalty recipient
|
|
1160
|
+
FACTORY: string; // Factory contract address
|
|
1161
|
+
PROTOCOL_FEE_BPS: number; // Protocol fee in basis points (250 = 2.5%)
|
|
1162
|
+
ADMIN_FOR_CREDIT_BUY: string; // Credit purchase recipient
|
|
1163
|
+
REFERRAL_CLAIM: string; // Referral rewards address
|
|
1164
|
+
}
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
### Default Configuration (DEV)
|
|
1168
|
+
|
|
1169
|
+
```typescript
|
|
1170
|
+
const ETH_DEV = {
|
|
1171
|
+
FACTORY: "0xE9fda705f7cD9D5bf11cffF0997A9ef74927C834",
|
|
1172
|
+
TREASURY: "0x26ee601042126aA53fA212983f96C3571fab8e5E",
|
|
1173
|
+
LP_SELL_OUT: "0x19A1f2d12A7DbD5834787BB98373d93D41682db8",
|
|
1174
|
+
LP_PROVIDER: "0xc796E2c7fD16a8033D55fA61Ab575E2Cd27B4d47",
|
|
1175
|
+
ROYALTY: "0x9c8969323255a476BD5A249DBC9f091728dbD671",
|
|
1176
|
+
ADMIN_FOR_CREDIT_BUY: "0x63B3Cc8944B917AAc841429097F28be09AB636a7",
|
|
1177
|
+
REFERRAL_CLAIM: "0xaE1f8271458285b71b465e4aC00E23a21cD8aF42",
|
|
1178
|
+
PROTOCOL_FEE_BPS: 250, // 2.5%
|
|
1179
|
+
};
|
|
1180
|
+
```
|
|
1181
|
+
|
|
1182
|
+
### Supported Networks
|
|
1183
|
+
|
|
1184
|
+
| Network | Chain ID | RPC URL | Explorer |
|
|
1185
|
+
| ---------------------- | -------- | ------------------------ | ---------------------------- |
|
|
1186
|
+
| Base Mainnet | 8453 | https://mainnet.base.org | https://basescan.org |
|
|
1187
|
+
| Base Sepolia (Testnet) | 84532 | https://sepolia.base.org | https://sepolia.basescan.org |
|
|
1188
|
+
|
|
1189
|
+
---
|
|
1190
|
+
|
|
1191
|
+
## Advanced Usage
|
|
1192
|
+
|
|
1193
|
+
### Price Monitoring System
|
|
1194
|
+
|
|
1195
|
+
```typescript
|
|
1196
|
+
class PriceMonitor {
|
|
1197
|
+
private sdk: FlipmemeSDK;
|
|
1198
|
+
private collectionAddress: string;
|
|
1199
|
+
|
|
1200
|
+
constructor(sdk: FlipmemeSDK, collectionAddress: string) {
|
|
1201
|
+
this.sdk = sdk;
|
|
1202
|
+
this.collectionAddress = collectionAddress;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
async getCurrentMetrics() {
|
|
1206
|
+
const [info, price, totalSupply, balance] = await Promise.all([
|
|
1207
|
+
this.sdk.getCollectionInfo(this.collectionAddress),
|
|
1208
|
+
this.sdk.calculatePrice(this.collectionAddress),
|
|
1209
|
+
this.sdk.totalSupply(this.collectionAddress),
|
|
1210
|
+
this.sdk.balanceOf(
|
|
1211
|
+
this.collectionAddress,
|
|
1212
|
+
await this.getSigner().getAddress()
|
|
1213
|
+
),
|
|
1214
|
+
]);
|
|
1215
|
+
|
|
1216
|
+
return {
|
|
1217
|
+
maxSupply: info[3].toNumber(),
|
|
1218
|
+
currentMint: info[4].toNumber(),
|
|
1219
|
+
isSoldOut: info[5],
|
|
1220
|
+
currentPrice: ethers.utils.formatEther(price),
|
|
1221
|
+
totalSupply: parseInt(totalSupply),
|
|
1222
|
+
userBalance: parseInt(balance),
|
|
1223
|
+
progress: (info[4].toNumber() / info[3].toNumber()) * 100,
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
async startMonitoring(intervalMs: number = 5000) {
|
|
1228
|
+
console.log("Starting price monitoring...");
|
|
1229
|
+
|
|
1230
|
+
setInterval(async () => {
|
|
1231
|
+
const metrics = await this.getCurrentMetrics();
|
|
1232
|
+
console.log(`
|
|
1233
|
+
Progress: ${metrics.progress.toFixed(1)}%
|
|
1234
|
+
Price: ${metrics.currentPrice} ETH
|
|
1235
|
+
Minted: ${metrics.currentMint}/${metrics.maxSupply}
|
|
1236
|
+
Your Balance: ${metrics.userBalance}
|
|
1237
|
+
`);
|
|
1238
|
+
}, intervalMs);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
private getSigner(): ethers.Signer {
|
|
1242
|
+
// Return your signer instance
|
|
1243
|
+
return this.sdk["ethereum"]?.["config"].signer!;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
// Usage
|
|
1248
|
+
const monitor = new PriceMonitor(sdk, collectionAddress);
|
|
1249
|
+
await monitor.startMonitoring(10000); // Update every 10 seconds
|
|
1250
|
+
```
|
|
1251
|
+
|
|
1252
|
+
### Batch NFT Operations
|
|
1253
|
+
|
|
1254
|
+
```typescript
|
|
1255
|
+
async function batchMintAndDistribute(
|
|
1256
|
+
sdk: FlipmemeSDK,
|
|
1257
|
+
collectionAddress: string,
|
|
1258
|
+
recipients: string[],
|
|
1259
|
+
baseUri: string
|
|
1260
|
+
) {
|
|
1261
|
+
console.log(`Minting ${recipients.length} NFTs...`);
|
|
1262
|
+
|
|
1263
|
+
// 1. Mint all NFTs to your address
|
|
1264
|
+
const mintResult = await sdk.buy({
|
|
1265
|
+
collectionAddress,
|
|
1266
|
+
baseUri,
|
|
1267
|
+
amount: recipients.length,
|
|
1268
|
+
tokenIds: [],
|
|
1269
|
+
});
|
|
1270
|
+
|
|
1271
|
+
console.log(`Minted ${mintResult.successCount} NFTs`);
|
|
1272
|
+
|
|
1273
|
+
// 2. Get the collection contract
|
|
1274
|
+
const collection = Collection__factory.connect(
|
|
1275
|
+
collectionAddress,
|
|
1276
|
+
sdk["ethereum"]!["config"].signer!
|
|
1277
|
+
);
|
|
1278
|
+
|
|
1279
|
+
// 3. Get current total supply to determine token IDs
|
|
1280
|
+
const totalSupply = await sdk.totalSupply(collectionAddress);
|
|
1281
|
+
const firstTokenId = parseInt(totalSupply) - recipients.length;
|
|
1282
|
+
|
|
1283
|
+
// 4. Transfer to recipients
|
|
1284
|
+
console.log("Distributing NFTs...");
|
|
1285
|
+
for (let i = 0; i < recipients.length; i++) {
|
|
1286
|
+
const tokenId = firstTokenId + i;
|
|
1287
|
+
const recipient = recipients[i];
|
|
1288
|
+
|
|
1289
|
+
console.log(`Transferring token #${tokenId} to ${recipient}...`);
|
|
1290
|
+
const tx = await collection.transferFrom(
|
|
1291
|
+
await sdk["ethereum"]!["config"].signer!.getAddress(),
|
|
1292
|
+
recipient,
|
|
1293
|
+
tokenId
|
|
1294
|
+
);
|
|
1295
|
+
await tx.wait();
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
console.log("โ
All NFTs distributed!");
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// Usage
|
|
1302
|
+
await batchMintAndDistribute(
|
|
1303
|
+
sdk,
|
|
1304
|
+
collectionAddress,
|
|
1305
|
+
[
|
|
1306
|
+
"0x1111111111111111111111111111111111111111",
|
|
1307
|
+
"0x2222222222222222222222222222222222222222",
|
|
1308
|
+
"0x3333333333333333333333333333333333333333",
|
|
1309
|
+
],
|
|
1310
|
+
"ipfs://QmYourTokens/"
|
|
1311
|
+
);
|
|
1312
|
+
```
|
|
1313
|
+
|
|
1314
|
+
### Portfolio Management
|
|
1315
|
+
|
|
1316
|
+
```typescript
|
|
1317
|
+
interface PortfolioItem {
|
|
1318
|
+
collectionAddress: string;
|
|
1319
|
+
collectionId: string;
|
|
1320
|
+
tokenIds: number[];
|
|
1321
|
+
currentValue: string;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
async function getPortfolio(
|
|
1325
|
+
sdk: FlipmemeSDK,
|
|
1326
|
+
collections: string[],
|
|
1327
|
+
owner: string
|
|
1328
|
+
): Promise<PortfolioItem[]> {
|
|
1329
|
+
const portfolio: PortfolioItem[] = [];
|
|
1330
|
+
|
|
1331
|
+
for (const collectionAddress of collections) {
|
|
1332
|
+
// Get collection info
|
|
1333
|
+
const info = await sdk.getCollectionInfo(collectionAddress);
|
|
1334
|
+
const collectionId = info[1].toString();
|
|
1335
|
+
const totalMinted = info[4].toNumber();
|
|
1336
|
+
|
|
1337
|
+
// Find owned tokens
|
|
1338
|
+
const tokenIds: number[] = [];
|
|
1339
|
+
for (let i = 0; i < totalMinted; i++) {
|
|
1340
|
+
try {
|
|
1341
|
+
const tokenOwner = await sdk.ownerOf(collectionAddress, i);
|
|
1342
|
+
if (tokenOwner.toLowerCase() === owner.toLowerCase()) {
|
|
1343
|
+
tokenIds.push(i);
|
|
1344
|
+
}
|
|
1345
|
+
} catch {
|
|
1346
|
+
continue;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
if (tokenIds.length > 0) {
|
|
1351
|
+
// Calculate current value
|
|
1352
|
+
const value = await sdk.getTotalPrice(
|
|
1353
|
+
PurcahseType.SELL,
|
|
1354
|
+
collectionAddress,
|
|
1355
|
+
tokenIds.length
|
|
1356
|
+
);
|
|
1357
|
+
|
|
1358
|
+
portfolio.push({
|
|
1359
|
+
collectionAddress,
|
|
1360
|
+
collectionId,
|
|
1361
|
+
tokenIds,
|
|
1362
|
+
currentValue: ethers.utils.formatEther(value),
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
return portfolio;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
// Usage
|
|
1371
|
+
const myPortfolio = await getPortfolio(
|
|
1372
|
+
sdk,
|
|
1373
|
+
[collection1, collection2, collection3],
|
|
1374
|
+
await signer.getAddress()
|
|
1375
|
+
);
|
|
1376
|
+
|
|
1377
|
+
console.log("Your Portfolio:");
|
|
1378
|
+
myPortfolio.forEach((item, idx) => {
|
|
1379
|
+
console.log(`\n${idx + 1}. Collection ${item.collectionId}`);
|
|
1380
|
+
console.log(` NFTs owned: ${item.tokenIds.length}`);
|
|
1381
|
+
console.log(` Current value: ${item.currentValue} ETH`);
|
|
1382
|
+
console.log(` Token IDs: ${item.tokenIds.join(", ")}`);
|
|
1383
|
+
});
|
|
1384
|
+
```
|
|
1385
|
+
|
|
1386
|
+
### Automated Trading Bot
|
|
1387
|
+
|
|
1388
|
+
```typescript
|
|
1389
|
+
class FlipBot {
|
|
1390
|
+
private sdk: FlipmemeSDK;
|
|
1391
|
+
private collectionAddress: string;
|
|
1392
|
+
private targetProfit: number; // in percentage
|
|
1393
|
+
|
|
1394
|
+
constructor(
|
|
1395
|
+
sdk: FlipmemeSDK,
|
|
1396
|
+
collectionAddress: string,
|
|
1397
|
+
targetProfit: number = 10
|
|
1398
|
+
) {
|
|
1399
|
+
this.sdk = sdk;
|
|
1400
|
+
this.collectionAddress = collectionAddress;
|
|
1401
|
+
this.targetProfit = targetProfit;
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
async checkArbitrage() {
|
|
1405
|
+
const currentPrice = await this.sdk.calculatePrice(this.collectionAddress);
|
|
1406
|
+
const info = await this.sdk.getCollectionInfo(this.collectionAddress);
|
|
1407
|
+
|
|
1408
|
+
// Check if there are NFTs in the pool
|
|
1409
|
+
// (You'd need to add a method to check pool size)
|
|
1410
|
+
|
|
1411
|
+
const buyPrice = await this.sdk.getTotalPrice(
|
|
1412
|
+
PurcahseType.BUY,
|
|
1413
|
+
this.collectionAddress,
|
|
1414
|
+
1
|
|
1415
|
+
);
|
|
1416
|
+
|
|
1417
|
+
const sellPrice = await this.sdk.getTotalPrice(
|
|
1418
|
+
PurcahseType.SELL,
|
|
1419
|
+
this.collectionAddress,
|
|
1420
|
+
1
|
|
1421
|
+
);
|
|
1422
|
+
|
|
1423
|
+
const buyPriceEth = parseFloat(ethers.utils.formatEther(buyPrice));
|
|
1424
|
+
const sellPriceEth = parseFloat(ethers.utils.formatEther(sellPrice));
|
|
1425
|
+
const profit = ((sellPriceEth - buyPriceEth) / buyPriceEth) * 100;
|
|
1426
|
+
|
|
1427
|
+
console.log(`
|
|
1428
|
+
Buy Price: ${buyPriceEth.toFixed(6)} ETH
|
|
1429
|
+
Sell Price: ${sellPriceEth.toFixed(6)} ETH
|
|
1430
|
+
Potential Profit: ${profit.toFixed(2)}%
|
|
1431
|
+
`);
|
|
1432
|
+
|
|
1433
|
+
return {
|
|
1434
|
+
buyPrice: buyPriceEth,
|
|
1435
|
+
sellPrice: sellPriceEth,
|
|
1436
|
+
profit,
|
|
1437
|
+
shouldTrade: profit >= this.targetProfit,
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
async executeTrade() {
|
|
1442
|
+
const analysis = await this.checkArbitrage();
|
|
1443
|
+
|
|
1444
|
+
if (analysis.shouldTrade) {
|
|
1445
|
+
console.log(
|
|
1446
|
+
`โ
Profit opportunity detected: ${analysis.profit.toFixed(2)}%`
|
|
1447
|
+
);
|
|
1448
|
+
console.log("Executing trade...");
|
|
1449
|
+
|
|
1450
|
+
// Buy
|
|
1451
|
+
const buyResult = await this.sdk.buy({
|
|
1452
|
+
collectionAddress: this.collectionAddress,
|
|
1453
|
+
baseUri: "",
|
|
1454
|
+
amount: 0,
|
|
1455
|
+
tokenIds: [0], // You'd need to know available token IDs
|
|
1456
|
+
});
|
|
1457
|
+
|
|
1458
|
+
console.log("Bought NFT");
|
|
1459
|
+
|
|
1460
|
+
// Sell immediately
|
|
1461
|
+
const sellResult = await this.sdk.flipSell({
|
|
1462
|
+
collectionAddress: this.collectionAddress,
|
|
1463
|
+
tokenIds: [0],
|
|
1464
|
+
});
|
|
1465
|
+
|
|
1466
|
+
console.log(`โ
Trade complete! Profit: ${analysis.profit.toFixed(2)}%`);
|
|
1467
|
+
return true;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
return false;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
async monitor(intervalMs: number = 10000) {
|
|
1474
|
+
console.log(`Starting bot with ${this.targetProfit}% profit target...`);
|
|
1475
|
+
|
|
1476
|
+
setInterval(async () => {
|
|
1477
|
+
try {
|
|
1478
|
+
await this.executeTrade();
|
|
1479
|
+
} catch (error) {
|
|
1480
|
+
console.error("Trade failed:", error);
|
|
1481
|
+
}
|
|
1482
|
+
}, intervalMs);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// Usage (use at your own risk!)
|
|
1487
|
+
const bot = new FlipBot(sdk, collectionAddress, 15); // 15% profit target
|
|
1488
|
+
await bot.monitor(5000); // Check every 5 seconds
|
|
1489
|
+
```
|
|
1490
|
+
|
|
1491
|
+
---
|
|
1492
|
+
|
|
1493
|
+
## Error Handling
|
|
1494
|
+
|
|
1495
|
+
### Common Errors
|
|
1496
|
+
|
|
1497
|
+
#### Insufficient Funds
|
|
1498
|
+
|
|
1499
|
+
```typescript
|
|
1500
|
+
try {
|
|
1501
|
+
await sdk.buy({
|
|
1502
|
+
collectionAddress,
|
|
1503
|
+
baseUri: "ipfs://...",
|
|
1504
|
+
amount: 10,
|
|
1505
|
+
tokenIds: [],
|
|
1506
|
+
});
|
|
1507
|
+
} catch (error) {
|
|
1508
|
+
if (error.code === "INSUFFICIENT_FUNDS") {
|
|
1509
|
+
console.error("You don't have enough ETH for this transaction");
|
|
1510
|
+
console.error(
|
|
1511
|
+
"Required:",
|
|
1512
|
+
ethers.utils.formatEther(error.transaction?.value || "0")
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
```
|
|
1517
|
+
|
|
1518
|
+
#### Collection Sold Out
|
|
1519
|
+
|
|
1520
|
+
```typescript
|
|
1521
|
+
try {
|
|
1522
|
+
await sdk.buy({
|
|
1523
|
+
/* ... */
|
|
1524
|
+
});
|
|
1525
|
+
} catch (error) {
|
|
1526
|
+
if (
|
|
1527
|
+
error.message.includes("sold out") ||
|
|
1528
|
+
error.message.includes("max supply")
|
|
1529
|
+
) {
|
|
1530
|
+
console.error("Collection is sold out!");
|
|
1531
|
+
|
|
1532
|
+
// Try buying from pool instead
|
|
1533
|
+
await sdk.buy({
|
|
1534
|
+
collectionAddress,
|
|
1535
|
+
baseUri: "",
|
|
1536
|
+
amount: 0,
|
|
1537
|
+
tokenIds: [availableTokenId],
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
```
|
|
1542
|
+
|
|
1543
|
+
#### User Rejected Transaction
|
|
1544
|
+
|
|
1545
|
+
```typescript
|
|
1546
|
+
try {
|
|
1547
|
+
await sdk.createCollection({
|
|
1548
|
+
/* ... */
|
|
1549
|
+
});
|
|
1550
|
+
} catch (error) {
|
|
1551
|
+
if (error.code === 4001 || error.code === "ACTION_REJECTED") {
|
|
1552
|
+
console.log("User rejected the transaction");
|
|
1553
|
+
// Show user-friendly message
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
```
|
|
1557
|
+
|
|
1558
|
+
### Comprehensive Error Handler
|
|
1559
|
+
|
|
1560
|
+
```typescript
|
|
1561
|
+
async function safeExecute<T>(
|
|
1562
|
+
operation: () => Promise<T>,
|
|
1563
|
+
operationName: string
|
|
1564
|
+
): Promise<T | null> {
|
|
1565
|
+
try {
|
|
1566
|
+
console.log(`Executing ${operationName}...`);
|
|
1567
|
+
const result = await operation();
|
|
1568
|
+
console.log(`โ
${operationName} successful`);
|
|
1569
|
+
return result;
|
|
1570
|
+
} catch (error: any) {
|
|
1571
|
+
console.error(`โ ${operationName} failed:`);
|
|
1572
|
+
|
|
1573
|
+
// Ethers errors
|
|
1574
|
+
if (error.code) {
|
|
1575
|
+
switch (error.code) {
|
|
1576
|
+
case "INSUFFICIENT_FUNDS":
|
|
1577
|
+
console.error("Insufficient funds for transaction");
|
|
1578
|
+
break;
|
|
1579
|
+
case "ACTION_REJECTED":
|
|
1580
|
+
console.error("User rejected transaction");
|
|
1581
|
+
break;
|
|
1582
|
+
case "UNPREDICTABLE_GAS_LIMIT":
|
|
1583
|
+
console.error("Cannot estimate gas - transaction may fail");
|
|
1584
|
+
break;
|
|
1585
|
+
case "NETWORK_ERROR":
|
|
1586
|
+
console.error("Network error - check your connection");
|
|
1587
|
+
break;
|
|
1588
|
+
default:
|
|
1589
|
+
console.error(`Error code: ${error.code}`);
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
// Contract revert errors
|
|
1594
|
+
if (error.reason) {
|
|
1595
|
+
console.error("Contract error:", error.reason);
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// Full error details
|
|
1599
|
+
console.error("Full error:", error.message);
|
|
1600
|
+
|
|
1601
|
+
return null;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
// Usage
|
|
1606
|
+
const result = await safeExecute(
|
|
1607
|
+
() =>
|
|
1608
|
+
sdk.buy({
|
|
1609
|
+
collectionAddress,
|
|
1610
|
+
baseUri: "ipfs://...",
|
|
1611
|
+
amount: 5,
|
|
1612
|
+
tokenIds: [],
|
|
1613
|
+
}),
|
|
1614
|
+
"NFT Purchase"
|
|
1615
|
+
);
|
|
1616
|
+
|
|
1617
|
+
if (result) {
|
|
1618
|
+
console.log("Success:", result);
|
|
1619
|
+
}
|
|
1620
|
+
```
|
|
1621
|
+
|
|
1622
|
+
### Retry Logic
|
|
1623
|
+
|
|
1624
|
+
```typescript
|
|
1625
|
+
async function withRetry<T>(
|
|
1626
|
+
operation: () => Promise<T>,
|
|
1627
|
+
maxRetries: number = 3,
|
|
1628
|
+
delayMs: number = 1000
|
|
1629
|
+
): Promise<T> {
|
|
1630
|
+
let lastError: Error;
|
|
1631
|
+
|
|
1632
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
1633
|
+
try {
|
|
1634
|
+
return await operation();
|
|
1635
|
+
} catch (error: any) {
|
|
1636
|
+
lastError = error;
|
|
1637
|
+
console.log(`Attempt ${attempt} failed:`, error.message);
|
|
1638
|
+
|
|
1639
|
+
// Don't retry user rejections
|
|
1640
|
+
if (error.code === 4001 || error.code === "ACTION_REJECTED") {
|
|
1641
|
+
throw error;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
if (attempt < maxRetries) {
|
|
1645
|
+
console.log(`Retrying in ${delayMs}ms...`);
|
|
1646
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1647
|
+
delayMs *= 2; // Exponential backoff
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
throw lastError!;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// Usage
|
|
1656
|
+
const result = await withRetry(
|
|
1657
|
+
() =>
|
|
1658
|
+
sdk.buy({
|
|
1659
|
+
/* ... */
|
|
1660
|
+
}),
|
|
1661
|
+
3,
|
|
1662
|
+
2000
|
|
1663
|
+
);
|
|
1664
|
+
```
|
|
1665
|
+
|
|
1666
|
+
---
|
|
1667
|
+
|
|
1668
|
+
## Best Practices
|
|
1669
|
+
|
|
1670
|
+
### 1. Always Check Prices Before Buying
|
|
1671
|
+
|
|
1672
|
+
```typescript
|
|
1673
|
+
// โ Bad: Blind purchase
|
|
1674
|
+
await sdk.buy({ collectionAddress, amount: 5, tokenIds: [], baseUri: "..." });
|
|
1675
|
+
|
|
1676
|
+
// โ
Good: Check price first
|
|
1677
|
+
const estimatedPrice = await sdk.getTotalPrice(
|
|
1678
|
+
PurcahseType.BUY,
|
|
1679
|
+
collectionAddress,
|
|
1680
|
+
5
|
|
1681
|
+
);
|
|
1682
|
+
console.log("This will cost:", ethers.utils.formatEther(estimatedPrice), "ETH");
|
|
1683
|
+
|
|
1684
|
+
const userConfirmed = await confirmWithUser(estimatedPrice);
|
|
1685
|
+
if (userConfirmed) {
|
|
1686
|
+
await sdk.buy({ collectionAddress, amount: 5, tokenIds: [], baseUri: "..." });
|
|
1687
|
+
}
|
|
1688
|
+
```
|
|
1689
|
+
|
|
1690
|
+
### 2. Handle Collection State
|
|
1691
|
+
|
|
1692
|
+
```typescript
|
|
1693
|
+
// โ
Check if collection is sold out before minting
|
|
1694
|
+
const info = await sdk.getCollectionInfo(collectionAddress);
|
|
1695
|
+
const remaining = info[3].sub(info[4]).toNumber();
|
|
1696
|
+
|
|
1697
|
+
if (remaining === 0) {
|
|
1698
|
+
console.log("Collection sold out! Buying from pool instead...");
|
|
1699
|
+
// Buy from liquidity pool
|
|
1700
|
+
} else {
|
|
1701
|
+
console.log(`${remaining} NFTs remaining`);
|
|
1702
|
+
// Mint new NFTs
|
|
1703
|
+
}
|
|
1704
|
+
```
|
|
1705
|
+
|
|
1706
|
+
### 3. Use Environment Variables
|
|
1707
|
+
|
|
1708
|
+
```typescript
|
|
1709
|
+
// โ
Good: Secure configuration
|
|
1710
|
+
import dotenv from "dotenv";
|
|
1711
|
+
dotenv.config();
|
|
1712
|
+
|
|
1713
|
+
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL);
|
|
1714
|
+
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
|
|
1715
|
+
|
|
1716
|
+
const sdk = new FlipmemeSDK(
|
|
1717
|
+
BlockchainType.ETHEREUM,
|
|
1718
|
+
process.env.NODE_ENV === "production" ? PLATFORM.PROD : PLATFORM.DEV,
|
|
1719
|
+
{ signer, chainId: parseInt(process.env.CHAIN_ID!), provider }
|
|
1720
|
+
);
|
|
1721
|
+
```
|
|
1722
|
+
|
|
1723
|
+
### 4. Implement Proper Error Handling
|
|
1724
|
+
|
|
1725
|
+
```typescript
|
|
1726
|
+
// โ
Comprehensive error handling
|
|
1727
|
+
try {
|
|
1728
|
+
const result = await sdk.createCollection({
|
|
1729
|
+
name: "My Collection",
|
|
1730
|
+
symbol: "MC",
|
|
1731
|
+
totalSupply: 100,
|
|
1732
|
+
sessionId: `session-${Date.now()}`,
|
|
1733
|
+
tokenUri: "ipfs://...",
|
|
1734
|
+
});
|
|
1735
|
+
|
|
1736
|
+
// Log success
|
|
1737
|
+
console.log("Collection created:", result.collectionAddress);
|
|
1738
|
+
|
|
1739
|
+
// Store in database
|
|
1740
|
+
await database.saveCollection(result);
|
|
1741
|
+
} catch (error: any) {
|
|
1742
|
+
// Log for debugging
|
|
1743
|
+
console.error("Collection creation failed:", error);
|
|
1744
|
+
|
|
1745
|
+
// User-friendly message
|
|
1746
|
+
if (error.code === "INSUFFICIENT_FUNDS") {
|
|
1747
|
+
alert("You need more ETH to create a collection");
|
|
1748
|
+
} else if (error.code === "ACTION_REJECTED") {
|
|
1749
|
+
alert("Transaction was cancelled");
|
|
1750
|
+
} else {
|
|
1751
|
+
alert("Failed to create collection. Please try again.");
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
// Track error
|
|
1755
|
+
analytics.trackError("create_collection_failed", error);
|
|
1756
|
+
}
|
|
1757
|
+
```
|
|
1758
|
+
|
|
1759
|
+
### 5. Optimize Gas Usage
|
|
1760
|
+
|
|
1761
|
+
```typescript
|
|
1762
|
+
// โ
Use profileSell for multiple collections
|
|
1763
|
+
// Instead of:
|
|
1764
|
+
await sdk.flipSell({ collectionAddress: addr1, tokenIds: [1, 2] });
|
|
1765
|
+
await sdk.flipSell({ collectionAddress: addr2, tokenIds: [3, 4] });
|
|
1766
|
+
|
|
1767
|
+
// Do this (saves ~30% gas):
|
|
1768
|
+
await sdk.profileSell({
|
|
1769
|
+
data: {
|
|
1770
|
+
[collectionId1]: { tokenIds: [1, 2], collectionId: collectionId1 },
|
|
1771
|
+
[collectionId2]: { tokenIds: [3, 4], collectionId: collectionId2 },
|
|
1772
|
+
},
|
|
1773
|
+
slippage: 0,
|
|
1774
|
+
});
|
|
1775
|
+
```
|
|
1776
|
+
|
|
1777
|
+
### 6. Monitor Transaction Status
|
|
1778
|
+
|
|
1779
|
+
```typescript
|
|
1780
|
+
async function buyWithConfirmation(sdk: FlipmemeSDK, params: EthBuyParams) {
|
|
1781
|
+
console.log("Initiating purchase...");
|
|
1782
|
+
|
|
1783
|
+
// Start transaction
|
|
1784
|
+
const result = await sdk.buy(params);
|
|
1785
|
+
|
|
1786
|
+
console.log("Transaction submitted, waiting for confirmation...");
|
|
1787
|
+
|
|
1788
|
+
// Get transaction hash (would need to modify SDK to return this)
|
|
1789
|
+
// const txHash = result.transactionHash;
|
|
1790
|
+
|
|
1791
|
+
// Wait for confirmations
|
|
1792
|
+
// const receipt = await provider.waitForTransaction(txHash, 2);
|
|
1793
|
+
|
|
1794
|
+
console.log("โ
Confirmed!");
|
|
1795
|
+
return result;
|
|
1796
|
+
}
|
|
1797
|
+
```
|
|
1798
|
+
|
|
1799
|
+
### 7. Cache Collection Data
|
|
1800
|
+
|
|
1801
|
+
```typescript
|
|
1802
|
+
class CollectionCache {
|
|
1803
|
+
private cache = new Map<string, { data: any; timestamp: number }>();
|
|
1804
|
+
private ttl = 60000; // 1 minute
|
|
1805
|
+
|
|
1806
|
+
async getCollectionInfo(sdk: FlipmemeSDK, address: string) {
|
|
1807
|
+
const cached = this.cache.get(address);
|
|
1808
|
+
|
|
1809
|
+
if (cached && Date.now() - cached.timestamp < this.ttl) {
|
|
1810
|
+
return cached.data;
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
const data = await sdk.getCollectionInfo(address);
|
|
1814
|
+
this.cache.set(address, { data, timestamp: Date.now() });
|
|
1815
|
+
return data;
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
invalidate(address: string) {
|
|
1819
|
+
this.cache.delete(address);
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
clear() {
|
|
1823
|
+
this.cache.clear();
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
// Usage
|
|
1828
|
+
const cache = new CollectionCache();
|
|
1829
|
+
const info = await cache.getCollectionInfo(sdk, collectionAddress);
|
|
1830
|
+
```
|
|
1831
|
+
|
|
1832
|
+
### 8. Test on Testnet First
|
|
1833
|
+
|
|
1834
|
+
```typescript
|
|
1835
|
+
// โ
Always test on testnet before mainnet
|
|
1836
|
+
const TESTNET_CONFIG = {
|
|
1837
|
+
chainId: 84532, // Base Sepolia
|
|
1838
|
+
rpcUrl: "https://sepolia.base.org",
|
|
1839
|
+
explorer: "https://sepolia.basescan.org",
|
|
1840
|
+
};
|
|
1841
|
+
|
|
1842
|
+
const MAINNET_CONFIG = {
|
|
1843
|
+
chainId: 8453, // Base Mainnet
|
|
1844
|
+
rpcUrl: "https://mainnet.base.org",
|
|
1845
|
+
explorer: "https://basescan.org",
|
|
1846
|
+
};
|
|
1847
|
+
|
|
1848
|
+
const config =
|
|
1849
|
+
process.env.USE_TESTNET === "true" ? TESTNET_CONFIG : MAINNET_CONFIG;
|
|
1850
|
+
```
|
|
1851
|
+
|
|
1852
|
+
---
|
|
1853
|
+
|
|
1854
|
+
## Examples
|
|
1855
|
+
|
|
1856
|
+
### Complete DApp Integration
|
|
1857
|
+
|
|
1858
|
+
```typescript
|
|
1859
|
+
import { ethers } from "ethers";
|
|
1860
|
+
import {
|
|
1861
|
+
FlipmemeSDK,
|
|
1862
|
+
BlockchainType,
|
|
1863
|
+
PLATFORM,
|
|
1864
|
+
PurcahseType,
|
|
1865
|
+
} from "flipmeme-sdk";
|
|
1866
|
+
|
|
1867
|
+
class FlipNFT {
|
|
1868
|
+
private sdk: FlipmemeSDK;
|
|
1869
|
+
private provider: ethers.providers.Web3Provider;
|
|
1870
|
+
private signer: ethers.Signer;
|
|
1871
|
+
|
|
1872
|
+
async initialize() {
|
|
1873
|
+
// Connect to MetaMask
|
|
1874
|
+
if (!window.ethereum) {
|
|
1875
|
+
throw new Error("Please install MetaMask");
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
this.provider = new ethers.providers.Web3Provider(window.ethereum);
|
|
1879
|
+
await this.provider.send("eth_requestAccounts", []);
|
|
1880
|
+
this.signer = this.provider.getSigner();
|
|
1881
|
+
|
|
1882
|
+
// Initialize SDK
|
|
1883
|
+
const network = await this.provider.getNetwork();
|
|
1884
|
+
this.sdk = new FlipmemeSDK(BlockchainType.ETHEREUM, PLATFORM.PROD, {
|
|
1885
|
+
signer: this.signer,
|
|
1886
|
+
chainId: network.chainId,
|
|
1887
|
+
provider: this.provider,
|
|
1888
|
+
});
|
|
1889
|
+
|
|
1890
|
+
console.log("Connected:", await this.signer.getAddress());
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
async createCollection(params: {
|
|
1894
|
+
name: string;
|
|
1895
|
+
symbol: string;
|
|
1896
|
+
totalSupply: number;
|
|
1897
|
+
premint: number;
|
|
1898
|
+
metadataUri: string;
|
|
1899
|
+
}) {
|
|
1900
|
+
try {
|
|
1901
|
+
const { collectionId, collectionAddress } =
|
|
1902
|
+
await this.sdk.createCollection({
|
|
1903
|
+
name: params.name,
|
|
1904
|
+
symbol: params.symbol,
|
|
1905
|
+
totalSupply: params.totalSupply,
|
|
1906
|
+
premint: params.premint,
|
|
1907
|
+
sessionId: `create-${Date.now()}-${Math.random()}`,
|
|
1908
|
+
tokenUri: params.metadataUri,
|
|
1909
|
+
startPrice: ethers.utils.parseEther("0.001").toString(),
|
|
1910
|
+
endPrice: ethers.utils.parseEther("0.1").toString(),
|
|
1911
|
+
});
|
|
1912
|
+
|
|
1913
|
+
console.log("โ
Collection created!");
|
|
1914
|
+
console.log("ID:", collectionId);
|
|
1915
|
+
console.log("Address:", collectionAddress);
|
|
1916
|
+
|
|
1917
|
+
return { collectionId, collectionAddress };
|
|
1918
|
+
} catch (error) {
|
|
1919
|
+
console.error("Failed to create collection:", error);
|
|
1920
|
+
throw error;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
async buyNFTs(collectionAddress: string, amount: number, baseUri: string) {
|
|
1925
|
+
try {
|
|
1926
|
+
// Get price quote
|
|
1927
|
+
const totalPrice = await this.sdk.getTotalPrice(
|
|
1928
|
+
PurcahseType.BUY,
|
|
1929
|
+
collectionAddress,
|
|
1930
|
+
amount
|
|
1931
|
+
);
|
|
1932
|
+
|
|
1933
|
+
console.log(
|
|
1934
|
+
`Buying ${amount} NFTs for ${ethers.utils.formatEther(totalPrice)} ETH`
|
|
1935
|
+
);
|
|
1936
|
+
|
|
1937
|
+
// Execute purchase
|
|
1938
|
+
const result = await this.sdk.buy({
|
|
1939
|
+
collectionAddress,
|
|
1940
|
+
baseUri,
|
|
1941
|
+
amount,
|
|
1942
|
+
tokenIds: [],
|
|
1943
|
+
});
|
|
1944
|
+
|
|
1945
|
+
console.log(`โ
Purchased ${result.successCount} NFTs`);
|
|
1946
|
+
return result;
|
|
1947
|
+
} catch (error) {
|
|
1948
|
+
console.error("Purchase failed:", error);
|
|
1949
|
+
throw error;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
async sellNFTs(collectionAddress: string, tokenIds: number[]) {
|
|
1954
|
+
try {
|
|
1955
|
+
// Get sell quote
|
|
1956
|
+
const totalPrice = await this.sdk.getTotalPrice(
|
|
1957
|
+
PurcahseType.SELL,
|
|
1958
|
+
collectionAddress,
|
|
1959
|
+
tokenIds.length
|
|
1960
|
+
);
|
|
1961
|
+
|
|
1962
|
+
console.log(
|
|
1963
|
+
`Selling ${tokenIds.length} NFTs for ${ethers.utils.formatEther(
|
|
1964
|
+
totalPrice
|
|
1965
|
+
)} ETH`
|
|
1966
|
+
);
|
|
1967
|
+
|
|
1968
|
+
// Execute sale
|
|
1969
|
+
const result = await this.sdk.flipSell({
|
|
1970
|
+
collectionAddress,
|
|
1971
|
+
tokenIds,
|
|
1972
|
+
});
|
|
1973
|
+
|
|
1974
|
+
console.log(`โ
Sold ${result.successCount} NFTs`);
|
|
1975
|
+
return result;
|
|
1976
|
+
} catch (error) {
|
|
1977
|
+
console.error("Sale failed:", error);
|
|
1978
|
+
throw error;
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
async getMyNFTs(collectionAddress: string): Promise<number[]> {
|
|
1983
|
+
const myAddress = await this.signer.getAddress();
|
|
1984
|
+
const balance = await this.sdk.balanceOf(collectionAddress, myAddress);
|
|
1985
|
+
const totalSupply = await this.sdk.totalSupply(collectionAddress);
|
|
1986
|
+
|
|
1987
|
+
const myTokens: number[] = [];
|
|
1988
|
+
|
|
1989
|
+
for (let i = 0; i < parseInt(totalSupply); i++) {
|
|
1990
|
+
try {
|
|
1991
|
+
const owner = await this.sdk.ownerOf(collectionAddress, i);
|
|
1992
|
+
if (owner.toLowerCase() === myAddress.toLowerCase()) {
|
|
1993
|
+
myTokens.push(i);
|
|
1994
|
+
}
|
|
1995
|
+
} catch {
|
|
1996
|
+
continue;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
return myTokens;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
async getCollectionStats(collectionAddress: string) {
|
|
2004
|
+
const info = await this.sdk.getCollectionInfo(collectionAddress);
|
|
2005
|
+
const currentPrice = await this.sdk.calculatePrice(collectionAddress);
|
|
2006
|
+
|
|
2007
|
+
return {
|
|
2008
|
+
creator: info[2],
|
|
2009
|
+
maxSupply: info[3].toNumber(),
|
|
2010
|
+
minted: info[4].toNumber(),
|
|
2011
|
+
remaining: info[3].sub(info[4]).toNumber(),
|
|
2012
|
+
isSoldOut: info[5],
|
|
2013
|
+
currentPrice: ethers.utils.formatEther(currentPrice),
|
|
2014
|
+
progress: (info[4].toNumber() / info[3].toNumber()) * 100,
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
// Usage
|
|
2020
|
+
const app = new FlipNFT();
|
|
2021
|
+
await app.initialize();
|
|
2022
|
+
|
|
2023
|
+
// Create collection
|
|
2024
|
+
const { collectionAddress } = await app.createCollection({
|
|
2025
|
+
name: "Cool Cats",
|
|
2026
|
+
symbol: "COOL",
|
|
2027
|
+
totalSupply: 100,
|
|
2028
|
+
premint: 5,
|
|
2029
|
+
metadataUri: "ipfs://QmYourMetadata/",
|
|
2030
|
+
});
|
|
2031
|
+
|
|
2032
|
+
// Buy NFTs
|
|
2033
|
+
await app.buyNFTs(collectionAddress, 3, "ipfs://QmYourTokens/");
|
|
2034
|
+
|
|
2035
|
+
// Check stats
|
|
2036
|
+
const stats = await app.getCollectionStats(collectionAddress);
|
|
2037
|
+
console.log("Collection Stats:", stats);
|
|
2038
|
+
|
|
2039
|
+
// Get my NFTs
|
|
2040
|
+
const myNFTs = await app.getMyNFTs(collectionAddress);
|
|
2041
|
+
console.log("My NFTs:", myNFTs);
|
|
2042
|
+
|
|
2043
|
+
// Sell some
|
|
2044
|
+
if (myNFTs.length > 0) {
|
|
2045
|
+
await app.sellNFTs(collectionAddress, myNFTs.slice(0, 2));
|
|
2046
|
+
}
|
|
2047
|
+
```
|
|
2048
|
+
|
|
2049
|
+
### React Integration Example
|
|
2050
|
+
|
|
2051
|
+
```typescript
|
|
2052
|
+
// hooks/useFlipmeme.ts
|
|
2053
|
+
import { useState, useEffect } from "react";
|
|
2054
|
+
import { ethers } from "ethers";
|
|
2055
|
+
import { FlipmemeSDK, BlockchainType, PLATFORM } from "flipmeme-sdk";
|
|
2056
|
+
|
|
2057
|
+
export function useFlipmeme() {
|
|
2058
|
+
const [sdk, setSdk] = useState<FlipmemeSDK | null>(null);
|
|
2059
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
2060
|
+
const [address, setAddress] = useState<string>("");
|
|
2061
|
+
|
|
2062
|
+
useEffect(() => {
|
|
2063
|
+
initializeSDK();
|
|
2064
|
+
}, []);
|
|
2065
|
+
|
|
2066
|
+
async function initializeSDK() {
|
|
2067
|
+
if (!window.ethereum) return;
|
|
2068
|
+
|
|
2069
|
+
try {
|
|
2070
|
+
const provider = new ethers.providers.Web3Provider(window.ethereum);
|
|
2071
|
+
await provider.send("eth_requestAccounts", []);
|
|
2072
|
+
const signer = provider.getSigner();
|
|
2073
|
+
const network = await provider.getNetwork();
|
|
2074
|
+
const userAddress = await signer.getAddress();
|
|
2075
|
+
|
|
2076
|
+
const flipSDK = new FlipmemeSDK(BlockchainType.ETHEREUM, PLATFORM.PROD, {
|
|
2077
|
+
signer,
|
|
2078
|
+
chainId: network.chainId,
|
|
2079
|
+
provider,
|
|
2080
|
+
});
|
|
2081
|
+
|
|
2082
|
+
setSdk(flipSDK);
|
|
2083
|
+
setIsConnected(true);
|
|
2084
|
+
setAddress(userAddress);
|
|
2085
|
+
} catch (error) {
|
|
2086
|
+
console.error("Failed to initialize SDK:", error);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
return { sdk, isConnected, address, initialize: initializeSDK };
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
// components/CollectionCard.tsx
|
|
2094
|
+
import React, { useState, useEffect } from "react";
|
|
2095
|
+
import { useFlipmeme } from "../hooks/useFlipmeme";
|
|
2096
|
+
|
|
2097
|
+
interface CollectionCardProps {
|
|
2098
|
+
collectionAddress: string;
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
export function CollectionCard({ collectionAddress }: CollectionCardProps) {
|
|
2102
|
+
const { sdk } = useFlipmeme();
|
|
2103
|
+
const [stats, setStats] = useState<any>(null);
|
|
2104
|
+
const [loading, setLoading] = useState(true);
|
|
2105
|
+
|
|
2106
|
+
useEffect(() => {
|
|
2107
|
+
loadStats();
|
|
2108
|
+
}, [sdk, collectionAddress]);
|
|
2109
|
+
|
|
2110
|
+
async function loadStats() {
|
|
2111
|
+
if (!sdk) return;
|
|
2112
|
+
|
|
2113
|
+
try {
|
|
2114
|
+
setLoading(true);
|
|
2115
|
+
const info = await sdk.getCollectionInfo(collectionAddress);
|
|
2116
|
+
const price = await sdk.calculatePrice(collectionAddress);
|
|
2117
|
+
|
|
2118
|
+
setStats({
|
|
2119
|
+
minted: info[4].toNumber(),
|
|
2120
|
+
maxSupply: info[3].toNumber(),
|
|
2121
|
+
isSoldOut: info[5],
|
|
2122
|
+
currentPrice: ethers.utils.formatEther(price),
|
|
2123
|
+
});
|
|
2124
|
+
} catch (error) {
|
|
2125
|
+
console.error("Failed to load stats:", error);
|
|
2126
|
+
} finally {
|
|
2127
|
+
setLoading(false);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
if (loading) return <div>Loading...</div>;
|
|
2132
|
+
if (!stats) return <div>Error loading collection</div>;
|
|
2133
|
+
|
|
2134
|
+
return (
|
|
2135
|
+
<div className="collection-card">
|
|
2136
|
+
<h3>Collection Stats</h3>
|
|
2137
|
+
<p>
|
|
2138
|
+
Minted: {stats.minted} / {stats.maxSupply}
|
|
2139
|
+
</p>
|
|
2140
|
+
<p>Current Price: {stats.currentPrice} ETH</p>
|
|
2141
|
+
{stats.isSoldOut && <p className="sold-out">SOLD OUT</p>}
|
|
2142
|
+
<button onClick={loadStats}>Refresh</button>
|
|
2143
|
+
</div>
|
|
2144
|
+
);
|
|
2145
|
+
}
|
|
2146
|
+
```
|
|
2147
|
+
|
|
2148
|
+
---
|
|
2149
|
+
|
|
2150
|
+
## Changelog
|
|
2151
|
+
|
|
2152
|
+
### Version 1.0.0
|
|
2153
|
+
|
|
2154
|
+
- Initial EVM SDK release
|
|
2155
|
+
- Support for Base network
|
|
2156
|
+
- Core collection and trading functionality
|
|
2157
|
+
- Bonding curve pricing system
|
|
2158
|
+
- Multi-collection batch operations
|
|
2159
|
+
|
|
2160
|
+
---
|
|
2161
|
+
|
|
2162
|
+
## Support & Resources
|
|
2163
|
+
|
|
2164
|
+
### Documentation
|
|
2165
|
+
|
|
2166
|
+
- **GitHub**: [flipmeme-sdk repository]
|
|
2167
|
+
- **API Docs**: This document
|
|
2168
|
+
- **Examples**: `/demo/eth/` directory
|
|
2169
|
+
|
|
2170
|
+
### Community
|
|
2171
|
+
|
|
2172
|
+
- **Discord**: [Join our community]
|
|
2173
|
+
- **Twitter**: [@flipmeme]
|
|
2174
|
+
- **Support**: support@flipmeme.com
|
|
2175
|
+
|
|
2176
|
+
### Security
|
|
2177
|
+
|
|
2178
|
+
For security concerns, please email security@flipmeme.com
|
|
2179
|
+
|
|
2180
|
+
---
|
|
2181
|
+
|
|
2182
|
+
## License
|
|
2183
|
+
|
|
2184
|
+
[License Type] - See LICENSE file for details
|
|
2185
|
+
|
|
2186
|
+
---
|
|
2187
|
+
|
|
2188
|
+
**Last Updated**: October 6, 2025
|
|
2189
|
+
**SDK Version**: 1.0.0
|
|
2190
|
+
**Compatible Networks**: Ethereum, Base, EVM-compatible chains
|