@defillama/sdk 5.0.236 → 5.0.240
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/package.json +3 -3
- package/build/Balances.d.ts +0 -139
- package/build/Balances.js +0 -363
- package/build/ChainApi.d.ts +0 -142
- package/build/ChainApi.js +0 -297
- package/build/abi/abi2.d.ts +0 -5
- package/build/abi/abi2.js +0 -215
- package/build/abi/cachedABIs.d.ts +0 -4
- package/build/abi/cachedABIs.js +0 -8
- package/build/abi/convertResults.d.ts +0 -5
- package/build/abi/convertResults.js +0 -60
- package/build/abi/index.d.ts +0 -31
- package/build/abi/index.js +0 -327
- package/build/abi/multicall.d.ts +0 -23
- package/build/abi/multicall.js +0 -382
- package/build/abi/multicall3.d.ts +0 -7
- package/build/abi/multicall3.js +0 -397
- package/build/abi/rpcCall.d.ts +0 -4
- package/build/abi/rpcCall.js +0 -62
- package/build/abi/tron.d.ts +0 -33
- package/build/abi/tron.js +0 -110
- package/build/api.d.ts +0 -10
- package/build/api.js +0 -45
- package/build/api2.d.ts +0 -5
- package/build/api2.js +0 -41
- package/build/chains/algorand.d.ts +0 -277
- package/build/chains/algorand.js +0 -618
- package/build/chains/aptos.d.ts +0 -230
- package/build/chains/aptos.js +0 -612
- package/build/chains/cardano.d.ts +0 -242
- package/build/chains/cardano.js +0 -407
- package/build/chains/cosmos.d.ts +0 -252
- package/build/chains/cosmos.js +0 -618
- package/build/chains/icp.d.ts +0 -235
- package/build/chains/icp.js +0 -1040
- package/build/chains/index.d.ts +0 -23
- package/build/chains/index.js +0 -59
- package/build/chains/near.d.ts +0 -192
- package/build/chains/near.js +0 -446
- package/build/chains/rpc.d.ts +0 -125
- package/build/chains/rpc.js +0 -326
- package/build/chains/starknet.d.ts +0 -205
- package/build/chains/starknet.js +0 -884
- package/build/chains/stellar.d.ts +0 -410
- package/build/chains/stellar.js +0 -1082
- package/build/chains/substrate.d.ts +0 -333
- package/build/chains/substrate.js +0 -1042
- package/build/chains/sui.d.ts +0 -365
- package/build/chains/sui.js +0 -1135
- package/build/chains/svm.d.ts +0 -340
- package/build/chains/svm.js +0 -760
- package/build/chains/tezos.d.ts +0 -225
- package/build/chains/tezos.js +0 -412
- package/build/chains/ton.d.ts +0 -353
- package/build/chains/ton.js +0 -788
- package/build/chains/tron.d.ts +0 -45
- package/build/chains/tron.js +0 -110
- package/build/chains/utxo.d.ts +0 -202
- package/build/chains/utxo.js +0 -592
- package/build/chains/xrpl.d.ts +0 -179
- package/build/chains/xrpl.js +0 -467
- package/build/computeTVL/blocks.d.ts +0 -1
- package/build/computeTVL/blocks.js +0 -12
- package/build/computeTVL/humanizeNumber.d.ts +0 -1
- package/build/computeTVL/humanizeNumber.js +0 -19
- package/build/erc20/index.d.ts +0 -31
- package/build/erc20/index.js +0 -56
- package/build/eth/index.d.ts +0 -22
- package/build/eth/index.js +0 -118
- package/build/general.d.ts +0 -5
- package/build/general.js +0 -20
- package/build/generalUtil.d.ts +0 -8
- package/build/generalUtil.js +0 -73
- package/build/index.d.ts +0 -32
- package/build/index.js +0 -74
- package/build/providers.json +0 -1
- package/build/types.d.ts +0 -76
- package/build/types.js +0 -2
- package/build/util/LlamaProvider.d.ts +0 -46
- package/build/util/LlamaProvider.js +0 -365
- package/build/util/blocks.d.ts +0 -36
- package/build/util/blocks.js +0 -292
- package/build/util/cache.d.ts +0 -45
- package/build/util/cache.js +0 -309
- package/build/util/chainUtils/data.json +0 -1356
- package/build/util/chainUtils/index.d.ts +0 -11
- package/build/util/chainUtils/index.js +0 -136
- package/build/util/coins.d.ts +0 -19
- package/build/util/coins.js +0 -113
- package/build/util/common.d.ts +0 -34
- package/build/util/common.js +0 -373
- package/build/util/computeTVL.d.ts +0 -19
- package/build/util/computeTVL.js +0 -102
- package/build/util/cosmos.d.ts +0 -1
- package/build/util/cosmos.js +0 -8
- package/build/util/debugLog.d.ts +0 -4
- package/build/util/debugLog.js +0 -18
- package/build/util/discord.d.ts +0 -1
- package/build/util/discord.js +0 -33
- package/build/util/elastic.d.ts +0 -43
- package/build/util/elastic.js +0 -113
- package/build/util/env.d.ts +0 -19
- package/build/util/env.js +0 -158
- package/build/util/graph.d.ts +0 -8
- package/build/util/graph.js +0 -50
- package/build/util/index.d.ts +0 -32
- package/build/util/index.js +0 -227
- package/build/util/indexer.compatibility.d.ts +0 -6
- package/build/util/indexer.compatibility.js +0 -64
- package/build/util/indexer.d.ts +0 -92
- package/build/util/indexer.js +0 -744
- package/build/util/indexerSql.d.ts +0 -9
- package/build/util/indexerSql.js +0 -102
- package/build/util/internal-cache.d.ts +0 -11
- package/build/util/internal-cache.js +0 -77
- package/build/util/logs.d.ts +0 -55
- package/build/util/logs.decode.shared.d.ts +0 -8
- package/build/util/logs.decode.shared.js +0 -289
- package/build/util/logs.js +0 -401
- package/build/util/promisePool.d.ts +0 -8
- package/build/util/promisePool.js +0 -31
- package/build/util/r2.d.ts +0 -4
- package/build/util/r2.js +0 -52
- package/build/util/removeRedundantRPCs.d.ts +0 -1
- package/build/util/removeRedundantRPCs.js +0 -49
- package/build/util/transactions.d.ts +0 -35
- package/build/util/transactions.js +0 -22
- package/build/util/updateChainLabels.d.ts +0 -1
- package/build/util/updateChainLabels.js +0 -5
- package/build/util/updateProviderList.d.ts +0 -1
- package/build/util/updateProviderList.js +0 -274
package/build/chains/starknet.js
DELETED
|
@@ -1,884 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.cairoErc20Abis = exports.erc20AbisCairo1 = exports.erc20Abis = exports.DEFAULT_EVENTS_CHUNK_SIZE = exports.number = exports.FIELD_PRIME = exports.ADDR_BOUND = exports.BATCH_CHUNK_SIZE = exports.AGGREGATE_CHUNK_SIZE = exports.DEFAULT_MULTICALL_ADDRESS = exports.DEFAULT_ENDPOINTS = exports.CHAIN = void 0;
|
|
4
|
-
exports.getEndpoints = getEndpoints;
|
|
5
|
-
exports.getMulticallAddress = getMulticallAddress;
|
|
6
|
-
exports.toBigInt = toBigInt;
|
|
7
|
-
exports.toHex = toHex;
|
|
8
|
-
exports.addAddressPadding = addAddressPadding;
|
|
9
|
-
exports.validateAndParseAddress = validateAndParseAddress;
|
|
10
|
-
exports.isStarknetAddress = isStarknetAddress;
|
|
11
|
-
exports.starknetKeccak = starknetKeccak;
|
|
12
|
-
exports.getSelectorFromName = getSelectorFromName;
|
|
13
|
-
exports.parseUint256 = parseUint256;
|
|
14
|
-
exports.shortStringToFelt = shortStringToFelt;
|
|
15
|
-
exports.feltToShortString = feltToShortString;
|
|
16
|
-
exports.feltArrToStr = feltArrToStr;
|
|
17
|
-
exports.encodeCalldata = encodeCalldata;
|
|
18
|
-
exports.decodeOutput = decodeOutput;
|
|
19
|
-
exports.toBlockId = toBlockId;
|
|
20
|
-
exports.formCallBody = formCallBody;
|
|
21
|
-
exports.parseOutput = parseOutput;
|
|
22
|
-
exports.rpc = rpc;
|
|
23
|
-
exports.getBlockNumber = getBlockNumber;
|
|
24
|
-
exports.getBlock = getBlock;
|
|
25
|
-
exports.getBlockAtTimestamp = getBlockAtTimestamp;
|
|
26
|
-
exports.getNonce = getNonce;
|
|
27
|
-
exports.getClassHashAt = getClassHashAt;
|
|
28
|
-
exports.call = call;
|
|
29
|
-
exports.multiCall = multiCall;
|
|
30
|
-
exports.getLogs = getLogs;
|
|
31
|
-
/**
|
|
32
|
-
* Starknet client: dependency-free JSON-RPC transport + Cairo ABI codec.
|
|
33
|
-
*
|
|
34
|
-
* Replaces (and consolidates) these per-repo copies:
|
|
35
|
-
* - DefiLlama-Adapters projects/helper/utils/starknet.js (codec: selectors, addresses, encodeCalldata, decodeOutput)
|
|
36
|
-
* - DefiLlama-Adapters projects/helper/chain/starknet.js (call, multiCall via aggregator / batched, rpc, getBlockNumber, getLogs)
|
|
37
|
-
* - DefiLlama-Adapters projects/helper/env.js (STARKNET_RPC / STARKNET_MULTICALL defaults)
|
|
38
|
-
* - coins src/adapters/utils/starknet.ts (feltArrToStr, cairoErc20Abis, aggregate result layout)
|
|
39
|
-
* - defi l2/utils.ts (starknetU256ToNumber, hardcoded total_supply / aggregate selectors)
|
|
40
|
-
* - peggedassets-server src/adapters/peggedAssets/helper/starknet.js (defaultAbis shape)
|
|
41
|
-
*
|
|
42
|
-
* Only the chain plumbing is ported: no TVL helpers (sumTokens / dexExport live
|
|
43
|
-
* with their callers) and no on-disk log cache.
|
|
44
|
-
*
|
|
45
|
-
* Decoding mirrors starknet.js v5 (CallData.parse) so existing ABIs keep working:
|
|
46
|
-
* - scalars (felt, felt252, uN, ContractAddress, ...) decode to BigInt
|
|
47
|
-
* - core::bool decodes to boolean
|
|
48
|
-
* - core::integer::u256 decodes to a single BigInt (low + high << 128)
|
|
49
|
-
* - bare Cairo 0 "Uint256" WITHOUT a struct definition decodes as ONE felt
|
|
50
|
-
* (that is what starknet.js did, and adapters such as jediswap reordered
|
|
51
|
-
* their ABIs around it); with a struct definition it decodes to { low, high }
|
|
52
|
-
* - structs decode to objects keyed by member name, tuples to objects keyed
|
|
53
|
-
* by index (or member name for Cairo 0 named tuples), arrays to arrays
|
|
54
|
-
* - a function with a single unnamed output returns the value directly,
|
|
55
|
-
* otherwise an object keyed by output name
|
|
56
|
-
*
|
|
57
|
-
* Env: `STARKNET_RPC` (comma separated list, rotated on failure) overrides
|
|
58
|
-
* `DEFAULT_ENDPOINTS`; `STARKNET_MULTICALL` overrides the aggregator address;
|
|
59
|
-
* `STARKNET_RPC_CONCURRENCY` overrides the (default 1) request concurrency.
|
|
60
|
-
*/
|
|
61
|
-
const ethers_1 = require("ethers");
|
|
62
|
-
const env_1 = require("../util/env");
|
|
63
|
-
const debugLog_1 = require("../util/debugLog");
|
|
64
|
-
const rpc_1 = require("./rpc");
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
|
-
// config
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
exports.CHAIN = 'starknet';
|
|
69
|
-
exports.DEFAULT_ENDPOINTS = [
|
|
70
|
-
'https://api.zan.top/public/starknet-mainnet',
|
|
71
|
-
'https://starknet-mainnet.public.blastapi.io',
|
|
72
|
-
];
|
|
73
|
-
// On-chain Multicall aggregator. Collapses N starknet_call executions into a
|
|
74
|
-
// single one (the node executes all sub-calls in one call frame), which is the
|
|
75
|
-
// real cost win when the RPC bills per sub-call rather than per HTTP request.
|
|
76
|
-
// aggregate(calls: Array<Call>) -> (block_number: u64, results: Array<Span<felt252>>)
|
|
77
|
-
// NOTE: aggregate reverts atomically: if any sub-call reverts, the whole call
|
|
78
|
-
// reverts, so multiCall falls back to per-call batching to preserve permitFailure.
|
|
79
|
-
exports.DEFAULT_MULTICALL_ADDRESS = '0x01a33330996310a1e3fa1df5b16c1e07f0491fdd20c441126e02613b948f0225';
|
|
80
|
-
exports.AGGREGATE_CHUNK_SIZE = 50;
|
|
81
|
-
exports.BATCH_CHUNK_SIZE = 25;
|
|
82
|
-
const CHUNK_SLEEP_MS = 200;
|
|
83
|
-
function getEndpoints() {
|
|
84
|
-
return (0, rpc_1.getEndpoints)(exports.CHAIN, exports.DEFAULT_ENDPOINTS);
|
|
85
|
-
}
|
|
86
|
-
function getMulticallAddress() {
|
|
87
|
-
return (0, env_1.getEnvValue)('STARKNET_MULTICALL', exports.DEFAULT_MULTICALL_ADDRESS);
|
|
88
|
-
}
|
|
89
|
-
const rpcOptions = () => ({ chain: exports.CHAIN, defaultEndpoints: exports.DEFAULT_ENDPOINTS });
|
|
90
|
-
// Public Starknet RPCs are stingy, so everything funnels through one limiter
|
|
91
|
-
// (override with STARKNET_RPC_CONCURRENCY).
|
|
92
|
-
const limiter = () => (0, rpc_1.getLimiter)('STARKNET', 1);
|
|
93
|
-
// ---------------------------------------------------------------------------
|
|
94
|
-
// numbers / addresses / selectors
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
const ZERO = BigInt(0);
|
|
97
|
-
const ONE = BigInt(1);
|
|
98
|
-
const SHIFT_128 = BigInt(128);
|
|
99
|
-
const MASK_250 = (ONE << BigInt(250)) - ONE;
|
|
100
|
-
const MASK_128 = (ONE << SHIFT_128) - ONE;
|
|
101
|
-
exports.ADDR_BOUND = (ONE << BigInt(251)) - BigInt(256);
|
|
102
|
-
exports.FIELD_PRIME = (ONE << BigInt(251)) + BigInt(17) * (ONE << BigInt(192)) + ONE;
|
|
103
|
-
function toBigInt(value) {
|
|
104
|
-
if (typeof value === 'bigint')
|
|
105
|
-
return value;
|
|
106
|
-
if (typeof value === 'number')
|
|
107
|
-
return BigInt(value);
|
|
108
|
-
if (typeof value === 'string')
|
|
109
|
-
return BigInt(value.trim());
|
|
110
|
-
if (typeof value === 'boolean')
|
|
111
|
-
return value ? ONE : ZERO;
|
|
112
|
-
throw new Error(`starknet: cannot convert ${value} to BigInt`);
|
|
113
|
-
}
|
|
114
|
-
function toHex(value) {
|
|
115
|
-
return '0x' + toBigInt(value).toString(16);
|
|
116
|
-
}
|
|
117
|
-
function addAddressPadding(address) {
|
|
118
|
-
return '0x' + toBigInt(address).toString(16).padStart(64, '0');
|
|
119
|
-
}
|
|
120
|
-
function validateAndParseAddress(address) {
|
|
121
|
-
let n;
|
|
122
|
-
try {
|
|
123
|
-
n = toBigInt(address);
|
|
124
|
-
}
|
|
125
|
-
catch (e) {
|
|
126
|
-
throw new Error(`Starknet Address is not a number: ${address}`);
|
|
127
|
-
}
|
|
128
|
-
if (n < ZERO || n >= exports.ADDR_BOUND)
|
|
129
|
-
throw new Error(`Starknet Address out of range: ${address}`);
|
|
130
|
-
return addAddressPadding(n);
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Loose check for a Starknet address: 0x-prefixed hex, non-zero, inside the
|
|
134
|
-
* address bound, and not shaped like an EVM address (exactly 40 hex chars).
|
|
135
|
-
*/
|
|
136
|
-
function isStarknetAddress(value) {
|
|
137
|
-
if (typeof value !== 'string')
|
|
138
|
-
return false;
|
|
139
|
-
const v = value.trim();
|
|
140
|
-
if (!/^0x[0-9a-fA-F]{1,64}$/.test(v))
|
|
141
|
-
return false;
|
|
142
|
-
if (v.length === 42)
|
|
143
|
-
return false;
|
|
144
|
-
const n = BigInt(v);
|
|
145
|
-
return n > ZERO && n < exports.ADDR_BOUND;
|
|
146
|
-
}
|
|
147
|
-
/** starknet_keccak: keccak256 of the utf8 name, masked to 250 bits */
|
|
148
|
-
function starknetKeccak(str) {
|
|
149
|
-
return BigInt((0, ethers_1.keccak256)((0, ethers_1.toUtf8Bytes)(str))) & MASK_250;
|
|
150
|
-
}
|
|
151
|
-
function getSelectorFromName(name) {
|
|
152
|
-
return toHex(starknetKeccak(name));
|
|
153
|
-
}
|
|
154
|
-
/** u256 as { low, high } object or [low, high] felt pair -> bigint */
|
|
155
|
-
function parseUint256(value) {
|
|
156
|
-
if (Array.isArray(value)) {
|
|
157
|
-
if (value.length < 2)
|
|
158
|
-
throw new Error('starknet: u256 needs [low, high]');
|
|
159
|
-
return toBigInt(value[0]) + (toBigInt(value[1]) << SHIFT_128);
|
|
160
|
-
}
|
|
161
|
-
if (value !== null && typeof value === 'object')
|
|
162
|
-
return toBigInt(value.low) + (toBigInt(value.high) << SHIFT_128);
|
|
163
|
-
return toBigInt(value);
|
|
164
|
-
}
|
|
165
|
-
/** Cairo short string (<= 31 utf8 bytes) -> hex felt */
|
|
166
|
-
function shortStringToFelt(str) {
|
|
167
|
-
const bytes = Buffer.from(str, 'utf8');
|
|
168
|
-
if (bytes.length > 31)
|
|
169
|
-
throw new Error(`starknet: short string too long: ${str}`);
|
|
170
|
-
return '0x' + (bytes.toString('hex') || '0');
|
|
171
|
-
}
|
|
172
|
-
/** hex/decimal/bigint felt -> utf8 short string (leading zero bytes dropped) */
|
|
173
|
-
function feltToShortString(felt) {
|
|
174
|
-
const n = toBigInt(felt);
|
|
175
|
-
if (n === ZERO)
|
|
176
|
-
return '';
|
|
177
|
-
let hex = n.toString(16);
|
|
178
|
-
if (hex.length % 2)
|
|
179
|
-
hex = '0' + hex;
|
|
180
|
-
return Buffer.from(hex, 'hex').toString('utf8');
|
|
181
|
-
}
|
|
182
|
-
/** concatenate an array of short-string felts (e.g. a felt* name) */
|
|
183
|
-
function feltArrToStr(felts) {
|
|
184
|
-
return felts.reduce((memo, felt) => memo + feltToShortString(felt), '');
|
|
185
|
-
}
|
|
186
|
-
exports.number = {
|
|
187
|
-
toHex,
|
|
188
|
-
toBigInt,
|
|
189
|
-
toBN: toBigInt,
|
|
190
|
-
hexToDecimalString: (v) => toBigInt(v).toString(),
|
|
191
|
-
};
|
|
192
|
-
const isLen = (name) => /_len$/.test(name !== null && name !== void 0 ? name : '');
|
|
193
|
-
const isCairo1Type = (type) => type.includes('::');
|
|
194
|
-
const isTypeArray = (type) => /\*/.test(type) || type.startsWith('core::array::Array::') || type.startsWith('core::array::Span::');
|
|
195
|
-
const isTypeTuple = (type) => /^\(.*\)$/.test(type);
|
|
196
|
-
const isTypeU256 = (type) => type === 'core::integer::u256';
|
|
197
|
-
const isTypeLegacyU256 = (type) => type === 'Uint256';
|
|
198
|
-
const isTypeBool = (type) => type === 'core::bool';
|
|
199
|
-
const isTypeOption = (type) => type.startsWith('core::option::Option::');
|
|
200
|
-
const isTypeResult = (type) => type.startsWith('core::result::Result::');
|
|
201
|
-
function getArrayType(type) {
|
|
202
|
-
if (isCairo1Type(type))
|
|
203
|
-
return type.substring(type.indexOf('<') + 1, type.lastIndexOf('>'));
|
|
204
|
-
return type.replace('*', '');
|
|
205
|
-
}
|
|
206
|
-
// "(a, (b, c), core::array::Array::<x>)" -> ["a", "(b, c)", "core::array::Array::<x>"]
|
|
207
|
-
// Cairo 0 named tuples "(x: felt, y: felt)" -> [{ name: 'x', type: 'felt' }, ...]
|
|
208
|
-
function extractTupleMemberTypes(type) {
|
|
209
|
-
const inner = type.replace(/\s/g, '').slice(1, -1);
|
|
210
|
-
const members = [];
|
|
211
|
-
let depth = 0, current = '';
|
|
212
|
-
for (const ch of inner) {
|
|
213
|
-
if (ch === '(' || ch === '<')
|
|
214
|
-
depth++;
|
|
215
|
-
else if (ch === ')' || ch === '>')
|
|
216
|
-
depth--;
|
|
217
|
-
if (ch === ',' && depth === 0) {
|
|
218
|
-
members.push(current);
|
|
219
|
-
current = '';
|
|
220
|
-
}
|
|
221
|
-
else
|
|
222
|
-
current += ch;
|
|
223
|
-
}
|
|
224
|
-
if (current)
|
|
225
|
-
members.push(current);
|
|
226
|
-
return members.map((m) => {
|
|
227
|
-
if (isCairo1Type(m) || !m.includes(':'))
|
|
228
|
-
return m;
|
|
229
|
-
const idx = m.indexOf(':');
|
|
230
|
-
return { name: m.slice(0, idx), type: m.slice(idx + 1) };
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
const memberType = (m) => typeof m === 'string' ? m : m.type;
|
|
234
|
-
const memberName = (m, i) => typeof m === 'string' ? i : m.name;
|
|
235
|
-
function getAbiStructs(abi) {
|
|
236
|
-
const structs = {};
|
|
237
|
-
abi.forEach((entry) => { if (entry && entry.type === 'struct')
|
|
238
|
-
structs[entry.name] = entry; });
|
|
239
|
-
return structs;
|
|
240
|
-
}
|
|
241
|
-
function getAbiEnums(abi) {
|
|
242
|
-
const enums = {};
|
|
243
|
-
abi.forEach((entry) => { if (entry && entry.type === 'enum')
|
|
244
|
-
enums[entry.name] = entry; });
|
|
245
|
-
delete enums['core::bool'];
|
|
246
|
-
return enums;
|
|
247
|
-
}
|
|
248
|
-
// ---------------------------------------------------------------------------
|
|
249
|
-
// calldata encoding
|
|
250
|
-
// ---------------------------------------------------------------------------
|
|
251
|
-
const isHexString = (v) => /^0x[0-9a-fA-F]+$/.test(v);
|
|
252
|
-
const isDecimalString = (v) => /^\d+$/.test(v);
|
|
253
|
-
/** encode a single felt (returns hex string) */
|
|
254
|
-
function felt(value) {
|
|
255
|
-
if (typeof value === 'bigint' || typeof value === 'number' || typeof value === 'boolean')
|
|
256
|
-
return toHex(value);
|
|
257
|
-
if (typeof value === 'string') {
|
|
258
|
-
const v = value.trim();
|
|
259
|
-
if (isHexString(v) || isDecimalString(v))
|
|
260
|
-
return toHex(v);
|
|
261
|
-
return shortStringToFelt(v);
|
|
262
|
-
}
|
|
263
|
-
throw new Error(`starknet: cannot encode ${JSON.stringify(value)} as felt`);
|
|
264
|
-
}
|
|
265
|
-
function encodeU256(value) {
|
|
266
|
-
if (value !== null && typeof value === 'object' && !Array.isArray(value))
|
|
267
|
-
return [felt(value.low), felt(value.high)];
|
|
268
|
-
if (Array.isArray(value))
|
|
269
|
-
return [felt(value[0]), felt(value[1])];
|
|
270
|
-
const n = toBigInt(value);
|
|
271
|
-
return [toHex(n & MASK_128), toHex(n >> SHIFT_128)];
|
|
272
|
-
}
|
|
273
|
-
function encodeValue(value, type, structs, enums) {
|
|
274
|
-
var _a;
|
|
275
|
-
if (value === undefined)
|
|
276
|
-
throw new Error(`starknet: missing parameter for type ${type}`);
|
|
277
|
-
if (isTypeU256(type) || isTypeLegacyU256(type))
|
|
278
|
-
return encodeU256(value);
|
|
279
|
-
if (Array.isArray(value)) {
|
|
280
|
-
const itemType = getArrayType(type);
|
|
281
|
-
const out = [felt(value.length)];
|
|
282
|
-
value.forEach((v) => out.push(...encodeValue(v, itemType, structs, enums)));
|
|
283
|
-
return out;
|
|
284
|
-
}
|
|
285
|
-
if (structs[type] && ((_a = structs[type].members) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
286
|
-
const out = [];
|
|
287
|
-
structs[type].members.forEach((m) => out.push(...encodeValue(value[m.name], m.type, structs, enums)));
|
|
288
|
-
return out;
|
|
289
|
-
}
|
|
290
|
-
if (isTypeTuple(type)) {
|
|
291
|
-
const memberTypes = extractTupleMemberTypes(type);
|
|
292
|
-
const elements = Object.values(value);
|
|
293
|
-
if (elements.length !== memberTypes.length)
|
|
294
|
-
throw new Error(`starknet: tuple size mismatch for ${type}`);
|
|
295
|
-
const out = [];
|
|
296
|
-
memberTypes.forEach((m, i) => out.push(...encodeValue(elements[i], memberType(m), structs, enums)));
|
|
297
|
-
return out;
|
|
298
|
-
}
|
|
299
|
-
if (enums[type])
|
|
300
|
-
throw new Error(`starknet: enum inputs are not supported (${type})`);
|
|
301
|
-
if (typeof value === 'object')
|
|
302
|
-
throw new Error(`starknet: parameter ${JSON.stringify(value)} does not match abi type ${type}`);
|
|
303
|
-
return [felt(value)];
|
|
304
|
-
}
|
|
305
|
-
function asFnAbi(abiOrParams, key) {
|
|
306
|
-
if (Array.isArray(abiOrParams))
|
|
307
|
-
return { name: '', type: 'function', [key]: abiOrParams };
|
|
308
|
-
return abiOrParams;
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* Encode function arguments into calldata (array of hex felts).
|
|
312
|
-
* @param fnAbi function abi entry (or just its `inputs` array)
|
|
313
|
-
* @param params positional arguments
|
|
314
|
-
* @param allAbi struct/enum definitions referenced by the function
|
|
315
|
-
*/
|
|
316
|
-
function encodeCalldata(fnAbi, params = [], allAbi = []) {
|
|
317
|
-
var _a;
|
|
318
|
-
const abi = asFnAbi(fnAbi, 'inputs');
|
|
319
|
-
if (!Array.isArray(params))
|
|
320
|
-
params = [params];
|
|
321
|
-
const structs = getAbiStructs([abi, ...allAbi]);
|
|
322
|
-
const enums = getAbiEnums([abi, ...allAbi]);
|
|
323
|
-
const inputs = (_a = abi.inputs) !== null && _a !== void 0 ? _a : [];
|
|
324
|
-
const calldata = [];
|
|
325
|
-
let idx = 0;
|
|
326
|
-
for (const input of inputs) {
|
|
327
|
-
// Cairo 0: `foo_len` inputs are derived from the array that follows
|
|
328
|
-
if (isLen(input.name) && !isCairo1Type(input.type))
|
|
329
|
-
continue;
|
|
330
|
-
calldata.push(...encodeValue(params[idx++], input.type, structs, enums));
|
|
331
|
-
}
|
|
332
|
-
return calldata;
|
|
333
|
-
}
|
|
334
|
-
function next(it) {
|
|
335
|
-
const { value, done } = it.next();
|
|
336
|
-
if (done)
|
|
337
|
-
throw new Error('starknet: response too short for abi');
|
|
338
|
-
return value;
|
|
339
|
-
}
|
|
340
|
-
function decodeBase(type, it) {
|
|
341
|
-
if (isTypeBool(type))
|
|
342
|
-
return Boolean(BigInt(next(it)));
|
|
343
|
-
if (isTypeU256(type)) {
|
|
344
|
-
const low = BigInt(next(it));
|
|
345
|
-
const high = BigInt(next(it));
|
|
346
|
-
return (high << SHIFT_128) + low;
|
|
347
|
-
}
|
|
348
|
-
return BigInt(next(it));
|
|
349
|
-
}
|
|
350
|
-
function decodeValue(it, type, structs, enums) {
|
|
351
|
-
var _a, _b;
|
|
352
|
-
if (type === '()')
|
|
353
|
-
return {};
|
|
354
|
-
if (isTypeU256(type))
|
|
355
|
-
return decodeBase(type, it);
|
|
356
|
-
if (isTypeArray(type)) {
|
|
357
|
-
const itemType = getArrayType(type);
|
|
358
|
-
const len = BigInt(next(it));
|
|
359
|
-
const out = [];
|
|
360
|
-
while (out.length < len)
|
|
361
|
-
out.push(decodeValue(it, itemType, structs, enums));
|
|
362
|
-
return out;
|
|
363
|
-
}
|
|
364
|
-
if (structs[type]) {
|
|
365
|
-
const out = {};
|
|
366
|
-
((_a = structs[type].members) !== null && _a !== void 0 ? _a : []).forEach((m) => { out[m.name] = decodeValue(it, m.type, structs, enums); });
|
|
367
|
-
return out;
|
|
368
|
-
}
|
|
369
|
-
if (enums[type]) {
|
|
370
|
-
const variantNum = Number(BigInt(next(it)));
|
|
371
|
-
const variant = ((_b = enums[type].variants) !== null && _b !== void 0 ? _b : [])[variantNum];
|
|
372
|
-
if (!variant)
|
|
373
|
-
throw new Error(`starknet: unknown variant ${variantNum} for ${type}`);
|
|
374
|
-
const content = decodeValue(it, variant.type, structs, enums);
|
|
375
|
-
if (isTypeOption(type))
|
|
376
|
-
return variantNum === 0 ? content : undefined;
|
|
377
|
-
if (isTypeResult(type))
|
|
378
|
-
return variantNum === 0 ? { Ok: content } : { Err: content };
|
|
379
|
-
return { [variant.name]: content };
|
|
380
|
-
}
|
|
381
|
-
if (isTypeTuple(type)) {
|
|
382
|
-
const out = {};
|
|
383
|
-
extractTupleMemberTypes(type).forEach((m, i) => {
|
|
384
|
-
out[memberName(m, i)] = decodeValue(it, memberType(m), structs, enums);
|
|
385
|
-
});
|
|
386
|
-
return out;
|
|
387
|
-
}
|
|
388
|
-
return decodeBase(type, it);
|
|
389
|
-
}
|
|
390
|
-
function decodeField(it, output, structs, enums, parsed) {
|
|
391
|
-
var _a;
|
|
392
|
-
const { name, type } = output;
|
|
393
|
-
if (isLen(name))
|
|
394
|
-
return BigInt(next(it));
|
|
395
|
-
if (structs[type] || isTypeTuple(type) || enums[type])
|
|
396
|
-
return decodeValue(it, type, structs, enums);
|
|
397
|
-
if (isTypeArray(type)) {
|
|
398
|
-
if (isCairo1Type(type))
|
|
399
|
-
return decodeValue(it, type, structs, enums);
|
|
400
|
-
// Cairo 0 `foo: felt*` is preceded by a `foo_len: felt` output
|
|
401
|
-
const out = [];
|
|
402
|
-
const len = (_a = parsed[`${name}_len`]) !== null && _a !== void 0 ? _a : ZERO;
|
|
403
|
-
const itemType = type.replace('*', '');
|
|
404
|
-
while (out.length < len)
|
|
405
|
-
out.push(decodeValue(it, itemType, structs, enums));
|
|
406
|
-
return out;
|
|
407
|
-
}
|
|
408
|
-
return decodeBase(type, it);
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Decode a starknet_call result according to the function abi.
|
|
412
|
-
* @param fnAbi function abi entry (or just its `outputs` array)
|
|
413
|
-
* @param result array of felts returned by the node
|
|
414
|
-
* @param allAbi struct/enum definitions referenced by the function
|
|
415
|
-
*/
|
|
416
|
-
function decodeOutput(fnAbi, result, allAbi = []) {
|
|
417
|
-
var _a;
|
|
418
|
-
const abi = asFnAbi(fnAbi, 'outputs');
|
|
419
|
-
const structs = getAbiStructs([abi, ...allAbi]);
|
|
420
|
-
const enums = getAbiEnums([abi, ...allAbi]);
|
|
421
|
-
const it = result.flat()[Symbol.iterator]();
|
|
422
|
-
const parsed = {};
|
|
423
|
-
const outputs = (_a = abi.outputs) !== null && _a !== void 0 ? _a : [];
|
|
424
|
-
outputs.forEach((output, idx) => {
|
|
425
|
-
var _a;
|
|
426
|
-
const key = (_a = output.name) !== null && _a !== void 0 ? _a : idx;
|
|
427
|
-
parsed[key] = decodeField(it, output, structs, enums, parsed);
|
|
428
|
-
if (parsed[key] && parsed[`${key}_len`])
|
|
429
|
-
delete parsed[`${key}_len`];
|
|
430
|
-
});
|
|
431
|
-
return Object.keys(parsed).length === 1 && 0 in parsed ? parsed[0] : parsed;
|
|
432
|
-
}
|
|
433
|
-
// ---------------------------------------------------------------------------
|
|
434
|
-
// call bodies / output post-processing
|
|
435
|
-
// ---------------------------------------------------------------------------
|
|
436
|
-
function toBlockId(block) {
|
|
437
|
-
if (block === undefined || block === null)
|
|
438
|
-
return 'latest';
|
|
439
|
-
if (typeof block === 'number')
|
|
440
|
-
return { block_number: block };
|
|
441
|
-
if (typeof block === 'string') {
|
|
442
|
-
if (['latest', 'pending', 'pre_confirmed', 'l1_accepted'].includes(block))
|
|
443
|
-
return block;
|
|
444
|
-
if (/^0x[0-9a-fA-F]+$/.test(block))
|
|
445
|
-
return { block_hash: block };
|
|
446
|
-
if (/^\d+$/.test(block))
|
|
447
|
-
return { block_number: +block };
|
|
448
|
-
throw new Error(`starknet: invalid block id ${block}`);
|
|
449
|
-
}
|
|
450
|
-
return block;
|
|
451
|
-
}
|
|
452
|
-
/**
|
|
453
|
-
* Build a `starknet_call` JSON-RPC request. `abi.customInput === 'address'`
|
|
454
|
-
* passes params through as raw felts (u256 inputs pre-split by the caller).
|
|
455
|
-
*/
|
|
456
|
-
function formCallBody({ abi, target, params = [], allAbi = [], block }, id = 0) {
|
|
457
|
-
if (!abi)
|
|
458
|
-
throw new Error('starknet: missing abi');
|
|
459
|
-
if (!target)
|
|
460
|
-
throw new Error(`starknet: missing target for ${abi.name}`);
|
|
461
|
-
if ((params || params === 0) && !Array.isArray(params))
|
|
462
|
-
params = [params];
|
|
463
|
-
if (params === undefined || params === null)
|
|
464
|
-
params = [];
|
|
465
|
-
let calldata = abi.customInput === 'address' ? params : encodeCalldata(abi, params, allAbi);
|
|
466
|
-
// The RPC rejects calldata felts without a 0x prefix, normalize everything.
|
|
467
|
-
calldata = calldata.map((i) => toHex(i));
|
|
468
|
-
const requestData = {
|
|
469
|
-
contract_address: String(target).toLowerCase(),
|
|
470
|
-
entry_point_selector: getSelectorFromName(abi.name),
|
|
471
|
-
calldata,
|
|
472
|
-
};
|
|
473
|
-
return { jsonrpc: '2.0', id, method: 'starknet_call', params: [requestData, toBlockId(block)] };
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* Decode a call result and apply the legacy post-processing: BigInts at depth 1
|
|
477
|
-
* become strings, a single non-Cairo 1 output is unwrapped, `Uint256` outputs
|
|
478
|
-
* (or `customType: 'Uint256' | 'number'`) become numbers, `customType: 'address'`
|
|
479
|
-
* is padded.
|
|
480
|
-
*/
|
|
481
|
-
function parseOutput(result, abi, allAbi = [], { permitFailure = false, error } = {}) {
|
|
482
|
-
var _a, _b;
|
|
483
|
-
if (!result) {
|
|
484
|
-
if (permitFailure)
|
|
485
|
-
return null;
|
|
486
|
-
throw new Error(`Starknet call ${(_a = abi === null || abi === void 0 ? void 0 : abi.name) !== null && _a !== void 0 ? _a : ''} failed: ${formatRpcError(error) || 'no result'}`);
|
|
487
|
-
}
|
|
488
|
-
let response = decodeOutput(abi, result, allAbi);
|
|
489
|
-
if (typeof response === 'bigint')
|
|
490
|
-
response = response.toString();
|
|
491
|
-
else if (response && typeof response === 'object') {
|
|
492
|
-
for (const key in response) {
|
|
493
|
-
if (typeof response[key] === 'bigint')
|
|
494
|
-
response[key] = response[key].toString();
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
const outputs = (_b = abi.outputs) !== null && _b !== void 0 ? _b : [];
|
|
498
|
-
if (outputs.length === 1 && !outputs[0].type.includes('::')) {
|
|
499
|
-
const name = outputs[0].name;
|
|
500
|
-
if (name !== undefined && response && typeof response === 'object')
|
|
501
|
-
response = response[name];
|
|
502
|
-
if (outputs[0].type === 'Uint256')
|
|
503
|
-
return +response;
|
|
504
|
-
}
|
|
505
|
-
if (outputs.length === 1) {
|
|
506
|
-
switch (abi.customType) {
|
|
507
|
-
case 'address': return validateAndParseAddress(response);
|
|
508
|
-
case 'Uint256':
|
|
509
|
-
case 'number': return +response;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
return response;
|
|
513
|
-
}
|
|
514
|
-
function formatRpcError(e) {
|
|
515
|
-
var _a, _b, _c, _d, _e, _f;
|
|
516
|
-
if (!e)
|
|
517
|
-
return '';
|
|
518
|
-
const data = (_a = e.data) !== null && _a !== void 0 ? _a : (_b = e.error) === null || _b === void 0 ? void 0 : _b.data;
|
|
519
|
-
const message = (_c = e.message) !== null && _c !== void 0 ? _c : (_d = e.error) === null || _d === void 0 ? void 0 : _d.message;
|
|
520
|
-
const revert = (_f = (_e = data === null || data === void 0 ? void 0 : data.revert_error) !== null && _e !== void 0 ? _e : data === null || data === void 0 ? void 0 : data.execution_error) !== null && _f !== void 0 ? _f : (typeof data === 'string' ? data : undefined);
|
|
521
|
-
if (revert) {
|
|
522
|
-
const text = typeof revert === 'string' ? revert : JSON.stringify(revert);
|
|
523
|
-
return message ? `${message}: ${text}` : text;
|
|
524
|
-
}
|
|
525
|
-
return message ? String(message) : JSON.stringify(e);
|
|
526
|
-
}
|
|
527
|
-
// ---------------------------------------------------------------------------
|
|
528
|
-
// json-rpc
|
|
529
|
-
// ---------------------------------------------------------------------------
|
|
530
|
-
/** Raw JSON-RPC call against the Starknet endpoints (STARKNET_RPC rotation + retries) */
|
|
531
|
-
async function rpc(method, params = []) {
|
|
532
|
-
try {
|
|
533
|
-
return await (0, rpc_1.jsonRpc)(method, params, rpcOptions());
|
|
534
|
-
}
|
|
535
|
-
catch (e) {
|
|
536
|
-
if (e instanceof rpc_1.JsonRpcError)
|
|
537
|
-
throw new Error(`Starknet ${method} failed: ${formatRpcError(e)}`);
|
|
538
|
-
throw e;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
async function getBlockNumber() {
|
|
542
|
-
return +(await rpc('starknet_blockNumber'));
|
|
543
|
-
}
|
|
544
|
-
async function getBlock({ blockNumber, block } = {}) {
|
|
545
|
-
const id = toBlockId(block !== null && block !== void 0 ? block : blockNumber);
|
|
546
|
-
const res = await rpc('starknet_getBlockWithTxHashes', [id]);
|
|
547
|
-
return { number: +res.block_number, timestamp: +res.timestamp, hash: res.block_hash };
|
|
548
|
-
}
|
|
549
|
-
/** Last block whose timestamp is <= `timestamp` (binary search, no cache) */
|
|
550
|
-
async function getBlockAtTimestamp({ timestamp }) {
|
|
551
|
-
if (timestamp > 1e12)
|
|
552
|
-
timestamp = Math.floor(timestamp / 1000);
|
|
553
|
-
let hi = await getBlock({ blockNumber: 'latest' });
|
|
554
|
-
if (hi.timestamp <= timestamp)
|
|
555
|
-
return { number: hi.number, timestamp: hi.timestamp };
|
|
556
|
-
let lo = { number: 0, timestamp: 0 };
|
|
557
|
-
while (hi.number - lo.number > 1) {
|
|
558
|
-
const mid = Math.floor((lo.number + hi.number) / 2);
|
|
559
|
-
const b = await getBlock({ blockNumber: mid });
|
|
560
|
-
if (b.timestamp <= timestamp)
|
|
561
|
-
lo = { number: b.number, timestamp: b.timestamp };
|
|
562
|
-
else
|
|
563
|
-
hi = b;
|
|
564
|
-
}
|
|
565
|
-
if (lo.number === 0 && lo.timestamp === 0) {
|
|
566
|
-
const b = await getBlock({ blockNumber: 0 });
|
|
567
|
-
if (b.timestamp > timestamp)
|
|
568
|
-
throw new Error(`starknet: timestamp ${timestamp} is before genesis`);
|
|
569
|
-
return { number: b.number, timestamp: b.timestamp };
|
|
570
|
-
}
|
|
571
|
-
return lo;
|
|
572
|
-
}
|
|
573
|
-
async function getNonce({ address, block }) {
|
|
574
|
-
return rpc('starknet_getNonce', [toBlockId(block), String(address).toLowerCase()]);
|
|
575
|
-
}
|
|
576
|
-
async function getClassHashAt({ address, block }) {
|
|
577
|
-
return rpc('starknet_getClassHashAt', [toBlockId(block), String(address).toLowerCase()]);
|
|
578
|
-
}
|
|
579
|
-
// ---------------------------------------------------------------------------
|
|
580
|
-
// call / multiCall
|
|
581
|
-
// ---------------------------------------------------------------------------
|
|
582
|
-
async function rawCall(request, block, permitFailure, abiName) {
|
|
583
|
-
try {
|
|
584
|
-
return await (0, rpc_1.jsonRpc)('starknet_call', [request, toBlockId(block)], rpcOptions());
|
|
585
|
-
}
|
|
586
|
-
catch (e) {
|
|
587
|
-
if (permitFailure) {
|
|
588
|
-
(0, debugLog_1.debugLog)(`[starknet] call ${abiName} on ${request.contract_address} failed (permitted): ${formatRpcError(e)}`);
|
|
589
|
-
return null;
|
|
590
|
-
}
|
|
591
|
-
throw new Error(`Starknet call ${abiName} on ${request.contract_address} failed: ${formatRpcError(e)}`);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
async function call({ abi, target, params = [], allAbi = [], permitFailure = false, block }) {
|
|
595
|
-
const body = formCallBody({ abi, target, params, allAbi, block });
|
|
596
|
-
const result = await limiter()(() => rawCall(body.params[0], block, permitFailure, abi.name));
|
|
597
|
-
return parseOutput(result, abi, allAbi, { permitFailure });
|
|
598
|
-
}
|
|
599
|
-
function normalizeCalls({ abi: rootAbi, target: rootTarget, calls, allAbi = [] }) {
|
|
600
|
-
return calls.map((callArgs, i) => {
|
|
601
|
-
var _a;
|
|
602
|
-
let item;
|
|
603
|
-
if (callArgs === null || typeof callArgs !== 'object') {
|
|
604
|
-
if (!rootTarget)
|
|
605
|
-
item = { target: callArgs, params: [], abi: rootAbi, allAbi };
|
|
606
|
-
else
|
|
607
|
-
item = { target: rootTarget, params: callArgs, abi: rootAbi, allAbi };
|
|
608
|
-
}
|
|
609
|
-
else {
|
|
610
|
-
const { target, params, abi } = callArgs;
|
|
611
|
-
item = { target: (target || rootTarget), params, abi: (abi || rootAbi), allAbi: (_a = callArgs.allAbi) !== null && _a !== void 0 ? _a : allAbi };
|
|
612
|
-
}
|
|
613
|
-
if (!item.abi)
|
|
614
|
-
throw new Error(`starknet: multiCall item ${i} has no abi`);
|
|
615
|
-
if (!item.target)
|
|
616
|
-
throw new Error(`starknet: multiCall item ${i} has no target`);
|
|
617
|
-
return item;
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
/**
|
|
621
|
-
* Run many `starknet_call`s. By default one on-chain `aggregate` call per 50
|
|
622
|
-
* sub-calls; if the aggregator reverts (any sub-call reverting reverts the whole
|
|
623
|
-
* batch) or `useAggregator` is false, falls back to JSON-RPC batches of 25 so
|
|
624
|
-
* `permitFailure` still applies per call. Results are in call order.
|
|
625
|
-
*/
|
|
626
|
-
async function multiCall(options) {
|
|
627
|
-
const { calls = [], permitFailure = false, useAggregator = true, block } = options;
|
|
628
|
-
if (!calls.length)
|
|
629
|
-
return [];
|
|
630
|
-
const normalized = normalizeCalls(options);
|
|
631
|
-
return limiter()(async () => {
|
|
632
|
-
if (useAggregator) {
|
|
633
|
-
try {
|
|
634
|
-
return await aggregateMultiCall(normalized, block);
|
|
635
|
-
}
|
|
636
|
-
catch (e) {
|
|
637
|
-
(0, debugLog_1.debugLog)(`[starknet] aggregate multicall failed, falling back to batched calls: ${formatRpcError(e)}`);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
return batchedMultiCall(normalized, permitFailure, block);
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
/** One starknet_call to the on-chain aggregator covering all sub-calls. */
|
|
644
|
-
async function aggregateMultiCall(calls, block) {
|
|
645
|
-
const aggregator = getMulticallAddress();
|
|
646
|
-
const aggSelector = getSelectorFromName('aggregate');
|
|
647
|
-
const response = [];
|
|
648
|
-
const chunks = (0, rpc_1.sliceIntoChunks)(calls, exports.AGGREGATE_CHUNK_SIZE);
|
|
649
|
-
let offset = 0;
|
|
650
|
-
for (let c = 0; c < chunks.length; c++) {
|
|
651
|
-
const chunk = chunks[c];
|
|
652
|
-
if (c > 0)
|
|
653
|
-
await (0, rpc_1.sleep)(CHUNK_SLEEP_MS);
|
|
654
|
-
// Build the Call[] calldata: [n, (to, selector, calldata_len, ...calldata) x n]
|
|
655
|
-
const aggCalldata = [toHex(chunk.length)];
|
|
656
|
-
chunk.forEach((item) => {
|
|
657
|
-
const body = formCallBody(item).params[0];
|
|
658
|
-
aggCalldata.push(body.contract_address, body.entry_point_selector, toHex(body.calldata.length), ...body.calldata);
|
|
659
|
-
});
|
|
660
|
-
const request = { contract_address: aggregator, entry_point_selector: aggSelector, calldata: aggCalldata };
|
|
661
|
-
const result = await (0, rpc_1.jsonRpc)('starknet_call', [request, toBlockId(block)], rpcOptions());
|
|
662
|
-
if (!Array.isArray(result))
|
|
663
|
-
throw new Error('aggregate failed: empty result');
|
|
664
|
-
// result layout: [block_number, results_len, (span_len, ...span_felts) x results_len]
|
|
665
|
-
let i = 1; // skip block_number
|
|
666
|
-
const resultsLen = Number(toBigInt(result[i++]));
|
|
667
|
-
if (resultsLen !== chunk.length)
|
|
668
|
-
throw new Error(`aggregate returned ${resultsLen} results for ${chunk.length} calls`);
|
|
669
|
-
for (let j = 0; j < resultsLen; j++) {
|
|
670
|
-
const spanLen = Number(toBigInt(result[i++]));
|
|
671
|
-
const span = result.slice(i, i + spanLen);
|
|
672
|
-
i += spanLen;
|
|
673
|
-
const item = chunk[j];
|
|
674
|
-
response[offset + j] = parseOutput(span, item.abi, item.allAbi);
|
|
675
|
-
}
|
|
676
|
-
offset += chunk.length;
|
|
677
|
-
}
|
|
678
|
-
return response;
|
|
679
|
-
}
|
|
680
|
-
/** N individual starknet_call requests packed into JSON-RPC batches. */
|
|
681
|
-
async function batchedMultiCall(calls, permitFailure, block) {
|
|
682
|
-
const bodies = calls.map((item, id) => formCallBody({ ...item, block }, id));
|
|
683
|
-
const response = new Array(calls.length);
|
|
684
|
-
const chunks = (0, rpc_1.sliceIntoChunks)(bodies, exports.BATCH_CHUNK_SIZE);
|
|
685
|
-
for (let c = 0; c < chunks.length; c++) {
|
|
686
|
-
const chunk = chunks[c];
|
|
687
|
-
if (c > 0)
|
|
688
|
-
await (0, rpc_1.sleep)(CHUNK_SLEEP_MS);
|
|
689
|
-
let results;
|
|
690
|
-
if (chunk.length === 1) {
|
|
691
|
-
// some providers (e.g. lava.build) answer a one-element batch with a bare
|
|
692
|
-
// object; use a plain call for that case
|
|
693
|
-
const body = chunk[0];
|
|
694
|
-
results = [await rawCall(body.params[0], block, permitFailure, calls[body.id].abi.name)];
|
|
695
|
-
}
|
|
696
|
-
else {
|
|
697
|
-
try {
|
|
698
|
-
results = await (0, rpc_1.jsonRpcBatch)(chunk.map((b) => ({ method: b.method, params: b.params, id: b.id })), { ...rpcOptions(), permitFailure });
|
|
699
|
-
}
|
|
700
|
-
catch (e) {
|
|
701
|
-
if (e instanceof rpc_1.JsonRpcError)
|
|
702
|
-
throw new Error(`Starknet call ${e.method} failed: ${formatRpcError(e)}`);
|
|
703
|
-
throw e;
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
chunk.forEach((body, i) => {
|
|
707
|
-
const id = body.id;
|
|
708
|
-
const item = calls[id];
|
|
709
|
-
response[id] = parseOutput(results[i], item.abi, item.allAbi, { permitFailure });
|
|
710
|
-
});
|
|
711
|
-
}
|
|
712
|
-
return response;
|
|
713
|
-
}
|
|
714
|
-
// ---------------------------------------------------------------------------
|
|
715
|
-
// events
|
|
716
|
-
// ---------------------------------------------------------------------------
|
|
717
|
-
/** starknet_getEvents page size; public providers reject larger pages ("Requested page size is too big") */
|
|
718
|
-
exports.DEFAULT_EVENTS_CHUNK_SIZE = 1000;
|
|
719
|
-
const MIN_EVENTS_CHUNK_SIZE = 100;
|
|
720
|
-
function isPageSizeError(e) {
|
|
721
|
-
var _a, _b;
|
|
722
|
-
const message = String((_b = (_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e) !== null && _b !== void 0 ? _b : '').toLowerCase();
|
|
723
|
-
return message.includes('page size') || message.includes('chunk_size') || message.includes('chunk size');
|
|
724
|
-
}
|
|
725
|
-
/**
|
|
726
|
-
* Fetch events emitted by `target` via `starknet_getEvents`, following
|
|
727
|
-
* `continuation_token` until the range [fromBlock, toBlock] is exhausted.
|
|
728
|
-
* Returns raw events: { block_number, transaction_hash, from_address, keys, data }.
|
|
729
|
-
*/
|
|
730
|
-
async function getLogs({ target, fromBlock, toBlock, topics, keys, chunkSize = exports.DEFAULT_EVENTS_CHUNK_SIZE }) {
|
|
731
|
-
if (!target)
|
|
732
|
-
throw new Error('Missing target!');
|
|
733
|
-
if (fromBlock === undefined || fromBlock === null)
|
|
734
|
-
throw new Error('Missing fromBlock!');
|
|
735
|
-
if (!keys && topics)
|
|
736
|
-
keys = [topics];
|
|
737
|
-
const keyFilter = (keys !== null && keys !== void 0 ? keys : []).map((k) => (Array.isArray(k) ? k : [k]).map((i) => toHex(i)));
|
|
738
|
-
const address = String(target).toLowerCase();
|
|
739
|
-
return limiter()(async () => {
|
|
740
|
-
var _a;
|
|
741
|
-
const end = toBlock !== null && toBlock !== void 0 ? toBlock : await getBlockNumber();
|
|
742
|
-
if (fromBlock > end)
|
|
743
|
-
return [];
|
|
744
|
-
const filter = {
|
|
745
|
-
from_block: { block_number: fromBlock },
|
|
746
|
-
to_block: { block_number: end },
|
|
747
|
-
address,
|
|
748
|
-
keys: keyFilter,
|
|
749
|
-
chunk_size: chunkSize,
|
|
750
|
-
};
|
|
751
|
-
const logs = [];
|
|
752
|
-
let pages = 0;
|
|
753
|
-
while (true) {
|
|
754
|
-
let page;
|
|
755
|
-
try {
|
|
756
|
-
page = await rpc('starknet_getEvents', [filter]);
|
|
757
|
-
}
|
|
758
|
-
catch (e) {
|
|
759
|
-
// providers cap chunk_size at different values; shrink and retry the same page
|
|
760
|
-
if (isPageSizeError(e) && filter.chunk_size > MIN_EVENTS_CHUNK_SIZE) {
|
|
761
|
-
filter.chunk_size = Math.max(MIN_EVENTS_CHUNK_SIZE, Math.floor(filter.chunk_size / 2));
|
|
762
|
-
(0, debugLog_1.debugLog)(`[starknet] getLogs page size rejected, retrying with chunk_size ${filter.chunk_size}`);
|
|
763
|
-
continue;
|
|
764
|
-
}
|
|
765
|
-
throw e;
|
|
766
|
-
}
|
|
767
|
-
logs.push(...((_a = page === null || page === void 0 ? void 0 : page.events) !== null && _a !== void 0 ? _a : []));
|
|
768
|
-
pages++;
|
|
769
|
-
if (!(page === null || page === void 0 ? void 0 : page.continuation_token))
|
|
770
|
-
break;
|
|
771
|
-
filter.continuation_token = page.continuation_token;
|
|
772
|
-
}
|
|
773
|
-
(0, debugLog_1.debugLog)(`[starknet] getLogs ${address} [${fromBlock}, ${end}]: ${logs.length} events in ${pages} page(s)`);
|
|
774
|
-
return logs;
|
|
775
|
-
});
|
|
776
|
-
}
|
|
777
|
-
// ---------------------------------------------------------------------------
|
|
778
|
-
// ERC20 ABIs
|
|
779
|
-
// ---------------------------------------------------------------------------
|
|
780
|
-
/**
|
|
781
|
-
* Cairo 0 style ERC20 ABIs (camelCase entrypoints, `felt` / `Uint256` types).
|
|
782
|
-
* `balanceOf` uses `customInput: 'address'` so the owner felt is passed through
|
|
783
|
-
* untouched; `decimals` casts to a number.
|
|
784
|
-
*/
|
|
785
|
-
exports.erc20Abis = {
|
|
786
|
-
balanceOf: {
|
|
787
|
-
name: 'balanceOf',
|
|
788
|
-
type: 'function',
|
|
789
|
-
inputs: [{ name: 'account', type: 'felt' }],
|
|
790
|
-
outputs: [{ name: 'balance', type: 'Uint256' }],
|
|
791
|
-
stateMutability: 'view',
|
|
792
|
-
customInput: 'address',
|
|
793
|
-
},
|
|
794
|
-
totalSupply: {
|
|
795
|
-
name: 'totalSupply',
|
|
796
|
-
type: 'function',
|
|
797
|
-
inputs: [],
|
|
798
|
-
outputs: [{ name: 'totalSupply', type: 'Uint256' }],
|
|
799
|
-
stateMutability: 'view',
|
|
800
|
-
},
|
|
801
|
-
decimals: {
|
|
802
|
-
name: 'decimals',
|
|
803
|
-
type: 'function',
|
|
804
|
-
inputs: [],
|
|
805
|
-
outputs: [{ name: 'decimals', type: 'felt' }],
|
|
806
|
-
stateMutability: 'view',
|
|
807
|
-
customType: 'number',
|
|
808
|
-
},
|
|
809
|
-
name: {
|
|
810
|
-
name: 'name',
|
|
811
|
-
type: 'function',
|
|
812
|
-
inputs: [],
|
|
813
|
-
outputs: [{ name: 'name', type: 'felt' }],
|
|
814
|
-
stateMutability: 'view',
|
|
815
|
-
},
|
|
816
|
-
symbol: {
|
|
817
|
-
name: 'symbol',
|
|
818
|
-
type: 'function',
|
|
819
|
-
inputs: [],
|
|
820
|
-
outputs: [{ name: 'symbol', type: 'felt' }],
|
|
821
|
-
stateMutability: 'view',
|
|
822
|
-
},
|
|
823
|
-
allowance: {
|
|
824
|
-
name: 'allowance',
|
|
825
|
-
type: 'function',
|
|
826
|
-
inputs: [{ name: 'owner', type: 'felt' }, { name: 'spender', type: 'felt' }],
|
|
827
|
-
outputs: [{ name: 'remaining', type: 'Uint256' }],
|
|
828
|
-
stateMutability: 'view',
|
|
829
|
-
},
|
|
830
|
-
};
|
|
831
|
-
/**
|
|
832
|
-
* Cairo 1 style ERC20 ABIs (snake_case entrypoints, `core::` types). u256
|
|
833
|
-
* outputs decode to a bigint (returned as a decimal string by `call`).
|
|
834
|
-
*/
|
|
835
|
-
exports.erc20AbisCairo1 = {
|
|
836
|
-
balanceOf: {
|
|
837
|
-
name: 'balance_of',
|
|
838
|
-
type: 'function',
|
|
839
|
-
inputs: [{ name: 'account', type: 'core::starknet::contract_address::ContractAddress' }],
|
|
840
|
-
outputs: [{ type: 'core::integer::u256' }],
|
|
841
|
-
state_mutability: 'view',
|
|
842
|
-
},
|
|
843
|
-
totalSupply: {
|
|
844
|
-
name: 'total_supply',
|
|
845
|
-
type: 'function',
|
|
846
|
-
inputs: [],
|
|
847
|
-
outputs: [{ type: 'core::integer::u256' }],
|
|
848
|
-
state_mutability: 'view',
|
|
849
|
-
},
|
|
850
|
-
decimals: {
|
|
851
|
-
name: 'decimals',
|
|
852
|
-
type: 'function',
|
|
853
|
-
inputs: [],
|
|
854
|
-
outputs: [{ type: 'core::integer::u8' }],
|
|
855
|
-
state_mutability: 'view',
|
|
856
|
-
customType: 'number',
|
|
857
|
-
},
|
|
858
|
-
name: {
|
|
859
|
-
name: 'name',
|
|
860
|
-
type: 'function',
|
|
861
|
-
inputs: [],
|
|
862
|
-
outputs: [{ type: 'core::felt252' }],
|
|
863
|
-
state_mutability: 'view',
|
|
864
|
-
},
|
|
865
|
-
symbol: {
|
|
866
|
-
name: 'symbol',
|
|
867
|
-
type: 'function',
|
|
868
|
-
inputs: [],
|
|
869
|
-
outputs: [{ type: 'core::felt252' }],
|
|
870
|
-
state_mutability: 'view',
|
|
871
|
-
},
|
|
872
|
-
allowance: {
|
|
873
|
-
name: 'allowance',
|
|
874
|
-
type: 'function',
|
|
875
|
-
inputs: [
|
|
876
|
-
{ name: 'owner', type: 'core::starknet::contract_address::ContractAddress' },
|
|
877
|
-
{ name: 'spender', type: 'core::starknet::contract_address::ContractAddress' },
|
|
878
|
-
],
|
|
879
|
-
outputs: [{ type: 'core::integer::u256' }],
|
|
880
|
-
state_mutability: 'view',
|
|
881
|
-
},
|
|
882
|
-
};
|
|
883
|
-
/** alias matching the coins repo export */
|
|
884
|
-
exports.cairoErc20Abis = exports.erc20AbisCairo1;
|