hood-cli 0.1.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/LICENSE +201 -0
- package/README.md +471 -0
- package/contracts/ERC20.sol +70 -0
- package/dist/chunk-CWEBZB3L.js +2248 -0
- package/dist/chunk-CWEBZB3L.js.map +1 -0
- package/dist/cli.js +1996 -0
- package/dist/cli.js.map +1 -0
- package/dist/dist-PVZTS5LZ.js +138 -0
- package/dist/dist-PVZTS5LZ.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,2248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// node_modules/hoodchain/dist/index.js
|
|
4
|
+
import {
|
|
5
|
+
createPublicClient,
|
|
6
|
+
createWalletClient,
|
|
7
|
+
http
|
|
8
|
+
} from "viem";
|
|
9
|
+
import { robinhood, robinhoodTestnet } from "viem/chains";
|
|
10
|
+
import { formatUnits } from "viem";
|
|
11
|
+
import {
|
|
12
|
+
encodeFunctionData,
|
|
13
|
+
encodePacked
|
|
14
|
+
} from "viem";
|
|
15
|
+
import { formatUnits as formatUnits2, parseUnits } from "viem";
|
|
16
|
+
import { parseTransaction, keccak256 } from "viem";
|
|
17
|
+
function createHoodClient(config = {}) {
|
|
18
|
+
const network = config.chain ?? "mainnet";
|
|
19
|
+
const chain = network === "testnet" ? robinhoodTestnet : robinhood;
|
|
20
|
+
const transport = config.transport ?? http(config.rpcUrl);
|
|
21
|
+
const publicClient = createPublicClient({
|
|
22
|
+
chain,
|
|
23
|
+
transport,
|
|
24
|
+
batch: { multicall: true }
|
|
25
|
+
});
|
|
26
|
+
const wallet = config.account ? createWalletClient({ chain, transport, account: config.account }) : null;
|
|
27
|
+
return {
|
|
28
|
+
chain,
|
|
29
|
+
network,
|
|
30
|
+
public: publicClient,
|
|
31
|
+
wallet,
|
|
32
|
+
account: config.account ?? null,
|
|
33
|
+
acknowledgeStockTokenEligibility: config.acknowledgeStockTokenEligibility ?? false
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
var MAINNET_ADDRESSES = {
|
|
37
|
+
/** USDG — Paxos Global Dollar, the chain's dollar stablecoin. 6 decimals. */
|
|
38
|
+
usdg: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
|
|
39
|
+
/** Canonical WETH9. */
|
|
40
|
+
weth: "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73",
|
|
41
|
+
/** Uniswap v3 factory. */
|
|
42
|
+
uniswapV3Factory: "0x1f7d7550B1b028f7571E69A784071F0205FD2EfA",
|
|
43
|
+
/** Uniswap QuoterV2. */
|
|
44
|
+
quoterV2: "0x33e885eD0Ec9bF04EcfB19341582aADCb4c8A9E7",
|
|
45
|
+
/** Uniswap SwapRouter02. */
|
|
46
|
+
swapRouter02: "0xCaf681a66D020601342297493863E78C959E5cb2",
|
|
47
|
+
/** Uniswap UniversalRouter. */
|
|
48
|
+
universalRouter: "0x53BF6B0684Ec7eF91e1387Da3D1a1769bC5A6F77",
|
|
49
|
+
/** Uniswap NonfungiblePositionManager. */
|
|
50
|
+
nonfungiblePositionManager: "0x73991a25C818Bf1f1128dEAaB1492D45638DE0D3",
|
|
51
|
+
/** Multicall3 (canonical deterministic deployment). */
|
|
52
|
+
multicall3: "0xca11bde05977b3631167028862be2a173976ca11"
|
|
53
|
+
};
|
|
54
|
+
var TESTNET_ADDRESSES = {
|
|
55
|
+
/** Testnet USDG ("Global Dollar", 6 decimals). */
|
|
56
|
+
usdg: "0x7E955252E15c84f5768B83c41a71F9eba181802F",
|
|
57
|
+
/** Canonical testnet WETH9 (matches the official protocol-contracts L2 WETH). */
|
|
58
|
+
weth: "0x7943e237c7F95DA44E0301572D358911207852Fa",
|
|
59
|
+
/** Community Uniswap v3 factory (the one with liquid Stock Token pools). */
|
|
60
|
+
uniswapV3Factory: "0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865",
|
|
61
|
+
/** QuoterV2 linked to the factory above. */
|
|
62
|
+
quoterV2: "0xcf05Fc31A6B693DD0bEB76e958ae4BCD490dc985",
|
|
63
|
+
/** Classic v3 SwapRouter (struct-level deadline) linked to the factory above. */
|
|
64
|
+
swapRouter: "0x1b81D678ffb9C0263b24A97847620C99d213eB14",
|
|
65
|
+
/** NonfungiblePositionManager linked to the factory above. */
|
|
66
|
+
nonfungiblePositionManager: "0x46A15B0b27311cedF172AB29E4f4766fbE7F4364",
|
|
67
|
+
/** Multicall3 (canonical deterministic deployment, in viem's chain def). */
|
|
68
|
+
multicall3: "0xca11bde05977b3631167028862be2a173976ca11"
|
|
69
|
+
};
|
|
70
|
+
var TESTNET_STOCK_TOKENS = {
|
|
71
|
+
TSLA: "0xC9f9c86933092BbbfFF3CCb4b105A4A94bf3Bd4E",
|
|
72
|
+
AMZN: "0x5884aD2f920c162CFBbACc88C9C51AA75eC09E02",
|
|
73
|
+
PLTR: "0x1FBE1a0e43594b3455993B5dE5Fd0A7A266298d0",
|
|
74
|
+
NFLX: "0x3b8262A63d25f0477c4DDE23F83cfe22Cb768C93",
|
|
75
|
+
AMD: "0x71178BAc73cBeb415514eB542a8995b82669778d"
|
|
76
|
+
};
|
|
77
|
+
var MAINNET_FEED_URL = "wss://feed.mainnet.chain.robinhood.com";
|
|
78
|
+
var MAINNET_EXPLORER_URL = "https://robinhoodchain.blockscout.com";
|
|
79
|
+
var USDG_DECIMALS = 6;
|
|
80
|
+
var STOCK_TOKEN_DECIMALS = 18;
|
|
81
|
+
var FEED_DECIMALS = 8;
|
|
82
|
+
var V3_FEE_TIERS = [100, 500, 3e3, 1e4];
|
|
83
|
+
var HoodchainError = class extends Error {
|
|
84
|
+
name = "HoodchainError";
|
|
85
|
+
};
|
|
86
|
+
var UnknownSymbolError = class extends HoodchainError {
|
|
87
|
+
name = "UnknownSymbolError";
|
|
88
|
+
/** The symbol that failed to resolve. */
|
|
89
|
+
symbol;
|
|
90
|
+
constructor(symbol) {
|
|
91
|
+
super(
|
|
92
|
+
`Unknown Stock Token symbol "${symbol}". Symbols are case-insensitive tickers as listed on-chain (e.g. "AAPL", "TSLA"). Call listStockTokens() to enumerate the registry.`
|
|
93
|
+
);
|
|
94
|
+
this.symbol = symbol;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
var FeedNotFoundError = class extends HoodchainError {
|
|
98
|
+
name = "FeedNotFoundError";
|
|
99
|
+
symbol;
|
|
100
|
+
constructor(symbol) {
|
|
101
|
+
super(
|
|
102
|
+
`Stock Token "${symbol}" has no Chainlink price feed in the registry. Its balance can still be read, but it cannot be priced on-chain.`
|
|
103
|
+
);
|
|
104
|
+
this.symbol = symbol;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var StaleFeedError = class extends HoodchainError {
|
|
108
|
+
name = "StaleFeedError";
|
|
109
|
+
symbol;
|
|
110
|
+
/** Feed timestamp, seconds since epoch. */
|
|
111
|
+
updatedAt;
|
|
112
|
+
/** Age of the answer in seconds at read time. */
|
|
113
|
+
ageSeconds;
|
|
114
|
+
/** The staleness window that was exceeded. */
|
|
115
|
+
maxAgeSeconds;
|
|
116
|
+
constructor(symbol, updatedAt, ageSeconds, maxAgeSeconds) {
|
|
117
|
+
super(
|
|
118
|
+
`Chainlink feed for "${symbol}" is stale: answer is ${ageSeconds}s old (updatedAt ${new Date(updatedAt * 1e3).toISOString()}), allowed max is ${maxAgeSeconds}s. Stock feeds pause outside market hours (24/5); pass a larger maxAgeSeconds if weekend/holiday reads are acceptable.`
|
|
119
|
+
);
|
|
120
|
+
this.symbol = symbol;
|
|
121
|
+
this.updatedAt = updatedAt;
|
|
122
|
+
this.ageSeconds = ageSeconds;
|
|
123
|
+
this.maxAgeSeconds = maxAgeSeconds;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var InvalidFeedAnswerError = class extends HoodchainError {
|
|
127
|
+
name = "InvalidFeedAnswerError";
|
|
128
|
+
symbol;
|
|
129
|
+
constructor(symbol, detail) {
|
|
130
|
+
super(`Chainlink feed for "${symbol}" returned an invalid answer: ${detail}`);
|
|
131
|
+
this.symbol = symbol;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
var NoRouteError = class extends HoodchainError {
|
|
135
|
+
name = "NoRouteError";
|
|
136
|
+
tokenIn;
|
|
137
|
+
tokenOut;
|
|
138
|
+
constructor(tokenIn, tokenOut, detail) {
|
|
139
|
+
super(
|
|
140
|
+
`No swappable Uniswap v3 route from ${tokenIn} to ${tokenOut}` + (detail ? `: ${detail}` : ".") + ` Pools may exist without liquidity; try a different intermediate or amount.`
|
|
141
|
+
);
|
|
142
|
+
this.tokenIn = tokenIn;
|
|
143
|
+
this.tokenOut = tokenOut;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
var SlippageExceededError = class extends HoodchainError {
|
|
147
|
+
name = "SlippageExceededError";
|
|
148
|
+
constructor(detail) {
|
|
149
|
+
super(`Slippage bound exceeded: ${detail}`);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
var NoAccountError = class extends HoodchainError {
|
|
153
|
+
name = "NoAccountError";
|
|
154
|
+
constructor(operation) {
|
|
155
|
+
super(
|
|
156
|
+
`${operation} requires a wallet. Pass an \`account\` to createHoodClient (e.g. privateKeyToAccount(process.env.ROBINHOOD_CHAIN_PRIVATE_KEY)).`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
var StockTokenEligibilityError = class extends HoodchainError {
|
|
161
|
+
name = "StockTokenEligibilityError";
|
|
162
|
+
constructor() {
|
|
163
|
+
super(
|
|
164
|
+
"Refusing to build a swap that acquires a Stock Token: eligibility not acknowledged. Stock Tokens may not be offered, sold, or delivered to US persons (issuer: Robinhood Assets (Jersey) Ltd; extra limits: Canada, UK, Switzerland). If the operator of this software is eligible, set `acknowledgeStockTokenEligibility: true` in createHoodClient config."
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
var FeedConnectionError = class extends HoodchainError {
|
|
169
|
+
name = "FeedConnectionError";
|
|
170
|
+
constructor(url, attempts, lastError) {
|
|
171
|
+
super(
|
|
172
|
+
`Sequencer feed connection to ${url} failed after ${attempts} attempts` + (lastError ? `: ${lastError}` : ".")
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
var stock_tokens_default = {
|
|
177
|
+
$schema: "./stock-tokens.schema.json",
|
|
178
|
+
chainId: 4663,
|
|
179
|
+
generatedAtBlock: 7690522,
|
|
180
|
+
sources: {
|
|
181
|
+
discovery: 'https://robinhoodchain.blockscout.com/api/v2/tokens?q=Robinhood Token (canonical name pattern "<Name> \u2022 Robinhood Token")',
|
|
182
|
+
feeds: "https://reference-data-directory.vercel.app/feeds-robinhood-mainnet.json",
|
|
183
|
+
verification: "on-chain via public RPC: shared beacon slot, symbol/name/decimals/uiMultiplier multicall, feed latestRoundData"
|
|
184
|
+
},
|
|
185
|
+
stockBeacon: "0xe10b6f6B275de231345c20D14Ab812db62151b00",
|
|
186
|
+
tokenCount: 95,
|
|
187
|
+
feedCount: 34,
|
|
188
|
+
tokens: [
|
|
189
|
+
{
|
|
190
|
+
address: "0x521Cf887E6531c6F667b5BC4D896E5d9bfE8EB2E",
|
|
191
|
+
symbol: "AAOI",
|
|
192
|
+
name: "Applied Optoelectronics \u2022 Robinhood Token",
|
|
193
|
+
decimals: 18,
|
|
194
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
195
|
+
feed: null,
|
|
196
|
+
feedDecimals: null
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
address: "0xaF3D76f1834A1d425780943C99Ea8A608f8a93f9",
|
|
200
|
+
symbol: "AAPL",
|
|
201
|
+
name: "Apple \u2022 Robinhood Token",
|
|
202
|
+
decimals: 18,
|
|
203
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
204
|
+
feed: "0x6B22A786bAa607d76728168703a39Ea9C99f2cD0",
|
|
205
|
+
feedDecimals: 8,
|
|
206
|
+
feedAnswerAtGeneration: "31550000000",
|
|
207
|
+
feedUpdatedAtGeneration: 1783710274
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
address: "0x36046893810a7E7fCE501229d57dc3FC8c8716d0",
|
|
211
|
+
symbol: "AMAT",
|
|
212
|
+
name: "Applied Materials \u2022 Robinhood Token",
|
|
213
|
+
decimals: 18,
|
|
214
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
215
|
+
feed: null,
|
|
216
|
+
feedDecimals: null
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
address: "0x86923f96303D656E4aa86D9d42D1e57ad2023fdC",
|
|
220
|
+
symbol: "AMD",
|
|
221
|
+
name: "AMD \u2022 Robinhood Token",
|
|
222
|
+
decimals: 18,
|
|
223
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
224
|
+
feed: "0x943A29E7ae51A4798823ca9eEd2ed533B2A22C72",
|
|
225
|
+
feedDecimals: 8,
|
|
226
|
+
feedAnswerAtGeneration: "55821015000",
|
|
227
|
+
feedUpdatedAtGeneration: 1783713346
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
address: "0x12f190a9F9d7D37a250758b26824B97CE941bF54",
|
|
231
|
+
symbol: "AMZN",
|
|
232
|
+
name: "Amazon \u2022 Robinhood Token",
|
|
233
|
+
decimals: 18,
|
|
234
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
235
|
+
feed: "0xD5a1508ceD74c084eBf3cBe853e2C968fB2a651C",
|
|
236
|
+
feedDecimals: 8,
|
|
237
|
+
feedAnswerAtGeneration: "24556770000",
|
|
238
|
+
feedUpdatedAtGeneration: 1783692103
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
address: "0xb8DBf92F9741c9ac1c32115E78581f23509916FD",
|
|
242
|
+
symbol: "APLD",
|
|
243
|
+
name: "Applied Digital \u2022 Robinhood Token",
|
|
244
|
+
decimals: 18,
|
|
245
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
246
|
+
feed: null,
|
|
247
|
+
feedDecimals: null
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
address: "0x47F93d52cBeC7C6D2CfC080e154002370a60dAEA",
|
|
251
|
+
symbol: "ASML",
|
|
252
|
+
name: "ASML Holding NV \u2022 Robinhood Token",
|
|
253
|
+
decimals: 18,
|
|
254
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
255
|
+
feed: "0xB4106147E8cce40b7d46124090d373A71b70f87D",
|
|
256
|
+
feedDecimals: 8,
|
|
257
|
+
feedAnswerAtGeneration: "179758755000",
|
|
258
|
+
feedUpdatedAtGeneration: 1783710661
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
address: "0x1AF6446f07eb1d97c546AFC8c9544cBDF3AD5137",
|
|
262
|
+
symbol: "ASTS",
|
|
263
|
+
name: "AST SpaceMobile \u2022 Robinhood Token",
|
|
264
|
+
decimals: 18,
|
|
265
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
266
|
+
feed: null,
|
|
267
|
+
feedDecimals: null
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
address: "0x156E175DD063a8cE274C50654eF40e0032b3fbcF",
|
|
271
|
+
symbol: "AVGO",
|
|
272
|
+
name: "Broadcom \u2022 Robinhood Token",
|
|
273
|
+
decimals: 18,
|
|
274
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
275
|
+
feed: null,
|
|
276
|
+
feedDecimals: null
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
address: "0x4D21483a44Bf67a86b77E3dA301411880797D452",
|
|
280
|
+
symbol: "BA",
|
|
281
|
+
name: "Boeing \u2022 Robinhood Token",
|
|
282
|
+
decimals: 18,
|
|
283
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
284
|
+
feed: null,
|
|
285
|
+
feedDecimals: null
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
address: "0xad25Ac6C84D497db898fa1E8387bf6Af3532a1c4",
|
|
289
|
+
symbol: "BABA",
|
|
290
|
+
name: "Alibaba \u2022 Robinhood Token",
|
|
291
|
+
decimals: 18,
|
|
292
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
293
|
+
feed: "0x62Cc8F9b5f56a33c9C8A60c8B92779f523c4E984",
|
|
294
|
+
feedDecimals: 8,
|
|
295
|
+
feedAnswerAtGeneration: "11219500000",
|
|
296
|
+
feedUpdatedAtGeneration: 1783707523
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
address: "0x822CC93fFD030293E9842c30BBD678F530701867",
|
|
300
|
+
symbol: "BE",
|
|
301
|
+
name: "Bloom Energy \u2022 Robinhood Token",
|
|
302
|
+
decimals: 18,
|
|
303
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
304
|
+
feed: null,
|
|
305
|
+
feedDecimals: null
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
address: "0x5c90450Bbb4273D7b2f17CF6917AEB237A569679",
|
|
309
|
+
symbol: "CBRS",
|
|
310
|
+
name: "Cerebras Systems \u2022 Robinhood Token",
|
|
311
|
+
decimals: 18,
|
|
312
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
313
|
+
feed: null,
|
|
314
|
+
feedDecimals: null
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
address: "0x9651342CeA770aE9a2969Ba2A52611523146aef9",
|
|
318
|
+
symbol: "CCL",
|
|
319
|
+
name: "Carnival Corporation \u2022 Robinhood Token",
|
|
320
|
+
decimals: 18,
|
|
321
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
322
|
+
feed: null,
|
|
323
|
+
feedDecimals: null
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
address: "0x8cF07C5A878945185d327aAa6e33FAa95F95e7bF",
|
|
327
|
+
symbol: "CELH",
|
|
328
|
+
name: "Celsius \u2022 Robinhood Token",
|
|
329
|
+
decimals: 18,
|
|
330
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
331
|
+
feed: null,
|
|
332
|
+
feedDecimals: null
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
address: "0xcBB95BBF36099d34dA091dc6Fa6F49EfA257Cee3",
|
|
336
|
+
symbol: "CLSK",
|
|
337
|
+
name: "CleanSpark \u2022 Robinhood Token",
|
|
338
|
+
decimals: 18,
|
|
339
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
340
|
+
feed: "0x810c12D3a554Bc47fd39597Fe3b3AAC4941F50eF",
|
|
341
|
+
feedDecimals: 8,
|
|
342
|
+
feedAnswerAtGeneration: "1286499999",
|
|
343
|
+
feedUpdatedAtGeneration: 1783713313
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
address: "0x6330D8C3178a418788dF01a47479c0ce7CCF450b",
|
|
347
|
+
symbol: "COIN",
|
|
348
|
+
name: "Coinbase \u2022 Robinhood Token",
|
|
349
|
+
decimals: 18,
|
|
350
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
351
|
+
feed: "0xA3a468A452940B7D6b69991207B508c609a98Ef2",
|
|
352
|
+
feedDecimals: 8,
|
|
353
|
+
feedAnswerAtGeneration: "15943135000",
|
|
354
|
+
feedUpdatedAtGeneration: 1783703473
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
address: "0x4EA005168D7F09a7A0Ba9D1DEf21a479950E44C2",
|
|
358
|
+
symbol: "COST",
|
|
359
|
+
name: "Costco \u2022 Robinhood Token",
|
|
360
|
+
decimals: 18,
|
|
361
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
362
|
+
feed: null,
|
|
363
|
+
feedDecimals: null
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
address: "0xdF0992E440dD0be65BD8439b609d6D4366bf1CB5",
|
|
367
|
+
symbol: "CRCL",
|
|
368
|
+
name: "Circle Internet Group \u2022 Robinhood Token",
|
|
369
|
+
decimals: 18,
|
|
370
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
371
|
+
feed: "0x6652eDf64bA3731C4F2D3ce821A0Fb1f1f6b482a",
|
|
372
|
+
feedDecimals: 8,
|
|
373
|
+
feedAnswerAtGeneration: "6634635000",
|
|
374
|
+
feedUpdatedAtGeneration: 1783717360
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
address: "0x5f10A1C971B69e47e059e1dC91901B59b3fB49C3",
|
|
378
|
+
symbol: "CRWV",
|
|
379
|
+
name: "CoreWeave \u2022 Robinhood Token",
|
|
380
|
+
decimals: 18,
|
|
381
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
382
|
+
feed: "0xe1b3aABCAFAd1c94708dc1367dcfF8Aa4407487C",
|
|
383
|
+
feedDecimals: 8,
|
|
384
|
+
feedAnswerAtGeneration: "8883970000",
|
|
385
|
+
feedUpdatedAtGeneration: 1783711393
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
address: "0x27c99fBde9D0d2AA4f4Bfb4943f237843DdF6958",
|
|
389
|
+
symbol: "DDOG",
|
|
390
|
+
name: "Datadog \u2022 Robinhood Token",
|
|
391
|
+
decimals: 18,
|
|
392
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
393
|
+
feed: null,
|
|
394
|
+
feedDecimals: null
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
address: "0x941AE714EC6D8130c7B75d67160Ca08f1e7d11Dd",
|
|
398
|
+
symbol: "DELL",
|
|
399
|
+
name: "Dell \u2022 Robinhood Token",
|
|
400
|
+
decimals: 18,
|
|
401
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
402
|
+
feed: null,
|
|
403
|
+
feedDecimals: null
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
address: "0x39EC44Bee4F6A116c6F9B8De566848a985C53C60",
|
|
407
|
+
symbol: "ELF",
|
|
408
|
+
name: "e.l.f. Beauty \u2022 Robinhood Token",
|
|
409
|
+
decimals: 18,
|
|
410
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
411
|
+
feed: null,
|
|
412
|
+
feedDecimals: null
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
address: "0x7f0aBeF0C07280F82c6a08ead09dEd6BAE2C13Fc",
|
|
416
|
+
symbol: "EWY",
|
|
417
|
+
name: "iShares MSCI South Korea fund \u2022 Robinhood Token",
|
|
418
|
+
decimals: 18,
|
|
419
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
420
|
+
feed: "0xEFdf54610B62A7753Ec30bDc380847c12D32e1D1",
|
|
421
|
+
feedDecimals: 8,
|
|
422
|
+
feedAnswerAtGeneration: "18377499999",
|
|
423
|
+
feedUpdatedAtGeneration: 1783705547
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
address: "0x25C288E6D899b9BC30160965aD9644c67e73bE0C",
|
|
427
|
+
symbol: "F",
|
|
428
|
+
name: "Ford Motor \u2022 Robinhood Token",
|
|
429
|
+
decimals: 18,
|
|
430
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
431
|
+
feed: null,
|
|
432
|
+
feedDecimals: null
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
address: "0x282e87451E10fA6679BC7D76C69BE44cD3fC777C",
|
|
436
|
+
symbol: "FLNC",
|
|
437
|
+
name: "Fluence Energy \u2022 Robinhood Token",
|
|
438
|
+
decimals: 18,
|
|
439
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
440
|
+
feed: null,
|
|
441
|
+
feedDecimals: null
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
address: "0xeB30663bDFf0622Ef4e4E5cBb4E975F19f33f51D",
|
|
445
|
+
symbol: "FUTU",
|
|
446
|
+
name: "Futu Holdings \u2022 Robinhood Token",
|
|
447
|
+
decimals: 18,
|
|
448
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
449
|
+
feed: null,
|
|
450
|
+
feedDecimals: null
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
address: "0x7c04E6A3368F2A1DE3874f0e80d2e0A1a9915da6",
|
|
454
|
+
symbol: "GLW",
|
|
455
|
+
name: "Corning \u2022 Robinhood Token",
|
|
456
|
+
decimals: 18,
|
|
457
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
458
|
+
feed: null,
|
|
459
|
+
feedDecimals: null
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
address: "0x1b0E319c6A659F002271B69dB8A7df2F911c153E",
|
|
463
|
+
symbol: "GME",
|
|
464
|
+
name: "GameStop \u2022 Robinhood Token",
|
|
465
|
+
decimals: 18,
|
|
466
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
467
|
+
feed: "0x27C71df6A64fB476468EdF256CF72c038baB5B67",
|
|
468
|
+
feedDecimals: 8,
|
|
469
|
+
feedAnswerAtGeneration: "2176125000",
|
|
470
|
+
feedUpdatedAtGeneration: 1783694006
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
address: "0x2e0847E8910a9732eB3fb1bb4b70a580ADAD4FE3",
|
|
474
|
+
symbol: "GOOGL",
|
|
475
|
+
name: "Alphabet Class A \u2022 Robinhood Token",
|
|
476
|
+
decimals: 18,
|
|
477
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
478
|
+
feed: "0xF6f373a037c30F0e5010d854385cA89185AE638b",
|
|
479
|
+
feedDecimals: 8,
|
|
480
|
+
feedAnswerAtGeneration: "35672000000",
|
|
481
|
+
feedUpdatedAtGeneration: 1783711606
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
address: "0xf1953DAB6FaD537488d5A022361FfAa8B4c95eC6",
|
|
485
|
+
symbol: "INOD",
|
|
486
|
+
name: "Innodata \u2022 Robinhood Token",
|
|
487
|
+
decimals: 18,
|
|
488
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
489
|
+
feed: null,
|
|
490
|
+
feedDecimals: null
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
address: "0xc72b96e0E48ecd4DC75E1e45396e26300BC39681",
|
|
494
|
+
symbol: "INTC",
|
|
495
|
+
name: "Intel \u2022 Robinhood Token",
|
|
496
|
+
decimals: 18,
|
|
497
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
498
|
+
feed: "0x3f390C5C24628Ac7C489515402235FeAD71D1913",
|
|
499
|
+
feedDecimals: 8,
|
|
500
|
+
feedAnswerAtGeneration: "10965095000",
|
|
501
|
+
feedUpdatedAtGeneration: 1783712771
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
address: "0x56d23beE5f41A7120170b0c603Dae30128e460e9",
|
|
505
|
+
symbol: "INTU",
|
|
506
|
+
name: "Intuit \u2022 Robinhood Token",
|
|
507
|
+
decimals: 18,
|
|
508
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
509
|
+
feed: null,
|
|
510
|
+
feedDecimals: null
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
address: "0x558378E000D634A36593E338eBacdd6207640EfE",
|
|
514
|
+
symbol: "IONQ",
|
|
515
|
+
name: "IonQ \u2022 Robinhood Token",
|
|
516
|
+
decimals: 18,
|
|
517
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
518
|
+
feed: "0x22EfeC4919baf55F360E0EDee4AbEB26DE4971eb",
|
|
519
|
+
feedDecimals: 8,
|
|
520
|
+
feedAnswerAtGeneration: "4308000000",
|
|
521
|
+
feedUpdatedAtGeneration: 1783711226
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
address: "0xF0AB0c93bE6F41369d302e55db1A96b3c430212D",
|
|
525
|
+
symbol: "IREN",
|
|
526
|
+
name: "IREN Limited \u2022 Robinhood Token",
|
|
527
|
+
decimals: 18,
|
|
528
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
529
|
+
feed: null,
|
|
530
|
+
feedDecimals: null
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
address: "0x8eF20885F94e3D9bc7eB3080279188Bd5ED7c08C",
|
|
534
|
+
symbol: "LITE",
|
|
535
|
+
name: "Lumentum \u2022 Robinhood Token",
|
|
536
|
+
decimals: 18,
|
|
537
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
538
|
+
feed: null,
|
|
539
|
+
feedDecimals: null
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
address: "0x8005d266423c7ea827372c9c864491e5786600ea",
|
|
543
|
+
symbol: "LLY",
|
|
544
|
+
name: "Eli Lilly \u2022 Robinhood Token",
|
|
545
|
+
decimals: 18,
|
|
546
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
547
|
+
feed: null,
|
|
548
|
+
feedDecimals: null
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
address: "0x4e62068525Ab11FE768e29dfD00ef909B9803016",
|
|
552
|
+
symbol: "LULU",
|
|
553
|
+
name: "Lululemon \u2022 Robinhood Token",
|
|
554
|
+
decimals: 18,
|
|
555
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
556
|
+
feed: null,
|
|
557
|
+
feedDecimals: null
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
address: "0xa5D4968421bA94814Be3B136b15cf422101aC1a3",
|
|
561
|
+
symbol: "LUNR",
|
|
562
|
+
name: "Intuitive Machines \u2022 Robinhood Token",
|
|
563
|
+
decimals: 18,
|
|
564
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
565
|
+
feed: null,
|
|
566
|
+
feedDecimals: null
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
address: "0xDdf2266b79abf0B48898959B0ed6E6adf512be74",
|
|
570
|
+
symbol: "MDB",
|
|
571
|
+
name: "MongoDB \u2022 Robinhood Token",
|
|
572
|
+
decimals: 18,
|
|
573
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
574
|
+
feed: null,
|
|
575
|
+
feedDecimals: null
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
address: "0xc0D6457C16Cc70d6790Dd43521C899C87ce02f35",
|
|
579
|
+
symbol: "META",
|
|
580
|
+
name: "Meta Platforms \u2022 Robinhood Token",
|
|
581
|
+
decimals: 18,
|
|
582
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
583
|
+
feed: "0x7C38C00C30BEe9378381E7B6135d7283356D71b1",
|
|
584
|
+
feedDecimals: 8,
|
|
585
|
+
feedAnswerAtGeneration: "67067485000",
|
|
586
|
+
feedUpdatedAtGeneration: 1783713401
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
address: "0x62fd0668e10D8B72339BE2DCF7643001688ff13B",
|
|
590
|
+
symbol: "MRVL",
|
|
591
|
+
name: "Marvell Technology \u2022 Robinhood Token",
|
|
592
|
+
decimals: 18,
|
|
593
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
594
|
+
feed: null,
|
|
595
|
+
feedDecimals: null
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
address: "0xe93237C50D904957Cf27E7B1133b510C669c2e74",
|
|
599
|
+
symbol: "MSFT",
|
|
600
|
+
name: "Microsoft \u2022 Robinhood Token",
|
|
601
|
+
decimals: 18,
|
|
602
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
603
|
+
feed: "0x45C3C877C15E6BA2EBB19eA114Ea508d14C1Af2E",
|
|
604
|
+
feedDecimals: 8,
|
|
605
|
+
feedAnswerAtGeneration: "38469000000",
|
|
606
|
+
feedUpdatedAtGeneration: 1783697881
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
address: "0xec262a75e413fAfD0dF80480274532C79D42da09",
|
|
610
|
+
symbol: "MSTR",
|
|
611
|
+
name: "Strategy Inc. \u2022 Robinhood Token",
|
|
612
|
+
decimals: 18,
|
|
613
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
614
|
+
feed: "0x396118bdFB181e6240E74D243F266B061c0edc3D",
|
|
615
|
+
feedDecimals: 8,
|
|
616
|
+
feedAnswerAtGeneration: "9469500000",
|
|
617
|
+
feedUpdatedAtGeneration: 1783712395
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
address: "0xfF080c8ce2E5feadaCa0Da81314Ae59D232d4afD",
|
|
621
|
+
symbol: "MU",
|
|
622
|
+
name: "Micron Technology \u2022 Robinhood Token",
|
|
623
|
+
decimals: 18,
|
|
624
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
625
|
+
feed: "0x425EEFdCf05ed6526C3cE61Af99429A228a6d596",
|
|
626
|
+
feedDecimals: 8,
|
|
627
|
+
feedAnswerAtGeneration: "97854340000",
|
|
628
|
+
feedUpdatedAtGeneration: 1783713684
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
address: "0x48961813349333209994750ffA89b3c5C22eC969",
|
|
632
|
+
symbol: "MXL",
|
|
633
|
+
name: "MaxLinear \u2022 Robinhood Token",
|
|
634
|
+
decimals: 18,
|
|
635
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
636
|
+
feed: null,
|
|
637
|
+
feedDecimals: null
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
address: "0x9D9c6684F596F66a64C030B93A886D51Fd4D7931",
|
|
641
|
+
symbol: "NBIS",
|
|
642
|
+
name: "Nebius Group \u2022 Robinhood Token",
|
|
643
|
+
decimals: 18,
|
|
644
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
645
|
+
feed: "0xE1D87B116Ba0fe898998f1D140339D1fA1E09705",
|
|
646
|
+
feedDecimals: 8,
|
|
647
|
+
feedAnswerAtGeneration: "21923750000",
|
|
648
|
+
feedUpdatedAtGeneration: 1783713491
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
address: "0xE0444EF8BF4eD74f74FD73686e2ddF4C1c5591E8",
|
|
652
|
+
symbol: "NFLX",
|
|
653
|
+
name: "Netflix \u2022 Robinhood Token",
|
|
654
|
+
decimals: 18,
|
|
655
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
656
|
+
feed: null,
|
|
657
|
+
feedDecimals: null
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
address: "0xBEF75684C43c4ea7BD18Dd532a2244674Ee8b926",
|
|
661
|
+
symbol: "NNE",
|
|
662
|
+
name: "Nano Nuclear Energy \u2022 Robinhood Token",
|
|
663
|
+
decimals: 18,
|
|
664
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
665
|
+
feed: null,
|
|
666
|
+
feedDecimals: null
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
address: "0x0C3260aF4B8f13a69c4c2dFb84fD667890CDFa14",
|
|
670
|
+
symbol: "NOW",
|
|
671
|
+
name: "ServiceNow \u2022 Robinhood Token",
|
|
672
|
+
decimals: 18,
|
|
673
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
674
|
+
feed: null,
|
|
675
|
+
feedDecimals: null
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
address: "0x408c14038a04f7bD235329E26d2bf569ee20e250",
|
|
679
|
+
symbol: "NU",
|
|
680
|
+
name: "Nu \u2022 Robinhood Token",
|
|
681
|
+
decimals: 18,
|
|
682
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
683
|
+
feed: null,
|
|
684
|
+
feedDecimals: null
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
address: "0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC",
|
|
688
|
+
symbol: "NVDA",
|
|
689
|
+
name: "NVIDIA \u2022 Robinhood Token",
|
|
690
|
+
decimals: 18,
|
|
691
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
692
|
+
feed: "0x379EC4f7C378F34a1B47E4F3cbeBCbAC3E8E9F15",
|
|
693
|
+
feedDecimals: 8,
|
|
694
|
+
feedAnswerAtGeneration: "21019000000",
|
|
695
|
+
feedUpdatedAtGeneration: 1783700623
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
address: "0xbE6702d7b70315376dC48a3293f24f0982F86386",
|
|
699
|
+
symbol: "NVTS",
|
|
700
|
+
name: "Navitas Semiconductor \u2022 Robinhood Token",
|
|
701
|
+
decimals: 18,
|
|
702
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
703
|
+
feed: null,
|
|
704
|
+
feedDecimals: null
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
address: "0xb0992820E760d836549ba69BC7598b4af75dEE03",
|
|
708
|
+
symbol: "ORCL",
|
|
709
|
+
name: "Oracle \u2022 Robinhood Token",
|
|
710
|
+
decimals: 18,
|
|
711
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
712
|
+
feed: "0x0e6a64a2B58A6693a531E6c555f3A5d042eEA844",
|
|
713
|
+
feedDecimals: 8,
|
|
714
|
+
feedAnswerAtGeneration: "14108370000",
|
|
715
|
+
feedUpdatedAtGeneration: 1783709137
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
address: "0x1Cdad396DB64BDa184d5182A97Dd9B3C62100b7D",
|
|
719
|
+
symbol: "P",
|
|
720
|
+
name: "Everpure \u2022 Robinhood Token",
|
|
721
|
+
decimals: 18,
|
|
722
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
723
|
+
feed: null,
|
|
724
|
+
feedDecimals: null
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
address: "0x9b23573b156B52565012F5cE02CDF60AFBaa70Be",
|
|
728
|
+
symbol: "PENG",
|
|
729
|
+
name: "Penguin Solutions \u2022 Robinhood Token",
|
|
730
|
+
decimals: 18,
|
|
731
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
732
|
+
feed: null,
|
|
733
|
+
feedDecimals: null
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
address: "0x894E1EC2D74FFE5AEF8Dc8A9e84686acCB964F2A",
|
|
737
|
+
symbol: "PLTR",
|
|
738
|
+
name: "Palantir Technologies \u2022 Robinhood Token",
|
|
739
|
+
decimals: 18,
|
|
740
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
741
|
+
feed: "0x820ABedFF239034956B7A9d2F0a331f9F075eB4c",
|
|
742
|
+
feedDecimals: 8,
|
|
743
|
+
feedAnswerAtGeneration: "12656140000",
|
|
744
|
+
feedUpdatedAtGeneration: 1783714557
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
address: "0xcf6B2D875361be807EAfa57458c80f28521F9333",
|
|
748
|
+
symbol: "POET",
|
|
749
|
+
name: "POET Technologies \u2022 Robinhood Token",
|
|
750
|
+
decimals: 18,
|
|
751
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
752
|
+
feed: null,
|
|
753
|
+
feedDecimals: null
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
address: "0x4189F0c66EBBB0bfeF1C31f763131361EF32f77C",
|
|
757
|
+
symbol: "PR",
|
|
758
|
+
name: "Permian Resources \u2022 Robinhood Token",
|
|
759
|
+
decimals: 18,
|
|
760
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
761
|
+
feed: null,
|
|
762
|
+
feedDecimals: null
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
address: "0xC583c60aeF9Dc401Da72cEC1B404743a93cea1Cc",
|
|
766
|
+
symbol: "QBTS",
|
|
767
|
+
name: "D-Wave Quantum \u2022 Robinhood Token",
|
|
768
|
+
decimals: 18,
|
|
769
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
770
|
+
feed: null,
|
|
771
|
+
feedDecimals: null
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
address: "0x0f17206447090e464C277571124dD2688E48AEA9",
|
|
775
|
+
symbol: "QCOM",
|
|
776
|
+
name: "Qualcomm \u2022 Robinhood Token",
|
|
777
|
+
decimals: 18,
|
|
778
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
779
|
+
feed: null,
|
|
780
|
+
feedDecimals: null
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
address: "0xD5f3879160bc7c32ebb4dC785F8a4F505888de68",
|
|
784
|
+
symbol: "QQQ",
|
|
785
|
+
name: "Invesco QQQ \u2022 Robinhood Token",
|
|
786
|
+
decimals: 18,
|
|
787
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
788
|
+
feed: "0x80901d846d5D7B030F26B480776EE3b29374C2ae",
|
|
789
|
+
feedDecimals: 8,
|
|
790
|
+
feedAnswerAtGeneration: "72545060000",
|
|
791
|
+
feedUpdatedAtGeneration: 1783702260
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
address: "0x59818904ab4cE163b3cE4FfB64f2D6Ca02c434B4",
|
|
795
|
+
symbol: "QUBT",
|
|
796
|
+
name: "Quantum Computing \u2022 Robinhood Token",
|
|
797
|
+
decimals: 18,
|
|
798
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
799
|
+
feed: null,
|
|
800
|
+
feedDecimals: null
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
address: "0xF0C4BF4C582cb3836e98394b1d4e7B7281101bE8",
|
|
804
|
+
symbol: "RBLX",
|
|
805
|
+
name: "Roblox \u2022 Robinhood Token",
|
|
806
|
+
decimals: 18,
|
|
807
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
808
|
+
feed: null,
|
|
809
|
+
feedDecimals: null
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
address: "0x05b37Fb53A299a1b874A619e1c4C404D52C36F4C",
|
|
813
|
+
symbol: "RDDT",
|
|
814
|
+
name: "Reddit \u2022 Robinhood Token",
|
|
815
|
+
decimals: 18,
|
|
816
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
817
|
+
feed: null,
|
|
818
|
+
feedDecimals: null
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
address: "0x92Ef19E82bD8fF36661DE838D5eaE7e5CEF0EfFE",
|
|
822
|
+
symbol: "RDW",
|
|
823
|
+
name: "Redwire \u2022 Robinhood Token",
|
|
824
|
+
decimals: 18,
|
|
825
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
826
|
+
feed: null,
|
|
827
|
+
feedDecimals: null
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
address: "0x284358abc07F9359f19f4b5b4aC91901Be2597Ba",
|
|
831
|
+
symbol: "RGTI",
|
|
832
|
+
name: "Rigetti Computing \u2022 Robinhood Token",
|
|
833
|
+
decimals: 18,
|
|
834
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
835
|
+
feed: "0x2A045cF1C49c61c166C036d2f06FA2D2d984f765",
|
|
836
|
+
feedDecimals: 8,
|
|
837
|
+
feedAnswerAtGeneration: "1657150813",
|
|
838
|
+
feedUpdatedAtGeneration: 1783713654
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
address: "0xB1BF26c1D20ff267A4f93550d1E0d06ac40a114B",
|
|
842
|
+
symbol: "RIVN",
|
|
843
|
+
name: "Rivian Automotive \u2022 Robinhood Token",
|
|
844
|
+
decimals: 18,
|
|
845
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
846
|
+
feed: null,
|
|
847
|
+
feedDecimals: null
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
address: "0x3b14C39E89D60D627b42a1A4CA45b5bb45Fc12e2",
|
|
851
|
+
symbol: "RKLB",
|
|
852
|
+
name: "Rocket Lab Corporation \u2022 Robinhood Token",
|
|
853
|
+
decimals: 18,
|
|
854
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
855
|
+
feed: "0x045477BF65Aef6f4F2386ad0164579e48381CC74",
|
|
856
|
+
feedDecimals: 8,
|
|
857
|
+
feedAnswerAtGeneration: "8128970000",
|
|
858
|
+
feedUpdatedAtGeneration: 1783726597
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
address: "0x95052ddcd5DC25641657424A8Cf04834997E1730",
|
|
862
|
+
symbol: "SATS",
|
|
863
|
+
name: "EchoStar \u2022 Robinhood Token",
|
|
864
|
+
decimals: 18,
|
|
865
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
866
|
+
feed: null,
|
|
867
|
+
feedDecimals: null
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
address: "0x92FD66527192E3e61d4DDd13322Aa222DE86F9B5",
|
|
871
|
+
symbol: "SGOV",
|
|
872
|
+
name: "iShares 0-3 Month Treasury Bond \u2022 Robinhood Token",
|
|
873
|
+
decimals: 18,
|
|
874
|
+
uiMultiplierAtGeneration: "1000957519890990718",
|
|
875
|
+
feed: "0xa0DF4ee0fFf975306345875E3548Fcc519577A11",
|
|
876
|
+
feedDecimals: 8,
|
|
877
|
+
feedAnswerAtGeneration: "10060428845",
|
|
878
|
+
feedUpdatedAtGeneration: 1783728100
|
|
879
|
+
},
|
|
880
|
+
{
|
|
881
|
+
address: "0xF53F66751B1Eff985311b693531E3290F600c410",
|
|
882
|
+
symbol: "SHOP",
|
|
883
|
+
name: "Shopify \u2022 Robinhood Token",
|
|
884
|
+
decimals: 18,
|
|
885
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
886
|
+
feed: null,
|
|
887
|
+
feedDecimals: null
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
address: "0x411eFb0E7f985935DAec3D4C3ebaEa0d0AD7D89f",
|
|
891
|
+
symbol: "SLV",
|
|
892
|
+
name: "iShares Silver Trust \u2022 Robinhood Token",
|
|
893
|
+
decimals: 18,
|
|
894
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
895
|
+
feed: "0x209b73908e92Ae021826eD79609845451Ecba2ce",
|
|
896
|
+
feedDecimals: 8,
|
|
897
|
+
feedAnswerAtGeneration: "5416490000",
|
|
898
|
+
feedUpdatedAtGeneration: 1783719808
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
address: "0xc01aA1fECeC0605b13bc84874ff7256C0f5F562a",
|
|
902
|
+
symbol: "SMCI",
|
|
903
|
+
name: "Super Micro Computer \u2022 Robinhood Token",
|
|
904
|
+
decimals: 18,
|
|
905
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
906
|
+
feed: null,
|
|
907
|
+
feedDecimals: null
|
|
908
|
+
},
|
|
909
|
+
{
|
|
910
|
+
address: "0xB90A19fF0Af67f7779afF50A882A9CfF42446400",
|
|
911
|
+
symbol: "SNDK",
|
|
912
|
+
name: "Sandisk Corporation \u2022 Robinhood Token",
|
|
913
|
+
decimals: 18,
|
|
914
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
915
|
+
feed: "0xfb133Fa4B7b385802B693a293606682Df47109A3",
|
|
916
|
+
feedDecimals: 8,
|
|
917
|
+
feedAnswerAtGeneration: "193162665000",
|
|
918
|
+
feedUpdatedAtGeneration: 1783727430
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
address: "0x98E75885157C80992A8D41b696D8c9C6Fb30A926",
|
|
922
|
+
symbol: "SOFI",
|
|
923
|
+
name: "SoFi Technologies \u2022 Robinhood Token",
|
|
924
|
+
decimals: 18,
|
|
925
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
926
|
+
feed: null,
|
|
927
|
+
feedDecimals: null
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
address: "0x75742c18BC1f1C5c5f448f4C9D9C6F66dafAAa38",
|
|
931
|
+
symbol: "SOXX",
|
|
932
|
+
name: "iShares Semiconductor ETF \u2022 Robinhood Token",
|
|
933
|
+
decimals: 18,
|
|
934
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
935
|
+
feed: null,
|
|
936
|
+
feedDecimals: null
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
address: "0x4a0E65A3EcceC6dBe60AE065F2e7bb85Fae35eEa",
|
|
940
|
+
symbol: "SPCX",
|
|
941
|
+
name: "Space Exploration Technologies Corp. Class A Common Stock \u2022 Robinhood Token",
|
|
942
|
+
decimals: 18,
|
|
943
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
944
|
+
feed: "0xB265810950ba6c5C0Ff821c9963014a56fD8Bffb",
|
|
945
|
+
feedDecimals: 8,
|
|
946
|
+
feedAnswerAtGeneration: "14572500000",
|
|
947
|
+
feedUpdatedAtGeneration: 1783713430
|
|
948
|
+
},
|
|
949
|
+
{
|
|
950
|
+
address: "0xAd622320e520de39e72d41EF07438C3Fd3354875",
|
|
951
|
+
symbol: "SPMO",
|
|
952
|
+
name: "Invesco S&P 500 Momentum ETF \u2022 Robinhood Token",
|
|
953
|
+
decimals: 18,
|
|
954
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
955
|
+
feed: null,
|
|
956
|
+
feedDecimals: null
|
|
957
|
+
},
|
|
958
|
+
{
|
|
959
|
+
address: "0x117cc2133c37B721F49dE2A7a74833232B3B4C0C",
|
|
960
|
+
symbol: "SPY",
|
|
961
|
+
name: "SPDR S&P 500 ETF Trust \u2022 Robinhood Token",
|
|
962
|
+
decimals: 18,
|
|
963
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
964
|
+
feed: "0x319724394D3A0e3669269846abE664Cd621f9f6A",
|
|
965
|
+
feedDecimals: 8,
|
|
966
|
+
feedAnswerAtGeneration: "75361500000",
|
|
967
|
+
feedUpdatedAtGeneration: 1783700203
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
address: "0x89776d4Cd68193597A2fC132cfaC1fDe36CCeA8a",
|
|
971
|
+
symbol: "TSEM",
|
|
972
|
+
name: "Tower Semiconductor \u2022 Robinhood Token",
|
|
973
|
+
decimals: 18,
|
|
974
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
975
|
+
feed: null,
|
|
976
|
+
feedDecimals: null
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
address: "0x322F0929c4625eD5bAd873c95208D54E1c003b2d",
|
|
980
|
+
symbol: "TSLA",
|
|
981
|
+
name: "Tesla \u2022 Robinhood Token",
|
|
982
|
+
decimals: 18,
|
|
983
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
984
|
+
feed: "0x4A1166a659A55625345e9515b32adECea5547C38",
|
|
985
|
+
feedDecimals: 8,
|
|
986
|
+
feedAnswerAtGeneration: "40782500000",
|
|
987
|
+
feedUpdatedAtGeneration: 1783713215
|
|
988
|
+
},
|
|
989
|
+
{
|
|
990
|
+
address: "0x58FfE4a942d3885bAa22D7520691F611EF09e7AA",
|
|
991
|
+
symbol: "TSM",
|
|
992
|
+
name: "Taiwan Semiconductor Manufacturing \u2022 Robinhood Token",
|
|
993
|
+
decimals: 18,
|
|
994
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
995
|
+
feed: "0x874cF94aa8eC88Fd9560094dD065f2fB3E41Fc2F",
|
|
996
|
+
feedDecimals: 8,
|
|
997
|
+
feedAnswerAtGeneration: "43437150000",
|
|
998
|
+
feedUpdatedAtGeneration: 1783713983
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
address: "0x5e81213613b6B86EaB4c6c50d718d34359459786",
|
|
1002
|
+
symbol: "TTWO",
|
|
1003
|
+
name: "Take-Two Interactive Software \u2022 Robinhood Token",
|
|
1004
|
+
decimals: 18,
|
|
1005
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
1006
|
+
feed: null,
|
|
1007
|
+
feedDecimals: null
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
address: "0x0E6e67Ba88e7b5d9B67636A215c76779B948dE79",
|
|
1011
|
+
symbol: "UMC",
|
|
1012
|
+
name: "United Microelectronics \u2022 Robinhood Token",
|
|
1013
|
+
decimals: 18,
|
|
1014
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
1015
|
+
feed: null,
|
|
1016
|
+
feedDecimals: null
|
|
1017
|
+
},
|
|
1018
|
+
{
|
|
1019
|
+
address: "0xf23250dac154D05Bb671CB0d0eBEf3c635c79CE2",
|
|
1020
|
+
symbol: "UPS",
|
|
1021
|
+
name: "UPS \u2022 Robinhood Token",
|
|
1022
|
+
decimals: 18,
|
|
1023
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
1024
|
+
feed: null,
|
|
1025
|
+
feedDecimals: null
|
|
1026
|
+
},
|
|
1027
|
+
{
|
|
1028
|
+
address: "0xd917B029C761D264c6A312BBbcDA868658eF86a6",
|
|
1029
|
+
symbol: "USAR",
|
|
1030
|
+
name: "USA Rare Earth \u2022 Robinhood Token",
|
|
1031
|
+
decimals: 18,
|
|
1032
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
1033
|
+
feed: "0xA994d3684e8400A6c8078226925779FdeE682DD9",
|
|
1034
|
+
feedDecimals: 8,
|
|
1035
|
+
feedAnswerAtGeneration: "1853505000",
|
|
1036
|
+
feedUpdatedAtGeneration: 1783709442
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
address: "0xa30FA36Db767ad9eD3f7a60fC79526fB4d56D344",
|
|
1040
|
+
symbol: "USO",
|
|
1041
|
+
name: "United States Oil Fund \u2022 Robinhood Token",
|
|
1042
|
+
decimals: 18,
|
|
1043
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
1044
|
+
feed: "0x75a9c76Ef439e2C7c2E5a34Ab105EcFe3766431c",
|
|
1045
|
+
feedDecimals: 8,
|
|
1046
|
+
feedAnswerAtGeneration: "10868500000",
|
|
1047
|
+
feedUpdatedAtGeneration: 1783708312
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
address: "0x82DA4646242e1D962e96e932269Dc644c94a9CaA",
|
|
1051
|
+
symbol: "WDAY",
|
|
1052
|
+
name: "Workday \u2022 Robinhood Token",
|
|
1053
|
+
decimals: 18,
|
|
1054
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
1055
|
+
feed: null,
|
|
1056
|
+
feedDecimals: null
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
address: "0xc93a8c440CEa26D7445dF01729f193b27965099f",
|
|
1060
|
+
symbol: "WEEK",
|
|
1061
|
+
name: "Roundhill Weekly T-Bill ETF \u2022 Robinhood Token",
|
|
1062
|
+
decimals: 18,
|
|
1063
|
+
uiMultiplierAtGeneration: "2006182524271844660",
|
|
1064
|
+
feed: null,
|
|
1065
|
+
feedDecimals: null
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
address: "0x15Cd20759CE7F3285c29A319dE2D1A2e098c6f43",
|
|
1069
|
+
symbol: "XLK",
|
|
1070
|
+
name: "State Street Technology Select Sector SPDR ETF \u2022 Robinhood Token",
|
|
1071
|
+
decimals: 18,
|
|
1072
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
1073
|
+
feed: null,
|
|
1074
|
+
feedDecimals: null
|
|
1075
|
+
},
|
|
1076
|
+
{
|
|
1077
|
+
address: "0xA8eB3BCcbf2017eE7CBfb652eB51CF2E1B153289",
|
|
1078
|
+
symbol: "XNDU",
|
|
1079
|
+
name: "Xanadu Quantum \u2022 Robinhood Token",
|
|
1080
|
+
decimals: 18,
|
|
1081
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
1082
|
+
feed: null,
|
|
1083
|
+
feedDecimals: null
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
address: "0xf9B46d3D1B22199D4D1025a9cEDB540A33F1a2d5",
|
|
1087
|
+
symbol: "XOM",
|
|
1088
|
+
name: "ExxonMobil Holdings Corporation \u2022 Robinhood Token",
|
|
1089
|
+
decimals: 18,
|
|
1090
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
1091
|
+
feed: null,
|
|
1092
|
+
feedDecimals: null
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
address: "0x44c4F142009036cF477eD2d09932051843137CF1",
|
|
1096
|
+
symbol: "ZM",
|
|
1097
|
+
name: "Zoom \u2022 Robinhood Token",
|
|
1098
|
+
decimals: 18,
|
|
1099
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
1100
|
+
feed: null,
|
|
1101
|
+
feedDecimals: null
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
address: "0x7dc013eB55e436f30d7ED1AFE4E36d6e45e3c3f7",
|
|
1105
|
+
symbol: "ZS",
|
|
1106
|
+
name: "Zscaler \u2022 Robinhood Token",
|
|
1107
|
+
decimals: 18,
|
|
1108
|
+
uiMultiplierAtGeneration: "1000000000000000000",
|
|
1109
|
+
feed: null,
|
|
1110
|
+
feedDecimals: null
|
|
1111
|
+
}
|
|
1112
|
+
]
|
|
1113
|
+
};
|
|
1114
|
+
var registry = stock_tokens_default;
|
|
1115
|
+
var bySymbol = new Map(registry.tokens.map((t) => [t.symbol.toUpperCase(), t]));
|
|
1116
|
+
var byAddress = new Map(registry.tokens.map((t) => [t.address.toLowerCase(), t]));
|
|
1117
|
+
function getRegistry() {
|
|
1118
|
+
return registry;
|
|
1119
|
+
}
|
|
1120
|
+
function listStockTokens() {
|
|
1121
|
+
return registry.tokens;
|
|
1122
|
+
}
|
|
1123
|
+
function listPricedStockTokens() {
|
|
1124
|
+
return registry.tokens.filter((t) => t.feed !== null);
|
|
1125
|
+
}
|
|
1126
|
+
function getStockToken(symbol) {
|
|
1127
|
+
const token = bySymbol.get(symbol.toUpperCase());
|
|
1128
|
+
if (!token) throw new UnknownSymbolError(symbol);
|
|
1129
|
+
return token;
|
|
1130
|
+
}
|
|
1131
|
+
function getStockTokenByAddress(address) {
|
|
1132
|
+
return byAddress.get(address.toLowerCase()) ?? null;
|
|
1133
|
+
}
|
|
1134
|
+
function isStockTokenSymbol(symbol) {
|
|
1135
|
+
return bySymbol.has(symbol.toUpperCase());
|
|
1136
|
+
}
|
|
1137
|
+
function isStockTokenAddress(address) {
|
|
1138
|
+
return byAddress.has(address.toLowerCase());
|
|
1139
|
+
}
|
|
1140
|
+
var erc20Abi = [
|
|
1141
|
+
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] },
|
|
1142
|
+
{ type: "function", name: "symbol", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] },
|
|
1143
|
+
{ type: "function", name: "decimals", stateMutability: "view", inputs: [], outputs: [{ type: "uint8" }] },
|
|
1144
|
+
{ type: "function", name: "totalSupply", stateMutability: "view", inputs: [], outputs: [{ type: "uint256" }] },
|
|
1145
|
+
{
|
|
1146
|
+
type: "function",
|
|
1147
|
+
name: "balanceOf",
|
|
1148
|
+
stateMutability: "view",
|
|
1149
|
+
inputs: [{ name: "account", type: "address" }],
|
|
1150
|
+
outputs: [{ type: "uint256" }]
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
type: "function",
|
|
1154
|
+
name: "allowance",
|
|
1155
|
+
stateMutability: "view",
|
|
1156
|
+
inputs: [
|
|
1157
|
+
{ name: "owner", type: "address" },
|
|
1158
|
+
{ name: "spender", type: "address" }
|
|
1159
|
+
],
|
|
1160
|
+
outputs: [{ type: "uint256" }]
|
|
1161
|
+
},
|
|
1162
|
+
{
|
|
1163
|
+
type: "function",
|
|
1164
|
+
name: "transfer",
|
|
1165
|
+
stateMutability: "nonpayable",
|
|
1166
|
+
inputs: [
|
|
1167
|
+
{ name: "to", type: "address" },
|
|
1168
|
+
{ name: "amount", type: "uint256" }
|
|
1169
|
+
],
|
|
1170
|
+
outputs: [{ type: "bool" }]
|
|
1171
|
+
},
|
|
1172
|
+
{
|
|
1173
|
+
type: "function",
|
|
1174
|
+
name: "approve",
|
|
1175
|
+
stateMutability: "nonpayable",
|
|
1176
|
+
inputs: [
|
|
1177
|
+
{ name: "spender", type: "address" },
|
|
1178
|
+
{ name: "amount", type: "uint256" }
|
|
1179
|
+
],
|
|
1180
|
+
outputs: [{ type: "bool" }]
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
type: "function",
|
|
1184
|
+
name: "transferFrom",
|
|
1185
|
+
stateMutability: "nonpayable",
|
|
1186
|
+
inputs: [
|
|
1187
|
+
{ name: "from", type: "address" },
|
|
1188
|
+
{ name: "to", type: "address" },
|
|
1189
|
+
{ name: "amount", type: "uint256" }
|
|
1190
|
+
],
|
|
1191
|
+
outputs: [{ type: "bool" }]
|
|
1192
|
+
},
|
|
1193
|
+
{
|
|
1194
|
+
type: "event",
|
|
1195
|
+
name: "Transfer",
|
|
1196
|
+
inputs: [
|
|
1197
|
+
{ name: "from", type: "address", indexed: true },
|
|
1198
|
+
{ name: "to", type: "address", indexed: true },
|
|
1199
|
+
{ name: "value", type: "uint256", indexed: false }
|
|
1200
|
+
]
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
type: "event",
|
|
1204
|
+
name: "Approval",
|
|
1205
|
+
inputs: [
|
|
1206
|
+
{ name: "owner", type: "address", indexed: true },
|
|
1207
|
+
{ name: "spender", type: "address", indexed: true },
|
|
1208
|
+
{ name: "value", type: "uint256", indexed: false }
|
|
1209
|
+
]
|
|
1210
|
+
}
|
|
1211
|
+
];
|
|
1212
|
+
var stockTokenAbi = [
|
|
1213
|
+
...erc20Abi,
|
|
1214
|
+
/** Shares-per-token ratio, scaled by 1e18 (ERC-8056). */
|
|
1215
|
+
{ type: "function", name: "uiMultiplier", stateMutability: "view", inputs: [], outputs: [{ type: "uint256" }] },
|
|
1216
|
+
/** Pending multiplier that takes effect at `effectiveAt`. */
|
|
1217
|
+
{ type: "function", name: "newUIMultiplier", stateMutability: "view", inputs: [], outputs: [{ type: "uint256" }] },
|
|
1218
|
+
/** Timestamp at which `newUIMultiplier` becomes active. */
|
|
1219
|
+
{ type: "function", name: "effectiveAt", stateMutability: "view", inputs: [], outputs: [{ type: "uint256" }] },
|
|
1220
|
+
/** Share-equivalent balance: `balanceOf(a) * uiMultiplier / 1e18`, computed on-chain. */
|
|
1221
|
+
{
|
|
1222
|
+
type: "function",
|
|
1223
|
+
name: "balanceOfUI",
|
|
1224
|
+
stateMutability: "view",
|
|
1225
|
+
inputs: [{ name: "account", type: "address" }],
|
|
1226
|
+
outputs: [{ type: "uint256" }]
|
|
1227
|
+
},
|
|
1228
|
+
/** Share-equivalent total supply. */
|
|
1229
|
+
{ type: "function", name: "totalSupplyUI", stateMutability: "view", inputs: [], outputs: [{ type: "uint256" }] },
|
|
1230
|
+
{ type: "function", name: "paused", stateMutability: "view", inputs: [], outputs: [{ type: "bool" }] },
|
|
1231
|
+
{ type: "function", name: "tokenPaused", stateMutability: "view", inputs: [], outputs: [{ type: "bool" }] },
|
|
1232
|
+
{ type: "function", name: "oraclePaused", stateMutability: "view", inputs: [], outputs: [{ type: "bool" }] },
|
|
1233
|
+
/** Issuer terms URI. */
|
|
1234
|
+
{ type: "function", name: "terms", stateMutability: "pure", inputs: [], outputs: [{ type: "string" }] },
|
|
1235
|
+
{ type: "function", name: "uid", stateMutability: "view", inputs: [], outputs: [{ type: "bytes32" }] },
|
|
1236
|
+
{
|
|
1237
|
+
type: "function",
|
|
1238
|
+
name: "nonces",
|
|
1239
|
+
stateMutability: "view",
|
|
1240
|
+
inputs: [{ name: "owner", type: "address" }],
|
|
1241
|
+
outputs: [{ type: "uint256" }]
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
type: "function",
|
|
1245
|
+
name: "permit",
|
|
1246
|
+
stateMutability: "nonpayable",
|
|
1247
|
+
inputs: [
|
|
1248
|
+
{ name: "owner", type: "address" },
|
|
1249
|
+
{ name: "spender", type: "address" },
|
|
1250
|
+
{ name: "value", type: "uint256" },
|
|
1251
|
+
{ name: "deadline", type: "uint256" },
|
|
1252
|
+
{ name: "v", type: "uint8" },
|
|
1253
|
+
{ name: "r", type: "bytes32" },
|
|
1254
|
+
{ name: "s", type: "bytes32" }
|
|
1255
|
+
],
|
|
1256
|
+
outputs: []
|
|
1257
|
+
}
|
|
1258
|
+
];
|
|
1259
|
+
var aggregatorV3Abi = [
|
|
1260
|
+
{ type: "function", name: "decimals", stateMutability: "view", inputs: [], outputs: [{ type: "uint8" }] },
|
|
1261
|
+
{ type: "function", name: "description", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] },
|
|
1262
|
+
{
|
|
1263
|
+
type: "function",
|
|
1264
|
+
name: "latestRoundData",
|
|
1265
|
+
stateMutability: "view",
|
|
1266
|
+
inputs: [],
|
|
1267
|
+
outputs: [
|
|
1268
|
+
{ name: "roundId", type: "uint80" },
|
|
1269
|
+
{ name: "answer", type: "int256" },
|
|
1270
|
+
{ name: "startedAt", type: "uint256" },
|
|
1271
|
+
{ name: "updatedAt", type: "uint256" },
|
|
1272
|
+
{ name: "answeredInRound", type: "uint80" }
|
|
1273
|
+
]
|
|
1274
|
+
}
|
|
1275
|
+
];
|
|
1276
|
+
var quoterV2Abi = [
|
|
1277
|
+
{
|
|
1278
|
+
type: "function",
|
|
1279
|
+
name: "quoteExactInputSingle",
|
|
1280
|
+
stateMutability: "nonpayable",
|
|
1281
|
+
inputs: [
|
|
1282
|
+
{
|
|
1283
|
+
name: "params",
|
|
1284
|
+
type: "tuple",
|
|
1285
|
+
components: [
|
|
1286
|
+
{ name: "tokenIn", type: "address" },
|
|
1287
|
+
{ name: "tokenOut", type: "address" },
|
|
1288
|
+
{ name: "amountIn", type: "uint256" },
|
|
1289
|
+
{ name: "fee", type: "uint24" },
|
|
1290
|
+
{ name: "sqrtPriceLimitX96", type: "uint160" }
|
|
1291
|
+
]
|
|
1292
|
+
}
|
|
1293
|
+
],
|
|
1294
|
+
outputs: [
|
|
1295
|
+
{ name: "amountOut", type: "uint256" },
|
|
1296
|
+
{ name: "sqrtPriceX96After", type: "uint160" },
|
|
1297
|
+
{ name: "initializedTicksCrossed", type: "uint32" },
|
|
1298
|
+
{ name: "gasEstimate", type: "uint256" }
|
|
1299
|
+
]
|
|
1300
|
+
},
|
|
1301
|
+
{
|
|
1302
|
+
type: "function",
|
|
1303
|
+
name: "quoteExactInput",
|
|
1304
|
+
stateMutability: "nonpayable",
|
|
1305
|
+
inputs: [
|
|
1306
|
+
{ name: "path", type: "bytes" },
|
|
1307
|
+
{ name: "amountIn", type: "uint256" }
|
|
1308
|
+
],
|
|
1309
|
+
outputs: [
|
|
1310
|
+
{ name: "amountOut", type: "uint256" },
|
|
1311
|
+
{ name: "sqrtPriceX96AfterList", type: "uint160[]" },
|
|
1312
|
+
{ name: "initializedTicksCrossedList", type: "uint32[]" },
|
|
1313
|
+
{ name: "gasEstimate", type: "uint256" }
|
|
1314
|
+
]
|
|
1315
|
+
},
|
|
1316
|
+
{ type: "function", name: "factory", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] },
|
|
1317
|
+
{ type: "function", name: "WETH9", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] }
|
|
1318
|
+
];
|
|
1319
|
+
var swapRouter02Abi = [
|
|
1320
|
+
{
|
|
1321
|
+
type: "function",
|
|
1322
|
+
name: "exactInputSingle",
|
|
1323
|
+
stateMutability: "payable",
|
|
1324
|
+
inputs: [
|
|
1325
|
+
{
|
|
1326
|
+
name: "params",
|
|
1327
|
+
type: "tuple",
|
|
1328
|
+
components: [
|
|
1329
|
+
{ name: "tokenIn", type: "address" },
|
|
1330
|
+
{ name: "tokenOut", type: "address" },
|
|
1331
|
+
{ name: "fee", type: "uint24" },
|
|
1332
|
+
{ name: "recipient", type: "address" },
|
|
1333
|
+
{ name: "amountIn", type: "uint256" },
|
|
1334
|
+
{ name: "amountOutMinimum", type: "uint256" },
|
|
1335
|
+
{ name: "sqrtPriceLimitX96", type: "uint160" }
|
|
1336
|
+
]
|
|
1337
|
+
}
|
|
1338
|
+
],
|
|
1339
|
+
outputs: [{ name: "amountOut", type: "uint256" }]
|
|
1340
|
+
},
|
|
1341
|
+
{
|
|
1342
|
+
type: "function",
|
|
1343
|
+
name: "exactInput",
|
|
1344
|
+
stateMutability: "payable",
|
|
1345
|
+
inputs: [
|
|
1346
|
+
{
|
|
1347
|
+
name: "params",
|
|
1348
|
+
type: "tuple",
|
|
1349
|
+
components: [
|
|
1350
|
+
{ name: "path", type: "bytes" },
|
|
1351
|
+
{ name: "recipient", type: "address" },
|
|
1352
|
+
{ name: "amountIn", type: "uint256" },
|
|
1353
|
+
{ name: "amountOutMinimum", type: "uint256" }
|
|
1354
|
+
]
|
|
1355
|
+
}
|
|
1356
|
+
],
|
|
1357
|
+
outputs: [{ name: "amountOut", type: "uint256" }]
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
type: "function",
|
|
1361
|
+
name: "multicall",
|
|
1362
|
+
stateMutability: "payable",
|
|
1363
|
+
inputs: [
|
|
1364
|
+
{ name: "deadline", type: "uint256" },
|
|
1365
|
+
{ name: "data", type: "bytes[]" }
|
|
1366
|
+
],
|
|
1367
|
+
outputs: [{ name: "results", type: "bytes[]" }]
|
|
1368
|
+
},
|
|
1369
|
+
{ type: "function", name: "factory", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] },
|
|
1370
|
+
{ type: "function", name: "WETH9", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] }
|
|
1371
|
+
];
|
|
1372
|
+
var swapRouterAbi = [
|
|
1373
|
+
{
|
|
1374
|
+
type: "function",
|
|
1375
|
+
name: "exactInputSingle",
|
|
1376
|
+
stateMutability: "payable",
|
|
1377
|
+
inputs: [
|
|
1378
|
+
{
|
|
1379
|
+
name: "params",
|
|
1380
|
+
type: "tuple",
|
|
1381
|
+
components: [
|
|
1382
|
+
{ name: "tokenIn", type: "address" },
|
|
1383
|
+
{ name: "tokenOut", type: "address" },
|
|
1384
|
+
{ name: "fee", type: "uint24" },
|
|
1385
|
+
{ name: "recipient", type: "address" },
|
|
1386
|
+
{ name: "deadline", type: "uint256" },
|
|
1387
|
+
{ name: "amountIn", type: "uint256" },
|
|
1388
|
+
{ name: "amountOutMinimum", type: "uint256" },
|
|
1389
|
+
{ name: "sqrtPriceLimitX96", type: "uint160" }
|
|
1390
|
+
]
|
|
1391
|
+
}
|
|
1392
|
+
],
|
|
1393
|
+
outputs: [{ name: "amountOut", type: "uint256" }]
|
|
1394
|
+
},
|
|
1395
|
+
{
|
|
1396
|
+
type: "function",
|
|
1397
|
+
name: "exactInput",
|
|
1398
|
+
stateMutability: "payable",
|
|
1399
|
+
inputs: [
|
|
1400
|
+
{
|
|
1401
|
+
name: "params",
|
|
1402
|
+
type: "tuple",
|
|
1403
|
+
components: [
|
|
1404
|
+
{ name: "path", type: "bytes" },
|
|
1405
|
+
{ name: "recipient", type: "address" },
|
|
1406
|
+
{ name: "deadline", type: "uint256" },
|
|
1407
|
+
{ name: "amountIn", type: "uint256" },
|
|
1408
|
+
{ name: "amountOutMinimum", type: "uint256" }
|
|
1409
|
+
]
|
|
1410
|
+
}
|
|
1411
|
+
],
|
|
1412
|
+
outputs: [{ name: "amountOut", type: "uint256" }]
|
|
1413
|
+
},
|
|
1414
|
+
{ type: "function", name: "factory", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] },
|
|
1415
|
+
{ type: "function", name: "WETH9", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] }
|
|
1416
|
+
];
|
|
1417
|
+
var weth9Abi = [
|
|
1418
|
+
...erc20Abi,
|
|
1419
|
+
{ type: "function", name: "deposit", stateMutability: "payable", inputs: [], outputs: [] },
|
|
1420
|
+
{
|
|
1421
|
+
type: "function",
|
|
1422
|
+
name: "withdraw",
|
|
1423
|
+
stateMutability: "nonpayable",
|
|
1424
|
+
inputs: [{ name: "wad", type: "uint256" }],
|
|
1425
|
+
outputs: []
|
|
1426
|
+
}
|
|
1427
|
+
];
|
|
1428
|
+
var uniswapV3FactoryAbi = [
|
|
1429
|
+
{
|
|
1430
|
+
type: "function",
|
|
1431
|
+
name: "getPool",
|
|
1432
|
+
stateMutability: "view",
|
|
1433
|
+
inputs: [
|
|
1434
|
+
{ name: "tokenA", type: "address" },
|
|
1435
|
+
{ name: "tokenB", type: "address" },
|
|
1436
|
+
{ name: "fee", type: "uint24" }
|
|
1437
|
+
],
|
|
1438
|
+
outputs: [{ type: "address" }]
|
|
1439
|
+
}
|
|
1440
|
+
];
|
|
1441
|
+
var uniswapV3PoolAbi = [
|
|
1442
|
+
{ type: "function", name: "liquidity", stateMutability: "view", inputs: [], outputs: [{ type: "uint128" }] },
|
|
1443
|
+
{
|
|
1444
|
+
type: "function",
|
|
1445
|
+
name: "slot0",
|
|
1446
|
+
stateMutability: "view",
|
|
1447
|
+
inputs: [],
|
|
1448
|
+
outputs: [
|
|
1449
|
+
{ name: "sqrtPriceX96", type: "uint160" },
|
|
1450
|
+
{ name: "tick", type: "int24" },
|
|
1451
|
+
{ name: "observationIndex", type: "uint16" },
|
|
1452
|
+
{ name: "observationCardinality", type: "uint16" },
|
|
1453
|
+
{ name: "observationCardinalityNext", type: "uint16" },
|
|
1454
|
+
{ name: "feeProtocol", type: "uint8" },
|
|
1455
|
+
{ name: "unlocked", type: "bool" }
|
|
1456
|
+
]
|
|
1457
|
+
}
|
|
1458
|
+
];
|
|
1459
|
+
var DEFAULT_MAX_FEED_AGE_SECONDS = 3 * 24 * 60 * 60;
|
|
1460
|
+
async function getQuote(client, symbol, options = {}) {
|
|
1461
|
+
const token = getStockToken(symbol);
|
|
1462
|
+
if (!token.feed) throw new FeedNotFoundError(token.symbol);
|
|
1463
|
+
const [roundId, answer, , updatedAtRaw] = await client.public.readContract({
|
|
1464
|
+
address: token.feed,
|
|
1465
|
+
abi: aggregatorV3Abi,
|
|
1466
|
+
functionName: "latestRoundData"
|
|
1467
|
+
});
|
|
1468
|
+
return toQuote(token, { roundId, answer, updatedAt: Number(updatedAtRaw) }, options);
|
|
1469
|
+
}
|
|
1470
|
+
function toQuote(token, round, options) {
|
|
1471
|
+
const maxAgeSeconds = options.maxAgeSeconds ?? DEFAULT_MAX_FEED_AGE_SECONDS;
|
|
1472
|
+
const answerDecimals = token.feedDecimals ?? 8;
|
|
1473
|
+
if (round.answer <= 0n) {
|
|
1474
|
+
throw new InvalidFeedAnswerError(token.symbol, `answer=${round.answer}`);
|
|
1475
|
+
}
|
|
1476
|
+
if (round.updatedAt === 0) {
|
|
1477
|
+
throw new InvalidFeedAnswerError(token.symbol, "round not complete (updatedAt=0)");
|
|
1478
|
+
}
|
|
1479
|
+
const ageSeconds = Math.max(0, Math.floor(Date.now() / 1e3) - round.updatedAt);
|
|
1480
|
+
if (ageSeconds > maxAgeSeconds) {
|
|
1481
|
+
throw new StaleFeedError(token.symbol, round.updatedAt, ageSeconds, maxAgeSeconds);
|
|
1482
|
+
}
|
|
1483
|
+
return {
|
|
1484
|
+
symbol: token.symbol,
|
|
1485
|
+
address: token.address,
|
|
1486
|
+
feed: token.feed,
|
|
1487
|
+
priceUsd: Number(formatUnits(round.answer, answerDecimals)),
|
|
1488
|
+
answer: round.answer,
|
|
1489
|
+
answerDecimals,
|
|
1490
|
+
roundId: round.roundId,
|
|
1491
|
+
updatedAt: round.updatedAt,
|
|
1492
|
+
ageSeconds
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
async function getMultiplier(client, symbol) {
|
|
1496
|
+
const token = getStockToken(symbol);
|
|
1497
|
+
try {
|
|
1498
|
+
return await client.public.readContract({
|
|
1499
|
+
address: token.address,
|
|
1500
|
+
abi: stockTokenAbi,
|
|
1501
|
+
functionName: "uiMultiplier"
|
|
1502
|
+
});
|
|
1503
|
+
} catch {
|
|
1504
|
+
return null;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
async function getPosition(client, owner, symbol, options = {}) {
|
|
1508
|
+
const token = getStockToken(symbol);
|
|
1509
|
+
const [position] = await readPositions(client, owner, [token], options);
|
|
1510
|
+
return position;
|
|
1511
|
+
}
|
|
1512
|
+
async function getPortfolio(client, owner, options = {}) {
|
|
1513
|
+
const tokens = listStockTokens();
|
|
1514
|
+
const balances = await client.public.multicall({
|
|
1515
|
+
contracts: tokens.map((t) => ({
|
|
1516
|
+
address: t.address,
|
|
1517
|
+
abi: stockTokenAbi,
|
|
1518
|
+
functionName: "balanceOf",
|
|
1519
|
+
args: [owner]
|
|
1520
|
+
})),
|
|
1521
|
+
allowFailure: false
|
|
1522
|
+
});
|
|
1523
|
+
const held = tokens.filter((_, i) => balances[i] > 0n);
|
|
1524
|
+
const positions = await readPositions(client, owner, held, options);
|
|
1525
|
+
const priced = positions.filter((p) => p.valueUsd !== null);
|
|
1526
|
+
return {
|
|
1527
|
+
owner,
|
|
1528
|
+
positions,
|
|
1529
|
+
totalUsd: priced.reduce((sum, p) => sum + p.valueUsd, 0),
|
|
1530
|
+
unpricedSymbols: positions.filter((p) => p.valueUsd === null).map((p) => p.symbol)
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
async function readPositions(client, owner, tokens, options) {
|
|
1534
|
+
if (tokens.length === 0) return [];
|
|
1535
|
+
const reads = await client.public.multicall({
|
|
1536
|
+
contracts: tokens.flatMap((t) => [
|
|
1537
|
+
{ address: t.address, abi: stockTokenAbi, functionName: "balanceOf", args: [owner] },
|
|
1538
|
+
{ address: t.address, abi: stockTokenAbi, functionName: "uiMultiplier" }
|
|
1539
|
+
]),
|
|
1540
|
+
allowFailure: false
|
|
1541
|
+
});
|
|
1542
|
+
const feedTokens = tokens.filter((t) => t.feed !== null);
|
|
1543
|
+
const feedReads = await client.public.multicall({
|
|
1544
|
+
contracts: feedTokens.map((t) => ({
|
|
1545
|
+
address: t.feed,
|
|
1546
|
+
abi: aggregatorV3Abi,
|
|
1547
|
+
functionName: "latestRoundData"
|
|
1548
|
+
})),
|
|
1549
|
+
allowFailure: true
|
|
1550
|
+
});
|
|
1551
|
+
const quoteBySymbol = /* @__PURE__ */ new Map();
|
|
1552
|
+
feedTokens.forEach((t, i) => {
|
|
1553
|
+
const read = feedReads[i];
|
|
1554
|
+
if (!read || read.status !== "success") {
|
|
1555
|
+
quoteBySymbol.set(t.symbol, null);
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
const [roundId, answer, , updatedAt] = read.result;
|
|
1559
|
+
try {
|
|
1560
|
+
quoteBySymbol.set(t.symbol, toQuote(t, { roundId, answer, updatedAt: Number(updatedAt) }, options));
|
|
1561
|
+
} catch {
|
|
1562
|
+
quoteBySymbol.set(t.symbol, null);
|
|
1563
|
+
}
|
|
1564
|
+
});
|
|
1565
|
+
return tokens.map((t, i) => {
|
|
1566
|
+
const balance = reads[i * 2];
|
|
1567
|
+
const uiMultiplier = reads[i * 2 + 1];
|
|
1568
|
+
const balanceTokens = Number(formatUnits(balance, t.decimals));
|
|
1569
|
+
const quote = quoteBySymbol.get(t.symbol) ?? null;
|
|
1570
|
+
return {
|
|
1571
|
+
symbol: t.symbol,
|
|
1572
|
+
address: t.address,
|
|
1573
|
+
balance,
|
|
1574
|
+
balanceTokens,
|
|
1575
|
+
uiMultiplier,
|
|
1576
|
+
shareEquivalent: Number(formatUnits(balance * uiMultiplier / 10n ** 18n, t.decimals)),
|
|
1577
|
+
valueUsd: quote ? balanceTokens * quote.priceUsd : null,
|
|
1578
|
+
quote
|
|
1579
|
+
};
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
function swapAddresses(client) {
|
|
1583
|
+
if (client.network === "testnet") {
|
|
1584
|
+
return {
|
|
1585
|
+
quoterV2: TESTNET_ADDRESSES.quoterV2,
|
|
1586
|
+
router: TESTNET_ADDRESSES.swapRouter,
|
|
1587
|
+
routerKind: "swapRouter",
|
|
1588
|
+
weth: TESTNET_ADDRESSES.weth,
|
|
1589
|
+
usdg: TESTNET_ADDRESSES.usdg
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
return {
|
|
1593
|
+
quoterV2: MAINNET_ADDRESSES.quoterV2,
|
|
1594
|
+
router: MAINNET_ADDRESSES.swapRouter02,
|
|
1595
|
+
routerKind: "swapRouter02",
|
|
1596
|
+
weth: MAINNET_ADDRESSES.weth,
|
|
1597
|
+
usdg: MAINNET_ADDRESSES.usdg
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
function encodePath(path, fees) {
|
|
1601
|
+
const types = [];
|
|
1602
|
+
const values = [];
|
|
1603
|
+
path.forEach((token, i) => {
|
|
1604
|
+
types.push("address");
|
|
1605
|
+
values.push(token);
|
|
1606
|
+
if (i < fees.length) {
|
|
1607
|
+
types.push("uint24");
|
|
1608
|
+
values.push(fees[i]);
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1611
|
+
return encodePacked(types, values);
|
|
1612
|
+
}
|
|
1613
|
+
async function quoteSwap(client, args, options = {}) {
|
|
1614
|
+
const { quoterV2, weth, usdg } = swapAddresses(client);
|
|
1615
|
+
const { tokenIn, tokenOut, amountIn } = args;
|
|
1616
|
+
const feeTiers = options.feeTiers ?? V3_FEE_TIERS;
|
|
1617
|
+
const candidates = [];
|
|
1618
|
+
for (const fee of feeTiers) {
|
|
1619
|
+
candidates.push({ fees: [fee], path: [tokenIn, tokenOut], encodedPath: encodePath([tokenIn, tokenOut], [fee]) });
|
|
1620
|
+
}
|
|
1621
|
+
const intermediates = (options.intermediates ?? [weth, usdg]).filter(
|
|
1622
|
+
(mid) => mid.toLowerCase() !== tokenIn.toLowerCase() && mid.toLowerCase() !== tokenOut.toLowerCase()
|
|
1623
|
+
);
|
|
1624
|
+
for (const mid of intermediates) {
|
|
1625
|
+
for (const feeA of [500, 3e3]) {
|
|
1626
|
+
for (const feeB of [500, 3e3]) {
|
|
1627
|
+
candidates.push({
|
|
1628
|
+
fees: [feeA, feeB],
|
|
1629
|
+
path: [tokenIn, mid, tokenOut],
|
|
1630
|
+
encodedPath: encodePath([tokenIn, mid, tokenOut], [feeA, feeB])
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
const results = await Promise.all(
|
|
1636
|
+
candidates.map(async (route) => {
|
|
1637
|
+
try {
|
|
1638
|
+
if (route.fees.length === 1) {
|
|
1639
|
+
const { result: result2 } = await client.public.simulateContract({
|
|
1640
|
+
address: quoterV2,
|
|
1641
|
+
abi: quoterV2Abi,
|
|
1642
|
+
functionName: "quoteExactInputSingle",
|
|
1643
|
+
args: [
|
|
1644
|
+
{
|
|
1645
|
+
tokenIn,
|
|
1646
|
+
tokenOut,
|
|
1647
|
+
amountIn,
|
|
1648
|
+
fee: route.fees[0],
|
|
1649
|
+
sqrtPriceLimitX96: 0n
|
|
1650
|
+
}
|
|
1651
|
+
]
|
|
1652
|
+
});
|
|
1653
|
+
return { route, amountIn, amountOut: result2[0], gasEstimate: result2[3] };
|
|
1654
|
+
}
|
|
1655
|
+
const { result } = await client.public.simulateContract({
|
|
1656
|
+
address: quoterV2,
|
|
1657
|
+
abi: quoterV2Abi,
|
|
1658
|
+
functionName: "quoteExactInput",
|
|
1659
|
+
args: [route.encodedPath, amountIn]
|
|
1660
|
+
});
|
|
1661
|
+
return { route, amountIn, amountOut: result[0], gasEstimate: result[3] };
|
|
1662
|
+
} catch {
|
|
1663
|
+
return null;
|
|
1664
|
+
}
|
|
1665
|
+
})
|
|
1666
|
+
);
|
|
1667
|
+
const best = results.filter((q) => q !== null && q.amountOut > 0n).sort((a, b) => b.amountOut > a.amountOut ? 1 : b.amountOut < a.amountOut ? -1 : 0)[0];
|
|
1668
|
+
if (!best) {
|
|
1669
|
+
throw new NoRouteError(
|
|
1670
|
+
tokenIn,
|
|
1671
|
+
tokenOut,
|
|
1672
|
+
`no direct pool (fees ${feeTiers.join("/")}) or two-hop route via ${intermediates.length} intermediates produced output for amountIn=${amountIn}`
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
return best;
|
|
1676
|
+
}
|
|
1677
|
+
function buildSwapTx(client, quote, options = {}) {
|
|
1678
|
+
const { router, routerKind } = swapAddresses(client);
|
|
1679
|
+
const tokenOut = quote.route.path[quote.route.path.length - 1];
|
|
1680
|
+
if (client.network === "mainnet" && isStockTokenAddress(tokenOut) && !client.acknowledgeStockTokenEligibility) {
|
|
1681
|
+
throw new StockTokenEligibilityError();
|
|
1682
|
+
}
|
|
1683
|
+
const recipient = options.recipient ?? client.account?.address;
|
|
1684
|
+
if (!recipient) throw new NoAccountError("buildSwapTx (or pass options.recipient)");
|
|
1685
|
+
const slippageBps = options.slippageBps ?? 50;
|
|
1686
|
+
const amountOutMinimum = quote.amountOut * BigInt(1e4 - slippageBps) / 10000n;
|
|
1687
|
+
const deadline = BigInt(Math.floor(Date.now() / 1e3) + (options.deadlineSeconds ?? 600));
|
|
1688
|
+
const singleHop = quote.route.fees.length === 1;
|
|
1689
|
+
let data;
|
|
1690
|
+
if (routerKind === "swapRouter") {
|
|
1691
|
+
data = singleHop ? encodeFunctionData({
|
|
1692
|
+
abi: swapRouterAbi,
|
|
1693
|
+
functionName: "exactInputSingle",
|
|
1694
|
+
args: [
|
|
1695
|
+
{
|
|
1696
|
+
tokenIn: quote.route.path[0],
|
|
1697
|
+
tokenOut,
|
|
1698
|
+
fee: quote.route.fees[0],
|
|
1699
|
+
recipient,
|
|
1700
|
+
deadline,
|
|
1701
|
+
amountIn: quote.amountIn,
|
|
1702
|
+
amountOutMinimum,
|
|
1703
|
+
sqrtPriceLimitX96: 0n
|
|
1704
|
+
}
|
|
1705
|
+
]
|
|
1706
|
+
}) : encodeFunctionData({
|
|
1707
|
+
abi: swapRouterAbi,
|
|
1708
|
+
functionName: "exactInput",
|
|
1709
|
+
args: [
|
|
1710
|
+
{
|
|
1711
|
+
path: quote.route.encodedPath,
|
|
1712
|
+
recipient,
|
|
1713
|
+
deadline,
|
|
1714
|
+
amountIn: quote.amountIn,
|
|
1715
|
+
amountOutMinimum
|
|
1716
|
+
}
|
|
1717
|
+
]
|
|
1718
|
+
});
|
|
1719
|
+
} else {
|
|
1720
|
+
const inner = singleHop ? encodeFunctionData({
|
|
1721
|
+
abi: swapRouter02Abi,
|
|
1722
|
+
functionName: "exactInputSingle",
|
|
1723
|
+
args: [
|
|
1724
|
+
{
|
|
1725
|
+
tokenIn: quote.route.path[0],
|
|
1726
|
+
tokenOut,
|
|
1727
|
+
fee: quote.route.fees[0],
|
|
1728
|
+
recipient,
|
|
1729
|
+
amountIn: quote.amountIn,
|
|
1730
|
+
amountOutMinimum,
|
|
1731
|
+
sqrtPriceLimitX96: 0n
|
|
1732
|
+
}
|
|
1733
|
+
]
|
|
1734
|
+
}) : encodeFunctionData({
|
|
1735
|
+
abi: swapRouter02Abi,
|
|
1736
|
+
functionName: "exactInput",
|
|
1737
|
+
args: [
|
|
1738
|
+
{
|
|
1739
|
+
path: quote.route.encodedPath,
|
|
1740
|
+
recipient,
|
|
1741
|
+
amountIn: quote.amountIn,
|
|
1742
|
+
amountOutMinimum
|
|
1743
|
+
}
|
|
1744
|
+
]
|
|
1745
|
+
});
|
|
1746
|
+
data = encodeFunctionData({
|
|
1747
|
+
abi: swapRouter02Abi,
|
|
1748
|
+
functionName: "multicall",
|
|
1749
|
+
args: [deadline, [inner]]
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
return { to: router, data, value: 0n, quote, amountOutMinimum, deadline };
|
|
1753
|
+
}
|
|
1754
|
+
async function ensureApproval(client, token, amount) {
|
|
1755
|
+
if (!client.wallet || !client.account) throw new NoAccountError("ensureApproval");
|
|
1756
|
+
const { router } = swapAddresses(client);
|
|
1757
|
+
const allowance = await client.public.readContract({
|
|
1758
|
+
address: token,
|
|
1759
|
+
abi: erc20Abi,
|
|
1760
|
+
functionName: "allowance",
|
|
1761
|
+
args: [client.account.address, router]
|
|
1762
|
+
});
|
|
1763
|
+
if (allowance >= amount) return null;
|
|
1764
|
+
const hash = await client.wallet.writeContract({
|
|
1765
|
+
address: token,
|
|
1766
|
+
abi: erc20Abi,
|
|
1767
|
+
functionName: "approve",
|
|
1768
|
+
args: [router, amount]
|
|
1769
|
+
});
|
|
1770
|
+
await client.public.waitForTransactionReceipt({ hash });
|
|
1771
|
+
return hash;
|
|
1772
|
+
}
|
|
1773
|
+
async function executeSwap(client, args, options = {}) {
|
|
1774
|
+
if (!client.wallet || !client.account) throw new NoAccountError("executeSwap");
|
|
1775
|
+
const quote = await quoteSwap(client, args, options);
|
|
1776
|
+
const tx = buildSwapTx(client, quote, options);
|
|
1777
|
+
await ensureApproval(client, args.tokenIn, args.amountIn);
|
|
1778
|
+
const hash = await client.wallet.sendTransaction({
|
|
1779
|
+
to: tx.to,
|
|
1780
|
+
data: tx.data,
|
|
1781
|
+
value: tx.value,
|
|
1782
|
+
account: client.account,
|
|
1783
|
+
chain: client.chain
|
|
1784
|
+
});
|
|
1785
|
+
const receipt = await client.public.waitForTransactionReceipt({ hash });
|
|
1786
|
+
return { hash, receipt, quote, amountOutMinimum: tx.amountOutMinimum };
|
|
1787
|
+
}
|
|
1788
|
+
function usdgAddress(client) {
|
|
1789
|
+
return client.network === "testnet" ? TESTNET_ADDRESSES.usdg : MAINNET_ADDRESSES.usdg;
|
|
1790
|
+
}
|
|
1791
|
+
function formatUsdg(amount) {
|
|
1792
|
+
return formatUnits2(amount, USDG_DECIMALS);
|
|
1793
|
+
}
|
|
1794
|
+
function parseUsdg(amount) {
|
|
1795
|
+
return parseUnits(amount, USDG_DECIMALS);
|
|
1796
|
+
}
|
|
1797
|
+
async function getUsdgBalance(client, owner) {
|
|
1798
|
+
return client.public.readContract({
|
|
1799
|
+
address: usdgAddress(client),
|
|
1800
|
+
abi: erc20Abi,
|
|
1801
|
+
functionName: "balanceOf",
|
|
1802
|
+
args: [owner]
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
async function getUsdgTotalSupply(client) {
|
|
1806
|
+
return client.public.readContract({
|
|
1807
|
+
address: usdgAddress(client),
|
|
1808
|
+
abi: erc20Abi,
|
|
1809
|
+
functionName: "totalSupply"
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
async function transferUsdg(client, to, amount) {
|
|
1813
|
+
if (!client.wallet) throw new NoAccountError("transferUsdg");
|
|
1814
|
+
return client.wallet.writeContract({
|
|
1815
|
+
address: usdgAddress(client),
|
|
1816
|
+
abi: erc20Abi,
|
|
1817
|
+
functionName: "transfer",
|
|
1818
|
+
args: [to, amount]
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
async function approveUsdg(client, spender, amount) {
|
|
1822
|
+
if (!client.wallet) throw new NoAccountError("approveUsdg");
|
|
1823
|
+
return client.wallet.writeContract({
|
|
1824
|
+
address: usdgAddress(client),
|
|
1825
|
+
abi: erc20Abi,
|
|
1826
|
+
functionName: "approve",
|
|
1827
|
+
args: [spender, amount]
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
async function getUsdgAllowance(client, owner, spender) {
|
|
1831
|
+
return client.public.readContract({
|
|
1832
|
+
address: usdgAddress(client),
|
|
1833
|
+
abi: erc20Abi,
|
|
1834
|
+
functionName: "allowance",
|
|
1835
|
+
args: [owner, spender]
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
var NOXA_ADDRESSES = {
|
|
1839
|
+
launchFactory: "0xD9eC2db5f3D1b236843925949fe5bd8a3836FCcB",
|
|
1840
|
+
locker: "0x7F03effbd7ceB22A3f80Dd468f67eF27826acD85",
|
|
1841
|
+
feeRouter: "0x9eFdC1A8e6E94f16A228e44f3025E1f346EE0417",
|
|
1842
|
+
/** First block with factory activity — lower bound for historical scans. */
|
|
1843
|
+
deployBlock: 61688n
|
|
1844
|
+
};
|
|
1845
|
+
var ODYSSEY_ADDRESSES = {
|
|
1846
|
+
/** Current bonding-curve factory. */
|
|
1847
|
+
bondingCurveFactory: "0xEb3FeeD2716cF0eEAda05B22e67424794e1f5a80",
|
|
1848
|
+
/** Variant that pays reflections in a reward token. */
|
|
1849
|
+
reflectionFactory: "0x6Ce85c4b7cE12903E5867652C265bCcce57f935F",
|
|
1850
|
+
/** Variant that skips the curve and lists instantly. */
|
|
1851
|
+
instantFactory: "0xD7601cEe401306fdea5833c6898181D9c770F800",
|
|
1852
|
+
robinLock: "0x5B41D59Fa0ce65750bc64e06D85bC999084493CD",
|
|
1853
|
+
/** Legacy first-generation factory (still emits historical launches). */
|
|
1854
|
+
legacyFactory: "0xAf9f3ce1d34909F59E88c23027f89d5807B0F915"
|
|
1855
|
+
};
|
|
1856
|
+
var noxaTokenLaunchedEvent = {
|
|
1857
|
+
type: "event",
|
|
1858
|
+
name: "TokenLaunched",
|
|
1859
|
+
inputs: [
|
|
1860
|
+
{ name: "token", type: "address", indexed: true },
|
|
1861
|
+
{ name: "deployer", type: "address", indexed: true },
|
|
1862
|
+
{ name: "dexFactory", type: "address", indexed: true },
|
|
1863
|
+
{ name: "pairToken", type: "address", indexed: false },
|
|
1864
|
+
{ name: "pool", type: "address", indexed: false },
|
|
1865
|
+
{ name: "dexId", type: "uint256", indexed: false },
|
|
1866
|
+
{ name: "launchConfigId", type: "uint256", indexed: false },
|
|
1867
|
+
{ name: "positionId", type: "uint256", indexed: false },
|
|
1868
|
+
{ name: "restrictionsEndBlock", type: "uint256", indexed: false },
|
|
1869
|
+
{ name: "initialBuyAmount", type: "uint256", indexed: false }
|
|
1870
|
+
]
|
|
1871
|
+
};
|
|
1872
|
+
var odysseyTokenCreatedEvent = {
|
|
1873
|
+
type: "event",
|
|
1874
|
+
name: "TokenCreated",
|
|
1875
|
+
inputs: [
|
|
1876
|
+
{ name: "token", type: "address", indexed: true },
|
|
1877
|
+
{ name: "creator", type: "address", indexed: true },
|
|
1878
|
+
{ name: "backingWallet", type: "address", indexed: false },
|
|
1879
|
+
{ name: "isMarginBacked", type: "bool", indexed: false },
|
|
1880
|
+
{ name: "threshold", type: "uint256", indexed: false }
|
|
1881
|
+
]
|
|
1882
|
+
};
|
|
1883
|
+
var odysseyTradedEvent = {
|
|
1884
|
+
type: "event",
|
|
1885
|
+
name: "Traded",
|
|
1886
|
+
inputs: [
|
|
1887
|
+
{ name: "token", type: "address", indexed: true },
|
|
1888
|
+
{ name: "trader", type: "address", indexed: true },
|
|
1889
|
+
{ name: "isBuy", type: "bool", indexed: false },
|
|
1890
|
+
{ name: "tokenAmount", type: "uint256", indexed: false },
|
|
1891
|
+
{ name: "quoteAmount", type: "uint256", indexed: false },
|
|
1892
|
+
{ name: "fee", type: "uint256", indexed: false },
|
|
1893
|
+
{ name: "virtualQuote", type: "uint256", indexed: false },
|
|
1894
|
+
{ name: "virtualToken", type: "uint256", indexed: false }
|
|
1895
|
+
]
|
|
1896
|
+
};
|
|
1897
|
+
var odysseyPoolMigratedEvent = {
|
|
1898
|
+
type: "event",
|
|
1899
|
+
name: "PoolMigrated",
|
|
1900
|
+
inputs: [
|
|
1901
|
+
{ name: "token", type: "address", indexed: true },
|
|
1902
|
+
{ name: "pool", type: "address", indexed: false },
|
|
1903
|
+
{ name: "tokenId", type: "uint256", indexed: false },
|
|
1904
|
+
{ name: "liquidity", type: "uint128", indexed: false },
|
|
1905
|
+
{ name: "tokenUsed", type: "uint256", indexed: false },
|
|
1906
|
+
{ name: "usdcUsed", type: "uint256", indexed: false }
|
|
1907
|
+
]
|
|
1908
|
+
};
|
|
1909
|
+
var ODYSSEY_FACTORIES = [
|
|
1910
|
+
ODYSSEY_ADDRESSES.bondingCurveFactory,
|
|
1911
|
+
ODYSSEY_ADDRESSES.reflectionFactory,
|
|
1912
|
+
ODYSSEY_ADDRESSES.instantFactory
|
|
1913
|
+
];
|
|
1914
|
+
async function getRecentLaunches(client, options = {}) {
|
|
1915
|
+
const latest = await client.public.getBlockNumber();
|
|
1916
|
+
const lookback = options.lookbackBlocks ?? 30000n;
|
|
1917
|
+
const chunk = options.chunkSize ?? 10000n;
|
|
1918
|
+
const fromBlock = latest > lookback ? latest - lookback : 0n;
|
|
1919
|
+
const launches = [];
|
|
1920
|
+
for (let start = fromBlock; start <= latest; start += chunk) {
|
|
1921
|
+
const end = start + chunk - 1n > latest ? latest : start + chunk - 1n;
|
|
1922
|
+
const [noxaLogs, odysseyLogs] = await Promise.all([
|
|
1923
|
+
options.launchpad === "odyssey" ? Promise.resolve([]) : client.public.getLogs({
|
|
1924
|
+
address: NOXA_ADDRESSES.launchFactory,
|
|
1925
|
+
event: noxaTokenLaunchedEvent,
|
|
1926
|
+
fromBlock: start,
|
|
1927
|
+
toBlock: end
|
|
1928
|
+
}),
|
|
1929
|
+
options.launchpad === "noxa" ? Promise.resolve([]) : client.public.getLogs({
|
|
1930
|
+
address: ODYSSEY_FACTORIES,
|
|
1931
|
+
event: odysseyTokenCreatedEvent,
|
|
1932
|
+
fromBlock: start,
|
|
1933
|
+
toBlock: end
|
|
1934
|
+
})
|
|
1935
|
+
]);
|
|
1936
|
+
for (const log of noxaLogs) {
|
|
1937
|
+
launches.push({
|
|
1938
|
+
launchpad: "noxa",
|
|
1939
|
+
token: log.args.token,
|
|
1940
|
+
creator: log.args.deployer,
|
|
1941
|
+
pool: log.args.pool ?? null,
|
|
1942
|
+
blockNumber: log.blockNumber,
|
|
1943
|
+
transactionHash: log.transactionHash
|
|
1944
|
+
});
|
|
1945
|
+
}
|
|
1946
|
+
for (const log of odysseyLogs) {
|
|
1947
|
+
launches.push({
|
|
1948
|
+
launchpad: "odyssey",
|
|
1949
|
+
token: log.args.token,
|
|
1950
|
+
creator: log.args.creator,
|
|
1951
|
+
pool: null,
|
|
1952
|
+
blockNumber: log.blockNumber,
|
|
1953
|
+
transactionHash: log.transactionHash
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
return launches.sort((a, b) => a.blockNumber < b.blockNumber ? -1 : 1);
|
|
1958
|
+
}
|
|
1959
|
+
function watchLaunches(client, onLaunch, options = {}) {
|
|
1960
|
+
const unwatchers = [];
|
|
1961
|
+
const pollingInterval = options.pollingInterval ?? 2e3;
|
|
1962
|
+
if (options.launchpad !== "odyssey") {
|
|
1963
|
+
unwatchers.push(
|
|
1964
|
+
client.public.watchContractEvent({
|
|
1965
|
+
address: NOXA_ADDRESSES.launchFactory,
|
|
1966
|
+
abi: [noxaTokenLaunchedEvent],
|
|
1967
|
+
eventName: "TokenLaunched",
|
|
1968
|
+
pollingInterval,
|
|
1969
|
+
onError: options.onError,
|
|
1970
|
+
onLogs: (logs) => {
|
|
1971
|
+
for (const log of logs) {
|
|
1972
|
+
onLaunch({
|
|
1973
|
+
launchpad: "noxa",
|
|
1974
|
+
token: log.args.token,
|
|
1975
|
+
creator: log.args.deployer,
|
|
1976
|
+
pool: log.args.pool ?? null,
|
|
1977
|
+
blockNumber: log.blockNumber,
|
|
1978
|
+
transactionHash: log.transactionHash
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
})
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
if (options.launchpad !== "noxa") {
|
|
1986
|
+
unwatchers.push(
|
|
1987
|
+
client.public.watchContractEvent({
|
|
1988
|
+
address: ODYSSEY_FACTORIES,
|
|
1989
|
+
abi: [odysseyTokenCreatedEvent],
|
|
1990
|
+
eventName: "TokenCreated",
|
|
1991
|
+
pollingInterval,
|
|
1992
|
+
onError: options.onError,
|
|
1993
|
+
onLogs: (logs) => {
|
|
1994
|
+
for (const log of logs) {
|
|
1995
|
+
onLaunch({
|
|
1996
|
+
launchpad: "odyssey",
|
|
1997
|
+
token: log.args.token,
|
|
1998
|
+
creator: log.args.creator,
|
|
1999
|
+
pool: null,
|
|
2000
|
+
blockNumber: log.blockNumber,
|
|
2001
|
+
transactionHash: log.transactionHash
|
|
2002
|
+
});
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
})
|
|
2006
|
+
);
|
|
2007
|
+
}
|
|
2008
|
+
return () => unwatchers.forEach((u) => u());
|
|
2009
|
+
}
|
|
2010
|
+
function watchCurveTrades(client, onTrade, options = {}) {
|
|
2011
|
+
return client.public.watchContractEvent({
|
|
2012
|
+
address: ODYSSEY_FACTORIES,
|
|
2013
|
+
abi: [odysseyTradedEvent],
|
|
2014
|
+
eventName: "Traded",
|
|
2015
|
+
...options.token ? { args: { token: options.token } } : {},
|
|
2016
|
+
pollingInterval: options.pollingInterval ?? 2e3,
|
|
2017
|
+
onError: options.onError,
|
|
2018
|
+
onLogs: (logs) => {
|
|
2019
|
+
for (const log of logs) {
|
|
2020
|
+
onTrade({
|
|
2021
|
+
launchpad: "odyssey",
|
|
2022
|
+
token: log.args.token,
|
|
2023
|
+
trader: log.args.trader,
|
|
2024
|
+
isBuy: log.args.isBuy,
|
|
2025
|
+
tokenAmount: log.args.tokenAmount,
|
|
2026
|
+
quoteAmount: log.args.quoteAmount,
|
|
2027
|
+
fee: log.args.fee,
|
|
2028
|
+
blockNumber: log.blockNumber,
|
|
2029
|
+
transactionHash: log.transactionHash
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
2035
|
+
function watchGraduations(client, onGraduation, options = {}) {
|
|
2036
|
+
return client.public.watchContractEvent({
|
|
2037
|
+
address: ODYSSEY_FACTORIES,
|
|
2038
|
+
abi: [odysseyPoolMigratedEvent],
|
|
2039
|
+
eventName: "PoolMigrated",
|
|
2040
|
+
pollingInterval: options.pollingInterval ?? 2e3,
|
|
2041
|
+
onError: options.onError,
|
|
2042
|
+
onLogs: (logs) => {
|
|
2043
|
+
for (const log of logs) {
|
|
2044
|
+
onGraduation({
|
|
2045
|
+
token: log.args.token,
|
|
2046
|
+
pool: log.args.pool,
|
|
2047
|
+
blockNumber: log.blockNumber,
|
|
2048
|
+
transactionHash: log.transactionHash
|
|
2049
|
+
});
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
2054
|
+
var L2MSG_KIND_BATCH = 3;
|
|
2055
|
+
var L2MSG_KIND_SIGNED_TX = 4;
|
|
2056
|
+
function decodeL2Msg(bytes, depth = 0) {
|
|
2057
|
+
if (bytes.length < 2 || depth > 4) return [];
|
|
2058
|
+
const kind = bytes[0];
|
|
2059
|
+
const body = bytes.subarray(1);
|
|
2060
|
+
if (kind === L2MSG_KIND_SIGNED_TX) {
|
|
2061
|
+
const raw = `0x${Buffer.from(body).toString("hex")}`;
|
|
2062
|
+
try {
|
|
2063
|
+
return [{ hash: keccak256(raw), transaction: parseTransaction(raw), raw }];
|
|
2064
|
+
} catch {
|
|
2065
|
+
return [];
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
if (kind === L2MSG_KIND_BATCH) {
|
|
2069
|
+
const out = [];
|
|
2070
|
+
let offset = 0;
|
|
2071
|
+
while (offset + 8 <= body.length) {
|
|
2072
|
+
const view = new DataView(body.buffer, body.byteOffset + offset, 8);
|
|
2073
|
+
const length = Number(view.getBigUint64(0));
|
|
2074
|
+
offset += 8;
|
|
2075
|
+
if (length <= 0 || offset + length > body.length) break;
|
|
2076
|
+
out.push(...decodeL2Msg(body.subarray(offset, offset + length), depth + 1));
|
|
2077
|
+
offset += length;
|
|
2078
|
+
}
|
|
2079
|
+
return out;
|
|
2080
|
+
}
|
|
2081
|
+
return [];
|
|
2082
|
+
}
|
|
2083
|
+
async function resolveWebSocket() {
|
|
2084
|
+
const g = globalThis;
|
|
2085
|
+
if (typeof g.WebSocket === "function") return g.WebSocket;
|
|
2086
|
+
try {
|
|
2087
|
+
const ws = await import("ws");
|
|
2088
|
+
return ws.default;
|
|
2089
|
+
} catch {
|
|
2090
|
+
throw new FeedConnectionError(
|
|
2091
|
+
MAINNET_FEED_URL,
|
|
2092
|
+
0,
|
|
2093
|
+
'no WebSocket implementation available \u2014 use Node \u2265 22 or install the optional "ws" peer dependency'
|
|
2094
|
+
);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
async function subscribeFeed(onMessage, options = {}) {
|
|
2098
|
+
const url = options.url ?? MAINNET_FEED_URL;
|
|
2099
|
+
const maxReconnects = options.maxReconnects ?? 10;
|
|
2100
|
+
const WS = await resolveWebSocket();
|
|
2101
|
+
let socket = null;
|
|
2102
|
+
let closed = false;
|
|
2103
|
+
let attempts = 0;
|
|
2104
|
+
const connect = () => {
|
|
2105
|
+
if (closed) return;
|
|
2106
|
+
socket = new WS(url);
|
|
2107
|
+
socket.onopen = () => {
|
|
2108
|
+
attempts = 0;
|
|
2109
|
+
options.onConnect?.();
|
|
2110
|
+
};
|
|
2111
|
+
socket.onmessage = (event) => {
|
|
2112
|
+
let payload;
|
|
2113
|
+
try {
|
|
2114
|
+
payload = JSON.parse(typeof event.data === "string" ? event.data : Buffer.from(event.data).toString());
|
|
2115
|
+
} catch {
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2118
|
+
for (const entry of payload.messages ?? []) {
|
|
2119
|
+
const m = entry;
|
|
2120
|
+
const header = m.message?.message?.header;
|
|
2121
|
+
const l2Msg = m.message?.message?.l2Msg;
|
|
2122
|
+
if (!header || typeof l2Msg !== "string") continue;
|
|
2123
|
+
onMessage({
|
|
2124
|
+
sequenceNumber: m.sequenceNumber ?? 0,
|
|
2125
|
+
kind: header.kind ?? 0,
|
|
2126
|
+
sender: header.sender ?? "0x0000000000000000000000000000000000000000",
|
|
2127
|
+
l1BlockNumber: header.blockNumber ?? 0,
|
|
2128
|
+
timestamp: header.timestamp ?? 0,
|
|
2129
|
+
l2MsgBase64: l2Msg,
|
|
2130
|
+
transactions: header.kind === 3 ? decodeL2Msg(Buffer.from(l2Msg, "base64")) : []
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2133
|
+
};
|
|
2134
|
+
socket.onerror = () => {
|
|
2135
|
+
options.onError?.(new Error(`sequencer feed socket error (${url})`));
|
|
2136
|
+
};
|
|
2137
|
+
socket.onclose = () => {
|
|
2138
|
+
if (closed) return;
|
|
2139
|
+
attempts += 1;
|
|
2140
|
+
if (attempts > maxReconnects) {
|
|
2141
|
+
options.onError?.(new FeedConnectionError(url, attempts));
|
|
2142
|
+
return;
|
|
2143
|
+
}
|
|
2144
|
+
const delay = Math.min((options.reconnectDelayMs ?? 1e3) * 2 ** (attempts - 1), 3e4);
|
|
2145
|
+
setTimeout(connect, delay);
|
|
2146
|
+
};
|
|
2147
|
+
};
|
|
2148
|
+
connect();
|
|
2149
|
+
return {
|
|
2150
|
+
close: () => {
|
|
2151
|
+
closed = true;
|
|
2152
|
+
socket?.close();
|
|
2153
|
+
}
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
function watchTransfers(client, args, onTransfer) {
|
|
2157
|
+
return client.public.watchContractEvent({
|
|
2158
|
+
address: args.token,
|
|
2159
|
+
abi: erc20Abi,
|
|
2160
|
+
eventName: "Transfer",
|
|
2161
|
+
pollingInterval: args.pollingInterval ?? 2e3,
|
|
2162
|
+
onError: args.onError,
|
|
2163
|
+
onLogs: (logs) => {
|
|
2164
|
+
for (const log of logs) {
|
|
2165
|
+
const a = log.args;
|
|
2166
|
+
if (!a.from || !a.to || a.value === void 0) continue;
|
|
2167
|
+
onTransfer({
|
|
2168
|
+
token: args.token,
|
|
2169
|
+
from: a.from,
|
|
2170
|
+
to: a.to,
|
|
2171
|
+
value: a.value,
|
|
2172
|
+
blockNumber: log.blockNumber,
|
|
2173
|
+
transactionHash: log.transactionHash
|
|
2174
|
+
});
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
});
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
export {
|
|
2181
|
+
createHoodClient,
|
|
2182
|
+
MAINNET_ADDRESSES,
|
|
2183
|
+
TESTNET_ADDRESSES,
|
|
2184
|
+
TESTNET_STOCK_TOKENS,
|
|
2185
|
+
MAINNET_FEED_URL,
|
|
2186
|
+
MAINNET_EXPLORER_URL,
|
|
2187
|
+
USDG_DECIMALS,
|
|
2188
|
+
STOCK_TOKEN_DECIMALS,
|
|
2189
|
+
FEED_DECIMALS,
|
|
2190
|
+
V3_FEE_TIERS,
|
|
2191
|
+
HoodchainError,
|
|
2192
|
+
UnknownSymbolError,
|
|
2193
|
+
FeedNotFoundError,
|
|
2194
|
+
StaleFeedError,
|
|
2195
|
+
InvalidFeedAnswerError,
|
|
2196
|
+
NoRouteError,
|
|
2197
|
+
SlippageExceededError,
|
|
2198
|
+
NoAccountError,
|
|
2199
|
+
StockTokenEligibilityError,
|
|
2200
|
+
FeedConnectionError,
|
|
2201
|
+
getRegistry,
|
|
2202
|
+
listStockTokens,
|
|
2203
|
+
listPricedStockTokens,
|
|
2204
|
+
getStockToken,
|
|
2205
|
+
getStockTokenByAddress,
|
|
2206
|
+
isStockTokenSymbol,
|
|
2207
|
+
isStockTokenAddress,
|
|
2208
|
+
erc20Abi,
|
|
2209
|
+
stockTokenAbi,
|
|
2210
|
+
aggregatorV3Abi,
|
|
2211
|
+
quoterV2Abi,
|
|
2212
|
+
swapRouter02Abi,
|
|
2213
|
+
swapRouterAbi,
|
|
2214
|
+
weth9Abi,
|
|
2215
|
+
uniswapV3FactoryAbi,
|
|
2216
|
+
uniswapV3PoolAbi,
|
|
2217
|
+
DEFAULT_MAX_FEED_AGE_SECONDS,
|
|
2218
|
+
getQuote,
|
|
2219
|
+
getMultiplier,
|
|
2220
|
+
getPosition,
|
|
2221
|
+
getPortfolio,
|
|
2222
|
+
swapAddresses,
|
|
2223
|
+
quoteSwap,
|
|
2224
|
+
buildSwapTx,
|
|
2225
|
+
ensureApproval,
|
|
2226
|
+
executeSwap,
|
|
2227
|
+
usdgAddress,
|
|
2228
|
+
formatUsdg,
|
|
2229
|
+
parseUsdg,
|
|
2230
|
+
getUsdgBalance,
|
|
2231
|
+
getUsdgTotalSupply,
|
|
2232
|
+
transferUsdg,
|
|
2233
|
+
approveUsdg,
|
|
2234
|
+
getUsdgAllowance,
|
|
2235
|
+
NOXA_ADDRESSES,
|
|
2236
|
+
ODYSSEY_ADDRESSES,
|
|
2237
|
+
noxaTokenLaunchedEvent,
|
|
2238
|
+
odysseyTokenCreatedEvent,
|
|
2239
|
+
odysseyTradedEvent,
|
|
2240
|
+
odysseyPoolMigratedEvent,
|
|
2241
|
+
getRecentLaunches,
|
|
2242
|
+
watchLaunches,
|
|
2243
|
+
watchCurveTrades,
|
|
2244
|
+
watchGraduations,
|
|
2245
|
+
subscribeFeed,
|
|
2246
|
+
watchTransfers
|
|
2247
|
+
};
|
|
2248
|
+
//# sourceMappingURL=chunk-CWEBZB3L.js.map
|