@reality.eth/contracts 3.2.25 → 3.2.27
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/chains/deployments/1/SAFE/RealityETH_ERC20-3.0.json +7 -0
- package/chains/deployments/10/OETH/RealityETH-3.0.json +3 -1
- package/chains/deployments/100/XDAI/RealityETH-3.0.json +3 -1
- package/chains/deployments/11155111/ETH/Arbitrator.json +5 -0
- package/chains/deployments/11155111/ETH/RealityETH-3.2.json +3 -1
- package/chains/deployments/11155111/SAFE/RealityETH_ERC20-3.0.json +7 -0
- package/chains/deployments/30/RBTC/RealityETH-3.2.json +6 -0
- package/chains/deployments/31/tRBTC/RealityETH-3.2.json +6 -0
- package/chains/deployments/8453/ETH/RealityETH-3.0.json +3 -1
- package/chains/supported.json +103 -15
- package/generated/chains.json +89 -1
- package/generated/contract_token_lookup.json +476 -0
- package/generated/contracts.json +53 -2
- package/generated/custom-templates.json +1995 -0
- package/generated/tokens.json +70 -23
- package/generated/website-data.js +1 -0
- package/package.json +7 -3
- package/scripts/create-hash-template.js +113 -0
- package/scripts/deploy.js +19 -37
- package/scripts/fetch-templates.js +214 -0
- package/scripts/generate_chains_json.js +12 -0
- package/scripts/generate_contract_token_lookup.js +40 -0
- package/scripts/generate_indexer_config.js +111 -0
- package/scripts/generate_ponder_config.js +222 -0
- package/scripts/generate_templates_bundle.js +46 -0
- package/scripts/generate_website_data.js +68 -0
- package/scripts/retry-chain1-templates.js +102 -0
- package/scripts/update_token_prices.js +146 -0
- package/tests/python/requirements.txt +0 -4
- package/tests/python/test.py +37 -34
- package/tests/python/test_erc20.py +27 -25
- package/tokens/ARETH.json +3 -2
- package/tokens/AVAX.json +2 -1
- package/tokens/BNB.json +2 -1
- package/tokens/BOND.json +2 -1
- package/tokens/CELO.json +3 -2
- package/tokens/CTH.json +2 -1
- package/tokens/DAOOS.json +2 -1
- package/tokens/DEXE.json +2 -1
- package/tokens/ETH.json +3 -2
- package/tokens/FOX.json +2 -1
- package/tokens/GNO.json +4 -3
- package/tokens/MATIC.json +2 -1
- package/tokens/MONAD.json +3 -2
- package/tokens/OETH.json +3 -2
- package/tokens/POLK.json +2 -1
- package/tokens/RBTC.json +8 -0
- package/tokens/SAFE.json +9 -0
- package/tokens/SUKU.json +2 -1
- package/tokens/SWISE.json +2 -1
- package/tokens/TLOS.json +3 -2
- package/tokens/TRST.json +2 -1
- package/tokens/UBQ.json +2 -1
- package/tokens/XDAI.json +3 -2
- package/tokens/ZBS.json +2 -1
- package/tokens/tRBTC.json +8 -0
package/scripts/deploy.js
CHANGED
|
@@ -18,20 +18,7 @@ const deployed_at = null;
|
|
|
18
18
|
var undef;
|
|
19
19
|
|
|
20
20
|
const defaultConfigs = {
|
|
21
|
-
//maxFeePerGas: 610000000000,
|
|
22
|
-
//maxPriorityFeePerGas: 10000000000,
|
|
23
|
-
//gasPrice: 70000000000,
|
|
24
|
-
//gasPrice: 10000000000,
|
|
25
|
-
// gasPrice: 100000000,
|
|
26
|
-
// gasPrice: 10000, // optimism 1000000,
|
|
27
|
-
/// gasPrice: 5000000000,
|
|
28
|
-
//gasLimit: 6000000, // optimism 4500000
|
|
29
21
|
gasLimit: 4500000,
|
|
30
|
-
//etherscanApiKey: 'TPA4BFDDIH8Q7YBQ4JMGN6WDDRRPAV6G34'
|
|
31
|
-
//gasLimit: 155734867 // arbitrum
|
|
32
|
-
//gasLimit: 7000000
|
|
33
|
-
//gasLimit: 800035294
|
|
34
|
-
// gasLimit: 4291955938
|
|
35
22
|
}
|
|
36
23
|
const task = process.argv[2]
|
|
37
24
|
const version = process.argv[3]
|
|
@@ -74,6 +61,7 @@ const chains = {
|
|
|
74
61
|
'backstopTestnet1': 88558801
|
|
75
62
|
}
|
|
76
63
|
const non_infura_chains = {
|
|
64
|
+
'mainnet': 'http://127.0.0.1:8545',
|
|
77
65
|
'gnosis': 'https://gnosis.oat.farm',
|
|
78
66
|
'scroll-alpha-testnet': 'https://alpha-rpc.scroll.io/l2',
|
|
79
67
|
'sokol': 'https://sokol.poa.network',
|
|
@@ -94,7 +82,7 @@ const non_infura_chains = {
|
|
|
94
82
|
'kintsugi': 'https://rpc.kintsugi.themerge.dev',
|
|
95
83
|
'monad': 'https://rpc.monad.xyz',
|
|
96
84
|
'mumbai': 'https://matic-mumbai.chainstacklabs.com',
|
|
97
|
-
'sepolia': '
|
|
85
|
+
'sepolia': 'http://127.0.0.1:8546',
|
|
98
86
|
'holesky': 'https://ethereum-holesky.publicnode.com',
|
|
99
87
|
'telosevm': 'https://mainnet.telos.net/evm',
|
|
100
88
|
'unichain': 'https://mainnet.unichain.org',
|
|
@@ -252,25 +240,17 @@ function realityETHName() {
|
|
|
252
240
|
return tmpl;
|
|
253
241
|
}
|
|
254
242
|
|
|
243
|
+
// Fetch current fee data and return EIP-1559 overrides: { maxFeePerGas, maxPriorityFeePerGas }.
|
|
244
|
+
// tip comes from eth_maxPriorityFeePerGas (actual market rate, not ethers v5's hardcoded 1.5 gwei).
|
|
245
|
+
// maxFeePerGas = baseFee * 2 + tip (gives headroom for a few blocks of fee movement).
|
|
255
246
|
async function waitForGas(provider) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const f = await provider.getFeeData()
|
|
265
|
-
console.log('fee', f)
|
|
266
|
-
throw new Error();
|
|
267
|
-
return;
|
|
268
|
-
if (f.gasPrice.gt(ethers.BigNumber.from(defaultConfigs.maxFeePerGas))) {
|
|
269
|
-
console.log('Gas is too expensive, got', f.gasPrice.toString(), 'but you will only pay ', defaultConfigs.maxFeePerGas, ', retrying...')
|
|
270
|
-
await sleep(2000);
|
|
271
|
-
await waitForGas(provider);
|
|
272
|
-
}
|
|
273
|
-
return true;
|
|
247
|
+
const block = await provider.getBlock('latest');
|
|
248
|
+
const base = block.baseFeePerGas || ethers.BigNumber.from(0);
|
|
249
|
+
const tipHex = await provider.send('eth_maxPriorityFeePerGas', []);
|
|
250
|
+
const tip = ethers.BigNumber.from(tipHex);
|
|
251
|
+
const maxFee = base.mul(2).add(tip);
|
|
252
|
+
console.log('base fee:', base.toString(), 'tip:', tip.toString(), 'maxFee:', maxFee.toString());
|
|
253
|
+
return { maxFeePerGas: maxFee, maxPriorityFeePerGas: tip };
|
|
274
254
|
}
|
|
275
255
|
|
|
276
256
|
async function deployRealityETH() {
|
|
@@ -287,7 +267,8 @@ async function deployRealityETH() {
|
|
|
287
267
|
txt = txt + ' (from address ' + signer.address + ')';
|
|
288
268
|
console.log(txt);
|
|
289
269
|
|
|
290
|
-
await waitForGas(provider);
|
|
270
|
+
const feeOverrides = await waitForGas(provider);
|
|
271
|
+
const txConfig = { ...defaultConfigs, ...feeOverrides };
|
|
291
272
|
|
|
292
273
|
if (deployed_at) {
|
|
293
274
|
|
|
@@ -311,7 +292,7 @@ async function deployRealityETH() {
|
|
|
311
292
|
}
|
|
312
293
|
|
|
313
294
|
} else {
|
|
314
|
-
confac.deploy(
|
|
295
|
+
confac.deploy(txConfig).then(function(result) {
|
|
315
296
|
const txid = result.deployTransaction.hash;
|
|
316
297
|
const address = result.address;
|
|
317
298
|
console.log('storing address', address);
|
|
@@ -327,7 +308,7 @@ async function deployRealityETH() {
|
|
|
327
308
|
}
|
|
328
309
|
|
|
329
310
|
//console.log('result was', result);
|
|
330
|
-
store_deployed_contract(tmpl, chain_id, token_name, settings);
|
|
311
|
+
store_deployed_contract(tmpl, chain_id, token_name, settings);
|
|
331
312
|
if (isERC20()) {
|
|
332
313
|
console.log('Setting token')
|
|
333
314
|
result.setToken(token_address);
|
|
@@ -442,9 +423,10 @@ async function deployFactory() {
|
|
|
442
423
|
txt = txt + ' (from address ' + signer.address + ')';
|
|
443
424
|
console.log(txt);
|
|
444
425
|
|
|
445
|
-
await waitForGas(provider);
|
|
426
|
+
const feeOverrides = await waitForGas(provider);
|
|
427
|
+
const txConfig = { ...defaultConfigs, ...feeOverrides };
|
|
446
428
|
|
|
447
|
-
confac.deploy(lib,
|
|
429
|
+
confac.deploy(lib, txConfig).then(function(result) {
|
|
448
430
|
const txid = result.deployTransaction.hash;
|
|
449
431
|
const address = result.address;
|
|
450
432
|
console.log('storing address', address);
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Fetches all templates from every deployed RealityETH contract across all chains.
|
|
3
|
+
//
|
|
4
|
+
// The contracts().templates(id) view returns the block number where
|
|
5
|
+
// LogNewTemplate was emitted for that template ID, or 0 if it doesn't exist.
|
|
6
|
+
// We call it for IDs 0, 1, 2, ... stopping at the first 0.
|
|
7
|
+
// For each unique block number we get a single pinpoint getLogs call that
|
|
8
|
+
// retrieves all LogNewTemplate events in that block for that contract.
|
|
9
|
+
//
|
|
10
|
+
// Usage:
|
|
11
|
+
// cd packages/contracts && node scripts/fetch-templates.js
|
|
12
|
+
//
|
|
13
|
+
// Output: generated/custom-templates.json
|
|
14
|
+
//
|
|
15
|
+
// Errors (e.g. RPC nodes that don't serve old logs) are written to _errors in
|
|
16
|
+
// the output so the file can be checked in and specific failures retried later
|
|
17
|
+
// with a different RPC.
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const ethers = require('ethers');
|
|
23
|
+
|
|
24
|
+
const CONTRACTS_JSON = path.join(__dirname, '../generated/contracts.json');
|
|
25
|
+
const SUPPORTED_JSON = path.join(__dirname, '../chains/supported.json');
|
|
26
|
+
const CHAINID_NET_JSON = path.join(__dirname, '../chains/chainid.network.json');
|
|
27
|
+
const OUTPUT_PATH = path.join(__dirname, '../generated/custom-templates.json');
|
|
28
|
+
|
|
29
|
+
const TEMPLATES_ABI = [
|
|
30
|
+
'function templates(uint256 template_id) view returns (uint256)',
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const LOG_TEMPLATE_TOPIC = ethers.utils.id('LogNewTemplate(uint256,address,string)');
|
|
34
|
+
|
|
35
|
+
const iface = new ethers.utils.Interface([
|
|
36
|
+
'event LogNewTemplate(uint256 indexed template_id, address indexed user, string question_text)',
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
40
|
+
|
|
41
|
+
async function fetchForContract(prov, chainId, address, existingTemplates, errors) {
|
|
42
|
+
const rc = new ethers.Contract(address, TEMPLATES_ABI, prov);
|
|
43
|
+
const templates = Object.assign({}, existingTemplates);
|
|
44
|
+
const blocksQueried = new Set();
|
|
45
|
+
|
|
46
|
+
for (let id = 0; ; id++) {
|
|
47
|
+
let blockNum;
|
|
48
|
+
try {
|
|
49
|
+
const bn = await rc.templates(id);
|
|
50
|
+
blockNum = bn.toNumber();
|
|
51
|
+
} catch (err) {
|
|
52
|
+
const msg = err.message || String(err);
|
|
53
|
+
console.error(` ✗ templates(${id}): ${msg.slice(0, 120)}`);
|
|
54
|
+
errors.push({ chainId, contract: address, templateId: id, phase: 'templates()', error: msg });
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (blockNum === 0) {
|
|
59
|
+
console.log(` templates(${id}) = 0 — done (${id} template${id !== 1 ? 's' : ''})`);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(` templates(${id}) = block ${blockNum}`);
|
|
64
|
+
|
|
65
|
+
if (!blocksQueried.has(blockNum)) {
|
|
66
|
+
blocksQueried.add(blockNum);
|
|
67
|
+
try {
|
|
68
|
+
const logs = await prov.getLogs({
|
|
69
|
+
address,
|
|
70
|
+
topics: [LOG_TEMPLATE_TOPIC],
|
|
71
|
+
fromBlock: blockNum,
|
|
72
|
+
toBlock: blockNum,
|
|
73
|
+
});
|
|
74
|
+
for (const log of logs) {
|
|
75
|
+
try {
|
|
76
|
+
const parsed = iface.parseLog(log);
|
|
77
|
+
const tid = parsed.args.template_id.toString();
|
|
78
|
+
const text = parsed.args.question_text;
|
|
79
|
+
templates[tid] = text;
|
|
80
|
+
console.log(` ✓ template ${tid}: ${text.slice(0, 80)}${text.length > 80 ? '…' : ''}`);
|
|
81
|
+
} catch (parseErr) {
|
|
82
|
+
console.error(` ✗ log parse failed: ${parseErr.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
await sleep(150);
|
|
86
|
+
} catch (logErr) {
|
|
87
|
+
const msg = logErr.message || String(logErr);
|
|
88
|
+
console.error(` ✗ getLogs(block ${blockNum}): ${msg.slice(0, 120)}`);
|
|
89
|
+
errors.push({ chainId, contract: address, block: blockNum, phase: 'getLogs', error: msg });
|
|
90
|
+
// Don't break — continue to next template ID in case we can get the block number at least
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
await sleep(150);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return templates;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function pickRpc(chainId, supported, publicRpcs) {
|
|
101
|
+
// Highest priority: env var (same convention as the indexer's .env.local)
|
|
102
|
+
const envRpc = process.env[`PONDER_RPC_URL_${chainId}`];
|
|
103
|
+
if (envRpc) return envRpc;
|
|
104
|
+
if (supported[chainId]?.hostedRPC) return supported[chainId].hostedRPC;
|
|
105
|
+
// Fall back to first public (non-template) HTTP RPC from chainid.network.json
|
|
106
|
+
const pub = (publicRpcs[chainId] || []).find(r => !r.includes('${') && r.startsWith('http'));
|
|
107
|
+
return pub || null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function main() {
|
|
111
|
+
const allContracts = JSON.parse(fs.readFileSync(CONTRACTS_JSON, 'utf8'));
|
|
112
|
+
const supported = JSON.parse(fs.readFileSync(SUPPORTED_JSON, 'utf8'));
|
|
113
|
+
const cidList = JSON.parse(fs.readFileSync(CHAINID_NET_JSON, 'utf8'));
|
|
114
|
+
const publicRpcs = Object.fromEntries(
|
|
115
|
+
cidList.filter(c => c.chainId).map(c => [String(c.chainId), c.rpc || []])
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Load existing output so we can skip already-complete contracts
|
|
119
|
+
let existing = { _errors: [] };
|
|
120
|
+
if (fs.existsSync(OUTPUT_PATH)) {
|
|
121
|
+
try {
|
|
122
|
+
existing = JSON.parse(fs.readFileSync(OUTPUT_PATH, 'utf8'));
|
|
123
|
+
console.log(`Loaded existing output from ${OUTPUT_PATH}`);
|
|
124
|
+
} catch { /* start fresh */ }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const output = { _errors: [] };
|
|
128
|
+
let totalContracts = 0;
|
|
129
|
+
let skipped = 0;
|
|
130
|
+
|
|
131
|
+
for (const [chainId, tokenMap] of Object.entries(allContracts)) {
|
|
132
|
+
const rpcUrl = pickRpc(chainId, supported, publicRpcs);
|
|
133
|
+
if (!rpcUrl) {
|
|
134
|
+
console.log(`\nChain ${chainId}: no RPC available, skipping`);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
output[chainId] = output[chainId] || {};
|
|
139
|
+
|
|
140
|
+
// Collect unique RealityETH contract addresses — skip arbitrators etc.
|
|
141
|
+
const seen = new Set();
|
|
142
|
+
const toFetch = [];
|
|
143
|
+
for (const [token, vers] of Object.entries(tokenMap)) {
|
|
144
|
+
for (const [ver, info] of Object.entries(vers)) {
|
|
145
|
+
if (!info.address) continue;
|
|
146
|
+
if (!ver.includes('RealityETH')) continue;
|
|
147
|
+
const lc = info.address.toLowerCase();
|
|
148
|
+
if (seen.has(lc)) continue;
|
|
149
|
+
seen.add(lc);
|
|
150
|
+
toFetch.push({ address: lc, ver, token });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!toFetch.length) continue;
|
|
155
|
+
|
|
156
|
+
const prov = new ethers.providers.JsonRpcProvider(rpcUrl, {
|
|
157
|
+
chainId: Number(chainId),
|
|
158
|
+
name: `chain-${chainId}`,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
for (const { address, ver } of toFetch) {
|
|
162
|
+
totalContracts++;
|
|
163
|
+
const chainExisting = existing[chainId] || {};
|
|
164
|
+
const contractExisting = chainExisting[address];
|
|
165
|
+
|
|
166
|
+
// Skip if already marked complete in a previous run
|
|
167
|
+
if (contractExisting && contractExisting._complete) {
|
|
168
|
+
console.log(`\nChain ${chainId} ${ver} ${address} — already complete, skipping`);
|
|
169
|
+
output[chainId][address] = contractExisting;
|
|
170
|
+
skipped++;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log(`\nChain ${chainId} ${ver} ${address} (via ${rpcUrl.slice(0, 50)})`);
|
|
175
|
+
|
|
176
|
+
const contractErrors = [];
|
|
177
|
+
const templates = await fetchForContract(
|
|
178
|
+
prov, chainId, address,
|
|
179
|
+
contractExisting || {}, contractErrors
|
|
180
|
+
);
|
|
181
|
+
output._errors.push(...contractErrors);
|
|
182
|
+
|
|
183
|
+
// Only mark complete if no getLogs errors — incomplete contracts will be
|
|
184
|
+
// retried automatically on the next run (e.g. with a better RPC set via env).
|
|
185
|
+
const hasLogGaps = contractErrors.some(e => e.phase === 'getLogs');
|
|
186
|
+
templates._complete = !hasLogGaps;
|
|
187
|
+
output[chainId][address] = templates;
|
|
188
|
+
|
|
189
|
+
// Save after every contract so progress isn't lost on failure
|
|
190
|
+
fs.writeFileSync(OUTPUT_PATH, JSON.stringify(output, null, 2));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Final save
|
|
195
|
+
fs.writeFileSync(OUTPUT_PATH, JSON.stringify(output, null, 2));
|
|
196
|
+
|
|
197
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
198
|
+
console.log(`Done. ${totalContracts} contract(s) processed (${skipped} skipped as already complete).`);
|
|
199
|
+
console.log(`Output: ${OUTPUT_PATH}`);
|
|
200
|
+
|
|
201
|
+
if (output._errors.length > 0) {
|
|
202
|
+
console.log(`\n${output._errors.length} error(s) — check _errors in the output file:`);
|
|
203
|
+
for (const e of output._errors) {
|
|
204
|
+
console.log(` chain ${e.chainId} ${e.contract} ${e.phase}${e.block ? ` block ${e.block}` : ` id ${e.templateId}`}: ${e.error.slice(0, 80)}`);
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
console.log('No errors.');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
main().catch(err => {
|
|
212
|
+
console.error('\nFatal error:', err.message || err);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
});
|
|
@@ -77,10 +77,22 @@ for (var ci = 0; ci< chain_id_list.length; ci++) {
|
|
|
77
77
|
chainparams['network_name'] = our_data['network_name'];
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
if (our_data['display_name']) {
|
|
81
|
+
chainparams['display_name'] = our_data['display_name'];
|
|
82
|
+
}
|
|
83
|
+
|
|
80
84
|
if (our_data['deprecated']) {
|
|
81
85
|
chainparams['deprecated'] = true;
|
|
82
86
|
}
|
|
83
87
|
|
|
88
|
+
if (our_data['realityETHIndexerSupport']) {
|
|
89
|
+
chainparams['realityETHIndexerSupport'] = true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (our_data['blocksPerDay']) {
|
|
93
|
+
chainparams['blocksPerDay'] = our_data['blocksPerDay'];
|
|
94
|
+
}
|
|
95
|
+
|
|
84
96
|
chainparams['blockExplorerUrls'] = [];
|
|
85
97
|
if ('explorers' in chain_info) {
|
|
86
98
|
for(var exi in chain_info['explorers']) {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Generates generated/contract_token_lookup.json:
|
|
3
|
+
// { chainId: { contractAddress: { symbol, decimals, approx_1_usd } } }
|
|
4
|
+
// Used by both the custom indexer and ponder to compute USD bond values.
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const GEN_DIR = path.join(__dirname, '..', 'generated');
|
|
12
|
+
const CONTRACTS = JSON.parse(fs.readFileSync(path.join(GEN_DIR, 'contracts.json'), 'utf8'));
|
|
13
|
+
const TOKENS = JSON.parse(fs.readFileSync(path.join(GEN_DIR, 'tokens.json'), 'utf8'));
|
|
14
|
+
|
|
15
|
+
const lookup = {};
|
|
16
|
+
|
|
17
|
+
for (const [chainId, tokenMap] of Object.entries(CONTRACTS)) {
|
|
18
|
+
for (const [symbol, contractMap] of Object.entries(tokenMap)) {
|
|
19
|
+
const tokenInfo = TOKENS[symbol];
|
|
20
|
+
if (!tokenInfo) {
|
|
21
|
+
console.warn(` Unknown token symbol: ${symbol} (chain ${chainId})`);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const { decimals, approx_1_usd } = tokenInfo;
|
|
25
|
+
if (approx_1_usd == null) {
|
|
26
|
+
console.warn(` No approx_1_usd for ${symbol} — run update_token_prices.js first`);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!lookup[chainId]) lookup[chainId] = {};
|
|
30
|
+
for (const contractDef of Object.values(contractMap)) {
|
|
31
|
+
const addr = contractDef.address.toLowerCase();
|
|
32
|
+
lookup[chainId][addr] = { symbol, decimals, approx_1_usd };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const outPath = path.join(GEN_DIR, 'contract_token_lookup.json');
|
|
38
|
+
fs.writeFileSync(outPath, JSON.stringify(lookup, null, 2) + '\n');
|
|
39
|
+
console.log(`Written: ${outPath}`);
|
|
40
|
+
console.log(`Chains: ${Object.keys(lookup).length}, contracts: ${Object.values(lookup).reduce((s, m) => s + Object.keys(m).length, 0)}`);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// AUTO-GENERATES two files from contracts.json + chains.json:
|
|
3
|
+
// packages/indexer/active-chains.json — chain list for sync.js ACTIVE_CHAINS
|
|
4
|
+
// packages/indexer/scripts/chains-config.json — chain/explorer config for fetch-event-blocks.js
|
|
5
|
+
//
|
|
6
|
+
// Reads realityETHIndexerSupport, indexer_batch_size, indexer_name, and explorer_api_*
|
|
7
|
+
// fields from supported.json to drive generation.
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
const contractsDir = path.resolve(__dirname, '..');
|
|
15
|
+
const contracts = JSON.parse(fs.readFileSync(path.join(contractsDir, 'generated/contracts.json'), 'utf8'));
|
|
16
|
+
const chains = JSON.parse(fs.readFileSync(path.join(contractsDir, 'generated/chains.json'), 'utf8'));
|
|
17
|
+
const supported = JSON.parse(fs.readFileSync(path.join(contractsDir, 'chains/supported.json'), 'utf8'));
|
|
18
|
+
|
|
19
|
+
const ETHERSCAN_V2 = 'https://api.etherscan.io/v2/api';
|
|
20
|
+
const DEFAULT_BATCH = 5_000;
|
|
21
|
+
|
|
22
|
+
// Skip arbitrators and pre-release versions.
|
|
23
|
+
function isSkipped(ver) {
|
|
24
|
+
return ver.startsWith('Arbitrator') || ver.endsWith('-rc1') || ver.endsWith('-rc2');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Name used in indexer logs and as the fetch-event-blocks.js CHAINS key.
|
|
28
|
+
// Prefers supported.json indexer_name, then normalized network_name.
|
|
29
|
+
function indexerName(id) {
|
|
30
|
+
const sup = supported[String(id)] || {};
|
|
31
|
+
if (sup.indexer_name) return sup.indexer_name;
|
|
32
|
+
const nn = chains[String(id)]?.network_name;
|
|
33
|
+
if (!nn) return `chain_${id}`;
|
|
34
|
+
return nn.toLowerCase().replace(/[-\s]+/g, '_');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Short label for the contracts[] name field in chains-config.json.
|
|
38
|
+
// Must be unique within each chain since it's used as a meta key for HWM tracking.
|
|
39
|
+
function contractLabel(token, ver) {
|
|
40
|
+
const m = ver.match(/-(\d+\.\d+)$/);
|
|
41
|
+
const v = m ? m[1] : '?';
|
|
42
|
+
return ver.startsWith('RealityETH_ERC20-') ? `ERC20-${token}-v${v}` : `v${v}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Chain IDs that have realityETHIndexerSupport set in chains.json.
|
|
46
|
+
const indexedIds = Object.keys(chains)
|
|
47
|
+
.map(Number)
|
|
48
|
+
.filter(id => chains[String(id)]?.realityETHIndexerSupport)
|
|
49
|
+
.sort((a, b) => a - b);
|
|
50
|
+
|
|
51
|
+
const activeChains = [];
|
|
52
|
+
const chainsConfig = {};
|
|
53
|
+
|
|
54
|
+
for (const chainId of indexedIds) {
|
|
55
|
+
const sup = supported[String(chainId)] || {};
|
|
56
|
+
const byToken = contracts[String(chainId)] || {};
|
|
57
|
+
const name = indexerName(chainId);
|
|
58
|
+
|
|
59
|
+
// Collect all non-skipped contract addresses with their start blocks.
|
|
60
|
+
const entries = [];
|
|
61
|
+
for (const [token, byVersion] of Object.entries(byToken)) {
|
|
62
|
+
for (const [ver, data] of Object.entries(byVersion)) {
|
|
63
|
+
if (isSkipped(ver)) continue;
|
|
64
|
+
const addrs = Array.isArray(data.address) ? data.address : [data.address];
|
|
65
|
+
for (const addr of addrs.filter(Boolean)) {
|
|
66
|
+
entries.push({
|
|
67
|
+
address: addr.toLowerCase(),
|
|
68
|
+
startBlock: data.block || 0,
|
|
69
|
+
label: contractLabel(token, ver),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!entries.length) continue;
|
|
75
|
+
entries.sort((a, b) => a.startBlock - b.startBlock);
|
|
76
|
+
|
|
77
|
+
// active-chains.json entry (consumed by sync.js mkChain).
|
|
78
|
+
activeChains.push({
|
|
79
|
+
id: chainId,
|
|
80
|
+
name,
|
|
81
|
+
batchSize: sup.indexer_batch_size || DEFAULT_BATCH,
|
|
82
|
+
noExplorerRefresh: !!sup.no_explorer_refresh,
|
|
83
|
+
startBlock: entries[0].startBlock,
|
|
84
|
+
addresses: entries.map(e => e.address),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// chains-config.json entry (consumed by fetch-event-blocks.js).
|
|
88
|
+
// Default: Etherscan V2 with chainId param and ETHERSCAN_API_KEY.
|
|
89
|
+
// Overrides come from explorer_api_* fields in supported.json.
|
|
90
|
+
const apiUrl = sup.explorer_api_url || ETHERSCAN_V2;
|
|
91
|
+
const apiKeyEnv = 'explorer_api_key_env' in sup ? sup.explorer_api_key_env : 'ETHERSCAN_API_KEY';
|
|
92
|
+
const apiKeyReq = sup.explorer_api_key_required || false;
|
|
93
|
+
const apiChainId = sup.explorer_api_no_chain_id ? null : chainId;
|
|
94
|
+
|
|
95
|
+
chainsConfig[name] = {
|
|
96
|
+
chainId: apiChainId,
|
|
97
|
+
apiUrl,
|
|
98
|
+
apiKeyEnv,
|
|
99
|
+
apiKeyRequired: apiKeyReq,
|
|
100
|
+
contracts: entries.map(e => ({ address: e.address, startBlock: e.startBlock, name: e.label })),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const activeChainPath = path.resolve(contractsDir, '../indexer/active-chains.json');
|
|
105
|
+
const chainsConfigPath = path.resolve(contractsDir, '../indexer/scripts/chains-config.json');
|
|
106
|
+
|
|
107
|
+
fs.writeFileSync(activeChainPath, JSON.stringify(activeChains, null, 2) + '\n');
|
|
108
|
+
console.log('Wrote', activeChainPath);
|
|
109
|
+
|
|
110
|
+
fs.writeFileSync(chainsConfigPath, JSON.stringify(chainsConfig, null, 2) + '\n');
|
|
111
|
+
console.log('Wrote', chainsConfigPath);
|