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.
@@ -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
+ });