gethstorage-sdk 0.0.0-stage → 5.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +207 -2
- package/dist/browser/index.d.mts +288 -0
- package/dist/browser/index.mjs +1023 -0
- package/dist/node/file.cjs +92 -0
- package/dist/node/file.d.mts +15 -0
- package/dist/node/file.d.ts +15 -0
- package/dist/node/file.mjs +56 -0
- package/dist/node/index.cjs +1069 -0
- package/dist/node/index.d.mts +288 -0
- package/dist/node/index.d.ts +288 -0
- package/dist/node/index.mjs +1023 -0
- package/package.json +65 -6
|
@@ -0,0 +1,1069 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
AVALANCHE_GAS_CHAIN_IDS: () => AVALANCHE_GAS_CHAIN_IDS,
|
|
34
|
+
AVALANCHE_MIN_BASE_FEE_PER_GAS: () => AVALANCHE_MIN_BASE_FEE_PER_GAS,
|
|
35
|
+
BASE_FEE_MULTIPLIER_DENOMINATOR: () => BASE_FEE_MULTIPLIER_DENOMINATOR,
|
|
36
|
+
BASE_FEE_MULTIPLIER_NUMERATOR: () => BASE_FEE_MULTIPLIER_NUMERATOR,
|
|
37
|
+
CALLDATA_CHUNK_OVERHEAD: () => CALLDATA_CHUNK_OVERHEAD,
|
|
38
|
+
CONTRACT_MAX_CHUNK_SIZE: () => CONTRACT_MAX_CHUNK_SIZE,
|
|
39
|
+
DEFAULT_MAX_CHUNK_SIZE_KB: () => DEFAULT_MAX_CHUNK_SIZE_KB,
|
|
40
|
+
DEFAULT_PRIORITY_FEE_PER_GAS: () => DEFAULT_PRIORITY_FEE_PER_GAS,
|
|
41
|
+
ETHSTORAGE_MAPPING: () => ETHSTORAGE_MAPPING,
|
|
42
|
+
FlatDirectory: () => FlatDirectory,
|
|
43
|
+
FlatDirectoryAbi: () => FlatDirectoryAbi,
|
|
44
|
+
FlatDirectoryBytecode: () => FlatDirectoryBytecode,
|
|
45
|
+
LEGACY_GAS_CHAIN_IDS: () => LEGACY_GAS_CHAIN_IDS,
|
|
46
|
+
MAX_CHUNKS: () => MAX_CHUNKS,
|
|
47
|
+
toChunkSizeBytes: () => toChunkSizeBytes,
|
|
48
|
+
utils: () => utils_exports
|
|
49
|
+
});
|
|
50
|
+
module.exports = __toCommonJS(index_exports);
|
|
51
|
+
|
|
52
|
+
// src/utils/index.ts
|
|
53
|
+
var utils_exports = {};
|
|
54
|
+
__export(utils_exports, {
|
|
55
|
+
TxSender: () => TxSender,
|
|
56
|
+
calcTxCost: () => calcTxCost,
|
|
57
|
+
getChainId: () => getChainId,
|
|
58
|
+
getChunkCounts: () => getChunkCounts,
|
|
59
|
+
getChunkHashes: () => getChunkHashes,
|
|
60
|
+
getContentChunk: () => getContentChunk,
|
|
61
|
+
getUploadInfo: () => getUploadInfo,
|
|
62
|
+
isBuffer: () => isBuffer,
|
|
63
|
+
isFile: () => isFile,
|
|
64
|
+
isNodejs: () => isNodejs,
|
|
65
|
+
stableRetry: () => stableRetry,
|
|
66
|
+
stringToHex: () => stringToHex
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// src/utils/util.ts
|
|
70
|
+
var import_ethers = require("ethers");
|
|
71
|
+
var stringToHex = (s) => import_ethers.ethers.hexlify(import_ethers.ethers.toUtf8Bytes(s));
|
|
72
|
+
async function getChainId(rpc) {
|
|
73
|
+
const provider = new import_ethers.ethers.JsonRpcProvider(rpc);
|
|
74
|
+
const network = await provider.getNetwork();
|
|
75
|
+
return Number(network.chainId);
|
|
76
|
+
}
|
|
77
|
+
async function getContentChunk(content, start, end) {
|
|
78
|
+
if (isBuffer(content)) {
|
|
79
|
+
return content.slice(start, Math.min(end, content.length));
|
|
80
|
+
} else {
|
|
81
|
+
const slice = content.slice(start, Math.min(end, content.size));
|
|
82
|
+
const data = await slice.arrayBuffer();
|
|
83
|
+
return new Uint8Array(data);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function isBuffer(content) {
|
|
87
|
+
return content instanceof Uint8Array;
|
|
88
|
+
}
|
|
89
|
+
function isFile(content) {
|
|
90
|
+
if (isNodejs()) {
|
|
91
|
+
return content && typeof content === "object" && typeof content.isNodeJs === "boolean" && content.isNodeJs;
|
|
92
|
+
} else {
|
|
93
|
+
return content instanceof File;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function isNodejs() {
|
|
97
|
+
return typeof process !== "undefined" && !!process.versions && !!process.versions.node;
|
|
98
|
+
}
|
|
99
|
+
function calcTxCost(receipt) {
|
|
100
|
+
if (!receipt) {
|
|
101
|
+
return { normalGasCost: 0n };
|
|
102
|
+
}
|
|
103
|
+
const normalGasCost = receipt.gasUsed * receipt.gasPrice;
|
|
104
|
+
return {
|
|
105
|
+
normalGasCost
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/utils/uploader.ts
|
|
110
|
+
var import_ethers2 = require("ethers");
|
|
111
|
+
|
|
112
|
+
// src/utils/retry.ts
|
|
113
|
+
var FIXED_TOTAL_RETRIES = 5;
|
|
114
|
+
var FIXED_BASE_DELAY = 100;
|
|
115
|
+
var FIXED_MAX_DELAY = 5e3;
|
|
116
|
+
var INTERNAL_FIXED_RETRIES = {
|
|
117
|
+
SOCKET: 5,
|
|
118
|
+
// Connection/Socket errors (high retry chance)
|
|
119
|
+
NETWORK: 3,
|
|
120
|
+
// DNS/Reachability errors
|
|
121
|
+
TIMEOUT: 3,
|
|
122
|
+
// Request timeouts
|
|
123
|
+
RATE_LIMIT: 5,
|
|
124
|
+
// 429 errors (high retry chance after delay)
|
|
125
|
+
SERVER: 2,
|
|
126
|
+
// HTTP 5xx errors
|
|
127
|
+
RPC_SERVER: 2,
|
|
128
|
+
// JSON-RPC internal errors (-32xxx)
|
|
129
|
+
CLIENT: 0,
|
|
130
|
+
// Client errors (4xx) are never retried
|
|
131
|
+
UNKNOWN: 1
|
|
132
|
+
// Unclassified errors (minimal safeguard retry)
|
|
133
|
+
};
|
|
134
|
+
function classifyError(error) {
|
|
135
|
+
const code = error.code || error.cause?.code || "";
|
|
136
|
+
const message = (error.message || error.cause?.message || "").toLowerCase();
|
|
137
|
+
const statusCode = error.statusCode || error.response?.statusCode;
|
|
138
|
+
if (["ECONNRESET", "ECONNREFUSED", "EPIPE", "ECONNABORTED"].includes(String(code))) return "SOCKET";
|
|
139
|
+
if (["ETIMEDOUT", "ENOTFOUND", "EHOSTUNREACH", "ENETUNREACH"].includes(String(code))) return "NETWORK";
|
|
140
|
+
if (message.includes("timeout") || message.includes("timed out")) return "TIMEOUT";
|
|
141
|
+
if (statusCode === 429 || code === 429 || message.includes("rate limit")) return "RATE_LIMIT";
|
|
142
|
+
if ([-32e3, -32603, -32601, -32600].includes(Number(code))) return "RPC_SERVER";
|
|
143
|
+
if (typeof statusCode === "number") {
|
|
144
|
+
if (statusCode >= 500) return "SERVER";
|
|
145
|
+
if (statusCode >= 400) return "CLIENT";
|
|
146
|
+
}
|
|
147
|
+
return "UNKNOWN";
|
|
148
|
+
}
|
|
149
|
+
function isRetryable(error) {
|
|
150
|
+
const type = classifyError(error);
|
|
151
|
+
if (type === "CLIENT") return false;
|
|
152
|
+
const retryableTypes = ["SOCKET", "NETWORK", "TIMEOUT", "RATE_LIMIT", "SERVER", "RPC_SERVER", "UNKNOWN"];
|
|
153
|
+
return retryableTypes.includes(type);
|
|
154
|
+
}
|
|
155
|
+
function computeDelay(attempt) {
|
|
156
|
+
const base = FIXED_BASE_DELAY;
|
|
157
|
+
const max = FIXED_MAX_DELAY;
|
|
158
|
+
if (attempt < 0) return base;
|
|
159
|
+
let delay = base * Math.pow(2, attempt);
|
|
160
|
+
delay = Math.min(delay, max);
|
|
161
|
+
const jitter = delay * 0.2 * (Math.random() * 2 - 1);
|
|
162
|
+
return Math.floor(Math.max(delay + jitter, 0));
|
|
163
|
+
}
|
|
164
|
+
async function stableRetry(fn, ...args) {
|
|
165
|
+
const totalRetries = FIXED_TOTAL_RETRIES;
|
|
166
|
+
const errorTypeRetries = INTERNAL_FIXED_RETRIES;
|
|
167
|
+
const errorTypeCount = {
|
|
168
|
+
SOCKET: 0,
|
|
169
|
+
NETWORK: 0,
|
|
170
|
+
TIMEOUT: 0,
|
|
171
|
+
RATE_LIMIT: 0,
|
|
172
|
+
SERVER: 0,
|
|
173
|
+
RPC_SERVER: 0,
|
|
174
|
+
CLIENT: 0,
|
|
175
|
+
UNKNOWN: 0
|
|
176
|
+
};
|
|
177
|
+
let lastError = {};
|
|
178
|
+
let totalAttempts = 0;
|
|
179
|
+
while (true) {
|
|
180
|
+
try {
|
|
181
|
+
return await fn(...args);
|
|
182
|
+
} catch (err) {
|
|
183
|
+
lastError = err;
|
|
184
|
+
totalAttempts++;
|
|
185
|
+
const type = classifyError(err);
|
|
186
|
+
if (!isRetryable(err)) {
|
|
187
|
+
const wrapErr = new Error(`Non-retryable error: ${err.message} (type: ${type})`);
|
|
188
|
+
wrapErr.cause = err;
|
|
189
|
+
throw wrapErr;
|
|
190
|
+
}
|
|
191
|
+
errorTypeCount[type] += 1;
|
|
192
|
+
const typeMax = errorTypeRetries[type] || 0;
|
|
193
|
+
if (errorTypeCount[type] > typeMax) {
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
if (totalAttempts > totalRetries) {
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
const delay = computeDelay(errorTypeCount[type] - 1);
|
|
200
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const lastErrorType = classifyError(lastError);
|
|
204
|
+
const finalError = new Error(`Retry failed after ${totalAttempts} attempts (last error type: ${lastErrorType}): ${lastError?.message || "Unknown error"}`);
|
|
205
|
+
finalError.cause = lastError;
|
|
206
|
+
throw finalError;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/param/abi.ts
|
|
210
|
+
var FlatDirectoryAbi = [
|
|
211
|
+
"constructor(uint8 slotLimit, uint32 maxChunkSize, address storageAddress) public",
|
|
212
|
+
"function setDefault(bytes memory _defaultFile) external",
|
|
213
|
+
"function writeChunkByCalldata(bytes memory name, uint256 chunkId, bytes calldata data) external payable",
|
|
214
|
+
"function remove(bytes memory name) external returns (uint256)",
|
|
215
|
+
"function truncate(bytes memory name, uint256 chunkId) external returns (uint256)",
|
|
216
|
+
"function readChunk(bytes memory name, uint256 chunkId) external view returns (bytes memory, bool)",
|
|
217
|
+
"function getUploadInfo(bytes memory name) external view returns (uint8 mode, uint256 chunkCount, uint256 storageCost)",
|
|
218
|
+
"function getChunkHashesBatch((bytes,uint256[])[] memory fileChunks) external view returns (bytes32[] memory)",
|
|
219
|
+
"function getChunkCountsBatch(bytes[] memory names) external view returns (uint256[] memory)"
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
// src/param/constant.ts
|
|
223
|
+
var MAINNET_CHAIN_ID = 1;
|
|
224
|
+
var SEPOLIA_CHAIN_ID = 11155111;
|
|
225
|
+
var QUARKCHAIN_L2_TESTNET_CHAIN_ID = 3335;
|
|
226
|
+
var ETHSTORAGE_MAPPING = {
|
|
227
|
+
[MAINNET_CHAIN_ID]: "0xf0193d6E8fc186e77b6E63af4151db07524f6a7A",
|
|
228
|
+
[SEPOLIA_CHAIN_ID]: "0xAb3d380A268d088BA21Eb313c1C23F3BEC5cfe93",
|
|
229
|
+
[QUARKCHAIN_L2_TESTNET_CHAIN_ID]: "0x64003adbdf3014f7E38FC6BE752EB047b95da89A"
|
|
230
|
+
};
|
|
231
|
+
var CONTRACT_MAX_CHUNK_SIZE = (4 * 31 + 3) * 1024 - 4;
|
|
232
|
+
var MAX_CHUNKS = 100;
|
|
233
|
+
var CALLDATA_CHUNK_OVERHEAD = 326;
|
|
234
|
+
var DEFAULT_MAX_CHUNK_SIZE_KB = 64;
|
|
235
|
+
var DEFAULT_PRIORITY_FEE_PER_GAS = 100n;
|
|
236
|
+
var BASE_FEE_MULTIPLIER_NUMERATOR = 11n;
|
|
237
|
+
var BASE_FEE_MULTIPLIER_DENOMINATOR = 10n;
|
|
238
|
+
var LEGACY_GAS_CHAIN_IDS = /* @__PURE__ */ new Set([56, 97]);
|
|
239
|
+
var AVALANCHE_GAS_CHAIN_IDS = /* @__PURE__ */ new Set([43114, 43113]);
|
|
240
|
+
var AVALANCHE_MIN_BASE_FEE_PER_GAS = 25000000000n;
|
|
241
|
+
function toChunkSizeBytes(maxChunkSizeKB) {
|
|
242
|
+
const kb = maxChunkSizeKB && maxChunkSizeKB > 0 ? maxChunkSizeKB : DEFAULT_MAX_CHUNK_SIZE_KB;
|
|
243
|
+
return kb * 1024 - CALLDATA_CHUNK_OVERHEAD;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/param/bytecode.ts
|
|
247
|
+
var FlatDirectoryBytecode = "0x60c06040525f60a0908152600a90620000199082620001cb565b5034801562000026575f80fd5b5060405162003bb238038062003bb2833981016040819052620000499162000293565b60ff8316608052828282818133806200007b57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6200008681620000da565b506004805463ffffffff909316600160a01b0263ffffffff60a01b1990931692909217909155600580546001600160a01b039092166001600160a01b031990921691909117905550620002f9945050505050565b600480546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b634e487b7160e01b5f52604160045260245ffd5b600181811c908216806200015457607f821691505b6020821081036200017357634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115620001c6575f81815260208120601f850160051c81016020861015620001a15750805b601f850160051c820191505b81811015620001c257828155600101620001ad565b5050505b505050565b81516001600160401b03811115620001e757620001e76200012b565b620001ff81620001f884546200013f565b8462000179565b602080601f83116001811462000235575f84156200021d5750858301515b5f19600386901b1c1916600185901b178555620001c2565b5f85815260208120601f198616915b82811015620002655788860151825594840194600190910190840162000244565b50858210156200028357878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b5f805f60608486031215620002a6575f80fd5b835160ff81168114620002b7575f80fd5b602085015190935063ffffffff81168114620002d1575f80fd5b60408501519092506001600160a01b0381168114620002ee575f80fd5b809150509250925092565b608051613899620003195f395f818161057801526124ae01526138995ff3fe60806040526004361062000211575f3560e01c8063761ab10e116200011a578063cf86bf9311620000a2578063f14c7ad7116200006c578063f14c7ad71462000850578063f2fde38b1462000867578063f53264ea146200088b578063f916c5b014620008af5762000211565b8063cf86bf9314620007b0578063d84eb56c14620007eb578063dc38b0a2146200080f578063dd473fae14620008335762000211565b8063956a343311620000e4578063956a343314620006fb578063a741f1cb146200071f578063c708dd141462000752578063caf1283614620007765762000211565b8063761ab10e146200063c5780638bf4515c146200066f5780638da5cb5b146200069357806393b7628f14620006b25762000211565b80631ccbc6da116200019e57806354fd4d50116200016857806354fd4d5014620005ae57806358edef4c14620005dd5780635ba1d9e51462000601578063715018a614620006255762000211565b80631ccbc6da146200050857806342216bed146200052e5780634db654f914620005525780634eed7cf114620005695762000211565b8063152a4bb311620001e0578063152a4bb314620004415780631a7237e014620004765780631c5ee10c14620004aa5780631c993ad514620004e45762000211565b8063038cd79f146200038e5780630936286114620003b45780631089f40f14620003e357806311ce02671462000407575b3480156200021d575f80fd5b505f366060808284036200024157505060408051602081019091525f815262000383565b83835f81811062000256576200025662002bca565b9050013560f81c60f81b6001600160f81b031916602f60f81b14620002a257505060408051808201909152600e81526d0d2dcc6dee4e4cac6e840e0c2e8d60931b602082015262000383565b8383620002b160018262002bf2565b818110620002c357620002c362002bca565b909101356001600160f81b031916602f60f81b03905062000325576200031c620002f1846001818862002c08565b600a604051602001620003079392919062002c65565b604051602081830303815290604052620008d3565b50905062000376565b6200037262000338846001818862002c08565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250620008d392505050565b5090505b620003818162000997565b505b915050805190602001f35b3480156200039a575f80fd5b50620003b2620003ac36600462002e1f565b620009ca565b005b348015620003c0575f80fd5b50620003cb620009e7565b604051620003da919062002eda565b60405180910390f35b348015620003ef575f80fd5b50620003b26200040136600462002ef5565b62000a7b565b34801562000413575f80fd5b5060055462000428906001600160a01b031681565b6040516001600160a01b039091168152602001620003da565b3480156200044d575f80fd5b50620004656200045f36600462002f1a565b62000aab565b604051620003da9392919062002f80565b34801562000482575f80fd5b506200049a6200049436600462002fa3565b62000b5e565b604051620003da92919062002fe8565b348015620004b6575f80fd5b50620004ce620004c836600462002f1a565b62000c09565b60408051928352602083019190915201620003da565b348015620004f0575f80fd5b50620003b26200050236600462002f1a565b62000cad565b34801562000514575f80fd5b506200051f62000cc9565b604051908152602001620003da565b3480156200053a575f80fd5b506200051f6200054c36600462002fa3565b62000d3c565b620003b2620005633660046200309c565b62000dd5565b34801562000575575f80fd5b507f000000000000000000000000000000000000000000000000000000000000000060ff1615155b6040519015158152602001620003da565b348015620005ba575f80fd5b506040805180820190915260058152640312e312e360dc1b6020820152620003cb565b348015620005e9575f80fd5b506200051f620005fb36600462002f1a565b62000f39565b3480156200060d575f80fd5b506200059d6200061f36600462002fa3565b62000fcc565b34801562000631575f80fd5b50620003b26200104f565b34801562000648575f80fd5b50620006606200065a36600462003127565b62001066565b604051620003da919062003174565b3480156200067b575f80fd5b506200049a6200068d36600462002f1a565b620008d3565b3480156200069f575f80fd5b506004546001600160a01b031662000428565b348015620006be575f80fd5b50620006ec620006d036600462002f1a565b80516020918201205f9081526009909152604090205460ff1690565b604051620003da9190620031d8565b34801562000707575f80fd5b506200051f62000719366004620031ed565b62001215565b3480156200072b575f80fd5b50620007436200073d3660046200320e565b620012b5565b604051620003da9190620032c4565b3480156200075e575f80fd5b50620007436200077036600462003309565b62001370565b34801562000782575f80fd5b506200079a6200079436600462002fa3565b62001514565b60408051928352901515602083015201620003da565b348015620007bc575f80fd5b50600454620007d590600160a01b900463ffffffff1681565b60405163ffffffff9091168152602001620003da565b348015620007f7575f80fd5b506200051f6200080936600462002fa3565b62001591565b3480156200081b575f80fd5b50620003b26200082d36600462003411565b6200160a565b3480156200083f575f80fd5b50651b585b9d585b60d21b6200051f565b3480156200085c575f80fd5b506200059d62001636565b34801562000873575f80fd5b50620003b26200088536600462003411565b62001660565b34801562000897575f80fd5b50620003b2620008a936600462003439565b620016a3565b348015620008bb575f80fd5b506200051f620008cd36600462002f1a565b62001794565b60605f805f620008fe8580516020918201205f8181526009909252604090912054909160ff90911690565b909250905060028160028111156200091a576200091a62002f4f565b0362000936576200092b8262001812565b935093505050915091565b60018160028111156200094d576200094d62002f4f565b036200095e576200092b82620019e9565b5f5b6040519080825280601f01601f1916602001820160405280156200098b576020820181803683370190505b50955f95509350505050565b5f81516040620009a89190620034aa565b9050601f19620009ba82601f620034aa565b1690506020808303528060208303f35b620009d462001b26565b620009e2835f8484620016a3565b505050565b600a8054620009f69062002c31565b80601f016020809104026020016040519081016040528092919081815260200182805462000a249062002c31565b801562000a735780601f1062000a495761010080835404028352916020019162000a73565b820191905f5260205f20905b81548152906001019060200180831162000a5557829003601f168201915b505050505081565b62000a8562001b26565b6004805463ffffffff909216600160a01b0263ffffffff60a01b19909216919091179055565b80516020808301919091205f8181526009909252604082205460ff16919081906002848181111562000ae15762000ae162002f4f565b0362000afe575f818152600760205260409020545b925062000b33565b600184600281111562000b155762000b1562002f4f565b0362000b2f575f8181526002602052604090205462000af6565b5f92505b6005546001600160a01b031662000b4b575f62000b55565b62000b5562000cc9565b93959294505050565b60605f805f62000b898680516020918201205f8181526009909252604090912054909160ff90911690565b9092509050600281600281111562000ba55762000ba562002f4f565b0362000bc35762000bb7828662001b55565b93509350505062000c02565b600181600281111562000bda5762000bda62002f4f565b0362000bec5762000bb7828662001c2c565b5050604080515f80825260208201909252925090505b9250929050565b80516020808301919091205f8181526009909252604082205482919060ff166002818181111562000c3e5762000c3e62002f4f565b0362000c64575f828152600860209081526040808320546007909252909120546200092b565b600181600281111562000c7b5762000c7b62002f4f565b0362000ca1575f828152600360209081526040808320546002909252909120546200092b565b505f9485945092505050565b62000cb762001b26565b600a62000cc5828262003509565b5050565b60055460408051630e65e36d60e11b815290515f926001600160a01b031691631ccbc6da9160048083019260209291908290030181865afa15801562000d11573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000d379190620035d1565b905090565b81516020808401919091205f8181526009909252604082205460ff166002818181111562000d6e5762000d6e62002f4f565b0362000d8a5762000d80828562001215565b9250505062000dcf565b600181600281111562000da15762000da162002f4f565b0362000dc9575f62000db4838662001c2c565b508051602090910120935062000dcf92505050565b505f9150505b92915050565b62000ddf62001b26565b62000de962001636565b62000e545760405162461bcd60e51b815260206004820152603060248201527f5468652063757272656e74206e6574776f726b20646f6573206e6f742073757060448201526f1c1bdc9d08189b1bd8881d5c1b1bd85960821b60648201526084015b60405180910390fd5b82516020808501919091205f81815260099092526040822054909160ff9091169081600281111562000e8a5762000e8a62002f4f565b148062000eab5750600281600281111562000ea95762000ea962002f4f565b145b62000ef05760405162461bcd60e51b8152602060048201526014602482015273496e76616c69642073746f72616765206d6f646560601b604482015260640162000e4b565b5f81600281111562000f065762000f0662002f4f565b0362000f25575f828152600960205260409020805460ff191660021790555b62000f3282858562001c9f565b5050505050565b5f62000f4462001b26565b81516020808401919091205f81815260099092526040909120805460ff19811690915560ff166002818181111562000f805762000f8062002f4f565b0362000f9a5762000f92825f62001fbc565b949350505050565b600181600281111562000fb15762000fb162002f4f565b0362000fc35762000f92825f620020e9565b505f9392505050565b5f62000fd762001b26565b82516020808501919091205f818152600990925260409091205460ff16600281818111156200100a576200100a62002f4f565b036200101c5762000d8082856200218a565b600181600281111562001033576200103362002f4f565b03620010455762000d808285620022ac565b505f949350505050565b6200105962001b26565b620010645f6200233d565b565b60605f620010748562001794565b90505f620010838486620034aa565b905081811115620010915750805b5f858211620010a1575f620010ad565b620010ad868362002bf2565b90505f80620010d78980516020918201205f8181526009909252604090912054909160ff90911690565b91509150826001600160401b03811115620010f657620010f662002cec565b6040519080825280602002602001820160405280156200112b57816020015b6060815260200190600190039081620011155790505b5095505f5b8381101562001208575f62001146828b620034aa565b905060028360028111156200115f576200115f62002f4f565b03620011985762001171848262001b55565b5088838151811062001187576200118762002bca565b6020026020010181905250620011f2565b6001836002811115620011af57620011af62002f4f565b03620011c15762001171848262001c2c565b604080515f8152602081019091528851899084908110620011e657620011e662002bca565b60200260200101819052505b5080620011ff81620035e9565b91505062001130565b5050505050509392505050565b6005545f83815260066020908152604080832085845290915280822054905163d8389dc560e01b815291926001600160a01b03169163d8389dc591620012619160040190815260200190565b602060405180830381865afa1580156200127d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620012a3919062003604565b67ffffffffffffffff19169392505050565b60605f82516001600160401b03811115620012d457620012d462002cec565b604051908082528060200260200182016040528015620012fe578160200160208202803683370190505b5090505f5b835181101562001369576200133484828151811062001326576200132662002bca565b602002602001015162001794565b82828151811062001349576200134962002bca565b6020908102919091010152806200136081620035e9565b91505062001303565b5092915050565b60605f805b8351811015620013c55783818151811062001394576200139462002bca565b6020026020010151602001515182620013ae9190620034aa565b915080620013bc81620035e9565b91505062001375565b505f816001600160401b03811115620013e257620013e262002cec565b6040519080825280602002602001820160405280156200140c578160200160208202803683370190505b5090505f805b85518110156200150a575f5b86828151811062001433576200143362002bca565b60200260200101516020015151811015620014f457620014af87838151811062001461576200146162002bca565b60200260200101515f015188848151811062001481576200148162002bca565b6020026020010151602001518381518110620014a157620014a162002bca565b602002602001015162000d3c565b848481518110620014c457620014c462002bca565b602090810291909101015282620014db81620035e9565b9350508080620014eb90620035e9565b9150506200141e565b50806200150181620035e9565b91505062001412565b5090949350505050565b81516020808401919091205f8181526009909252604082205482919060ff166002818181111562001549576200154962002f4f565b036200155b5762000bb782866200238e565b600181600281111562001572576200157262002f4f565b03620015845762000bb782866200244a565b505f958695509350505050565b5f6200159c62001b26565b82516020808501919091205f818152600990925260409091205460ff1660028181811115620015cf57620015cf62002f4f565b03620015e15762000d80828562001fbc565b6001816002811115620015f857620015f862002f4f565b03620010455762000d808285620020e9565b6200161462001b26565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b6005545f906001600160a01b03161580159062000d3757505f6200165962000cc9565b1015905090565b6200166a62001b26565b6001600160a01b0381166200169557604051631e4fbdf760e01b81525f600482015260240162000e4b565b620016a0816200233d565b50565b620016ad62001b26565b83516020808601919091205f81815260099092526040822054909160ff90911690816002811115620016e357620016e362002f4f565b1480620017045750600181600281111562001702576200170262002f4f565b145b620017495760405162461bcd60e51b8152602060048201526014602482015273496e76616c69642073746f72616765206d6f646560601b604482015260640162000e4b565b5f8160028111156200175f576200175f62002f4f565b036200177e575f828152600960205260409020805460ff191660011790555b6200178c828686866200249d565b505050505050565b80516020808301919091205f8181526009909252604082205460ff1660028181811115620017c657620017c662002f4f565b03620017e157505f9081526007602052604090205492915050565b6001816002811115620017f857620017f862002f4f565b0362000fc3575f8281526002602052604090205462000f92565b60605f805f6200183a855f908152600860209081526040808320546007909252909120549091565b91509150805f036200184d575f62000960565b5f826001600160401b0381111562001869576200186962002cec565b6040519080825280601f01601f19166020018201604052801562001894576020820181803683370190505b5090505f805b83811015620019da575f88815260066020908152604080832084845290915280822054600554915163afd5644d60e01b8152600481018290529092916001600160a01b03169063afd5644d90602401602060405180830381865afa15801562001905573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200192b9190620035d1565b60055460405163bea94b8b60e01b81529192506001600160a01b03169063bea94b8b90620019659085906002905f9087906004016200362e565b5f60405180830381865afa15801562001980573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052620019a991908101906200365b565b508060406020868801013e620019c08185620034aa565b935050508080620019d190620035e9565b9150506200189a565b50909660019650945050505050565b60605f805f62001a11855f908152600360209081526040808320546002909252909120549091565b91509150805f0362001a24575f62000960565b5f826001600160401b0381111562001a405762001a4062002cec565b6040519080825280601f01601f19166020018201604052801562001a6b576020820181803683370190505b509050602081015f5b83811015620019da575f888152602081815260408083208484529091528120549062001aa08262002582565b1562001ae15762001ab18260e01c90565b5f8b8152600160209081526040808320878452909152902090915062001ad990838662002596565b505062001b00565b8162001aed8162002646565b50915062001afc81866200266b565b5050505b62001b0c8185620034aa565b93505050808062001b1d90620035e9565b91505062001a74565b6004546001600160a01b03163314620010645760405163118cdaa760e01b815233600482015260240162000e4b565b60605f8062001b6585856200238e565b509050600181101562001b8b575050604080515f80825260208201909252915062000c02565b6005545f86815260066020908152604080832088845290915280822054905163bea94b8b60e01b815291926001600160a01b03169163bea94b8b9162001bdb91600290869088906004016200362e565b5f60405180830381865afa15801562001bf6573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405262001c1f91908101906200365b565b9660019650945050505050565b5f828152602081815260408083208484529091528120546060919062001c528162002582565b1562001c89575f858152600160209081526040808320878452909152902062001c7c9082620026a3565b6001925092505062000c02565b62001c948162002746565b925092505062000c02565b815160055460408051630e65e36d60e11b815290515f926001600160a01b031691631ccbc6da9160048083019260209291908290030181865afa15801562001ce9573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062001d0f9190620035d1565b905062001d1d8282620036d5565b34101562001d655760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b604482015260640162000e4b565b5f5b828110156200178c5783818151811062001d855762001d8562002bca565b60200260200101515f10801562001dd05750600460149054906101000a900463ffffffff1663ffffffff1684828151811062001dc55762001dc562002bca565b602002602001015111155b62001e155760405162461bcd60e51b8152602060048201526014602482015273092dcecc2d8d2c840c6d0eadcd640d8cadccee8d60631b604482015260640162000e4b565b62001e5a8686838151811062001e2f5762001e2f62002bca565b602002602001015186848151811062001e4c5762001e4c62002bca565b6020026020010151620027c5565b5f338787848151811062001e725762001e7262002bca565b602002602001015160405160200162001ea9939291906001600160a01b039390931683526020830191909152604082015260600190565b60405160208183030381529060405280519060200120905060055f9054906101000a90046001600160a01b03166001600160a01b0316634581a92084838589878151811062001efc5762001efc62002bca565b60200260200101516040518563ffffffff1660e01b815260040162001f34939291909283526020830191909152604082015260600190565b5f604051808303818588803b15801562001f4c575f80fd5b505af115801562001f5f573d5f803e3d5ffd5b50505050508060065f8981526020019081526020015f205f88858151811062001f8c5762001f8c62002bca565b602002602001015181526020019081526020015f208190555050808062001fb390620035e9565b91505062001d67565b5f5b5f83815260066020908152604080832085845290915290205415620020e3576005545f84815260066020908152604080832086845290915280822054905163afd5644d60e01b815291926001600160a01b03169163afd5644d91620020299160040190815260200190565b602060405180830381865afa15801562002045573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200206b9190620035d1565b90508060085f8681526020019081526020015f205f8282546200208f919062002bf2565b90915550505f848152600660209081526040808320868452825280832083905586835260079091528120805491620020c783620036ef565b91905055508280620020d990620035e9565b9350505062001fbe565b50919050565b5f5b5f8381526020818152604080832085845290915290205415620020e3575f6200211584846200244a565b505f858152602081815260408083208784528252808320839055878352600290915281208054929350906200214a83620036ef565b90915550505f84815260036020526040812080548392906200216e90849062002bf2565b909155508390506200218081620035e9565b93505050620020eb565b5f82815260076020526040812054620021a69060019062002bf2565b8214620021b557505f62000dcf565b6005545f84815260066020908152604080832086845290915280822054905163afd5644d60e01b815291926001600160a01b03169163afd5644d91620022019160040190815260200190565b602060405180830381865afa1580156200221d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620022439190620035d1565b5f8581526006602090815260408083208784528252808320839055878352600790915281208054929350906200227983620036ef565b90915550505f84815260086020526040812080548392906200229d90849062002bf2565b90915550600195945050505050565b5f82815260026020526040812054620022c89060019062002bf2565b8214620022d757505f62000dcf565b5f620022e484846200244a565b505f858152602081815260408083208784528252808320839055878352600290915281208054929350906200231983620036ef565b90915550505f84815260036020526040812080548392906200229d90849062002bf2565b600480546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f8281526007602052604081205481908310620023b057505f90508062000c02565b6005545f85815260066020908152604080832087845290915280822054905163afd5644d60e01b815291926001600160a01b03169163afd5644d91620023fc9160040190815260200190565b602060405180830381865afa15801562002418573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200243e9190620035d1565b95600195509350505050565b5f8281526020818152604080832084845290915281205481908062002476575f80925092505062000c02565b620024818162002582565b15620024925762001c7c8160e01c90565b62001c948162002646565b620024aa8484836200293c565b60ff7f0000000000000000000000000000000000000000000000000000000000000000168111156200250f57620024f2620024e6838362002a5e565b6001600160a01b031690565b5f858152602081815260408083208784529091529020556200257c565b5f8481526001602090815260408083208684528252918290208251601f85018390048302810183019093528383526200256492909185908590819084018382808284375f9201919091525062002aa092505050565b5f858152602081815260408083208784529091529020555b50505050565b5f806200258f8360e01c90565b1192915050565b5f805f620025a48562002b38565b808652909350905083601c8411156200263857601c81015f805b60206001620025cf601c8a62002bf2565b620025dc906020620034aa565b620025e8919062002bf2565b620025f4919062003707565b81101562002634575f81815260208b815260409091205480855292506200261d908490620034aa565b9250806200262b81620035e9565b915050620025be565b5050505b600192505050935093915050565b5f806001600160a01b0383166200266157505f928392509050565b50503b9081151590565b5f805f806200267a8662002646565b915091508062002692575f8093509350505062000c02565b815f86883c50946001945092505050565b60605f620026b18362002b52565b92509050601c8111156200136957603c82015f805b60206001620026d7601c8762002bf2565b620026e4906020620034aa565b620026f0919062002bf2565b620026fc919062003707565b8110156200273c575f8181526020888152604090912054808552925062002725908490620034aa565b9250806200273381620035e9565b915050620026c6565b5050505092915050565b60605f805f620027568562002646565b915091508062002767575f62000960565b5f826001600160401b0381111562002783576200278362002cec565b6040519080825280601f01601f191660200182016040528015620027ae576020820181803683370190505b509050825f60208301883c95600195509350505050565b5f838152600760205260409020548211156200281d5760405162461bcd60e51b8152602060048201526016602482015275135d5cdd081c995c1b1858d9481bdc88185c1c195b9960521b604482015260640162000e4b565b5f83815260076020526040902054821015620028f2576005545f84815260066020908152604080832086845290915280822054905163afd5644d60e01b815291926001600160a01b03169163afd5644d916200287f9160040190815260200190565b602060405180830381865afa1580156200289b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620028c19190620035d1565b90508060085f8681526020019081526020015f205f828254620028e5919062002bf2565b9091555062002913915050565b5f8381526007602052604081208054916200290d83620035e9565b91905055505b5f838152600860205260408120805483929062002932908490620034aa565b9091555050505050565b5f8381526020818152604080832085845290915290205480620029fb578215806200298e57505f848152602081905260408120816200297d60018762002bf2565b81526020019081526020015f205414155b620029d55760405162461bcd60e51b81526020600482015260166024820152751b5d5cdd081c995c1b1858d9481bdc88185c1c195b9960521b604482015260640162000e4b565b5f848152600260205260408120805491620029f083620035e9565b919050555062002a34565b5f62002a0885856200244a565b5090508060035f8781526020019081526020015f205f82825462002a2d919062002bf2565b9091555050505b5f848152600360205260408120805484929062002a53908490620034aa565b909155505050505050565b5f80838360405162002a709062002bbc565b62002a7d92919062003727565b604051809103905ff08015801562002a97573d5f803e3d5ffd5b50949350505050565b805160208083015160e083901b911c1790601c81111562001369575f603c8401815b6020600162002ad3601c8762002bf2565b62002ae0906020620034aa565b62002aec919062002bf2565b62002af8919062003707565b8110156200273c578151925062002b11826020620034aa565b5f82815260208990526040902084905591508062002b2f81620035e9565b91505062002ac2565b5f8062002b458360e01c90565b9360209390931b92915050565b5f606062002b608360e01c90565b9150602083901b9250816001600160401b0381111562002b845762002b8462002cec565b6040519080825280601f01601f19166020018201604052801562002baf576020820181803683370190505b5060208101939093525091565b61010e806200375683390190565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b8181038181111562000dcf5762000dcf62002bde565b5f808585111562002c17575f80fd5b8386111562002c24575f80fd5b5050820193919092039150565b600181811c9082168062002c4657607f821691505b602082108103620020e357634e487b7160e01b5f52602260045260245ffd5b828482375f8382015f81525f845462002c7e8162002c31565b6001828116801562002c99576001811462002caf5762002cdd565b60ff198416865282151583028601945062002cdd565b885f526020805f205f5b8581101562002cd45781548982015290840190820162002cb9565b50505082860194505b50929998505050505050505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b038111828210171562002d255762002d2562002cec565b60405290565b604051601f8201601f191681016001600160401b038111828210171562002d565762002d5662002cec565b604052919050565b5f6001600160401b0382111562002d795762002d7962002cec565b50601f01601f191660200190565b5f82601f83011262002d97575f80fd5b813562002dae62002da88262002d5e565b62002d2b565b81815284602083860101111562002dc3575f80fd5b816020850160208301375f918101602001919091529392505050565b5f8083601f84011262002df0575f80fd5b5081356001600160401b0381111562002e07575f80fd5b60208301915083602082850101111562000c02575f80fd5b5f805f6040848603121562002e32575f80fd5b83356001600160401b038082111562002e49575f80fd5b62002e578783880162002d87565b9450602086013591508082111562002e6d575f80fd5b5062002e7c8682870162002ddf565b9497909650939450505050565b5f5b8381101562002ea557818101518382015260200162002e8b565b50505f910152565b5f815180845262002ec681602086016020860162002e89565b601f01601f19169290920160200192915050565b602081525f62002eee602083018462002ead565b9392505050565b5f6020828403121562002f06575f80fd5b813563ffffffff8116811462002eee575f80fd5b5f6020828403121562002f2b575f80fd5b81356001600160401b0381111562002f41575f80fd5b62000f928482850162002d87565b634e487b7160e01b5f52602160045260245ffd5b60038110620016a057634e487b7160e01b5f52602160045260245ffd5b6060810162002f8f8562002f63565b938152602081019290925260409091015290565b5f806040838503121562002fb5575f80fd5b82356001600160401b0381111562002fcb575f80fd5b62002fd98582860162002d87565b95602094909401359450505050565b604081525f62002ffc604083018562002ead565b905082151560208301529392505050565b5f6001600160401b0382111562003028576200302862002cec565b5060051b60200190565b5f82601f83011262003042575f80fd5b813560206200305562002da8836200300d565b82815260059290921b8401810191818101908684111562003074575f80fd5b8286015b8481101562003091578035835291830191830162003078565b509695505050505050565b5f805f60608486031215620030af575f80fd5b83356001600160401b0380821115620030c6575f80fd5b620030d48783880162002d87565b94506020860135915080821115620030ea575f80fd5b620030f88783880162003032565b935060408601359150808211156200310e575f80fd5b506200311d8682870162003032565b9150509250925092565b5f805f606084860312156200313a575f80fd5b83356001600160401b0381111562003150575f80fd5b6200315e8682870162002d87565b9660208601359650604090950135949350505050565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b82811015620031cb57603f19888603018452620031b885835162002ead565b9450928501929085019060010162003199565b5092979650505050505050565b60208101620031e78362002f63565b91905290565b5f8060408385031215620031ff575f80fd5b50508035926020909101359150565b5f602080838503121562003220575f80fd5b82356001600160401b038082111562003237575f80fd5b818501915085601f8301126200324b575f80fd5b81356200325c62002da8826200300d565b81815260059190911b830184019084810190888311156200327b575f80fd5b8585015b83811015620032b75780358581111562003298575f8081fd5b620032a88b89838a010162002d87565b8452509186019186016200327f565b5098975050505050505050565b602080825282518282018190525f9190848201906040850190845b81811015620032fd57835183529284019291840191600101620032df565b50909695505050505050565b5f60208083850312156200331b575f80fd5b82356001600160401b038082111562003332575f80fd5b818501915085601f83011262003346575f80fd5b81356200335762002da8826200300d565b81815260059190911b8301840190848101908883111562003376575f80fd5b8585015b83811015620032b75780358581111562003393575f8081fd5b86016040818c03601f1901811315620033ab575f8081fd5b620033b562002d00565b8983013588811115620033c7575f8081fd5b620033d78e8c8387010162002d87565b825250908201359087821115620033ed575f8081fd5b620033fd8d8b8486010162003032565b818b0152855250509186019186016200337a565b5f6020828403121562003422575f80fd5b81356001600160a01b038116811462002eee575f80fd5b5f805f80606085870312156200344d575f80fd5b84356001600160401b038082111562003464575f80fd5b620034728883890162002d87565b95506020870135945060408701359150808211156200348f575f80fd5b506200349e8782880162002ddf565b95989497509550505050565b8082018082111562000dcf5762000dcf62002bde565b601f821115620009e2575f81815260208120601f850160051c81016020861015620034e85750805b601f850160051c820191505b818110156200178c57828155600101620034f4565b81516001600160401b0381111562003525576200352562002cec565b6200353d8162003536845462002c31565b84620034c0565b602080601f83116001811462003573575f84156200355b5750858301515b5f19600386901b1c1916600185901b1785556200178c565b5f85815260208120601f198616915b82811015620035a35788860151825594840194600190910190840162003582565b5085821015620035c157878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b5f60208284031215620035e2575f80fd5b5051919050565b5f60018201620035fd57620035fd62002bde565b5060010190565b5f6020828403121562003615575f80fd5b815167ffffffffffffffff198116811462002eee575f80fd5b84815260808101620036408562002f63565b84602083015283604083015282606083015295945050505050565b5f602082840312156200366c575f80fd5b81516001600160401b0381111562003682575f80fd5b8201601f8101841362003693575f80fd5b8051620036a462002da88262002d5e565b818152856020838501011115620036b9575f80fd5b620036cc82602083016020860162002e89565b95945050505050565b808202811582820484141762000dcf5762000dcf62002bde565b5f8162003700576200370062002bde565b505f190190565b5f826200372257634e487b7160e01b5f52601260045260245ffd5b500490565b60208152816020820152818360408301375f818301604090810191909152601f909201601f1916010191905056fe608060405234801561000f575f80fd5b5060405161010e38038061010e83398101604081905261002e9161004b565b80518060208301f35b634e487b7160e01b5f52604160045260245ffd5b5f602080838503121561005c575f80fd5b82516001600160401b0380821115610072575f80fd5b818501915085601f830112610085575f80fd5b81518181111561009757610097610037565b604051601f8201601f19908116603f011681019083821181831017156100bf576100bf610037565b8160405282815288868487010111156100d6575f80fd5b5f93505b828410156100f757848401860151818501870152928501926100da565b5f8684830101528096505050505050509291505056fea2646970667358221220ebcd139e7374a9f1c5b5d7c1230cab1e82cbda5396f436b329517280494c00bb64736f6c63430008140033";
|
|
248
|
+
|
|
249
|
+
// src/utils/uploader.ts
|
|
250
|
+
var TxSender = class {
|
|
251
|
+
#provider;
|
|
252
|
+
#wallet;
|
|
253
|
+
#chainId;
|
|
254
|
+
constructor(rpc, privateKey) {
|
|
255
|
+
this.#provider = new import_ethers2.ethers.JsonRpcProvider(rpc);
|
|
256
|
+
this.#wallet = new import_ethers2.ethers.Wallet(privateKey, this.#provider);
|
|
257
|
+
}
|
|
258
|
+
async getChainId() {
|
|
259
|
+
if (this.#chainId === void 0) {
|
|
260
|
+
const network = await stableRetry(() => this.#provider.getNetwork());
|
|
261
|
+
this.#chainId = Number(network.chainId);
|
|
262
|
+
}
|
|
263
|
+
return this.#chainId;
|
|
264
|
+
}
|
|
265
|
+
// ====================== Gas API ======================
|
|
266
|
+
async getFeeData() {
|
|
267
|
+
return await stableRetry(() => this.#provider.getFeeData());
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Base fee of the latest block (EIP-1559), or null on legacy chains.
|
|
271
|
+
*/
|
|
272
|
+
async getBaseFee() {
|
|
273
|
+
const block = await stableRetry(() => this.#provider.getBlock("latest"));
|
|
274
|
+
return block?.baseFeePerGas ?? null;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Resolve concrete gas fees for a transaction, by chain fee model:
|
|
278
|
+
* - Legacy chains (no base fee, e.g. BNB Chain): a single gasPrice paid in full,
|
|
279
|
+
* taken from the node unless overridden via custom.gasPrice.
|
|
280
|
+
* - Avalanche C-Chain: EIP-1559 with a protocol-enforced minimum base fee of
|
|
281
|
+
* 25 nAVAX; the node-reported base fee is clamped to that floor.
|
|
282
|
+
* - Other (EIP-1559) chains: tip defaults to 100 wei,
|
|
283
|
+
* maxFeePerGas defaults to 1.1 * baseFee + tip (baseFee from the latest block).
|
|
284
|
+
* Caller-provided fields take precedence. effectiveGasPrice is the expected
|
|
285
|
+
* price actually charged per gas unit.
|
|
286
|
+
*/
|
|
287
|
+
async resolveGasFees(custom = {}) {
|
|
288
|
+
const chainId = await this.getChainId();
|
|
289
|
+
const isLegacyChain = LEGACY_GAS_CHAIN_IDS.has(chainId);
|
|
290
|
+
let baseFee = isLegacyChain ? null : await this.getBaseFee();
|
|
291
|
+
if (AVALANCHE_GAS_CHAIN_IDS.has(chainId) && (baseFee == null || baseFee < AVALANCHE_MIN_BASE_FEE_PER_GAS)) {
|
|
292
|
+
baseFee = AVALANCHE_MIN_BASE_FEE_PER_GAS;
|
|
293
|
+
}
|
|
294
|
+
if (baseFee != null) {
|
|
295
|
+
const maxPriorityFeePerGas = custom.maxPriorityFeePerGas ?? DEFAULT_PRIORITY_FEE_PER_GAS;
|
|
296
|
+
const bufferedBaseFee = baseFee * BASE_FEE_MULTIPLIER_NUMERATOR / BASE_FEE_MULTIPLIER_DENOMINATOR;
|
|
297
|
+
const maxFeePerGas = custom.maxFeePerGas ?? bufferedBaseFee + maxPriorityFeePerGas;
|
|
298
|
+
const baseAndTip = baseFee + maxPriorityFeePerGas;
|
|
299
|
+
const effectiveGasPrice = maxFeePerGas < baseAndTip ? maxFeePerGas : baseAndTip;
|
|
300
|
+
return { type: "eip1559", maxFeePerGas, maxPriorityFeePerGas, effectiveGasPrice };
|
|
301
|
+
}
|
|
302
|
+
const feeData = await this.getFeeData();
|
|
303
|
+
const gasPrice = custom.gasPrice ?? feeData.gasPrice;
|
|
304
|
+
if (gasPrice != null) {
|
|
305
|
+
return { type: "legacy", gasPrice, effectiveGasPrice: gasPrice };
|
|
306
|
+
}
|
|
307
|
+
if (feeData.maxFeePerGas != null && feeData.maxPriorityFeePerGas != null) {
|
|
308
|
+
const maxFeePerGas = custom.maxFeePerGas ?? feeData.maxFeePerGas;
|
|
309
|
+
const maxPriorityFeePerGas = custom.maxPriorityFeePerGas ?? feeData.maxPriorityFeePerGas;
|
|
310
|
+
return { type: "eip1559", maxFeePerGas, maxPriorityFeePerGas, effectiveGasPrice: maxFeePerGas };
|
|
311
|
+
}
|
|
312
|
+
throw new Error("Failed to resolve gas fees: the network provides neither base fee nor gas price.");
|
|
313
|
+
}
|
|
314
|
+
async waitForReceipt(hash) {
|
|
315
|
+
if (!hash || !import_ethers2.ethers.isHexString(hash)) throw new Error("Invalid transaction hash");
|
|
316
|
+
return await stableRetry(() => this.#provider.waitForTransaction(hash));
|
|
317
|
+
}
|
|
318
|
+
async getTransactionResult(hash) {
|
|
319
|
+
const receipt = await this.waitForReceipt(hash);
|
|
320
|
+
return { txCost: calcTxCost(receipt), success: receipt?.status === 1 };
|
|
321
|
+
}
|
|
322
|
+
// ====================== Public Send API ======================
|
|
323
|
+
/**
|
|
324
|
+
* Sends a single transaction. No locking or manual nonce management:
|
|
325
|
+
* uploads are strictly sequential, so the wallet's automatic nonce
|
|
326
|
+
* handling is always up to date.
|
|
327
|
+
*/
|
|
328
|
+
async sendTx(tx) {
|
|
329
|
+
return stableRetry(() => this.#wallet.sendTransaction(tx));
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// src/utils/web3.ts
|
|
334
|
+
async function getUploadInfo(contract, hexName) {
|
|
335
|
+
const result = await stableRetry(() => contract["getUploadInfo"](hexName));
|
|
336
|
+
return {
|
|
337
|
+
fileMode: Number(result[0]),
|
|
338
|
+
oldChunkCount: Number(result[1]),
|
|
339
|
+
cost: result[2]
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
async function getChunkCounts(contract, batch) {
|
|
343
|
+
const names = batch.map((key) => stringToHex(key));
|
|
344
|
+
const counts = await stableRetry(() => contract["getChunkCountsBatch"](names));
|
|
345
|
+
const results = [];
|
|
346
|
+
let index = 0;
|
|
347
|
+
for (const key of batch) {
|
|
348
|
+
results.push({ key, chunkCount: Number(counts[index++]) });
|
|
349
|
+
}
|
|
350
|
+
return results;
|
|
351
|
+
}
|
|
352
|
+
async function getChunkHashes(contract, batch) {
|
|
353
|
+
const fileChunks = batch.map((file) => [
|
|
354
|
+
stringToHex(file.name),
|
|
355
|
+
file.chunkIds
|
|
356
|
+
]);
|
|
357
|
+
const hashes = await stableRetry(() => contract["getChunkHashesBatch"](fileChunks));
|
|
358
|
+
const results = [];
|
|
359
|
+
let index = 0;
|
|
360
|
+
for (const file of batch) {
|
|
361
|
+
for (let j = 0; j < file.chunkIds.length; j++) {
|
|
362
|
+
results.push({ name: file.name, chunkId: file.chunkIds[j], hash: hashes[index++] });
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return results;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// node_modules/yocto-queue/index.js
|
|
369
|
+
var Node = class {
|
|
370
|
+
value;
|
|
371
|
+
next;
|
|
372
|
+
constructor(value) {
|
|
373
|
+
this.value = value;
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
var Queue = class {
|
|
377
|
+
#head;
|
|
378
|
+
#tail;
|
|
379
|
+
#size;
|
|
380
|
+
constructor() {
|
|
381
|
+
this.clear();
|
|
382
|
+
}
|
|
383
|
+
enqueue(value) {
|
|
384
|
+
const node = new Node(value);
|
|
385
|
+
if (this.#head) {
|
|
386
|
+
this.#tail.next = node;
|
|
387
|
+
this.#tail = node;
|
|
388
|
+
} else {
|
|
389
|
+
this.#head = node;
|
|
390
|
+
this.#tail = node;
|
|
391
|
+
}
|
|
392
|
+
this.#size++;
|
|
393
|
+
}
|
|
394
|
+
dequeue() {
|
|
395
|
+
const current = this.#head;
|
|
396
|
+
if (!current) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
this.#head = this.#head.next;
|
|
400
|
+
this.#size--;
|
|
401
|
+
if (!this.#head) {
|
|
402
|
+
this.#tail = void 0;
|
|
403
|
+
}
|
|
404
|
+
return current.value;
|
|
405
|
+
}
|
|
406
|
+
peek() {
|
|
407
|
+
if (!this.#head) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
return this.#head.value;
|
|
411
|
+
}
|
|
412
|
+
clear() {
|
|
413
|
+
this.#head = void 0;
|
|
414
|
+
this.#tail = void 0;
|
|
415
|
+
this.#size = 0;
|
|
416
|
+
}
|
|
417
|
+
get size() {
|
|
418
|
+
return this.#size;
|
|
419
|
+
}
|
|
420
|
+
*[Symbol.iterator]() {
|
|
421
|
+
let current = this.#head;
|
|
422
|
+
while (current) {
|
|
423
|
+
yield current.value;
|
|
424
|
+
current = current.next;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
*drain() {
|
|
428
|
+
while (this.#head) {
|
|
429
|
+
yield this.dequeue();
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
// node_modules/p-limit/index.js
|
|
435
|
+
function pLimit(concurrency) {
|
|
436
|
+
let rejectOnClear = false;
|
|
437
|
+
if (typeof concurrency === "object") {
|
|
438
|
+
({ concurrency, rejectOnClear = false } = concurrency);
|
|
439
|
+
}
|
|
440
|
+
validateConcurrency(concurrency);
|
|
441
|
+
if (typeof rejectOnClear !== "boolean") {
|
|
442
|
+
throw new TypeError("Expected `rejectOnClear` to be a boolean");
|
|
443
|
+
}
|
|
444
|
+
const queue = new Queue();
|
|
445
|
+
let activeCount = 0;
|
|
446
|
+
const resumeNext = () => {
|
|
447
|
+
if (activeCount < concurrency && queue.size > 0) {
|
|
448
|
+
activeCount++;
|
|
449
|
+
queue.dequeue().run();
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
const next = () => {
|
|
453
|
+
activeCount--;
|
|
454
|
+
resumeNext();
|
|
455
|
+
};
|
|
456
|
+
const run = async (function_, resolve, arguments_) => {
|
|
457
|
+
const result = (async () => function_(...arguments_))();
|
|
458
|
+
resolve(result);
|
|
459
|
+
try {
|
|
460
|
+
await result;
|
|
461
|
+
} catch {
|
|
462
|
+
}
|
|
463
|
+
next();
|
|
464
|
+
};
|
|
465
|
+
const enqueue = (function_, resolve, reject, arguments_) => {
|
|
466
|
+
const queueItem = { reject };
|
|
467
|
+
new Promise((internalResolve) => {
|
|
468
|
+
queueItem.run = internalResolve;
|
|
469
|
+
queue.enqueue(queueItem);
|
|
470
|
+
}).then(run.bind(void 0, function_, resolve, arguments_));
|
|
471
|
+
if (activeCount < concurrency) {
|
|
472
|
+
resumeNext();
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
const generator = (function_, ...arguments_) => new Promise((resolve, reject) => {
|
|
476
|
+
enqueue(function_, resolve, reject, arguments_);
|
|
477
|
+
});
|
|
478
|
+
Object.defineProperties(generator, {
|
|
479
|
+
activeCount: {
|
|
480
|
+
get: () => activeCount
|
|
481
|
+
},
|
|
482
|
+
pendingCount: {
|
|
483
|
+
get: () => queue.size
|
|
484
|
+
},
|
|
485
|
+
clearQueue: {
|
|
486
|
+
value() {
|
|
487
|
+
if (!rejectOnClear) {
|
|
488
|
+
queue.clear();
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const abortError = AbortSignal.abort().reason;
|
|
492
|
+
while (queue.size > 0) {
|
|
493
|
+
queue.dequeue().reject(abortError);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
concurrency: {
|
|
498
|
+
get: () => concurrency,
|
|
499
|
+
set(newConcurrency) {
|
|
500
|
+
validateConcurrency(newConcurrency);
|
|
501
|
+
concurrency = newConcurrency;
|
|
502
|
+
queueMicrotask(() => {
|
|
503
|
+
while (activeCount < concurrency && queue.size > 0) {
|
|
504
|
+
resumeNext();
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
map: {
|
|
510
|
+
async value(iterable, function_) {
|
|
511
|
+
const promises = [];
|
|
512
|
+
try {
|
|
513
|
+
Array.from(iterable, (value, index) => {
|
|
514
|
+
const promise = generator(function_, value, index);
|
|
515
|
+
promises.push(promise);
|
|
516
|
+
return promise;
|
|
517
|
+
});
|
|
518
|
+
} catch (error) {
|
|
519
|
+
for (const promise of promises) {
|
|
520
|
+
promise.catch(() => {
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
throw error;
|
|
524
|
+
}
|
|
525
|
+
return Promise.all(promises);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
return generator;
|
|
530
|
+
}
|
|
531
|
+
function validateConcurrency(concurrency) {
|
|
532
|
+
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
|
|
533
|
+
throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// src/flatdirectory.ts
|
|
538
|
+
var import_ethers3 = require("ethers");
|
|
539
|
+
var defaultCallback = {
|
|
540
|
+
onProgress: () => {
|
|
541
|
+
},
|
|
542
|
+
onFail: () => {
|
|
543
|
+
},
|
|
544
|
+
onFinish: () => {
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
var FlatDirectory = class _FlatDirectory {
|
|
548
|
+
// ======================= Private fields =======================
|
|
549
|
+
#ethStorageRpc;
|
|
550
|
+
#contractAddr;
|
|
551
|
+
#wallet;
|
|
552
|
+
#txSender;
|
|
553
|
+
#isLoggingEnabled = true;
|
|
554
|
+
/**
|
|
555
|
+
* Private constructor - use create() factory method
|
|
556
|
+
*/
|
|
557
|
+
constructor() {
|
|
558
|
+
}
|
|
559
|
+
static async create(config) {
|
|
560
|
+
const flatDirectory = new _FlatDirectory();
|
|
561
|
+
await flatDirectory.#initialize(config);
|
|
562
|
+
return flatDirectory;
|
|
563
|
+
}
|
|
564
|
+
// ======================= Public methods =======================
|
|
565
|
+
/**
|
|
566
|
+
* Enable or disable logging
|
|
567
|
+
* @param value - true to enable logging, false to disable
|
|
568
|
+
*/
|
|
569
|
+
setLogEnabled(value) {
|
|
570
|
+
this.#isLoggingEnabled = value;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Deploy a new FlatDirectory contract
|
|
574
|
+
* @returns Contract address if successful, null otherwise
|
|
575
|
+
*/
|
|
576
|
+
async deploy() {
|
|
577
|
+
const chainId = await this.#txSenderChecked.getChainId();
|
|
578
|
+
const ethStorage = ETHSTORAGE_MAPPING[chainId] || "0x0000000000000000000000000000000000000000";
|
|
579
|
+
try {
|
|
580
|
+
const factory = new import_ethers3.ethers.ContractFactory(FlatDirectoryAbi, FlatDirectoryBytecode);
|
|
581
|
+
const tx = await factory.getDeployTransaction(0, CONTRACT_MAX_CHUNK_SIZE, ethStorage);
|
|
582
|
+
const resolvedFees = await this.#txSenderChecked.resolveGasFees();
|
|
583
|
+
this.#applyGasFees(tx, resolvedFees);
|
|
584
|
+
const txResponse = await this.#txSenderChecked.sendTx(tx);
|
|
585
|
+
this.#log(`Deploy transaction sent. Hash: ${txResponse.hash}`);
|
|
586
|
+
const receipt = await this.#txSenderChecked.waitForReceipt(txResponse.hash);
|
|
587
|
+
if (receipt?.status === 1 && receipt.contractAddress) {
|
|
588
|
+
this.#contractAddr = receipt.contractAddress;
|
|
589
|
+
this.#log(`Contract deployed successfully. Address: ${this.#contractAddr}`);
|
|
590
|
+
return this.#contractAddr;
|
|
591
|
+
}
|
|
592
|
+
this.#log(`Deployment failed! Transaction reverted (Hash: ${txResponse.hash})`, true);
|
|
593
|
+
return null;
|
|
594
|
+
} catch (e) {
|
|
595
|
+
this.#log(`Deployment failed! ${e.message || e}`, true);
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Set a file as the default file
|
|
601
|
+
* @param filename - Name of the file to set as default
|
|
602
|
+
* @returns true if successful, false otherwise
|
|
603
|
+
*/
|
|
604
|
+
async setDefault(filename) {
|
|
605
|
+
const hexName = filename ? stringToHex(filename) : "0x";
|
|
606
|
+
const fileContract = new import_ethers3.ethers.Contract(this.#contractAddrChecked, FlatDirectoryAbi, this.#walletChecked);
|
|
607
|
+
try {
|
|
608
|
+
const tx = await fileContract["setDefault"].populateTransaction(hexName);
|
|
609
|
+
const resolvedFees = await this.#txSenderChecked.resolveGasFees();
|
|
610
|
+
this.#applyGasFees(tx, resolvedFees);
|
|
611
|
+
const txResponse = await this.#txSenderChecked.sendTx(tx);
|
|
612
|
+
this.#log(`Setting default file (Key: ${filename}). Transaction sent (Hash: ${txResponse.hash})`);
|
|
613
|
+
const result = await this.#txSenderChecked.getTransactionResult(txResponse.hash);
|
|
614
|
+
return result.success;
|
|
615
|
+
} catch (e) {
|
|
616
|
+
this.#log(`Failed to set default file (Key: ${filename}). ${e.message || e}`, true);
|
|
617
|
+
}
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Remove a file from the contract
|
|
622
|
+
* @param key - File key to remove
|
|
623
|
+
* @returns true if successful, false otherwise
|
|
624
|
+
*/
|
|
625
|
+
async remove(key) {
|
|
626
|
+
const fileContract = new import_ethers3.ethers.Contract(this.#contractAddrChecked, FlatDirectoryAbi, this.#walletChecked);
|
|
627
|
+
try {
|
|
628
|
+
const tx = await fileContract["remove"].populateTransaction(stringToHex(key));
|
|
629
|
+
const resolvedFees = await this.#txSenderChecked.resolveGasFees();
|
|
630
|
+
this.#applyGasFees(tx, resolvedFees);
|
|
631
|
+
const txResponse = await this.#txSenderChecked.sendTx(tx);
|
|
632
|
+
this.#log(`File removal initiated (Key: ${key}). Hash: ${txResponse.hash}`);
|
|
633
|
+
const result = await this.#txSenderChecked.getTransactionResult(txResponse.hash);
|
|
634
|
+
return result.success;
|
|
635
|
+
} catch (e) {
|
|
636
|
+
this.#log(`Failed to remove file (Key: ${key}). ${e.message || e}`, true);
|
|
637
|
+
}
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Download a file from the contract
|
|
642
|
+
* @param key - File key to download
|
|
643
|
+
* @param cb - Download callback object
|
|
644
|
+
*/
|
|
645
|
+
async download(key, cb = defaultCallback) {
|
|
646
|
+
const hexName = stringToHex(key);
|
|
647
|
+
const provider = new import_ethers3.ethers.JsonRpcProvider(this.#ethStorageRpcChecked);
|
|
648
|
+
const contract = new import_ethers3.ethers.Contract(this.#contractAddrChecked, FlatDirectoryAbi, provider);
|
|
649
|
+
try {
|
|
650
|
+
const result = await getChunkCounts(contract, [key]);
|
|
651
|
+
const totalChunks = result[0].chunkCount;
|
|
652
|
+
if (totalChunks === 0) {
|
|
653
|
+
cb.onFinish();
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
await this.#download(contract, hexName, totalChunks, cb);
|
|
657
|
+
cb.onFinish();
|
|
658
|
+
} catch (err) {
|
|
659
|
+
cb.onFail(err);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Fetch chunk hashes for multiple files
|
|
664
|
+
* @param keys - Array of file keys
|
|
665
|
+
* @param maxChunks - Maximum number of chunks requested in a single call, defaults to MAX_CHUNKS
|
|
666
|
+
* @returns Object mapping file keys to their chunk hashes
|
|
667
|
+
*/
|
|
668
|
+
async fetchHashes(keys, maxChunks) {
|
|
669
|
+
if (!keys || !Array.isArray(keys)) {
|
|
670
|
+
throw new Error("Invalid keys.");
|
|
671
|
+
}
|
|
672
|
+
const contract = new import_ethers3.ethers.Contract(this.#contractAddrChecked, FlatDirectoryAbi, this.#walletChecked);
|
|
673
|
+
const fileInfos = await getChunkCounts(contract, keys);
|
|
674
|
+
return this.#fetchChunkHashes(fileInfos, maxChunks);
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Estimate cost for uploading a file
|
|
678
|
+
* @param request - Upload request details
|
|
679
|
+
* @returns Cost estimation object
|
|
680
|
+
*/
|
|
681
|
+
async estimateCost(request) {
|
|
682
|
+
const { key } = request;
|
|
683
|
+
if (!key) {
|
|
684
|
+
throw new Error(`FlatDirectory: Invalid key!`);
|
|
685
|
+
}
|
|
686
|
+
return await this.#estimateCostByCallData(request);
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Upload a file to the contract
|
|
690
|
+
* @param request - Upload request details
|
|
691
|
+
* @throws Error if upload fails
|
|
692
|
+
*/
|
|
693
|
+
async upload(request) {
|
|
694
|
+
const { key, callback } = request;
|
|
695
|
+
if (!callback) {
|
|
696
|
+
throw new Error(`FlatDirectory: Invalid callback object!`);
|
|
697
|
+
}
|
|
698
|
+
if (!key) {
|
|
699
|
+
callback.onFail(new Error(`FlatDirectory: Invalid key!`));
|
|
700
|
+
callback.onFinish(0, 0, 0n);
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
if (!this.#contractAddr) {
|
|
704
|
+
callback.onFail(new Error(`FlatDirectory: FlatDirectory not deployed!`));
|
|
705
|
+
callback.onFinish(0, 0, 0n);
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
if (!this.#wallet) {
|
|
709
|
+
callback.onFail(new Error(`FlatDirectory: Private key is required for this operation.`));
|
|
710
|
+
callback.onFinish(0, 0, 0n);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
await this.#uploadByCalldata(request);
|
|
714
|
+
}
|
|
715
|
+
// -------------------- Private Methods --------------------
|
|
716
|
+
async #initialize(config) {
|
|
717
|
+
const { privateKey, rpc, address, ethStorageRpc } = config;
|
|
718
|
+
this.#contractAddr = address;
|
|
719
|
+
this.#ethStorageRpc = ethStorageRpc;
|
|
720
|
+
if (privateKey && rpc) {
|
|
721
|
+
const provider = new import_ethers3.ethers.JsonRpcProvider(rpc);
|
|
722
|
+
this.#wallet = new import_ethers3.ethers.Wallet(privateKey, provider);
|
|
723
|
+
this.#txSender = new TxSender(rpc, privateKey);
|
|
724
|
+
if (!address) return;
|
|
725
|
+
} else if (!ethStorageRpc || !address) {
|
|
726
|
+
throw new Error("FlatDirectory: Read-only mode requires both 'ethStorageRpc' and 'address'");
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
async #download(contract, hexName, totalChunks, cb) {
|
|
730
|
+
const downloadedResults = /* @__PURE__ */ new Map();
|
|
731
|
+
let nextCallbackStart = 0;
|
|
732
|
+
const flushReadyChunks = () => {
|
|
733
|
+
while (downloadedResults.has(nextCallbackStart)) {
|
|
734
|
+
const data = downloadedResults.get(nextCallbackStart);
|
|
735
|
+
cb.onProgress(nextCallbackStart, totalChunks, data);
|
|
736
|
+
downloadedResults.delete(nextCallbackStart);
|
|
737
|
+
nextCallbackStart++;
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
const fetchChunk = (i) => stableRetry(() => contract["readChunk"](hexName, i));
|
|
741
|
+
const fetchSingle = async (i) => {
|
|
742
|
+
const [data] = await fetchChunk(i);
|
|
743
|
+
downloadedResults.set(i, import_ethers3.ethers.getBytes(data));
|
|
744
|
+
flushReadyChunks();
|
|
745
|
+
};
|
|
746
|
+
let maxConcurrency = 6;
|
|
747
|
+
if (typeof process !== "undefined" && process.versions?.node) {
|
|
748
|
+
try {
|
|
749
|
+
const os = await import("os");
|
|
750
|
+
const cores = os.cpus().length;
|
|
751
|
+
maxConcurrency = Math.max(2, Math.min(20, cores * 2));
|
|
752
|
+
} catch (err) {
|
|
753
|
+
maxConcurrency = 10;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
const limit = pLimit(maxConcurrency);
|
|
757
|
+
const quests = Array.from(
|
|
758
|
+
{ length: totalChunks },
|
|
759
|
+
(_, i) => limit(() => fetchSingle(i))
|
|
760
|
+
);
|
|
761
|
+
await Promise.all(quests);
|
|
762
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
763
|
+
flushReadyChunks();
|
|
764
|
+
}
|
|
765
|
+
async #fetchChunkHashes(fileInfos, maxChunks) {
|
|
766
|
+
const allHashes = {};
|
|
767
|
+
const chunkLimit = maxChunks && maxChunks > 0 ? maxChunks : MAX_CHUNKS;
|
|
768
|
+
const batchArray = [];
|
|
769
|
+
let currentBatch = {};
|
|
770
|
+
let currentChunkCount = 0;
|
|
771
|
+
for (const { key, chunkCount } of fileInfos) {
|
|
772
|
+
allHashes[key] = chunkCount === 0 ? [] : new Array(chunkCount);
|
|
773
|
+
for (let i = 0; i < chunkCount; i++) {
|
|
774
|
+
if (!currentBatch[key]) {
|
|
775
|
+
currentBatch[key] = [];
|
|
776
|
+
}
|
|
777
|
+
currentBatch[key].push(i);
|
|
778
|
+
currentChunkCount++;
|
|
779
|
+
if (currentChunkCount === chunkLimit) {
|
|
780
|
+
batchArray.push(currentBatch);
|
|
781
|
+
currentBatch = {};
|
|
782
|
+
currentChunkCount = 0;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
if (Object.keys(currentBatch).length > 0) {
|
|
787
|
+
batchArray.push(currentBatch);
|
|
788
|
+
}
|
|
789
|
+
if (batchArray.length > 0) {
|
|
790
|
+
const contract = new import_ethers3.ethers.Contract(this.#contractAddrChecked, FlatDirectoryAbi, this.#walletChecked);
|
|
791
|
+
const hashResults = await Promise.all(batchArray.map((batch) => {
|
|
792
|
+
const fileChunksArray = Object.keys(batch).map((name) => ({
|
|
793
|
+
name,
|
|
794
|
+
chunkIds: batch[name]
|
|
795
|
+
}));
|
|
796
|
+
return getChunkHashes(contract, fileChunksArray);
|
|
797
|
+
}));
|
|
798
|
+
hashResults.flat().forEach(({ name, chunkId, hash }) => {
|
|
799
|
+
allHashes[name][chunkId] = hash;
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
return allHashes;
|
|
803
|
+
}
|
|
804
|
+
// estimate cost
|
|
805
|
+
async #estimateCostByCallData(request) {
|
|
806
|
+
let { key, content, chunkHashes, maxFeePerGas: customMaxFee, maxPriorityFeePerGas: customTip, gasPrice: customGasPrice, maxChunkSize, maxChunks } = request;
|
|
807
|
+
if (toChunkSizeBytes(maxChunkSize) > CONTRACT_MAX_CHUNK_SIZE) {
|
|
808
|
+
throw new Error(`FlatDirectory: Chunk size ${maxChunkSize} KB exceeds the on-chain max payload of ${CONTRACT_MAX_CHUNK_SIZE} bytes.`);
|
|
809
|
+
}
|
|
810
|
+
const { chunkDataSize, calldataChunkCount } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
|
|
811
|
+
if (chunkDataSize === -1) {
|
|
812
|
+
throw new Error(`FlatDirectory: Invalid upload content!`);
|
|
813
|
+
}
|
|
814
|
+
const hexName = stringToHex(key);
|
|
815
|
+
const fileContract = new import_ethers3.ethers.Contract(this.#contractAddrChecked, FlatDirectoryAbi, this.#walletChecked);
|
|
816
|
+
const [{ oldChunkCount, fileMode }, resolvedFees] = await Promise.all([
|
|
817
|
+
getUploadInfo(fileContract, hexName),
|
|
818
|
+
this.#txSenderChecked.resolveGasFees({ maxFeePerGas: customMaxFee, maxPriorityFeePerGas: customTip, gasPrice: customGasPrice })
|
|
819
|
+
]);
|
|
820
|
+
if (fileMode !== 0 && fileMode !== 1) {
|
|
821
|
+
throw new Error(`FlatDirectory: This file does not support calldata upload!`);
|
|
822
|
+
}
|
|
823
|
+
if (!chunkHashes) {
|
|
824
|
+
const hashes = await this.#fetchChunkHashes([{ key, chunkCount: oldChunkCount }], maxChunks);
|
|
825
|
+
chunkHashes = hashes[key];
|
|
826
|
+
}
|
|
827
|
+
const effectiveGasPrice = resolvedFees.effectiveGasPrice;
|
|
828
|
+
let totalGasCost = 0n;
|
|
829
|
+
if (calldataChunkCount < oldChunkCount) {
|
|
830
|
+
const truncateGas = await stableRetry(() => fileContract["truncate"].estimateGas(hexName, calldataChunkCount));
|
|
831
|
+
totalGasCost += effectiveGasPrice * truncateGas;
|
|
832
|
+
}
|
|
833
|
+
let overwriteGasLimit = 0n;
|
|
834
|
+
let appendGasLimit = 0n;
|
|
835
|
+
const estimateChunk = (chunkId, hexData) => stableRetry(
|
|
836
|
+
() => fileContract["writeChunkByCalldata"].estimateGas(hexName, chunkId, hexData)
|
|
837
|
+
);
|
|
838
|
+
for (let i = 0; i < calldataChunkCount; i++) {
|
|
839
|
+
const chunk = await getContentChunk(content, i * chunkDataSize, (i + 1) * chunkDataSize);
|
|
840
|
+
if (i < chunkHashes.length && import_ethers3.ethers.keccak256(chunk) === chunkHashes[i]) {
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
const hexData = import_ethers3.ethers.hexlify(chunk);
|
|
844
|
+
const isAppend = i >= oldChunkCount;
|
|
845
|
+
const estChunkId = isAppend ? oldChunkCount : i;
|
|
846
|
+
let chunkGas;
|
|
847
|
+
if (i === calldataChunkCount - 1) {
|
|
848
|
+
chunkGas = await estimateChunk(estChunkId, hexData);
|
|
849
|
+
} else if (isAppend) {
|
|
850
|
+
if (appendGasLimit === 0n) {
|
|
851
|
+
appendGasLimit = await estimateChunk(estChunkId, hexData);
|
|
852
|
+
}
|
|
853
|
+
chunkGas = appendGasLimit;
|
|
854
|
+
} else {
|
|
855
|
+
if (overwriteGasLimit === 0n) {
|
|
856
|
+
overwriteGasLimit = await estimateChunk(estChunkId, hexData);
|
|
857
|
+
}
|
|
858
|
+
chunkGas = overwriteGasLimit;
|
|
859
|
+
}
|
|
860
|
+
totalGasCost += effectiveGasPrice * chunkGas;
|
|
861
|
+
}
|
|
862
|
+
return {
|
|
863
|
+
storageCost: 0n,
|
|
864
|
+
gasCost: totalGasCost
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
// upload
|
|
868
|
+
async #uploadByCalldata(request) {
|
|
869
|
+
let totalUploadChunks = 0, totalUploadSize = 0;
|
|
870
|
+
let totalCost = 0n;
|
|
871
|
+
let { key, content, callback, chunkHashes, maxFeePerGas, maxPriorityFeePerGas, gasPrice, maxChunkSize, maxChunks } = request;
|
|
872
|
+
const gasFee = { maxFeePerGas, maxPriorityFeePerGas, gasPrice };
|
|
873
|
+
if (toChunkSizeBytes(maxChunkSize) > CONTRACT_MAX_CHUNK_SIZE) {
|
|
874
|
+
callback.onFail(new Error(`FlatDirectory: Chunk size ${maxChunkSize} KB exceeds the on-chain max payload of ${CONTRACT_MAX_CHUNK_SIZE} bytes.`));
|
|
875
|
+
callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
const { chunkDataSize, calldataChunkCount } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
|
|
879
|
+
if (chunkDataSize === -1) {
|
|
880
|
+
callback.onFail(new Error(`FlatDirectory: Invalid upload content!`));
|
|
881
|
+
callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
const hexName = stringToHex(key);
|
|
885
|
+
const fileContract = new import_ethers3.ethers.Contract(this.#contractAddrChecked, FlatDirectoryAbi, this.#walletChecked);
|
|
886
|
+
const { oldChunkCount, fileMode } = await getUploadInfo(fileContract, hexName);
|
|
887
|
+
if (fileMode !== 0 && fileMode !== 1) {
|
|
888
|
+
callback.onFail(new Error(`FlatDirectory: This file does not support calldata upload!`));
|
|
889
|
+
callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
let resolvedFees;
|
|
893
|
+
try {
|
|
894
|
+
resolvedFees = await this.#txSenderChecked.resolveGasFees(gasFee);
|
|
895
|
+
} catch (e) {
|
|
896
|
+
callback.onFail(e);
|
|
897
|
+
callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
const clearResult = await this.#clearOldFile(
|
|
901
|
+
fileContract,
|
|
902
|
+
hexName,
|
|
903
|
+
calldataChunkCount,
|
|
904
|
+
oldChunkCount,
|
|
905
|
+
resolvedFees
|
|
906
|
+
);
|
|
907
|
+
totalCost += clearResult.gasCost;
|
|
908
|
+
if (!clearResult.success) {
|
|
909
|
+
callback.onFail(new Error(`FlatDirectory: Failed to truncate old data!`));
|
|
910
|
+
callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
if (!chunkHashes) {
|
|
914
|
+
try {
|
|
915
|
+
const hashes = await this.#fetchChunkHashes([{ key, chunkCount: oldChunkCount }], maxChunks);
|
|
916
|
+
chunkHashes = hashes[key];
|
|
917
|
+
} catch (e) {
|
|
918
|
+
callback.onFail(e);
|
|
919
|
+
callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
for (let i = 0; i < calldataChunkCount; i++) {
|
|
924
|
+
const chunk = await getContentChunk(content, i * chunkDataSize, (i + 1) * chunkDataSize);
|
|
925
|
+
if (i < chunkHashes.length && import_ethers3.ethers.keccak256(chunk) === chunkHashes[i]) {
|
|
926
|
+
callback.onProgress(i, calldataChunkCount, false);
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
let uploadResult;
|
|
930
|
+
try {
|
|
931
|
+
const txResponse = await this.#sendCalldataTx(
|
|
932
|
+
fileContract,
|
|
933
|
+
key,
|
|
934
|
+
hexName,
|
|
935
|
+
i,
|
|
936
|
+
chunk,
|
|
937
|
+
resolvedFees,
|
|
938
|
+
callback
|
|
939
|
+
);
|
|
940
|
+
uploadResult = await this.#txSenderChecked.getTransactionResult(txResponse.hash);
|
|
941
|
+
} catch (e) {
|
|
942
|
+
callback.onFail(e);
|
|
943
|
+
break;
|
|
944
|
+
}
|
|
945
|
+
totalCost += uploadResult.txCost.normalGasCost;
|
|
946
|
+
if (!uploadResult.success) {
|
|
947
|
+
callback.onFail(new Error("FlatDirectory: Sending transaction failed."));
|
|
948
|
+
break;
|
|
949
|
+
}
|
|
950
|
+
callback.onProgress(i, calldataChunkCount, true);
|
|
951
|
+
totalUploadChunks++;
|
|
952
|
+
totalUploadSize += chunk.length;
|
|
953
|
+
}
|
|
954
|
+
callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
|
|
955
|
+
}
|
|
956
|
+
async #clearOldFile(contract, key, chunkLength, oldChunkLength, resolvedFees) {
|
|
957
|
+
if (oldChunkLength <= chunkLength) {
|
|
958
|
+
return { success: true, gasCost: 0n };
|
|
959
|
+
}
|
|
960
|
+
try {
|
|
961
|
+
const tx = await contract.truncate.populateTransaction(key, chunkLength);
|
|
962
|
+
this.#applyGasFees(tx, resolvedFees);
|
|
963
|
+
const txResponse = await this.#txSenderChecked.sendTx(tx);
|
|
964
|
+
this.#log(`Truncate transaction sent (Key: ${key}, New length: ${chunkLength}). Hash: ${txResponse.hash}`);
|
|
965
|
+
let receipt;
|
|
966
|
+
try {
|
|
967
|
+
receipt = await txResponse.wait();
|
|
968
|
+
} catch {
|
|
969
|
+
const result = await this.#txSenderChecked.getTransactionResult(txResponse.hash).catch(() => null);
|
|
970
|
+
return {
|
|
971
|
+
success: result?.success === true,
|
|
972
|
+
gasCost: result?.txCost.normalGasCost ?? 0n
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
const gasCost = receipt ? receipt.gasUsed * receipt.gasPrice : 0n;
|
|
976
|
+
return { success: receipt?.status === 1, gasCost };
|
|
977
|
+
} catch (e) {
|
|
978
|
+
this.#log(`Failed to truncate old data for file (Key: ${key}). ${e.message || e}`, true);
|
|
979
|
+
return { success: false, gasCost: 0n };
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
// call data helper
|
|
983
|
+
#calculateCalldataChunkDetails(content, maxChunkSize) {
|
|
984
|
+
const maxChunkSizeBytes = toChunkSizeBytes(maxChunkSize);
|
|
985
|
+
const getChunkInfo = (size) => ({
|
|
986
|
+
chunkDataSize: size > maxChunkSizeBytes ? maxChunkSizeBytes : size,
|
|
987
|
+
calldataChunkCount: size > maxChunkSizeBytes ? Math.ceil(size / maxChunkSizeBytes) : 1
|
|
988
|
+
});
|
|
989
|
+
if (isFile(content)) {
|
|
990
|
+
return getChunkInfo(content.size);
|
|
991
|
+
} else if (isBuffer(content)) {
|
|
992
|
+
return getChunkInfo(content.length);
|
|
993
|
+
}
|
|
994
|
+
return { chunkDataSize: -1, calldataChunkCount: 1 };
|
|
995
|
+
}
|
|
996
|
+
async #sendCalldataTx(fileContract, key, hexName, chunkId, chunk, resolvedFees, callback) {
|
|
997
|
+
const hexData = import_ethers3.ethers.hexlify(chunk);
|
|
998
|
+
const tx = await fileContract["writeChunkByCalldata"].populateTransaction(hexName, chunkId, hexData);
|
|
999
|
+
this.#applyGasFees(tx, resolvedFees);
|
|
1000
|
+
const txResponse = await this.#txSenderChecked.sendTx(tx);
|
|
1001
|
+
this.#logTransactionHash(key, chunkId, txResponse.hash, callback);
|
|
1002
|
+
return txResponse;
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Copy resolved gas fees onto a transaction request, by fee model:
|
|
1006
|
+
* legacy chains use gasPrice, EIP-1559 chains use max/priority fee fields.
|
|
1007
|
+
*/
|
|
1008
|
+
#applyGasFees(tx, resolvedFees) {
|
|
1009
|
+
if (resolvedFees.type === "legacy") {
|
|
1010
|
+
tx.gasPrice = resolvedFees.gasPrice;
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
tx.maxFeePerGas = resolvedFees.maxFeePerGas;
|
|
1014
|
+
tx.maxPriorityFeePerGas = resolvedFees.maxPriorityFeePerGas;
|
|
1015
|
+
}
|
|
1016
|
+
#logTransactionHash(key, chunkId, hash, callback) {
|
|
1017
|
+
const primaryMessage = `Transaction hash: ${hash} for chunk ${chunkId}.`;
|
|
1018
|
+
const fullMessage = `${primaryMessage} (Key: ${key})`;
|
|
1019
|
+
this.#log(fullMessage);
|
|
1020
|
+
if (callback?.onTransactionSent) {
|
|
1021
|
+
callback.onTransactionSent(hash, chunkId);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
#log(message, isError = false) {
|
|
1025
|
+
if (!this.#isLoggingEnabled) return;
|
|
1026
|
+
const prefix = "FlatDirectory: ";
|
|
1027
|
+
if (isError) {
|
|
1028
|
+
console.error(`${prefix}${message}`);
|
|
1029
|
+
} else {
|
|
1030
|
+
console.log(`${prefix}${message}`);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
// -------------------- Getters --------------------
|
|
1034
|
+
get #contractAddrChecked() {
|
|
1035
|
+
if (!this.#contractAddr) throw new Error("FlatDirectory: Not deployed!");
|
|
1036
|
+
return this.#contractAddr;
|
|
1037
|
+
}
|
|
1038
|
+
get #ethStorageRpcChecked() {
|
|
1039
|
+
if (!this.#ethStorageRpc) throw new Error("FlatDirectory: 'ethStorageRpc' required.");
|
|
1040
|
+
return this.#ethStorageRpc;
|
|
1041
|
+
}
|
|
1042
|
+
get #walletChecked() {
|
|
1043
|
+
if (!this.#wallet) throw new Error("FlatDirectory: Private key required.");
|
|
1044
|
+
return this.#wallet;
|
|
1045
|
+
}
|
|
1046
|
+
get #txSenderChecked() {
|
|
1047
|
+
if (!this.#txSender) throw new Error("FlatDirectory: TxSender not initialized.");
|
|
1048
|
+
return this.#txSender;
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1052
|
+
0 && (module.exports = {
|
|
1053
|
+
AVALANCHE_GAS_CHAIN_IDS,
|
|
1054
|
+
AVALANCHE_MIN_BASE_FEE_PER_GAS,
|
|
1055
|
+
BASE_FEE_MULTIPLIER_DENOMINATOR,
|
|
1056
|
+
BASE_FEE_MULTIPLIER_NUMERATOR,
|
|
1057
|
+
CALLDATA_CHUNK_OVERHEAD,
|
|
1058
|
+
CONTRACT_MAX_CHUNK_SIZE,
|
|
1059
|
+
DEFAULT_MAX_CHUNK_SIZE_KB,
|
|
1060
|
+
DEFAULT_PRIORITY_FEE_PER_GAS,
|
|
1061
|
+
ETHSTORAGE_MAPPING,
|
|
1062
|
+
FlatDirectory,
|
|
1063
|
+
FlatDirectoryAbi,
|
|
1064
|
+
FlatDirectoryBytecode,
|
|
1065
|
+
LEGACY_GAS_CHAIN_IDS,
|
|
1066
|
+
MAX_CHUNKS,
|
|
1067
|
+
toChunkSizeBytes,
|
|
1068
|
+
utils
|
|
1069
|
+
});
|