clanker-sdk 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -6
- package/dist/index.d.mts +49 -9
- package/dist/index.d.ts +49 -9
- package/dist/index.js +281 -98
- package/dist/index.mjs +284 -100
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,6 +26,9 @@ CLANKER_API_KEY=your_api_key_here
|
|
|
26
26
|
|
|
27
27
|
# Dune Analytics API Key (required for market data)
|
|
28
28
|
DUNE_API_KEY=your_dune_api_key_here
|
|
29
|
+
|
|
30
|
+
# The Graph API Key (optional, for Uniswap data)
|
|
31
|
+
GRAPH_API_KEY=your_graph_api_key_here
|
|
29
32
|
```
|
|
30
33
|
|
|
31
34
|
Copy the `.env.example` file to get started:
|
|
@@ -36,10 +39,10 @@ cp .env.example .env
|
|
|
36
39
|
|
|
37
40
|
Make sure to add your API keys to the `.env` file and never commit it to version control.
|
|
38
41
|
|
|
39
|
-
### 3. Getting Your
|
|
40
|
-
|
|
41
|
-
To access market data features, you'll need a Dune Analytics API key:
|
|
42
|
+
### 3. Getting Your API Keys
|
|
42
43
|
|
|
44
|
+
#### Dune Analytics API Key
|
|
45
|
+
To access market data features:
|
|
43
46
|
1. Visit [dune.xyz](https://dune.xyz) and sign up for an account
|
|
44
47
|
2. Go to your [API Keys page](https://dune.com/settings/api)
|
|
45
48
|
3. Create a new API key
|
|
@@ -47,6 +50,13 @@ To access market data features, you'll need a Dune Analytics API key:
|
|
|
47
50
|
|
|
48
51
|
Note: Dune API access requires a paid subscription. Check their [pricing page](https://dune.com/pricing) for more details.
|
|
49
52
|
|
|
53
|
+
#### The Graph API Key (Optional)
|
|
54
|
+
To access Uniswap data:
|
|
55
|
+
1. Visit [thegraph.com](https://thegraph.com) and create an account
|
|
56
|
+
2. Go to your billing settings
|
|
57
|
+
3. Create an API key
|
|
58
|
+
4. Add the key to your `.env` file as `GRAPH_API_KEY`
|
|
59
|
+
|
|
50
60
|
### 4. Basic Usage
|
|
51
61
|
|
|
52
62
|
```typescript
|
|
@@ -59,8 +69,11 @@ dotenv.config();
|
|
|
59
69
|
// Initialize SDK for token operations
|
|
60
70
|
const clanker = new ClankerSDK(process.env.CLANKER_API_KEY);
|
|
61
71
|
|
|
62
|
-
// Initialize market data client
|
|
63
|
-
const marketData = new MarketDataClient(
|
|
72
|
+
// Initialize market data client with both Dune and Graph API keys
|
|
73
|
+
const marketData = new MarketDataClient(
|
|
74
|
+
process.env.DUNE_API_KEY,
|
|
75
|
+
process.env.GRAPH_API_KEY
|
|
76
|
+
);
|
|
64
77
|
|
|
65
78
|
// Deploy a token
|
|
66
79
|
const token = await clanker.deployToken({
|
|
@@ -85,7 +98,7 @@ const marketStats = await marketData.getClankerDictionary();
|
|
|
85
98
|
|
|
86
99
|
## Market Data Features
|
|
87
100
|
|
|
88
|
-
The SDK provides access to comprehensive market data through
|
|
101
|
+
The SDK provides access to comprehensive market data through multiple sources:
|
|
89
102
|
|
|
90
103
|
### Clanker Dictionary
|
|
91
104
|
Get detailed information about all Clanker tokens:
|
|
@@ -107,6 +120,20 @@ const dexStats = await marketData.getDexPairStats(
|
|
|
107
120
|
// - Volume/Liquidity ratios
|
|
108
121
|
```
|
|
109
122
|
|
|
123
|
+
### Uniswap Data
|
|
124
|
+
Get detailed Uniswap pool data for tokens (requires Graph API key):
|
|
125
|
+
```typescript
|
|
126
|
+
const uniswapData = await marketData.getUniswapData(
|
|
127
|
+
['0x1234...', '0x5678...'], // Array of token addresses
|
|
128
|
+
15_000_000 // Optional: Block number for historical data
|
|
129
|
+
);
|
|
130
|
+
// Returns: Array of tokens with:
|
|
131
|
+
// - WETH price
|
|
132
|
+
// - Transaction count
|
|
133
|
+
// - Volume in USD
|
|
134
|
+
// - Decimals
|
|
135
|
+
```
|
|
136
|
+
|
|
110
137
|
Supported chains for DEX stats:
|
|
111
138
|
- ethereum
|
|
112
139
|
- arbitrum
|
package/dist/index.d.mts
CHANGED
|
@@ -75,29 +75,69 @@ interface ClankerMarketData {
|
|
|
75
75
|
volume7d?: number;
|
|
76
76
|
liquidity?: number;
|
|
77
77
|
}
|
|
78
|
+
interface DexPairStats {
|
|
79
|
+
token_a_address: string;
|
|
80
|
+
token_a_symbol: string;
|
|
81
|
+
token_b_address: string;
|
|
82
|
+
token_b_symbol: string;
|
|
83
|
+
volume_24h: number;
|
|
84
|
+
volume_7d: number;
|
|
85
|
+
volume_30d: number;
|
|
86
|
+
liquidity: number;
|
|
87
|
+
volume_to_liquidity_ratio: number;
|
|
88
|
+
}
|
|
89
|
+
interface GraphToken {
|
|
90
|
+
contractAddress: string;
|
|
91
|
+
decimals: number;
|
|
92
|
+
transactionCount: number;
|
|
93
|
+
volumeUSD: number;
|
|
94
|
+
priceWETH: number;
|
|
95
|
+
}
|
|
96
|
+
interface CoinGeckoTokenData {
|
|
97
|
+
price: number;
|
|
98
|
+
marketCap: number;
|
|
99
|
+
volume24h: number;
|
|
100
|
+
priceChange24h: number;
|
|
101
|
+
lastUpdated: Date;
|
|
102
|
+
}
|
|
78
103
|
declare class MarketDataClient {
|
|
79
|
-
private readonly dune
|
|
80
|
-
private readonly
|
|
104
|
+
private readonly dune?;
|
|
105
|
+
private readonly duneApiKey?;
|
|
106
|
+
private readonly graphApiKey?;
|
|
107
|
+
private readonly geckoApiKey?;
|
|
81
108
|
private readonly DICTIONARY_QUERY_ID;
|
|
82
|
-
|
|
109
|
+
private readonly GRAPH_API_ENDPOINT;
|
|
110
|
+
private readonly UNISWAP_SUBGRAPH_ID;
|
|
111
|
+
private readonly COINGECKO_API_ENDPOINT;
|
|
112
|
+
constructor(duneApiKey?: string, graphApiKey?: string, geckoApiKey?: string);
|
|
113
|
+
/**
|
|
114
|
+
* Get market data from CoinGecko (requires CoinGecko API key)
|
|
115
|
+
* @param tokenIds Array of CoinGecko token IDs
|
|
116
|
+
*/
|
|
117
|
+
getGeckoTokenData(tokenIds: string[]): Promise<Record<string, CoinGeckoTokenData>>;
|
|
83
118
|
/**
|
|
84
|
-
* Get market data from the materialized view
|
|
119
|
+
* Get market data from the materialized view (requires Dune API key)
|
|
85
120
|
*/
|
|
86
121
|
getClankerDictionary(): Promise<ClankerMarketData[]>;
|
|
87
122
|
/**
|
|
88
|
-
* Get DEX pair stats for a specific chain
|
|
123
|
+
* Get DEX pair stats for a specific chain (requires Dune API key)
|
|
89
124
|
* @param chain - The blockchain to query (e.g., 'ethereum', 'arbitrum', etc.)
|
|
90
125
|
* @param tokenAddress - Optional token address to filter by
|
|
91
126
|
*/
|
|
92
|
-
getDexPairStats(chain: string, tokenAddress?: string): Promise<
|
|
127
|
+
getDexPairStats(chain: string, tokenAddress?: string): Promise<DexPairStats[]>;
|
|
93
128
|
/**
|
|
94
|
-
*
|
|
129
|
+
* Fetch Uniswap data for multiple tokens using The Graph (requires Graph API key)
|
|
130
|
+
* @param contractAddresses Array of token contract addresses
|
|
131
|
+
* @param blockNumber Optional block number for historical data
|
|
95
132
|
*/
|
|
96
|
-
|
|
133
|
+
getUniswapData(contractAddresses: string[], blockNumber?: number): Promise<GraphToken[]>;
|
|
97
134
|
/**
|
|
98
135
|
* Filter DEX pairs by token address
|
|
99
136
|
*/
|
|
100
137
|
private filterPairsByToken;
|
|
138
|
+
private buildUniswapQuery;
|
|
139
|
+
private transformUniswapData;
|
|
140
|
+
private calculatePrice;
|
|
101
141
|
}
|
|
102
142
|
|
|
103
|
-
export { ClankerError, type ClankerMarketData, ClankerSDK, type DeployTokenOptions, type DeployTokenWithSplitsOptions, type DeployedToken, type DeployedTokensResponse, type EstimatedRewardsResponse, MarketDataClient, type Token, type UncollectedFeesResponse, ClankerSDK as default };
|
|
143
|
+
export { ClankerError, type ClankerMarketData, ClankerSDK, type CoinGeckoTokenData, type DeployTokenOptions, type DeployTokenWithSplitsOptions, type DeployedToken, type DeployedTokensResponse, type DexPairStats, type EstimatedRewardsResponse, type GraphToken, MarketDataClient, type Token, type UncollectedFeesResponse, ClankerSDK as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -75,29 +75,69 @@ interface ClankerMarketData {
|
|
|
75
75
|
volume7d?: number;
|
|
76
76
|
liquidity?: number;
|
|
77
77
|
}
|
|
78
|
+
interface DexPairStats {
|
|
79
|
+
token_a_address: string;
|
|
80
|
+
token_a_symbol: string;
|
|
81
|
+
token_b_address: string;
|
|
82
|
+
token_b_symbol: string;
|
|
83
|
+
volume_24h: number;
|
|
84
|
+
volume_7d: number;
|
|
85
|
+
volume_30d: number;
|
|
86
|
+
liquidity: number;
|
|
87
|
+
volume_to_liquidity_ratio: number;
|
|
88
|
+
}
|
|
89
|
+
interface GraphToken {
|
|
90
|
+
contractAddress: string;
|
|
91
|
+
decimals: number;
|
|
92
|
+
transactionCount: number;
|
|
93
|
+
volumeUSD: number;
|
|
94
|
+
priceWETH: number;
|
|
95
|
+
}
|
|
96
|
+
interface CoinGeckoTokenData {
|
|
97
|
+
price: number;
|
|
98
|
+
marketCap: number;
|
|
99
|
+
volume24h: number;
|
|
100
|
+
priceChange24h: number;
|
|
101
|
+
lastUpdated: Date;
|
|
102
|
+
}
|
|
78
103
|
declare class MarketDataClient {
|
|
79
|
-
private readonly dune
|
|
80
|
-
private readonly
|
|
104
|
+
private readonly dune?;
|
|
105
|
+
private readonly duneApiKey?;
|
|
106
|
+
private readonly graphApiKey?;
|
|
107
|
+
private readonly geckoApiKey?;
|
|
81
108
|
private readonly DICTIONARY_QUERY_ID;
|
|
82
|
-
|
|
109
|
+
private readonly GRAPH_API_ENDPOINT;
|
|
110
|
+
private readonly UNISWAP_SUBGRAPH_ID;
|
|
111
|
+
private readonly COINGECKO_API_ENDPOINT;
|
|
112
|
+
constructor(duneApiKey?: string, graphApiKey?: string, geckoApiKey?: string);
|
|
113
|
+
/**
|
|
114
|
+
* Get market data from CoinGecko (requires CoinGecko API key)
|
|
115
|
+
* @param tokenIds Array of CoinGecko token IDs
|
|
116
|
+
*/
|
|
117
|
+
getGeckoTokenData(tokenIds: string[]): Promise<Record<string, CoinGeckoTokenData>>;
|
|
83
118
|
/**
|
|
84
|
-
* Get market data from the materialized view
|
|
119
|
+
* Get market data from the materialized view (requires Dune API key)
|
|
85
120
|
*/
|
|
86
121
|
getClankerDictionary(): Promise<ClankerMarketData[]>;
|
|
87
122
|
/**
|
|
88
|
-
* Get DEX pair stats for a specific chain
|
|
123
|
+
* Get DEX pair stats for a specific chain (requires Dune API key)
|
|
89
124
|
* @param chain - The blockchain to query (e.g., 'ethereum', 'arbitrum', etc.)
|
|
90
125
|
* @param tokenAddress - Optional token address to filter by
|
|
91
126
|
*/
|
|
92
|
-
getDexPairStats(chain: string, tokenAddress?: string): Promise<
|
|
127
|
+
getDexPairStats(chain: string, tokenAddress?: string): Promise<DexPairStats[]>;
|
|
93
128
|
/**
|
|
94
|
-
*
|
|
129
|
+
* Fetch Uniswap data for multiple tokens using The Graph (requires Graph API key)
|
|
130
|
+
* @param contractAddresses Array of token contract addresses
|
|
131
|
+
* @param blockNumber Optional block number for historical data
|
|
95
132
|
*/
|
|
96
|
-
|
|
133
|
+
getUniswapData(contractAddresses: string[], blockNumber?: number): Promise<GraphToken[]>;
|
|
97
134
|
/**
|
|
98
135
|
* Filter DEX pairs by token address
|
|
99
136
|
*/
|
|
100
137
|
private filterPairsByToken;
|
|
138
|
+
private buildUniswapQuery;
|
|
139
|
+
private transformUniswapData;
|
|
140
|
+
private calculatePrice;
|
|
101
141
|
}
|
|
102
142
|
|
|
103
|
-
export { ClankerError, type ClankerMarketData, ClankerSDK, type DeployTokenOptions, type DeployTokenWithSplitsOptions, type DeployedToken, type DeployedTokensResponse, type EstimatedRewardsResponse, MarketDataClient, type Token, type UncollectedFeesResponse, ClankerSDK as default };
|
|
143
|
+
export { ClankerError, type ClankerMarketData, ClankerSDK, type CoinGeckoTokenData, type DeployTokenOptions, type DeployTokenWithSplitsOptions, type DeployedToken, type DeployedTokensResponse, type DexPairStats, type EstimatedRewardsResponse, type GraphToken, MarketDataClient, type Token, type UncollectedFeesResponse, ClankerSDK as default };
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,26 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
mod
|
|
27
27
|
));
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var __async = (__this, __arguments, generator) => {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
var fulfilled = (value) => {
|
|
32
|
+
try {
|
|
33
|
+
step(generator.next(value));
|
|
34
|
+
} catch (e) {
|
|
35
|
+
reject(e);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var rejected = (value) => {
|
|
39
|
+
try {
|
|
40
|
+
step(generator.throw(value));
|
|
41
|
+
} catch (e) {
|
|
42
|
+
reject(e);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
46
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
47
|
+
});
|
|
48
|
+
};
|
|
29
49
|
|
|
30
50
|
// src/index.ts
|
|
31
51
|
var index_exports = {};
|
|
@@ -69,10 +89,11 @@ var ClankerSDK = class {
|
|
|
69
89
|
this.api.interceptors.response.use(
|
|
70
90
|
(response) => response,
|
|
71
91
|
(error) => {
|
|
92
|
+
var _a, _b;
|
|
72
93
|
if (error.response) {
|
|
73
94
|
throw new ClankerError(
|
|
74
|
-
error.response.data
|
|
75
|
-
error.response.data
|
|
95
|
+
((_a = error.response.data) == null ? void 0 : _a.message) || "API request failed",
|
|
96
|
+
(_b = error.response.data) == null ? void 0 : _b.code,
|
|
76
97
|
error.response.status,
|
|
77
98
|
error.response.data
|
|
78
99
|
);
|
|
@@ -82,60 +103,72 @@ var ClankerSDK = class {
|
|
|
82
103
|
);
|
|
83
104
|
}
|
|
84
105
|
// Get estimated uncollected fees
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
106
|
+
getEstimatedUncollectedFees(contractAddress) {
|
|
107
|
+
return __async(this, null, function* () {
|
|
108
|
+
if (!this.isValidAddress(contractAddress)) {
|
|
109
|
+
throw new ClankerError("Invalid contract address format");
|
|
110
|
+
}
|
|
111
|
+
const response = yield this.api.get(`/get-estimated-uncollected-fees/${contractAddress}`);
|
|
112
|
+
return response.data;
|
|
113
|
+
});
|
|
91
114
|
}
|
|
92
115
|
// Deploy a new token
|
|
93
|
-
|
|
94
|
-
this
|
|
95
|
-
|
|
96
|
-
|
|
116
|
+
deployToken(options) {
|
|
117
|
+
return __async(this, null, function* () {
|
|
118
|
+
this.validateDeployOptions(options);
|
|
119
|
+
const response = yield this.api.post("/tokens/deploy", options);
|
|
120
|
+
return response.data;
|
|
121
|
+
});
|
|
97
122
|
}
|
|
98
123
|
// Deploy token with splits
|
|
99
|
-
|
|
100
|
-
this
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
124
|
+
deployTokenWithSplits(options) {
|
|
125
|
+
return __async(this, null, function* () {
|
|
126
|
+
this.validateDeployOptions(options);
|
|
127
|
+
if (!this.isValidAddress(options.splitAddress)) {
|
|
128
|
+
throw new ClankerError("Invalid split address format");
|
|
129
|
+
}
|
|
130
|
+
const response = yield this.api.post("/tokens/deploy/with-splits", options);
|
|
131
|
+
return response.data;
|
|
132
|
+
});
|
|
106
133
|
}
|
|
107
134
|
// Fetch clankers deployed by address
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
135
|
+
fetchDeployedByAddress(address, page = 1) {
|
|
136
|
+
return __async(this, null, function* () {
|
|
137
|
+
if (!this.isValidAddress(address)) {
|
|
138
|
+
throw new ClankerError("Invalid address format");
|
|
139
|
+
}
|
|
140
|
+
if (page < 1) {
|
|
141
|
+
throw new ClankerError("Page number must be greater than 0");
|
|
142
|
+
}
|
|
143
|
+
const response = yield this.api.get(`/tokens/fetch-deployed-by-address`, {
|
|
144
|
+
params: { address, page }
|
|
145
|
+
});
|
|
146
|
+
return response.data;
|
|
117
147
|
});
|
|
118
|
-
return response.data;
|
|
119
148
|
}
|
|
120
149
|
// Get estimated rewards by pool address
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
150
|
+
getEstimatedRewardsByPoolAddress(poolAddress) {
|
|
151
|
+
return __async(this, null, function* () {
|
|
152
|
+
if (!this.isValidAddress(poolAddress)) {
|
|
153
|
+
throw new ClankerError("Invalid pool address format");
|
|
154
|
+
}
|
|
155
|
+
const response = yield this.api.get(`/tokens/estimate-rewards-by-pool-address`, {
|
|
156
|
+
params: { poolAddress }
|
|
157
|
+
});
|
|
158
|
+
return response.data;
|
|
127
159
|
});
|
|
128
|
-
return response.data;
|
|
129
160
|
}
|
|
130
161
|
// Fetch clanker by contract address
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
162
|
+
getClankerByAddress(address) {
|
|
163
|
+
return __async(this, null, function* () {
|
|
164
|
+
if (!this.isValidAddress(address)) {
|
|
165
|
+
throw new ClankerError("Invalid address format");
|
|
166
|
+
}
|
|
167
|
+
const response = yield this.api.get(`/get-clanker-by-address`, {
|
|
168
|
+
params: { address }
|
|
169
|
+
});
|
|
170
|
+
return response.data;
|
|
137
171
|
});
|
|
138
|
-
return response.data;
|
|
139
172
|
}
|
|
140
173
|
// Utility function to generate request key
|
|
141
174
|
generateRequestKey() {
|
|
@@ -168,85 +201,235 @@ var ClankerSDK = class {
|
|
|
168
201
|
|
|
169
202
|
// src/MarketData.ts
|
|
170
203
|
var import_client_sdk = require("@duneanalytics/client-sdk");
|
|
204
|
+
var import_axios2 = __toESM(require("axios"));
|
|
171
205
|
var MarketDataClient = class {
|
|
172
|
-
|
|
173
|
-
constructor(duneApiKey) {
|
|
206
|
+
constructor(duneApiKey, graphApiKey, geckoApiKey) {
|
|
174
207
|
this.DICTIONARY_QUERY_ID = 4405741;
|
|
175
|
-
|
|
176
|
-
|
|
208
|
+
this.GRAPH_API_ENDPOINT = "https://gateway.thegraph.com/api";
|
|
209
|
+
this.UNISWAP_SUBGRAPH_ID = "GqzP4Xaehti8KSfQmv3ZctFSjnSUYZ4En5NRsiTbvZpz";
|
|
210
|
+
this.COINGECKO_API_ENDPOINT = "https://pro-api.coingecko.com/api/v3";
|
|
211
|
+
this.duneApiKey = duneApiKey;
|
|
212
|
+
this.graphApiKey = graphApiKey;
|
|
213
|
+
this.geckoApiKey = geckoApiKey;
|
|
214
|
+
if (duneApiKey) {
|
|
215
|
+
this.dune = new import_client_sdk.DuneClient(duneApiKey);
|
|
177
216
|
}
|
|
178
|
-
this.apiKey = duneApiKey;
|
|
179
|
-
this.dune = new import_client_sdk.DuneClient(duneApiKey);
|
|
180
217
|
}
|
|
181
218
|
/**
|
|
182
|
-
* Get market data from
|
|
219
|
+
* Get market data from CoinGecko (requires CoinGecko API key)
|
|
220
|
+
* @param tokenIds Array of CoinGecko token IDs
|
|
183
221
|
*/
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (!
|
|
188
|
-
throw new ClankerError("
|
|
222
|
+
getGeckoTokenData(tokenIds) {
|
|
223
|
+
return __async(this, null, function* () {
|
|
224
|
+
var _a;
|
|
225
|
+
if (!this.geckoApiKey) {
|
|
226
|
+
throw new ClankerError("CoinGecko API key is required for getGeckoTokenData");
|
|
189
227
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
228
|
+
try {
|
|
229
|
+
const response = yield import_axios2.default.get(
|
|
230
|
+
`${this.COINGECKO_API_ENDPOINT}/simple/price`,
|
|
231
|
+
{
|
|
232
|
+
params: {
|
|
233
|
+
ids: tokenIds.join(","),
|
|
234
|
+
vs_currencies: "usd",
|
|
235
|
+
include_market_cap: true,
|
|
236
|
+
include_24hr_vol: true,
|
|
237
|
+
include_24hr_change: true,
|
|
238
|
+
include_last_updated_at: true
|
|
239
|
+
},
|
|
240
|
+
headers: {
|
|
241
|
+
"x-cg-pro-api-key": this.geckoApiKey,
|
|
242
|
+
"accept": "application/json"
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
const result = {};
|
|
247
|
+
for (const [id, data] of Object.entries(response.data)) {
|
|
248
|
+
result[id] = {
|
|
249
|
+
price: data.usd,
|
|
250
|
+
marketCap: data.usd_market_cap,
|
|
251
|
+
volume24h: data.usd_24h_vol,
|
|
252
|
+
priceChange24h: data.usd_24h_change,
|
|
253
|
+
lastUpdated: new Date(data.last_updated_at * 1e3)
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
return result;
|
|
257
|
+
} catch (error) {
|
|
258
|
+
if (import_axios2.default.isAxiosError(error) && ((_a = error.response) == null ? void 0 : _a.data)) {
|
|
259
|
+
if (error.response.status === 429) {
|
|
260
|
+
throw new ClankerError("CoinGecko API rate limit exceeded");
|
|
261
|
+
}
|
|
262
|
+
const errorMessage = typeof error.response.data === "object" && error.response.data !== null ? error.response.data.error || error.message : error.message;
|
|
263
|
+
throw new ClankerError(`Failed to fetch CoinGecko data: ${errorMessage}`);
|
|
264
|
+
}
|
|
265
|
+
throw new ClankerError("Failed to fetch CoinGecko data");
|
|
194
266
|
}
|
|
195
|
-
|
|
196
|
-
}
|
|
267
|
+
});
|
|
197
268
|
}
|
|
198
269
|
/**
|
|
199
|
-
* Get
|
|
200
|
-
* @param chain - The blockchain to query (e.g., 'ethereum', 'arbitrum', etc.)
|
|
201
|
-
* @param tokenAddress - Optional token address to filter by
|
|
270
|
+
* Get market data from the materialized view (requires Dune API key)
|
|
202
271
|
*/
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
"X-Dune-Api-Key": this.apiKey
|
|
272
|
+
getClankerDictionary() {
|
|
273
|
+
return __async(this, null, function* () {
|
|
274
|
+
var _a;
|
|
275
|
+
if (!this.dune || !this.duneApiKey) {
|
|
276
|
+
throw new ClankerError("Dune API key is required for getClankerDictionary");
|
|
209
277
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
278
|
+
try {
|
|
279
|
+
const result = yield this.dune.getLatestResult({ queryId: this.DICTIONARY_QUERY_ID });
|
|
280
|
+
if (!((_a = result == null ? void 0 : result.result) == null ? void 0 : _a.rows)) {
|
|
281
|
+
throw new ClankerError("No data returned from Dune");
|
|
282
|
+
}
|
|
283
|
+
const rows = result.result.rows;
|
|
284
|
+
return rows.map((row) => ({
|
|
285
|
+
name: row.name,
|
|
286
|
+
symbol: row.symbol,
|
|
287
|
+
marketCap: row.market_cap,
|
|
288
|
+
volume24h: row.volume_24h,
|
|
289
|
+
volume7d: row.volume_7d,
|
|
290
|
+
liquidity: row.liquidity
|
|
291
|
+
}));
|
|
292
|
+
} catch (error) {
|
|
293
|
+
if (error instanceof Error) {
|
|
294
|
+
throw new ClankerError(`Failed to fetch Clanker dictionary: ${error.message}`);
|
|
295
|
+
}
|
|
296
|
+
throw new ClankerError("Failed to fetch Clanker dictionary");
|
|
215
297
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Get DEX pair stats for a specific chain (requires Dune API key)
|
|
302
|
+
* @param chain - The blockchain to query (e.g., 'ethereum', 'arbitrum', etc.)
|
|
303
|
+
* @param tokenAddress - Optional token address to filter by
|
|
304
|
+
*/
|
|
305
|
+
getDexPairStats(chain, tokenAddress) {
|
|
306
|
+
return __async(this, null, function* () {
|
|
307
|
+
if (!this.duneApiKey) {
|
|
308
|
+
throw new ClankerError("Dune API key is required for getDexPairStats");
|
|
219
309
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
310
|
+
const url = `https://api.dune.com/api/v1/dex/pairs/${chain}`;
|
|
311
|
+
const options = {
|
|
312
|
+
method: "GET",
|
|
313
|
+
headers: {
|
|
314
|
+
"X-Dune-Api-Key": this.duneApiKey
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
try {
|
|
318
|
+
const response = yield fetch(url, options);
|
|
319
|
+
if (!response.ok) {
|
|
320
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
321
|
+
}
|
|
322
|
+
const data = yield response.json();
|
|
323
|
+
if (tokenAddress) {
|
|
324
|
+
return this.filterPairsByToken(data.result.rows, tokenAddress);
|
|
325
|
+
}
|
|
326
|
+
return data.result.rows;
|
|
327
|
+
} catch (error) {
|
|
328
|
+
if (error instanceof Error) {
|
|
329
|
+
throw new ClankerError(`Failed to fetch DEX pair stats: ${error.message}`);
|
|
330
|
+
}
|
|
331
|
+
throw new ClankerError("Failed to fetch DEX pair stats");
|
|
224
332
|
}
|
|
225
|
-
|
|
226
|
-
}
|
|
333
|
+
});
|
|
227
334
|
}
|
|
228
335
|
/**
|
|
229
|
-
*
|
|
336
|
+
* Fetch Uniswap data for multiple tokens using The Graph (requires Graph API key)
|
|
337
|
+
* @param contractAddresses Array of token contract addresses
|
|
338
|
+
* @param blockNumber Optional block number for historical data
|
|
230
339
|
*/
|
|
231
|
-
|
|
232
|
-
return
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
340
|
+
getUniswapData(contractAddresses, blockNumber) {
|
|
341
|
+
return __async(this, null, function* () {
|
|
342
|
+
if (!this.graphApiKey) {
|
|
343
|
+
throw new ClankerError("Graph API key is required for getUniswapData");
|
|
344
|
+
}
|
|
345
|
+
try {
|
|
346
|
+
const query = this.buildUniswapQuery(contractAddresses, blockNumber);
|
|
347
|
+
const response = yield import_axios2.default.post(
|
|
348
|
+
`${this.GRAPH_API_ENDPOINT}/${this.graphApiKey}/subgraphs/id/${this.UNISWAP_SUBGRAPH_ID}`,
|
|
349
|
+
{ query },
|
|
350
|
+
{ headers: { "Content-Type": "application/json" } }
|
|
351
|
+
);
|
|
352
|
+
return this.transformUniswapData(response.data.data, contractAddresses);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
if (error instanceof Error) {
|
|
355
|
+
throw new ClankerError(`Failed to fetch Uniswap data: ${error.message}`);
|
|
356
|
+
}
|
|
357
|
+
throw new ClankerError("Failed to fetch Uniswap data");
|
|
358
|
+
}
|
|
359
|
+
});
|
|
240
360
|
}
|
|
241
361
|
/**
|
|
242
362
|
* Filter DEX pairs by token address
|
|
243
363
|
*/
|
|
244
|
-
filterPairsByToken(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
364
|
+
filterPairsByToken(pairs, tokenAddress) {
|
|
365
|
+
return pairs.filter(
|
|
366
|
+
(pair) => {
|
|
367
|
+
var _a, _b;
|
|
368
|
+
return ((_a = pair.token_a_address) == null ? void 0 : _a.toLowerCase()) === tokenAddress.toLowerCase() || ((_b = pair.token_b_address) == null ? void 0 : _b.toLowerCase()) === tokenAddress.toLowerCase();
|
|
369
|
+
}
|
|
248
370
|
);
|
|
249
371
|
}
|
|
372
|
+
buildUniswapQuery(contractAddresses, blockNumber) {
|
|
373
|
+
const queryParts = contractAddresses.map((address) => {
|
|
374
|
+
const key = `token${address.toLowerCase()}`;
|
|
375
|
+
return `
|
|
376
|
+
${key}: token(id:"${address.toLowerCase()}"${blockNumber ? `, block: {number: ${blockNumber}}` : ""}) {
|
|
377
|
+
id,
|
|
378
|
+
whitelistPools(orderBy:createdAtBlockNumber, orderDirection:asc, first: 1) {
|
|
379
|
+
id,
|
|
380
|
+
createdAtBlockNumber,
|
|
381
|
+
token0 {
|
|
382
|
+
name,
|
|
383
|
+
symbol,
|
|
384
|
+
decimals
|
|
385
|
+
},
|
|
386
|
+
token1 {
|
|
387
|
+
name,
|
|
388
|
+
symbol,
|
|
389
|
+
decimals
|
|
390
|
+
},
|
|
391
|
+
sqrtPrice
|
|
392
|
+
},
|
|
393
|
+
untrackedVolumeUSD,
|
|
394
|
+
txCount,
|
|
395
|
+
decimals
|
|
396
|
+
}
|
|
397
|
+
`;
|
|
398
|
+
});
|
|
399
|
+
return `{ ${queryParts.join("\n")} }`;
|
|
400
|
+
}
|
|
401
|
+
transformUniswapData(data, addresses) {
|
|
402
|
+
const tokens = [];
|
|
403
|
+
for (const address of addresses) {
|
|
404
|
+
const key = `token${address.toLowerCase()}`;
|
|
405
|
+
const tokenData = data[key];
|
|
406
|
+
if (!tokenData || tokenData.whitelistPools.length === 0) {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
const pool = tokenData.whitelistPools[0];
|
|
410
|
+
if (pool.token1.symbol !== "WETH") {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
const price = this.calculatePrice(
|
|
414
|
+
Number(pool.sqrtPrice),
|
|
415
|
+
Number(pool.token0.decimals),
|
|
416
|
+
Number(pool.token1.decimals)
|
|
417
|
+
);
|
|
418
|
+
tokens.push({
|
|
419
|
+
contractAddress: tokenData.id,
|
|
420
|
+
decimals: Number(tokenData.decimals),
|
|
421
|
+
transactionCount: Number(tokenData.txCount),
|
|
422
|
+
volumeUSD: Number(tokenData.untrackedVolumeUSD),
|
|
423
|
+
priceWETH: price
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
return tokens;
|
|
427
|
+
}
|
|
428
|
+
calculatePrice(sqrtPriceX96, decimalsToken0, decimalsToken1) {
|
|
429
|
+
const price = Math.pow(sqrtPriceX96, 2) / Math.pow(2, 192);
|
|
430
|
+
const decimalAdjustment = Math.pow(10, decimalsToken1 - decimalsToken0);
|
|
431
|
+
return price * decimalAdjustment;
|
|
432
|
+
}
|
|
250
433
|
};
|
|
251
434
|
// Annotate the CommonJS export names for ESM import in node:
|
|
252
435
|
0 && (module.exports = {
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
var __async = (__this, __arguments, generator) => {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
var fulfilled = (value) => {
|
|
4
|
+
try {
|
|
5
|
+
step(generator.next(value));
|
|
6
|
+
} catch (e) {
|
|
7
|
+
reject(e);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var rejected = (value) => {
|
|
11
|
+
try {
|
|
12
|
+
step(generator.throw(value));
|
|
13
|
+
} catch (e) {
|
|
14
|
+
reject(e);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
18
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
1
22
|
// src/ClankerSDK.ts
|
|
2
23
|
import axios from "axios";
|
|
3
24
|
import { randomBytes } from "crypto";
|
|
@@ -30,10 +51,11 @@ var ClankerSDK = class {
|
|
|
30
51
|
this.api.interceptors.response.use(
|
|
31
52
|
(response) => response,
|
|
32
53
|
(error) => {
|
|
54
|
+
var _a, _b;
|
|
33
55
|
if (error.response) {
|
|
34
56
|
throw new ClankerError(
|
|
35
|
-
error.response.data
|
|
36
|
-
error.response.data
|
|
57
|
+
((_a = error.response.data) == null ? void 0 : _a.message) || "API request failed",
|
|
58
|
+
(_b = error.response.data) == null ? void 0 : _b.code,
|
|
37
59
|
error.response.status,
|
|
38
60
|
error.response.data
|
|
39
61
|
);
|
|
@@ -43,60 +65,72 @@ var ClankerSDK = class {
|
|
|
43
65
|
);
|
|
44
66
|
}
|
|
45
67
|
// Get estimated uncollected fees
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
68
|
+
getEstimatedUncollectedFees(contractAddress) {
|
|
69
|
+
return __async(this, null, function* () {
|
|
70
|
+
if (!this.isValidAddress(contractAddress)) {
|
|
71
|
+
throw new ClankerError("Invalid contract address format");
|
|
72
|
+
}
|
|
73
|
+
const response = yield this.api.get(`/get-estimated-uncollected-fees/${contractAddress}`);
|
|
74
|
+
return response.data;
|
|
75
|
+
});
|
|
52
76
|
}
|
|
53
77
|
// Deploy a new token
|
|
54
|
-
|
|
55
|
-
this
|
|
56
|
-
|
|
57
|
-
|
|
78
|
+
deployToken(options) {
|
|
79
|
+
return __async(this, null, function* () {
|
|
80
|
+
this.validateDeployOptions(options);
|
|
81
|
+
const response = yield this.api.post("/tokens/deploy", options);
|
|
82
|
+
return response.data;
|
|
83
|
+
});
|
|
58
84
|
}
|
|
59
85
|
// Deploy token with splits
|
|
60
|
-
|
|
61
|
-
this
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
86
|
+
deployTokenWithSplits(options) {
|
|
87
|
+
return __async(this, null, function* () {
|
|
88
|
+
this.validateDeployOptions(options);
|
|
89
|
+
if (!this.isValidAddress(options.splitAddress)) {
|
|
90
|
+
throw new ClankerError("Invalid split address format");
|
|
91
|
+
}
|
|
92
|
+
const response = yield this.api.post("/tokens/deploy/with-splits", options);
|
|
93
|
+
return response.data;
|
|
94
|
+
});
|
|
67
95
|
}
|
|
68
96
|
// Fetch clankers deployed by address
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
97
|
+
fetchDeployedByAddress(address, page = 1) {
|
|
98
|
+
return __async(this, null, function* () {
|
|
99
|
+
if (!this.isValidAddress(address)) {
|
|
100
|
+
throw new ClankerError("Invalid address format");
|
|
101
|
+
}
|
|
102
|
+
if (page < 1) {
|
|
103
|
+
throw new ClankerError("Page number must be greater than 0");
|
|
104
|
+
}
|
|
105
|
+
const response = yield this.api.get(`/tokens/fetch-deployed-by-address`, {
|
|
106
|
+
params: { address, page }
|
|
107
|
+
});
|
|
108
|
+
return response.data;
|
|
78
109
|
});
|
|
79
|
-
return response.data;
|
|
80
110
|
}
|
|
81
111
|
// Get estimated rewards by pool address
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
112
|
+
getEstimatedRewardsByPoolAddress(poolAddress) {
|
|
113
|
+
return __async(this, null, function* () {
|
|
114
|
+
if (!this.isValidAddress(poolAddress)) {
|
|
115
|
+
throw new ClankerError("Invalid pool address format");
|
|
116
|
+
}
|
|
117
|
+
const response = yield this.api.get(`/tokens/estimate-rewards-by-pool-address`, {
|
|
118
|
+
params: { poolAddress }
|
|
119
|
+
});
|
|
120
|
+
return response.data;
|
|
88
121
|
});
|
|
89
|
-
return response.data;
|
|
90
122
|
}
|
|
91
123
|
// Fetch clanker by contract address
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
124
|
+
getClankerByAddress(address) {
|
|
125
|
+
return __async(this, null, function* () {
|
|
126
|
+
if (!this.isValidAddress(address)) {
|
|
127
|
+
throw new ClankerError("Invalid address format");
|
|
128
|
+
}
|
|
129
|
+
const response = yield this.api.get(`/get-clanker-by-address`, {
|
|
130
|
+
params: { address }
|
|
131
|
+
});
|
|
132
|
+
return response.data;
|
|
98
133
|
});
|
|
99
|
-
return response.data;
|
|
100
134
|
}
|
|
101
135
|
// Utility function to generate request key
|
|
102
136
|
generateRequestKey() {
|
|
@@ -129,85 +163,235 @@ var ClankerSDK = class {
|
|
|
129
163
|
|
|
130
164
|
// src/MarketData.ts
|
|
131
165
|
import { DuneClient } from "@duneanalytics/client-sdk";
|
|
166
|
+
import axios2 from "axios";
|
|
132
167
|
var MarketDataClient = class {
|
|
133
|
-
|
|
134
|
-
constructor(duneApiKey) {
|
|
168
|
+
constructor(duneApiKey, graphApiKey, geckoApiKey) {
|
|
135
169
|
this.DICTIONARY_QUERY_ID = 4405741;
|
|
136
|
-
|
|
137
|
-
|
|
170
|
+
this.GRAPH_API_ENDPOINT = "https://gateway.thegraph.com/api";
|
|
171
|
+
this.UNISWAP_SUBGRAPH_ID = "GqzP4Xaehti8KSfQmv3ZctFSjnSUYZ4En5NRsiTbvZpz";
|
|
172
|
+
this.COINGECKO_API_ENDPOINT = "https://pro-api.coingecko.com/api/v3";
|
|
173
|
+
this.duneApiKey = duneApiKey;
|
|
174
|
+
this.graphApiKey = graphApiKey;
|
|
175
|
+
this.geckoApiKey = geckoApiKey;
|
|
176
|
+
if (duneApiKey) {
|
|
177
|
+
this.dune = new DuneClient(duneApiKey);
|
|
138
178
|
}
|
|
139
|
-
this.apiKey = duneApiKey;
|
|
140
|
-
this.dune = new DuneClient(duneApiKey);
|
|
141
179
|
}
|
|
142
180
|
/**
|
|
143
|
-
* Get market data from
|
|
181
|
+
* Get market data from CoinGecko (requires CoinGecko API key)
|
|
182
|
+
* @param tokenIds Array of CoinGecko token IDs
|
|
144
183
|
*/
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (!
|
|
149
|
-
throw new ClankerError("
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
184
|
+
getGeckoTokenData(tokenIds) {
|
|
185
|
+
return __async(this, null, function* () {
|
|
186
|
+
var _a;
|
|
187
|
+
if (!this.geckoApiKey) {
|
|
188
|
+
throw new ClankerError("CoinGecko API key is required for getGeckoTokenData");
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
const response = yield axios2.get(
|
|
192
|
+
`${this.COINGECKO_API_ENDPOINT}/simple/price`,
|
|
193
|
+
{
|
|
194
|
+
params: {
|
|
195
|
+
ids: tokenIds.join(","),
|
|
196
|
+
vs_currencies: "usd",
|
|
197
|
+
include_market_cap: true,
|
|
198
|
+
include_24hr_vol: true,
|
|
199
|
+
include_24hr_change: true,
|
|
200
|
+
include_last_updated_at: true
|
|
201
|
+
},
|
|
202
|
+
headers: {
|
|
203
|
+
"x-cg-pro-api-key": this.geckoApiKey,
|
|
204
|
+
"accept": "application/json"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
const result = {};
|
|
209
|
+
for (const [id, data] of Object.entries(response.data)) {
|
|
210
|
+
result[id] = {
|
|
211
|
+
price: data.usd,
|
|
212
|
+
marketCap: data.usd_market_cap,
|
|
213
|
+
volume24h: data.usd_24h_vol,
|
|
214
|
+
priceChange24h: data.usd_24h_change,
|
|
215
|
+
lastUpdated: new Date(data.last_updated_at * 1e3)
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
} catch (error) {
|
|
220
|
+
if (axios2.isAxiosError(error) && ((_a = error.response) == null ? void 0 : _a.data)) {
|
|
221
|
+
if (error.response.status === 429) {
|
|
222
|
+
throw new ClankerError("CoinGecko API rate limit exceeded");
|
|
223
|
+
}
|
|
224
|
+
const errorMessage = typeof error.response.data === "object" && error.response.data !== null ? error.response.data.error || error.message : error.message;
|
|
225
|
+
throw new ClankerError(`Failed to fetch CoinGecko data: ${errorMessage}`);
|
|
226
|
+
}
|
|
227
|
+
throw new ClankerError("Failed to fetch CoinGecko data");
|
|
228
|
+
}
|
|
229
|
+
});
|
|
158
230
|
}
|
|
159
231
|
/**
|
|
160
|
-
* Get
|
|
161
|
-
* @param chain - The blockchain to query (e.g., 'ethereum', 'arbitrum', etc.)
|
|
162
|
-
* @param tokenAddress - Optional token address to filter by
|
|
232
|
+
* Get market data from the materialized view (requires Dune API key)
|
|
163
233
|
*/
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
"X-Dune-Api-Key": this.apiKey
|
|
234
|
+
getClankerDictionary() {
|
|
235
|
+
return __async(this, null, function* () {
|
|
236
|
+
var _a;
|
|
237
|
+
if (!this.dune || !this.duneApiKey) {
|
|
238
|
+
throw new ClankerError("Dune API key is required for getClankerDictionary");
|
|
170
239
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
240
|
+
try {
|
|
241
|
+
const result = yield this.dune.getLatestResult({ queryId: this.DICTIONARY_QUERY_ID });
|
|
242
|
+
if (!((_a = result == null ? void 0 : result.result) == null ? void 0 : _a.rows)) {
|
|
243
|
+
throw new ClankerError("No data returned from Dune");
|
|
244
|
+
}
|
|
245
|
+
const rows = result.result.rows;
|
|
246
|
+
return rows.map((row) => ({
|
|
247
|
+
name: row.name,
|
|
248
|
+
symbol: row.symbol,
|
|
249
|
+
marketCap: row.market_cap,
|
|
250
|
+
volume24h: row.volume_24h,
|
|
251
|
+
volume7d: row.volume_7d,
|
|
252
|
+
liquidity: row.liquidity
|
|
253
|
+
}));
|
|
254
|
+
} catch (error) {
|
|
255
|
+
if (error instanceof Error) {
|
|
256
|
+
throw new ClankerError(`Failed to fetch Clanker dictionary: ${error.message}`);
|
|
257
|
+
}
|
|
258
|
+
throw new ClankerError("Failed to fetch Clanker dictionary");
|
|
176
259
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get DEX pair stats for a specific chain (requires Dune API key)
|
|
264
|
+
* @param chain - The blockchain to query (e.g., 'ethereum', 'arbitrum', etc.)
|
|
265
|
+
* @param tokenAddress - Optional token address to filter by
|
|
266
|
+
*/
|
|
267
|
+
getDexPairStats(chain, tokenAddress) {
|
|
268
|
+
return __async(this, null, function* () {
|
|
269
|
+
if (!this.duneApiKey) {
|
|
270
|
+
throw new ClankerError("Dune API key is required for getDexPairStats");
|
|
180
271
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
272
|
+
const url = `https://api.dune.com/api/v1/dex/pairs/${chain}`;
|
|
273
|
+
const options = {
|
|
274
|
+
method: "GET",
|
|
275
|
+
headers: {
|
|
276
|
+
"X-Dune-Api-Key": this.duneApiKey
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
try {
|
|
280
|
+
const response = yield fetch(url, options);
|
|
281
|
+
if (!response.ok) {
|
|
282
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
283
|
+
}
|
|
284
|
+
const data = yield response.json();
|
|
285
|
+
if (tokenAddress) {
|
|
286
|
+
return this.filterPairsByToken(data.result.rows, tokenAddress);
|
|
287
|
+
}
|
|
288
|
+
return data.result.rows;
|
|
289
|
+
} catch (error) {
|
|
290
|
+
if (error instanceof Error) {
|
|
291
|
+
throw new ClankerError(`Failed to fetch DEX pair stats: ${error.message}`);
|
|
292
|
+
}
|
|
293
|
+
throw new ClankerError("Failed to fetch DEX pair stats");
|
|
185
294
|
}
|
|
186
|
-
|
|
187
|
-
}
|
|
295
|
+
});
|
|
188
296
|
}
|
|
189
297
|
/**
|
|
190
|
-
*
|
|
298
|
+
* Fetch Uniswap data for multiple tokens using The Graph (requires Graph API key)
|
|
299
|
+
* @param contractAddresses Array of token contract addresses
|
|
300
|
+
* @param blockNumber Optional block number for historical data
|
|
191
301
|
*/
|
|
192
|
-
|
|
193
|
-
return
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
302
|
+
getUniswapData(contractAddresses, blockNumber) {
|
|
303
|
+
return __async(this, null, function* () {
|
|
304
|
+
if (!this.graphApiKey) {
|
|
305
|
+
throw new ClankerError("Graph API key is required for getUniswapData");
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
const query = this.buildUniswapQuery(contractAddresses, blockNumber);
|
|
309
|
+
const response = yield axios2.post(
|
|
310
|
+
`${this.GRAPH_API_ENDPOINT}/${this.graphApiKey}/subgraphs/id/${this.UNISWAP_SUBGRAPH_ID}`,
|
|
311
|
+
{ query },
|
|
312
|
+
{ headers: { "Content-Type": "application/json" } }
|
|
313
|
+
);
|
|
314
|
+
return this.transformUniswapData(response.data.data, contractAddresses);
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (error instanceof Error) {
|
|
317
|
+
throw new ClankerError(`Failed to fetch Uniswap data: ${error.message}`);
|
|
318
|
+
}
|
|
319
|
+
throw new ClankerError("Failed to fetch Uniswap data");
|
|
320
|
+
}
|
|
321
|
+
});
|
|
201
322
|
}
|
|
202
323
|
/**
|
|
203
324
|
* Filter DEX pairs by token address
|
|
204
325
|
*/
|
|
205
|
-
filterPairsByToken(
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
326
|
+
filterPairsByToken(pairs, tokenAddress) {
|
|
327
|
+
return pairs.filter(
|
|
328
|
+
(pair) => {
|
|
329
|
+
var _a, _b;
|
|
330
|
+
return ((_a = pair.token_a_address) == null ? void 0 : _a.toLowerCase()) === tokenAddress.toLowerCase() || ((_b = pair.token_b_address) == null ? void 0 : _b.toLowerCase()) === tokenAddress.toLowerCase();
|
|
331
|
+
}
|
|
209
332
|
);
|
|
210
333
|
}
|
|
334
|
+
buildUniswapQuery(contractAddresses, blockNumber) {
|
|
335
|
+
const queryParts = contractAddresses.map((address) => {
|
|
336
|
+
const key = `token${address.toLowerCase()}`;
|
|
337
|
+
return `
|
|
338
|
+
${key}: token(id:"${address.toLowerCase()}"${blockNumber ? `, block: {number: ${blockNumber}}` : ""}) {
|
|
339
|
+
id,
|
|
340
|
+
whitelistPools(orderBy:createdAtBlockNumber, orderDirection:asc, first: 1) {
|
|
341
|
+
id,
|
|
342
|
+
createdAtBlockNumber,
|
|
343
|
+
token0 {
|
|
344
|
+
name,
|
|
345
|
+
symbol,
|
|
346
|
+
decimals
|
|
347
|
+
},
|
|
348
|
+
token1 {
|
|
349
|
+
name,
|
|
350
|
+
symbol,
|
|
351
|
+
decimals
|
|
352
|
+
},
|
|
353
|
+
sqrtPrice
|
|
354
|
+
},
|
|
355
|
+
untrackedVolumeUSD,
|
|
356
|
+
txCount,
|
|
357
|
+
decimals
|
|
358
|
+
}
|
|
359
|
+
`;
|
|
360
|
+
});
|
|
361
|
+
return `{ ${queryParts.join("\n")} }`;
|
|
362
|
+
}
|
|
363
|
+
transformUniswapData(data, addresses) {
|
|
364
|
+
const tokens = [];
|
|
365
|
+
for (const address of addresses) {
|
|
366
|
+
const key = `token${address.toLowerCase()}`;
|
|
367
|
+
const tokenData = data[key];
|
|
368
|
+
if (!tokenData || tokenData.whitelistPools.length === 0) {
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
const pool = tokenData.whitelistPools[0];
|
|
372
|
+
if (pool.token1.symbol !== "WETH") {
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
const price = this.calculatePrice(
|
|
376
|
+
Number(pool.sqrtPrice),
|
|
377
|
+
Number(pool.token0.decimals),
|
|
378
|
+
Number(pool.token1.decimals)
|
|
379
|
+
);
|
|
380
|
+
tokens.push({
|
|
381
|
+
contractAddress: tokenData.id,
|
|
382
|
+
decimals: Number(tokenData.decimals),
|
|
383
|
+
transactionCount: Number(tokenData.txCount),
|
|
384
|
+
volumeUSD: Number(tokenData.untrackedVolumeUSD),
|
|
385
|
+
priceWETH: price
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
return tokens;
|
|
389
|
+
}
|
|
390
|
+
calculatePrice(sqrtPriceX96, decimalsToken0, decimalsToken1) {
|
|
391
|
+
const price = Math.pow(sqrtPriceX96, 2) / Math.pow(2, 192);
|
|
392
|
+
const decimalAdjustment = Math.pow(10, decimalsToken1 - decimalsToken0);
|
|
393
|
+
return price * decimalAdjustment;
|
|
394
|
+
}
|
|
211
395
|
};
|
|
212
396
|
export {
|
|
213
397
|
ClankerError,
|