|
1 | 1 | const RPC_URL = "https://hydration-rpc.n.dwellir.com"; |
2 | 2 |
|
3 | | -// Token contract addresses mapping |
| 3 | +// Token contract addresses mapping (ERC20 tokens) |
4 | 4 | const TOKEN_CONTRACTS = { |
5 | 5 | hollar: "0x531a654d1696ED52e7275A8cede955E82620f99a", |
6 | 6 | gigadot: "0x34d5ffb83d14d82f87aaf2f13be895a3c814c2ad", |
7 | 7 | gigaeth: "0x8a598fe3e3a471ce865332e330d303502a0e2f52", |
8 | | - // Add more tokens here as needed |
9 | 8 | }; |
10 | 9 |
|
11 | 10 | // Token decimals mapping |
12 | 11 | const TOKEN_DECIMALS = { |
13 | 12 | hollar: 18, |
14 | 13 | gigadot: 18, |
15 | 14 | gigaeth: 18, |
16 | | - // Add more tokens here as needed |
| 15 | + h2o: 12, |
| 16 | +}; |
| 17 | + |
| 18 | +// Substrate tokens: pre-encoded storage keys for tokens.totalIssuance |
| 19 | +const SUBSTRATE_STORAGE_KEYS = { |
| 20 | + h2o: "0x99971b5749ac43e0235e41b0d378691857c875e4cff74148e4628f264b974c805153cb1f00942ff401000000", |
17 | 21 | }; |
18 | 22 |
|
19 | 23 | // ERC20 totalSupply function signature |
20 | 24 | const TOTAL_SUPPLY_SIGNATURE = "0x18160ddd"; |
21 | 25 |
|
| 26 | +async function callSubstrateTotalIssuance(storageKey) { |
| 27 | + const response = await fetch(RPC_URL, { |
| 28 | + method: "POST", |
| 29 | + headers: { "Content-Type": "application/json" }, |
| 30 | + body: JSON.stringify({ |
| 31 | + jsonrpc: "2.0", |
| 32 | + method: "state_getStorage", |
| 33 | + params: [storageKey], |
| 34 | + id: 1, |
| 35 | + }), |
| 36 | + }); |
| 37 | + |
| 38 | + const data = await response.json(); |
| 39 | + |
| 40 | + if (data.error) { |
| 41 | + throw new Error(data.error.message || "RPC call failed"); |
| 42 | + } |
| 43 | + |
| 44 | + // Decode little-endian hex u128 |
| 45 | + const hex = data.result.slice(2); // strip 0x |
| 46 | + const bytes = hex.match(/.{2}/g).reverse().join(""); |
| 47 | + return BigInt("0x" + bytes); |
| 48 | +} |
| 49 | + |
22 | 50 | async function callTotalSupply(contractAddress) { |
23 | 51 | const response = await fetch(RPC_URL, { |
24 | 52 | method: "POST", |
@@ -81,22 +109,22 @@ export default async (fastify, opts) => { |
81 | 109 | handler: async (request, reply) => { |
82 | 110 | const token = request.params.token.toLowerCase(); |
83 | 111 |
|
84 | | - // Check if token exists in our mapping |
85 | | - const contractAddress = TOKEN_CONTRACTS[token]; |
86 | | - if (!contractAddress) { |
| 112 | + const decimals = TOKEN_DECIMALS[token]; |
| 113 | + if (!decimals) { |
87 | 114 | return reply.code(404).send({ |
88 | 115 | error: `Token '${token}' not found`, |
89 | 116 | }); |
90 | 117 | } |
91 | 118 |
|
92 | | - const decimals = TOKEN_DECIMALS[token]; |
93 | | - |
94 | 119 | try { |
95 | | - // Call the contract via JSON-RPC |
96 | | - const hexValue = await callTotalSupply(contractAddress); |
97 | | - |
98 | | - // Convert hex to BigInt |
99 | | - const rawSupply = BigInt(hexValue); |
| 120 | + let rawSupply; |
| 121 | + const storageKey = SUBSTRATE_STORAGE_KEYS[token]; |
| 122 | + if (storageKey) { |
| 123 | + rawSupply = await callSubstrateTotalIssuance(storageKey); |
| 124 | + } else { |
| 125 | + const contractAddress = TOKEN_CONTRACTS[token]; |
| 126 | + rawSupply = BigInt(await callTotalSupply(contractAddress)); |
| 127 | + } |
100 | 128 |
|
101 | 129 | // Convert to human-readable format by dividing by 10^decimals |
102 | 130 | const divisor = BigInt(10 ** decimals); |
|
0 commit comments