@lombard.finance/sdk-agentkit 0.1.0
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/LICENSE +21 -0
- package/README.md +125 -0
- package/dist/index.cjs +3738 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3719 -0
- package/dist/lombardActionProvider.d.ts +44 -0
- package/dist/lombardActionProvider.d.ts.map +1 -0
- package/dist/networks.d.ts +43 -0
- package/dist/networks.d.ts.map +1 -0
- package/dist/schemas.d.ts +72 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/utils.d.ts +28 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +74 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3719 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { CreateAction, EvmWalletProvider, ActionProvider } from '@coinbase/agentkit';
|
|
3
|
+
import { ChainId, CHAIN_ID_TO_VIEM_CHAIN_MAP, makePublicClient, getTokenContractInfo, getAssetRouterAddress, Token, getTokenAllowance, approveToken, depositToken, unstakeLBTC, redeemToken, depositEarn, getDepositsByAddress, getDepositStatus, getDepositStatusDisplay, claimLBTC, getLBTCExchangeRate, fromSatoshi, getUnstakesByAddress, requiresAutoMintFee, getNetworkFeeSignature, getLBTCMintingFee, signNetworkFee, storeNetworkFeeSignature } from '@lombard.finance/sdk';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { Env } from '@lombard.finance/sdk-common';
|
|
6
|
+
import { formatUnits as formatUnits$1 } from 'viem';
|
|
7
|
+
|
|
8
|
+
function defineChain(chain) {
|
|
9
|
+
const chainInstance = {
|
|
10
|
+
formatters: undefined,
|
|
11
|
+
fees: undefined,
|
|
12
|
+
serializers: undefined,
|
|
13
|
+
...chain,
|
|
14
|
+
};
|
|
15
|
+
function extend(base) {
|
|
16
|
+
return (fnOrExtended) => {
|
|
17
|
+
const properties = (typeof fnOrExtended === 'function' ? fnOrExtended(base) : fnOrExtended);
|
|
18
|
+
const combined = { ...base, ...properties };
|
|
19
|
+
return Object.assign(combined, { extend: extend(combined) });
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return Object.assign(chainInstance, {
|
|
23
|
+
extend: extend(chainInstance),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const version = '2.47.6';
|
|
28
|
+
|
|
29
|
+
let errorConfig = {
|
|
30
|
+
getDocsUrl: ({ docsBaseUrl, docsPath = '', docsSlug, }) => docsPath
|
|
31
|
+
? `${docsBaseUrl ?? 'https://viem.sh'}${docsPath}${docsSlug ? `#${docsSlug}` : ''}`
|
|
32
|
+
: undefined,
|
|
33
|
+
version: `viem@${version}`,
|
|
34
|
+
};
|
|
35
|
+
class BaseError extends Error {
|
|
36
|
+
constructor(shortMessage, args = {}) {
|
|
37
|
+
const details = (() => {
|
|
38
|
+
if (args.cause instanceof BaseError)
|
|
39
|
+
return args.cause.details;
|
|
40
|
+
if (args.cause?.message)
|
|
41
|
+
return args.cause.message;
|
|
42
|
+
return args.details;
|
|
43
|
+
})();
|
|
44
|
+
const docsPath = (() => {
|
|
45
|
+
if (args.cause instanceof BaseError)
|
|
46
|
+
return args.cause.docsPath || args.docsPath;
|
|
47
|
+
return args.docsPath;
|
|
48
|
+
})();
|
|
49
|
+
const docsUrl = errorConfig.getDocsUrl?.({ ...args, docsPath });
|
|
50
|
+
const message = [
|
|
51
|
+
shortMessage || 'An error occurred.',
|
|
52
|
+
'',
|
|
53
|
+
...(args.metaMessages ? [...args.metaMessages, ''] : []),
|
|
54
|
+
...(docsUrl ? [`Docs: ${docsUrl}`] : []),
|
|
55
|
+
...(details ? [`Details: ${details}`] : []),
|
|
56
|
+
...(errorConfig.version ? [`Version: ${errorConfig.version}`] : []),
|
|
57
|
+
].join('\n');
|
|
58
|
+
super(message, args.cause ? { cause: args.cause } : undefined);
|
|
59
|
+
Object.defineProperty(this, "details", {
|
|
60
|
+
enumerable: true,
|
|
61
|
+
configurable: true,
|
|
62
|
+
writable: true,
|
|
63
|
+
value: void 0
|
|
64
|
+
});
|
|
65
|
+
Object.defineProperty(this, "docsPath", {
|
|
66
|
+
enumerable: true,
|
|
67
|
+
configurable: true,
|
|
68
|
+
writable: true,
|
|
69
|
+
value: void 0
|
|
70
|
+
});
|
|
71
|
+
Object.defineProperty(this, "metaMessages", {
|
|
72
|
+
enumerable: true,
|
|
73
|
+
configurable: true,
|
|
74
|
+
writable: true,
|
|
75
|
+
value: void 0
|
|
76
|
+
});
|
|
77
|
+
Object.defineProperty(this, "shortMessage", {
|
|
78
|
+
enumerable: true,
|
|
79
|
+
configurable: true,
|
|
80
|
+
writable: true,
|
|
81
|
+
value: void 0
|
|
82
|
+
});
|
|
83
|
+
Object.defineProperty(this, "version", {
|
|
84
|
+
enumerable: true,
|
|
85
|
+
configurable: true,
|
|
86
|
+
writable: true,
|
|
87
|
+
value: void 0
|
|
88
|
+
});
|
|
89
|
+
Object.defineProperty(this, "name", {
|
|
90
|
+
enumerable: true,
|
|
91
|
+
configurable: true,
|
|
92
|
+
writable: true,
|
|
93
|
+
value: 'BaseError'
|
|
94
|
+
});
|
|
95
|
+
this.details = details;
|
|
96
|
+
this.docsPath = docsPath;
|
|
97
|
+
this.metaMessages = args.metaMessages;
|
|
98
|
+
this.name = args.name ?? this.name;
|
|
99
|
+
this.shortMessage = shortMessage;
|
|
100
|
+
this.version = version;
|
|
101
|
+
}
|
|
102
|
+
walk(fn) {
|
|
103
|
+
return walk(this, fn);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function walk(err, fn) {
|
|
107
|
+
if (fn?.(err))
|
|
108
|
+
return err;
|
|
109
|
+
if (err &&
|
|
110
|
+
typeof err === 'object' &&
|
|
111
|
+
'cause' in err &&
|
|
112
|
+
err.cause !== undefined)
|
|
113
|
+
return walk(err.cause, fn);
|
|
114
|
+
return fn ? null : err;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
class IntegerOutOfRangeError extends BaseError {
|
|
118
|
+
constructor({ max, min, signed, size, value, }) {
|
|
119
|
+
super(`Number "${value}" is not in safe ${size ? `${size * 8}-bit ${signed ? 'signed' : 'unsigned'} ` : ''}integer range ${max ? `(${min} to ${max})` : `(above ${min})`}`, { name: 'IntegerOutOfRangeError' });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
class SizeOverflowError extends BaseError {
|
|
123
|
+
constructor({ givenSize, maxSize }) {
|
|
124
|
+
super(`Size cannot exceed ${maxSize} bytes. Given size: ${givenSize} bytes.`, { name: 'SizeOverflowError' });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isHex(value, { strict = true } = {}) {
|
|
129
|
+
if (!value)
|
|
130
|
+
return false;
|
|
131
|
+
if (typeof value !== 'string')
|
|
132
|
+
return false;
|
|
133
|
+
return strict ? /^0x[0-9a-fA-F]*$/.test(value) : value.startsWith('0x');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @description Retrieves the size of the value (in bytes).
|
|
138
|
+
*
|
|
139
|
+
* @param value The value (hex or byte array) to retrieve the size of.
|
|
140
|
+
* @returns The size of the value (in bytes).
|
|
141
|
+
*/
|
|
142
|
+
function size(value) {
|
|
143
|
+
if (isHex(value, { strict: false }))
|
|
144
|
+
return Math.ceil((value.length - 2) / 2);
|
|
145
|
+
return value.length;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function trim(hexOrBytes, { dir = 'left' } = {}) {
|
|
149
|
+
let data = typeof hexOrBytes === 'string' ? hexOrBytes.replace('0x', '') : hexOrBytes;
|
|
150
|
+
let sliceLength = 0;
|
|
151
|
+
for (let i = 0; i < data.length - 1; i++) {
|
|
152
|
+
if (data[dir === 'left' ? i : data.length - i - 1].toString() === '0')
|
|
153
|
+
sliceLength++;
|
|
154
|
+
else
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
data =
|
|
158
|
+
dir === 'left'
|
|
159
|
+
? data.slice(sliceLength)
|
|
160
|
+
: data.slice(0, data.length - sliceLength);
|
|
161
|
+
if (typeof hexOrBytes === 'string') {
|
|
162
|
+
if (data.length === 1 && dir === 'right')
|
|
163
|
+
data = `${data}0`;
|
|
164
|
+
return `0x${data.length % 2 === 1 ? `0${data}` : data}`;
|
|
165
|
+
}
|
|
166
|
+
return data;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
class SliceOffsetOutOfBoundsError extends BaseError {
|
|
170
|
+
constructor({ offset, position, size, }) {
|
|
171
|
+
super(`Slice ${position === 'start' ? 'starting' : 'ending'} at offset "${offset}" is out-of-bounds (size: ${size}).`, { name: 'SliceOffsetOutOfBoundsError' });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
class SizeExceedsPaddingSizeError extends BaseError {
|
|
175
|
+
constructor({ size, targetSize, type, }) {
|
|
176
|
+
super(`${type.charAt(0).toUpperCase()}${type
|
|
177
|
+
.slice(1)
|
|
178
|
+
.toLowerCase()} size (${size}) exceeds padding size (${targetSize}).`, { name: 'SizeExceedsPaddingSizeError' });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function pad(hexOrBytes, { dir, size = 32 } = {}) {
|
|
183
|
+
if (typeof hexOrBytes === 'string')
|
|
184
|
+
return padHex(hexOrBytes, { dir, size });
|
|
185
|
+
return padBytes(hexOrBytes, { dir, size });
|
|
186
|
+
}
|
|
187
|
+
function padHex(hex_, { dir, size = 32 } = {}) {
|
|
188
|
+
if (size === null)
|
|
189
|
+
return hex_;
|
|
190
|
+
const hex = hex_.replace('0x', '');
|
|
191
|
+
if (hex.length > size * 2)
|
|
192
|
+
throw new SizeExceedsPaddingSizeError({
|
|
193
|
+
size: Math.ceil(hex.length / 2),
|
|
194
|
+
targetSize: size,
|
|
195
|
+
type: 'hex',
|
|
196
|
+
});
|
|
197
|
+
return `0x${hex[dir === 'right' ? 'padEnd' : 'padStart'](size * 2, '0')}`;
|
|
198
|
+
}
|
|
199
|
+
function padBytes(bytes, { dir, size = 32 } = {}) {
|
|
200
|
+
if (size === null)
|
|
201
|
+
return bytes;
|
|
202
|
+
if (bytes.length > size)
|
|
203
|
+
throw new SizeExceedsPaddingSizeError({
|
|
204
|
+
size: bytes.length,
|
|
205
|
+
targetSize: size,
|
|
206
|
+
type: 'bytes',
|
|
207
|
+
});
|
|
208
|
+
const paddedBytes = new Uint8Array(size);
|
|
209
|
+
for (let i = 0; i < size; i++) {
|
|
210
|
+
const padEnd = dir === 'right';
|
|
211
|
+
paddedBytes[padEnd ? i : size - i - 1] =
|
|
212
|
+
bytes[padEnd ? i : bytes.length - i - 1];
|
|
213
|
+
}
|
|
214
|
+
return paddedBytes;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const hexes = /*#__PURE__*/ Array.from({ length: 256 }, (_v, i) => i.toString(16).padStart(2, '0'));
|
|
218
|
+
/**
|
|
219
|
+
* Encodes a string, number, bigint, or ByteArray into a hex string
|
|
220
|
+
*
|
|
221
|
+
* - Docs: https://viem.sh/docs/utilities/toHex
|
|
222
|
+
* - Example: https://viem.sh/docs/utilities/toHex#usage
|
|
223
|
+
*
|
|
224
|
+
* @param value Value to encode.
|
|
225
|
+
* @param opts Options.
|
|
226
|
+
* @returns Hex value.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* import { toHex } from 'viem'
|
|
230
|
+
* const data = toHex('Hello world')
|
|
231
|
+
* // '0x48656c6c6f20776f726c6421'
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* import { toHex } from 'viem'
|
|
235
|
+
* const data = toHex(420)
|
|
236
|
+
* // '0x1a4'
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* import { toHex } from 'viem'
|
|
240
|
+
* const data = toHex('Hello world', { size: 32 })
|
|
241
|
+
* // '0x48656c6c6f20776f726c64210000000000000000000000000000000000000000'
|
|
242
|
+
*/
|
|
243
|
+
function toHex(value, opts = {}) {
|
|
244
|
+
if (typeof value === 'number' || typeof value === 'bigint')
|
|
245
|
+
return numberToHex(value, opts);
|
|
246
|
+
if (typeof value === 'string') {
|
|
247
|
+
return stringToHex(value, opts);
|
|
248
|
+
}
|
|
249
|
+
if (typeof value === 'boolean')
|
|
250
|
+
return boolToHex(value, opts);
|
|
251
|
+
return bytesToHex(value, opts);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Encodes a boolean into a hex string
|
|
255
|
+
*
|
|
256
|
+
* - Docs: https://viem.sh/docs/utilities/toHex#booltohex
|
|
257
|
+
*
|
|
258
|
+
* @param value Value to encode.
|
|
259
|
+
* @param opts Options.
|
|
260
|
+
* @returns Hex value.
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* import { boolToHex } from 'viem'
|
|
264
|
+
* const data = boolToHex(true)
|
|
265
|
+
* // '0x1'
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* import { boolToHex } from 'viem'
|
|
269
|
+
* const data = boolToHex(false)
|
|
270
|
+
* // '0x0'
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* import { boolToHex } from 'viem'
|
|
274
|
+
* const data = boolToHex(true, { size: 32 })
|
|
275
|
+
* // '0x0000000000000000000000000000000000000000000000000000000000000001'
|
|
276
|
+
*/
|
|
277
|
+
function boolToHex(value, opts = {}) {
|
|
278
|
+
const hex = `0x${Number(value)}`;
|
|
279
|
+
if (typeof opts.size === 'number') {
|
|
280
|
+
assertSize(hex, { size: opts.size });
|
|
281
|
+
return pad(hex, { size: opts.size });
|
|
282
|
+
}
|
|
283
|
+
return hex;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Encodes a bytes array into a hex string
|
|
287
|
+
*
|
|
288
|
+
* - Docs: https://viem.sh/docs/utilities/toHex#bytestohex
|
|
289
|
+
*
|
|
290
|
+
* @param value Value to encode.
|
|
291
|
+
* @param opts Options.
|
|
292
|
+
* @returns Hex value.
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* import { bytesToHex } from 'viem'
|
|
296
|
+
* const data = bytesToHex(Uint8Array.from([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])
|
|
297
|
+
* // '0x48656c6c6f20576f726c6421'
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* import { bytesToHex } from 'viem'
|
|
301
|
+
* const data = bytesToHex(Uint8Array.from([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]), { size: 32 })
|
|
302
|
+
* // '0x48656c6c6f20576f726c64210000000000000000000000000000000000000000'
|
|
303
|
+
*/
|
|
304
|
+
function bytesToHex(value, opts = {}) {
|
|
305
|
+
let string = '';
|
|
306
|
+
for (let i = 0; i < value.length; i++) {
|
|
307
|
+
string += hexes[value[i]];
|
|
308
|
+
}
|
|
309
|
+
const hex = `0x${string}`;
|
|
310
|
+
if (typeof opts.size === 'number') {
|
|
311
|
+
assertSize(hex, { size: opts.size });
|
|
312
|
+
return pad(hex, { dir: 'right', size: opts.size });
|
|
313
|
+
}
|
|
314
|
+
return hex;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Encodes a number or bigint into a hex string
|
|
318
|
+
*
|
|
319
|
+
* - Docs: https://viem.sh/docs/utilities/toHex#numbertohex
|
|
320
|
+
*
|
|
321
|
+
* @param value Value to encode.
|
|
322
|
+
* @param opts Options.
|
|
323
|
+
* @returns Hex value.
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* import { numberToHex } from 'viem'
|
|
327
|
+
* const data = numberToHex(420)
|
|
328
|
+
* // '0x1a4'
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* import { numberToHex } from 'viem'
|
|
332
|
+
* const data = numberToHex(420, { size: 32 })
|
|
333
|
+
* // '0x00000000000000000000000000000000000000000000000000000000000001a4'
|
|
334
|
+
*/
|
|
335
|
+
function numberToHex(value_, opts = {}) {
|
|
336
|
+
const { signed, size } = opts;
|
|
337
|
+
const value = BigInt(value_);
|
|
338
|
+
let maxValue;
|
|
339
|
+
if (size) {
|
|
340
|
+
if (signed)
|
|
341
|
+
maxValue = (1n << (BigInt(size) * 8n - 1n)) - 1n;
|
|
342
|
+
else
|
|
343
|
+
maxValue = 2n ** (BigInt(size) * 8n) - 1n;
|
|
344
|
+
}
|
|
345
|
+
else if (typeof value_ === 'number') {
|
|
346
|
+
maxValue = BigInt(Number.MAX_SAFE_INTEGER);
|
|
347
|
+
}
|
|
348
|
+
const minValue = typeof maxValue === 'bigint' && signed ? -maxValue - 1n : 0;
|
|
349
|
+
if ((maxValue && value > maxValue) || value < minValue) {
|
|
350
|
+
const suffix = typeof value_ === 'bigint' ? 'n' : '';
|
|
351
|
+
throw new IntegerOutOfRangeError({
|
|
352
|
+
max: maxValue ? `${maxValue}${suffix}` : undefined,
|
|
353
|
+
min: `${minValue}${suffix}`,
|
|
354
|
+
signed,
|
|
355
|
+
size,
|
|
356
|
+
value: `${value_}${suffix}`,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
const hex = `0x${(signed && value < 0 ? (1n << BigInt(size * 8)) + BigInt(value) : value).toString(16)}`;
|
|
360
|
+
if (size)
|
|
361
|
+
return pad(hex, { size });
|
|
362
|
+
return hex;
|
|
363
|
+
}
|
|
364
|
+
const encoder$1 = /*#__PURE__*/ new TextEncoder();
|
|
365
|
+
/**
|
|
366
|
+
* Encodes a UTF-8 string into a hex string
|
|
367
|
+
*
|
|
368
|
+
* - Docs: https://viem.sh/docs/utilities/toHex#stringtohex
|
|
369
|
+
*
|
|
370
|
+
* @param value Value to encode.
|
|
371
|
+
* @param opts Options.
|
|
372
|
+
* @returns Hex value.
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* import { stringToHex } from 'viem'
|
|
376
|
+
* const data = stringToHex('Hello World!')
|
|
377
|
+
* // '0x48656c6c6f20576f726c6421'
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* import { stringToHex } from 'viem'
|
|
381
|
+
* const data = stringToHex('Hello World!', { size: 32 })
|
|
382
|
+
* // '0x48656c6c6f20576f726c64210000000000000000000000000000000000000000'
|
|
383
|
+
*/
|
|
384
|
+
function stringToHex(value_, opts = {}) {
|
|
385
|
+
const value = encoder$1.encode(value_);
|
|
386
|
+
return bytesToHex(value, opts);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const encoder = /*#__PURE__*/ new TextEncoder();
|
|
390
|
+
/**
|
|
391
|
+
* Encodes a UTF-8 string, hex value, bigint, number or boolean to a byte array.
|
|
392
|
+
*
|
|
393
|
+
* - Docs: https://viem.sh/docs/utilities/toBytes
|
|
394
|
+
* - Example: https://viem.sh/docs/utilities/toBytes#usage
|
|
395
|
+
*
|
|
396
|
+
* @param value Value to encode.
|
|
397
|
+
* @param opts Options.
|
|
398
|
+
* @returns Byte array value.
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* import { toBytes } from 'viem'
|
|
402
|
+
* const data = toBytes('Hello world')
|
|
403
|
+
* // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* import { toBytes } from 'viem'
|
|
407
|
+
* const data = toBytes(420)
|
|
408
|
+
* // Uint8Array([1, 164])
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* import { toBytes } from 'viem'
|
|
412
|
+
* const data = toBytes(420, { size: 4 })
|
|
413
|
+
* // Uint8Array([0, 0, 1, 164])
|
|
414
|
+
*/
|
|
415
|
+
function toBytes$1(value, opts = {}) {
|
|
416
|
+
if (typeof value === 'number' || typeof value === 'bigint')
|
|
417
|
+
return numberToBytes(value, opts);
|
|
418
|
+
if (typeof value === 'boolean')
|
|
419
|
+
return boolToBytes(value, opts);
|
|
420
|
+
if (isHex(value))
|
|
421
|
+
return hexToBytes(value, opts);
|
|
422
|
+
return stringToBytes(value, opts);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Encodes a boolean into a byte array.
|
|
426
|
+
*
|
|
427
|
+
* - Docs: https://viem.sh/docs/utilities/toBytes#booltobytes
|
|
428
|
+
*
|
|
429
|
+
* @param value Boolean value to encode.
|
|
430
|
+
* @param opts Options.
|
|
431
|
+
* @returns Byte array value.
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* import { boolToBytes } from 'viem'
|
|
435
|
+
* const data = boolToBytes(true)
|
|
436
|
+
* // Uint8Array([1])
|
|
437
|
+
*
|
|
438
|
+
* @example
|
|
439
|
+
* import { boolToBytes } from 'viem'
|
|
440
|
+
* const data = boolToBytes(true, { size: 32 })
|
|
441
|
+
* // Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
|
|
442
|
+
*/
|
|
443
|
+
function boolToBytes(value, opts = {}) {
|
|
444
|
+
const bytes = new Uint8Array(1);
|
|
445
|
+
bytes[0] = Number(value);
|
|
446
|
+
if (typeof opts.size === 'number') {
|
|
447
|
+
assertSize(bytes, { size: opts.size });
|
|
448
|
+
return pad(bytes, { size: opts.size });
|
|
449
|
+
}
|
|
450
|
+
return bytes;
|
|
451
|
+
}
|
|
452
|
+
// We use very optimized technique to convert hex string to byte array
|
|
453
|
+
const charCodeMap = {
|
|
454
|
+
zero: 48,
|
|
455
|
+
nine: 57,
|
|
456
|
+
A: 65,
|
|
457
|
+
F: 70,
|
|
458
|
+
a: 97,
|
|
459
|
+
f: 102,
|
|
460
|
+
};
|
|
461
|
+
function charCodeToBase16(char) {
|
|
462
|
+
if (char >= charCodeMap.zero && char <= charCodeMap.nine)
|
|
463
|
+
return char - charCodeMap.zero;
|
|
464
|
+
if (char >= charCodeMap.A && char <= charCodeMap.F)
|
|
465
|
+
return char - (charCodeMap.A - 10);
|
|
466
|
+
if (char >= charCodeMap.a && char <= charCodeMap.f)
|
|
467
|
+
return char - (charCodeMap.a - 10);
|
|
468
|
+
return undefined;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Encodes a hex string into a byte array.
|
|
472
|
+
*
|
|
473
|
+
* - Docs: https://viem.sh/docs/utilities/toBytes#hextobytes
|
|
474
|
+
*
|
|
475
|
+
* @param hex Hex string to encode.
|
|
476
|
+
* @param opts Options.
|
|
477
|
+
* @returns Byte array value.
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* import { hexToBytes } from 'viem'
|
|
481
|
+
* const data = hexToBytes('0x48656c6c6f20776f726c6421')
|
|
482
|
+
* // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* import { hexToBytes } from 'viem'
|
|
486
|
+
* const data = hexToBytes('0x48656c6c6f20776f726c6421', { size: 32 })
|
|
487
|
+
* // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
|
488
|
+
*/
|
|
489
|
+
function hexToBytes(hex_, opts = {}) {
|
|
490
|
+
let hex = hex_;
|
|
491
|
+
if (opts.size) {
|
|
492
|
+
assertSize(hex, { size: opts.size });
|
|
493
|
+
hex = pad(hex, { dir: 'right', size: opts.size });
|
|
494
|
+
}
|
|
495
|
+
let hexString = hex.slice(2);
|
|
496
|
+
if (hexString.length % 2)
|
|
497
|
+
hexString = `0${hexString}`;
|
|
498
|
+
const length = hexString.length / 2;
|
|
499
|
+
const bytes = new Uint8Array(length);
|
|
500
|
+
for (let index = 0, j = 0; index < length; index++) {
|
|
501
|
+
const nibbleLeft = charCodeToBase16(hexString.charCodeAt(j++));
|
|
502
|
+
const nibbleRight = charCodeToBase16(hexString.charCodeAt(j++));
|
|
503
|
+
if (nibbleLeft === undefined || nibbleRight === undefined) {
|
|
504
|
+
throw new BaseError(`Invalid byte sequence ("${hexString[j - 2]}${hexString[j - 1]}" in "${hexString}").`);
|
|
505
|
+
}
|
|
506
|
+
bytes[index] = nibbleLeft * 16 + nibbleRight;
|
|
507
|
+
}
|
|
508
|
+
return bytes;
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Encodes a number into a byte array.
|
|
512
|
+
*
|
|
513
|
+
* - Docs: https://viem.sh/docs/utilities/toBytes#numbertobytes
|
|
514
|
+
*
|
|
515
|
+
* @param value Number to encode.
|
|
516
|
+
* @param opts Options.
|
|
517
|
+
* @returns Byte array value.
|
|
518
|
+
*
|
|
519
|
+
* @example
|
|
520
|
+
* import { numberToBytes } from 'viem'
|
|
521
|
+
* const data = numberToBytes(420)
|
|
522
|
+
* // Uint8Array([1, 164])
|
|
523
|
+
*
|
|
524
|
+
* @example
|
|
525
|
+
* import { numberToBytes } from 'viem'
|
|
526
|
+
* const data = numberToBytes(420, { size: 4 })
|
|
527
|
+
* // Uint8Array([0, 0, 1, 164])
|
|
528
|
+
*/
|
|
529
|
+
function numberToBytes(value, opts) {
|
|
530
|
+
const hex = numberToHex(value, opts);
|
|
531
|
+
return hexToBytes(hex);
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Encodes a UTF-8 string into a byte array.
|
|
535
|
+
*
|
|
536
|
+
* - Docs: https://viem.sh/docs/utilities/toBytes#stringtobytes
|
|
537
|
+
*
|
|
538
|
+
* @param value String to encode.
|
|
539
|
+
* @param opts Options.
|
|
540
|
+
* @returns Byte array value.
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* import { stringToBytes } from 'viem'
|
|
544
|
+
* const data = stringToBytes('Hello world!')
|
|
545
|
+
* // Uint8Array([72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33])
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* import { stringToBytes } from 'viem'
|
|
549
|
+
* const data = stringToBytes('Hello world!', { size: 32 })
|
|
550
|
+
* // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
|
551
|
+
*/
|
|
552
|
+
function stringToBytes(value, opts = {}) {
|
|
553
|
+
const bytes = encoder.encode(value);
|
|
554
|
+
if (typeof opts.size === 'number') {
|
|
555
|
+
assertSize(bytes, { size: opts.size });
|
|
556
|
+
return pad(bytes, { dir: 'right', size: opts.size });
|
|
557
|
+
}
|
|
558
|
+
return bytes;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function assertSize(hexOrBytes, { size: size$1 }) {
|
|
562
|
+
if (size(hexOrBytes) > size$1)
|
|
563
|
+
throw new SizeOverflowError({
|
|
564
|
+
givenSize: size(hexOrBytes),
|
|
565
|
+
maxSize: size$1,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Decodes a hex value into a bigint.
|
|
570
|
+
*
|
|
571
|
+
* - Docs: https://viem.sh/docs/utilities/fromHex#hextobigint
|
|
572
|
+
*
|
|
573
|
+
* @param hex Hex value to decode.
|
|
574
|
+
* @param opts Options.
|
|
575
|
+
* @returns BigInt value.
|
|
576
|
+
*
|
|
577
|
+
* @example
|
|
578
|
+
* import { hexToBigInt } from 'viem'
|
|
579
|
+
* const data = hexToBigInt('0x1a4', { signed: true })
|
|
580
|
+
* // 420n
|
|
581
|
+
*
|
|
582
|
+
* @example
|
|
583
|
+
* import { hexToBigInt } from 'viem'
|
|
584
|
+
* const data = hexToBigInt('0x00000000000000000000000000000000000000000000000000000000000001a4', { size: 32 })
|
|
585
|
+
* // 420n
|
|
586
|
+
*/
|
|
587
|
+
function hexToBigInt(hex, opts = {}) {
|
|
588
|
+
const { signed } = opts;
|
|
589
|
+
if (opts.size)
|
|
590
|
+
assertSize(hex, { size: opts.size });
|
|
591
|
+
const value = BigInt(hex);
|
|
592
|
+
if (!signed)
|
|
593
|
+
return value;
|
|
594
|
+
const size = (hex.length - 2) / 2;
|
|
595
|
+
const max = (1n << (BigInt(size) * 8n - 1n)) - 1n;
|
|
596
|
+
if (value <= max)
|
|
597
|
+
return value;
|
|
598
|
+
return value - BigInt(`0x${'f'.padStart(size * 2, 'f')}`) - 1n;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Decodes a hex string into a number.
|
|
602
|
+
*
|
|
603
|
+
* - Docs: https://viem.sh/docs/utilities/fromHex#hextonumber
|
|
604
|
+
*
|
|
605
|
+
* @param hex Hex value to decode.
|
|
606
|
+
* @param opts Options.
|
|
607
|
+
* @returns Number value.
|
|
608
|
+
*
|
|
609
|
+
* @example
|
|
610
|
+
* import { hexToNumber } from 'viem'
|
|
611
|
+
* const data = hexToNumber('0x1a4')
|
|
612
|
+
* // 420
|
|
613
|
+
*
|
|
614
|
+
* @example
|
|
615
|
+
* import { hexToNumber } from 'viem'
|
|
616
|
+
* const data = hexToBigInt('0x00000000000000000000000000000000000000000000000000000000000001a4', { size: 32 })
|
|
617
|
+
* // 420
|
|
618
|
+
*/
|
|
619
|
+
function hexToNumber(hex, opts = {}) {
|
|
620
|
+
const value = hexToBigInt(hex, opts);
|
|
621
|
+
const number = Number(value);
|
|
622
|
+
if (!Number.isSafeInteger(number))
|
|
623
|
+
throw new IntegerOutOfRangeError({
|
|
624
|
+
max: `${Number.MAX_SAFE_INTEGER}`,
|
|
625
|
+
min: `${Number.MIN_SAFE_INTEGER}`,
|
|
626
|
+
signed: opts.signed,
|
|
627
|
+
size: opts.size,
|
|
628
|
+
value: `${value}n`,
|
|
629
|
+
});
|
|
630
|
+
return number;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function defineFormatter(type, format) {
|
|
634
|
+
return ({ exclude, format: overrides, }) => {
|
|
635
|
+
return {
|
|
636
|
+
exclude,
|
|
637
|
+
format: (args, action) => {
|
|
638
|
+
const formatted = format(args, action);
|
|
639
|
+
if (exclude) {
|
|
640
|
+
for (const key of exclude) {
|
|
641
|
+
delete formatted[key];
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
...formatted,
|
|
646
|
+
...overrides(args, action),
|
|
647
|
+
};
|
|
648
|
+
},
|
|
649
|
+
type,
|
|
650
|
+
};
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const transactionType = {
|
|
655
|
+
'0x0': 'legacy',
|
|
656
|
+
'0x1': 'eip2930',
|
|
657
|
+
'0x2': 'eip1559',
|
|
658
|
+
'0x3': 'eip4844',
|
|
659
|
+
'0x4': 'eip7702',
|
|
660
|
+
};
|
|
661
|
+
function formatTransaction(transaction, _) {
|
|
662
|
+
const transaction_ = {
|
|
663
|
+
...transaction,
|
|
664
|
+
blockHash: transaction.blockHash ? transaction.blockHash : null,
|
|
665
|
+
blockNumber: transaction.blockNumber
|
|
666
|
+
? BigInt(transaction.blockNumber)
|
|
667
|
+
: null,
|
|
668
|
+
chainId: transaction.chainId ? hexToNumber(transaction.chainId) : undefined,
|
|
669
|
+
gas: transaction.gas ? BigInt(transaction.gas) : undefined,
|
|
670
|
+
gasPrice: transaction.gasPrice ? BigInt(transaction.gasPrice) : undefined,
|
|
671
|
+
maxFeePerBlobGas: transaction.maxFeePerBlobGas
|
|
672
|
+
? BigInt(transaction.maxFeePerBlobGas)
|
|
673
|
+
: undefined,
|
|
674
|
+
maxFeePerGas: transaction.maxFeePerGas
|
|
675
|
+
? BigInt(transaction.maxFeePerGas)
|
|
676
|
+
: undefined,
|
|
677
|
+
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas
|
|
678
|
+
? BigInt(transaction.maxPriorityFeePerGas)
|
|
679
|
+
: undefined,
|
|
680
|
+
nonce: transaction.nonce ? hexToNumber(transaction.nonce) : undefined,
|
|
681
|
+
to: transaction.to ? transaction.to : null,
|
|
682
|
+
transactionIndex: transaction.transactionIndex
|
|
683
|
+
? Number(transaction.transactionIndex)
|
|
684
|
+
: null,
|
|
685
|
+
type: transaction.type
|
|
686
|
+
? transactionType[transaction.type]
|
|
687
|
+
: undefined,
|
|
688
|
+
typeHex: transaction.type ? transaction.type : undefined,
|
|
689
|
+
value: transaction.value ? BigInt(transaction.value) : undefined,
|
|
690
|
+
v: transaction.v ? BigInt(transaction.v) : undefined,
|
|
691
|
+
};
|
|
692
|
+
if (transaction.authorizationList)
|
|
693
|
+
transaction_.authorizationList = formatAuthorizationList(transaction.authorizationList);
|
|
694
|
+
transaction_.yParity = (() => {
|
|
695
|
+
// If `yParity` is provided, we will use it.
|
|
696
|
+
if (transaction.yParity)
|
|
697
|
+
return Number(transaction.yParity);
|
|
698
|
+
// If no `yParity` provided, try derive from `v`.
|
|
699
|
+
if (typeof transaction_.v === 'bigint') {
|
|
700
|
+
if (transaction_.v === 0n || transaction_.v === 27n)
|
|
701
|
+
return 0;
|
|
702
|
+
if (transaction_.v === 1n || transaction_.v === 28n)
|
|
703
|
+
return 1;
|
|
704
|
+
if (transaction_.v >= 35n)
|
|
705
|
+
return transaction_.v % 2n === 0n ? 1 : 0;
|
|
706
|
+
}
|
|
707
|
+
return undefined;
|
|
708
|
+
})();
|
|
709
|
+
if (transaction_.type === 'legacy') {
|
|
710
|
+
delete transaction_.accessList;
|
|
711
|
+
delete transaction_.maxFeePerBlobGas;
|
|
712
|
+
delete transaction_.maxFeePerGas;
|
|
713
|
+
delete transaction_.maxPriorityFeePerGas;
|
|
714
|
+
delete transaction_.yParity;
|
|
715
|
+
}
|
|
716
|
+
if (transaction_.type === 'eip2930') {
|
|
717
|
+
delete transaction_.maxFeePerBlobGas;
|
|
718
|
+
delete transaction_.maxFeePerGas;
|
|
719
|
+
delete transaction_.maxPriorityFeePerGas;
|
|
720
|
+
}
|
|
721
|
+
if (transaction_.type === 'eip1559')
|
|
722
|
+
delete transaction_.maxFeePerBlobGas;
|
|
723
|
+
return transaction_;
|
|
724
|
+
}
|
|
725
|
+
const defineTransaction = /*#__PURE__*/ defineFormatter('transaction', formatTransaction);
|
|
726
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
727
|
+
function formatAuthorizationList(authorizationList) {
|
|
728
|
+
return authorizationList.map((authorization) => ({
|
|
729
|
+
address: authorization.address,
|
|
730
|
+
chainId: Number(authorization.chainId),
|
|
731
|
+
nonce: Number(authorization.nonce),
|
|
732
|
+
r: authorization.r,
|
|
733
|
+
s: authorization.s,
|
|
734
|
+
yParity: Number(authorization.yParity),
|
|
735
|
+
}));
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
function formatBlock(block, _) {
|
|
739
|
+
const transactions = (block.transactions ?? []).map((transaction) => {
|
|
740
|
+
if (typeof transaction === 'string')
|
|
741
|
+
return transaction;
|
|
742
|
+
return formatTransaction(transaction);
|
|
743
|
+
});
|
|
744
|
+
return {
|
|
745
|
+
...block,
|
|
746
|
+
baseFeePerGas: block.baseFeePerGas ? BigInt(block.baseFeePerGas) : null,
|
|
747
|
+
blobGasUsed: block.blobGasUsed ? BigInt(block.blobGasUsed) : undefined,
|
|
748
|
+
difficulty: block.difficulty ? BigInt(block.difficulty) : undefined,
|
|
749
|
+
excessBlobGas: block.excessBlobGas
|
|
750
|
+
? BigInt(block.excessBlobGas)
|
|
751
|
+
: undefined,
|
|
752
|
+
gasLimit: block.gasLimit ? BigInt(block.gasLimit) : undefined,
|
|
753
|
+
gasUsed: block.gasUsed ? BigInt(block.gasUsed) : undefined,
|
|
754
|
+
hash: block.hash ? block.hash : null,
|
|
755
|
+
logsBloom: block.logsBloom ? block.logsBloom : null,
|
|
756
|
+
nonce: block.nonce ? block.nonce : null,
|
|
757
|
+
number: block.number ? BigInt(block.number) : null,
|
|
758
|
+
size: block.size ? BigInt(block.size) : undefined,
|
|
759
|
+
timestamp: block.timestamp ? BigInt(block.timestamp) : undefined,
|
|
760
|
+
transactions,
|
|
761
|
+
totalDifficulty: block.totalDifficulty
|
|
762
|
+
? BigInt(block.totalDifficulty)
|
|
763
|
+
: null,
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
const defineBlock = /*#__PURE__*/ defineFormatter('block', formatBlock);
|
|
767
|
+
|
|
768
|
+
function formatLog(log, { args, eventName, } = {}) {
|
|
769
|
+
return {
|
|
770
|
+
...log,
|
|
771
|
+
blockHash: log.blockHash ? log.blockHash : null,
|
|
772
|
+
blockNumber: log.blockNumber ? BigInt(log.blockNumber) : null,
|
|
773
|
+
blockTimestamp: log.blockTimestamp
|
|
774
|
+
? BigInt(log.blockTimestamp)
|
|
775
|
+
: log.blockTimestamp === null
|
|
776
|
+
? null
|
|
777
|
+
: undefined,
|
|
778
|
+
logIndex: log.logIndex ? Number(log.logIndex) : null,
|
|
779
|
+
transactionHash: log.transactionHash ? log.transactionHash : null,
|
|
780
|
+
transactionIndex: log.transactionIndex
|
|
781
|
+
? Number(log.transactionIndex)
|
|
782
|
+
: null,
|
|
783
|
+
...(eventName ? { args, eventName } : {}),
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const receiptStatuses = {
|
|
788
|
+
'0x0': 'reverted',
|
|
789
|
+
'0x1': 'success',
|
|
790
|
+
};
|
|
791
|
+
function formatTransactionReceipt(transactionReceipt, _) {
|
|
792
|
+
const receipt = {
|
|
793
|
+
...transactionReceipt,
|
|
794
|
+
blockNumber: transactionReceipt.blockNumber
|
|
795
|
+
? BigInt(transactionReceipt.blockNumber)
|
|
796
|
+
: null,
|
|
797
|
+
contractAddress: transactionReceipt.contractAddress
|
|
798
|
+
? transactionReceipt.contractAddress
|
|
799
|
+
: null,
|
|
800
|
+
cumulativeGasUsed: transactionReceipt.cumulativeGasUsed
|
|
801
|
+
? BigInt(transactionReceipt.cumulativeGasUsed)
|
|
802
|
+
: null,
|
|
803
|
+
effectiveGasPrice: transactionReceipt.effectiveGasPrice
|
|
804
|
+
? BigInt(transactionReceipt.effectiveGasPrice)
|
|
805
|
+
: null,
|
|
806
|
+
gasUsed: transactionReceipt.gasUsed
|
|
807
|
+
? BigInt(transactionReceipt.gasUsed)
|
|
808
|
+
: null,
|
|
809
|
+
logs: transactionReceipt.logs
|
|
810
|
+
? transactionReceipt.logs.map((log) => formatLog(log))
|
|
811
|
+
: null,
|
|
812
|
+
to: transactionReceipt.to ? transactionReceipt.to : null,
|
|
813
|
+
transactionIndex: transactionReceipt.transactionIndex
|
|
814
|
+
? hexToNumber(transactionReceipt.transactionIndex)
|
|
815
|
+
: null,
|
|
816
|
+
status: transactionReceipt.status
|
|
817
|
+
? receiptStatuses[transactionReceipt.status]
|
|
818
|
+
: null,
|
|
819
|
+
type: transactionReceipt.type
|
|
820
|
+
? transactionType[transactionReceipt.type] || transactionReceipt.type
|
|
821
|
+
: null,
|
|
822
|
+
};
|
|
823
|
+
if (transactionReceipt.blobGasPrice)
|
|
824
|
+
receipt.blobGasPrice = BigInt(transactionReceipt.blobGasPrice);
|
|
825
|
+
if (transactionReceipt.blobGasUsed)
|
|
826
|
+
receipt.blobGasUsed = BigInt(transactionReceipt.blobGasUsed);
|
|
827
|
+
return receipt;
|
|
828
|
+
}
|
|
829
|
+
const defineTransactionReceipt = /*#__PURE__*/ defineFormatter('transactionReceipt', formatTransactionReceipt);
|
|
830
|
+
|
|
831
|
+
const maxUint256 = 2n ** 256n - 1n;
|
|
832
|
+
|
|
833
|
+
function concatHex(values) {
|
|
834
|
+
return `0x${values.reduce((acc, x) => acc + x.replace('0x', ''), '')}`;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
class NegativeOffsetError extends BaseError {
|
|
838
|
+
constructor({ offset }) {
|
|
839
|
+
super(`Offset \`${offset}\` cannot be negative.`, {
|
|
840
|
+
name: 'NegativeOffsetError',
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
class PositionOutOfBoundsError extends BaseError {
|
|
845
|
+
constructor({ length, position }) {
|
|
846
|
+
super(`Position \`${position}\` is out of bounds (\`0 < position < ${length}\`).`, { name: 'PositionOutOfBoundsError' });
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
class RecursiveReadLimitExceededError extends BaseError {
|
|
850
|
+
constructor({ count, limit }) {
|
|
851
|
+
super(`Recursive read limit of \`${limit}\` exceeded (recursive read count: \`${count}\`).`, { name: 'RecursiveReadLimitExceededError' });
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
const staticCursor = {
|
|
856
|
+
bytes: new Uint8Array(),
|
|
857
|
+
dataView: new DataView(new ArrayBuffer(0)),
|
|
858
|
+
position: 0,
|
|
859
|
+
positionReadCount: new Map(),
|
|
860
|
+
recursiveReadCount: 0,
|
|
861
|
+
recursiveReadLimit: Number.POSITIVE_INFINITY,
|
|
862
|
+
assertReadLimit() {
|
|
863
|
+
if (this.recursiveReadCount >= this.recursiveReadLimit)
|
|
864
|
+
throw new RecursiveReadLimitExceededError({
|
|
865
|
+
count: this.recursiveReadCount + 1,
|
|
866
|
+
limit: this.recursiveReadLimit,
|
|
867
|
+
});
|
|
868
|
+
},
|
|
869
|
+
assertPosition(position) {
|
|
870
|
+
if (position < 0 || position > this.bytes.length - 1)
|
|
871
|
+
throw new PositionOutOfBoundsError({
|
|
872
|
+
length: this.bytes.length,
|
|
873
|
+
position,
|
|
874
|
+
});
|
|
875
|
+
},
|
|
876
|
+
decrementPosition(offset) {
|
|
877
|
+
if (offset < 0)
|
|
878
|
+
throw new NegativeOffsetError({ offset });
|
|
879
|
+
const position = this.position - offset;
|
|
880
|
+
this.assertPosition(position);
|
|
881
|
+
this.position = position;
|
|
882
|
+
},
|
|
883
|
+
getReadCount(position) {
|
|
884
|
+
return this.positionReadCount.get(position || this.position) || 0;
|
|
885
|
+
},
|
|
886
|
+
incrementPosition(offset) {
|
|
887
|
+
if (offset < 0)
|
|
888
|
+
throw new NegativeOffsetError({ offset });
|
|
889
|
+
const position = this.position + offset;
|
|
890
|
+
this.assertPosition(position);
|
|
891
|
+
this.position = position;
|
|
892
|
+
},
|
|
893
|
+
inspectByte(position_) {
|
|
894
|
+
const position = position_ ?? this.position;
|
|
895
|
+
this.assertPosition(position);
|
|
896
|
+
return this.bytes[position];
|
|
897
|
+
},
|
|
898
|
+
inspectBytes(length, position_) {
|
|
899
|
+
const position = position_ ?? this.position;
|
|
900
|
+
this.assertPosition(position + length - 1);
|
|
901
|
+
return this.bytes.subarray(position, position + length);
|
|
902
|
+
},
|
|
903
|
+
inspectUint8(position_) {
|
|
904
|
+
const position = position_ ?? this.position;
|
|
905
|
+
this.assertPosition(position);
|
|
906
|
+
return this.bytes[position];
|
|
907
|
+
},
|
|
908
|
+
inspectUint16(position_) {
|
|
909
|
+
const position = position_ ?? this.position;
|
|
910
|
+
this.assertPosition(position + 1);
|
|
911
|
+
return this.dataView.getUint16(position);
|
|
912
|
+
},
|
|
913
|
+
inspectUint24(position_) {
|
|
914
|
+
const position = position_ ?? this.position;
|
|
915
|
+
this.assertPosition(position + 2);
|
|
916
|
+
return ((this.dataView.getUint16(position) << 8) +
|
|
917
|
+
this.dataView.getUint8(position + 2));
|
|
918
|
+
},
|
|
919
|
+
inspectUint32(position_) {
|
|
920
|
+
const position = position_ ?? this.position;
|
|
921
|
+
this.assertPosition(position + 3);
|
|
922
|
+
return this.dataView.getUint32(position);
|
|
923
|
+
},
|
|
924
|
+
pushByte(byte) {
|
|
925
|
+
this.assertPosition(this.position);
|
|
926
|
+
this.bytes[this.position] = byte;
|
|
927
|
+
this.position++;
|
|
928
|
+
},
|
|
929
|
+
pushBytes(bytes) {
|
|
930
|
+
this.assertPosition(this.position + bytes.length - 1);
|
|
931
|
+
this.bytes.set(bytes, this.position);
|
|
932
|
+
this.position += bytes.length;
|
|
933
|
+
},
|
|
934
|
+
pushUint8(value) {
|
|
935
|
+
this.assertPosition(this.position);
|
|
936
|
+
this.bytes[this.position] = value;
|
|
937
|
+
this.position++;
|
|
938
|
+
},
|
|
939
|
+
pushUint16(value) {
|
|
940
|
+
this.assertPosition(this.position + 1);
|
|
941
|
+
this.dataView.setUint16(this.position, value);
|
|
942
|
+
this.position += 2;
|
|
943
|
+
},
|
|
944
|
+
pushUint24(value) {
|
|
945
|
+
this.assertPosition(this.position + 2);
|
|
946
|
+
this.dataView.setUint16(this.position, value >> 8);
|
|
947
|
+
this.dataView.setUint8(this.position + 2, value & 255);
|
|
948
|
+
this.position += 3;
|
|
949
|
+
},
|
|
950
|
+
pushUint32(value) {
|
|
951
|
+
this.assertPosition(this.position + 3);
|
|
952
|
+
this.dataView.setUint32(this.position, value);
|
|
953
|
+
this.position += 4;
|
|
954
|
+
},
|
|
955
|
+
readByte() {
|
|
956
|
+
this.assertReadLimit();
|
|
957
|
+
this._touch();
|
|
958
|
+
const value = this.inspectByte();
|
|
959
|
+
this.position++;
|
|
960
|
+
return value;
|
|
961
|
+
},
|
|
962
|
+
readBytes(length, size) {
|
|
963
|
+
this.assertReadLimit();
|
|
964
|
+
this._touch();
|
|
965
|
+
const value = this.inspectBytes(length);
|
|
966
|
+
this.position += size ?? length;
|
|
967
|
+
return value;
|
|
968
|
+
},
|
|
969
|
+
readUint8() {
|
|
970
|
+
this.assertReadLimit();
|
|
971
|
+
this._touch();
|
|
972
|
+
const value = this.inspectUint8();
|
|
973
|
+
this.position += 1;
|
|
974
|
+
return value;
|
|
975
|
+
},
|
|
976
|
+
readUint16() {
|
|
977
|
+
this.assertReadLimit();
|
|
978
|
+
this._touch();
|
|
979
|
+
const value = this.inspectUint16();
|
|
980
|
+
this.position += 2;
|
|
981
|
+
return value;
|
|
982
|
+
},
|
|
983
|
+
readUint24() {
|
|
984
|
+
this.assertReadLimit();
|
|
985
|
+
this._touch();
|
|
986
|
+
const value = this.inspectUint24();
|
|
987
|
+
this.position += 3;
|
|
988
|
+
return value;
|
|
989
|
+
},
|
|
990
|
+
readUint32() {
|
|
991
|
+
this.assertReadLimit();
|
|
992
|
+
this._touch();
|
|
993
|
+
const value = this.inspectUint32();
|
|
994
|
+
this.position += 4;
|
|
995
|
+
return value;
|
|
996
|
+
},
|
|
997
|
+
get remaining() {
|
|
998
|
+
return this.bytes.length - this.position;
|
|
999
|
+
},
|
|
1000
|
+
setPosition(position) {
|
|
1001
|
+
const oldPosition = this.position;
|
|
1002
|
+
this.assertPosition(position);
|
|
1003
|
+
this.position = position;
|
|
1004
|
+
return () => (this.position = oldPosition);
|
|
1005
|
+
},
|
|
1006
|
+
_touch() {
|
|
1007
|
+
if (this.recursiveReadLimit === Number.POSITIVE_INFINITY)
|
|
1008
|
+
return;
|
|
1009
|
+
const count = this.getReadCount();
|
|
1010
|
+
this.positionReadCount.set(this.position, count + 1);
|
|
1011
|
+
if (count > 0)
|
|
1012
|
+
this.recursiveReadCount++;
|
|
1013
|
+
},
|
|
1014
|
+
};
|
|
1015
|
+
function createCursor(bytes, { recursiveReadLimit = 8_192 } = {}) {
|
|
1016
|
+
const cursor = Object.create(staticCursor);
|
|
1017
|
+
cursor.bytes = bytes;
|
|
1018
|
+
cursor.dataView = new DataView(bytes.buffer ?? bytes, bytes.byteOffset, bytes.byteLength);
|
|
1019
|
+
cursor.positionReadCount = new Map();
|
|
1020
|
+
cursor.recursiveReadLimit = recursiveReadLimit;
|
|
1021
|
+
return cursor;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
function toRlp(bytes, to = 'hex') {
|
|
1025
|
+
const encodable = getEncodable(bytes);
|
|
1026
|
+
const cursor = createCursor(new Uint8Array(encodable.length));
|
|
1027
|
+
encodable.encode(cursor);
|
|
1028
|
+
if (to === 'hex')
|
|
1029
|
+
return bytesToHex(cursor.bytes);
|
|
1030
|
+
return cursor.bytes;
|
|
1031
|
+
}
|
|
1032
|
+
function getEncodable(bytes) {
|
|
1033
|
+
if (Array.isArray(bytes))
|
|
1034
|
+
return getEncodableList(bytes.map((x) => getEncodable(x)));
|
|
1035
|
+
return getEncodableBytes(bytes);
|
|
1036
|
+
}
|
|
1037
|
+
function getEncodableList(list) {
|
|
1038
|
+
const bodyLength = list.reduce((acc, x) => acc + x.length, 0);
|
|
1039
|
+
const sizeOfBodyLength = getSizeOfLength(bodyLength);
|
|
1040
|
+
const length = (() => {
|
|
1041
|
+
if (bodyLength <= 55)
|
|
1042
|
+
return 1 + bodyLength;
|
|
1043
|
+
return 1 + sizeOfBodyLength + bodyLength;
|
|
1044
|
+
})();
|
|
1045
|
+
return {
|
|
1046
|
+
length,
|
|
1047
|
+
encode(cursor) {
|
|
1048
|
+
if (bodyLength <= 55) {
|
|
1049
|
+
cursor.pushByte(0xc0 + bodyLength);
|
|
1050
|
+
}
|
|
1051
|
+
else {
|
|
1052
|
+
cursor.pushByte(0xc0 + 55 + sizeOfBodyLength);
|
|
1053
|
+
if (sizeOfBodyLength === 1)
|
|
1054
|
+
cursor.pushUint8(bodyLength);
|
|
1055
|
+
else if (sizeOfBodyLength === 2)
|
|
1056
|
+
cursor.pushUint16(bodyLength);
|
|
1057
|
+
else if (sizeOfBodyLength === 3)
|
|
1058
|
+
cursor.pushUint24(bodyLength);
|
|
1059
|
+
else
|
|
1060
|
+
cursor.pushUint32(bodyLength);
|
|
1061
|
+
}
|
|
1062
|
+
for (const { encode } of list) {
|
|
1063
|
+
encode(cursor);
|
|
1064
|
+
}
|
|
1065
|
+
},
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
function getEncodableBytes(bytesOrHex) {
|
|
1069
|
+
const bytes = typeof bytesOrHex === 'string' ? hexToBytes(bytesOrHex) : bytesOrHex;
|
|
1070
|
+
const sizeOfBytesLength = getSizeOfLength(bytes.length);
|
|
1071
|
+
const length = (() => {
|
|
1072
|
+
if (bytes.length === 1 && bytes[0] < 0x80)
|
|
1073
|
+
return 1;
|
|
1074
|
+
if (bytes.length <= 55)
|
|
1075
|
+
return 1 + bytes.length;
|
|
1076
|
+
return 1 + sizeOfBytesLength + bytes.length;
|
|
1077
|
+
})();
|
|
1078
|
+
return {
|
|
1079
|
+
length,
|
|
1080
|
+
encode(cursor) {
|
|
1081
|
+
if (bytes.length === 1 && bytes[0] < 0x80) {
|
|
1082
|
+
cursor.pushBytes(bytes);
|
|
1083
|
+
}
|
|
1084
|
+
else if (bytes.length <= 55) {
|
|
1085
|
+
cursor.pushByte(0x80 + bytes.length);
|
|
1086
|
+
cursor.pushBytes(bytes);
|
|
1087
|
+
}
|
|
1088
|
+
else {
|
|
1089
|
+
cursor.pushByte(0x80 + 55 + sizeOfBytesLength);
|
|
1090
|
+
if (sizeOfBytesLength === 1)
|
|
1091
|
+
cursor.pushUint8(bytes.length);
|
|
1092
|
+
else if (sizeOfBytesLength === 2)
|
|
1093
|
+
cursor.pushUint16(bytes.length);
|
|
1094
|
+
else if (sizeOfBytesLength === 3)
|
|
1095
|
+
cursor.pushUint24(bytes.length);
|
|
1096
|
+
else
|
|
1097
|
+
cursor.pushUint32(bytes.length);
|
|
1098
|
+
cursor.pushBytes(bytes);
|
|
1099
|
+
}
|
|
1100
|
+
},
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
function getSizeOfLength(length) {
|
|
1104
|
+
if (length < 2 ** 8)
|
|
1105
|
+
return 1;
|
|
1106
|
+
if (length < 2 ** 16)
|
|
1107
|
+
return 2;
|
|
1108
|
+
if (length < 2 ** 24)
|
|
1109
|
+
return 3;
|
|
1110
|
+
if (length < 2 ** 32)
|
|
1111
|
+
return 4;
|
|
1112
|
+
throw new BaseError('Length is too large.');
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
const gweiUnits = {
|
|
1116
|
+
ether: -9,
|
|
1117
|
+
wei: 9,
|
|
1118
|
+
};
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* Divides a number by a given exponent of base 10 (10exponent), and formats it into a string representation of the number..
|
|
1122
|
+
*
|
|
1123
|
+
* - Docs: https://viem.sh/docs/utilities/formatUnits
|
|
1124
|
+
*
|
|
1125
|
+
* @example
|
|
1126
|
+
* import { formatUnits } from 'viem'
|
|
1127
|
+
*
|
|
1128
|
+
* formatUnits(420000000000n, 9)
|
|
1129
|
+
* // '420'
|
|
1130
|
+
*/
|
|
1131
|
+
function formatUnits(value, decimals) {
|
|
1132
|
+
let display = value.toString();
|
|
1133
|
+
const negative = display.startsWith('-');
|
|
1134
|
+
if (negative)
|
|
1135
|
+
display = display.slice(1);
|
|
1136
|
+
display = display.padStart(decimals, '0');
|
|
1137
|
+
let [integer, fraction] = [
|
|
1138
|
+
display.slice(0, display.length - decimals),
|
|
1139
|
+
display.slice(display.length - decimals),
|
|
1140
|
+
];
|
|
1141
|
+
fraction = fraction.replace(/(0+)$/, '');
|
|
1142
|
+
return `${negative ? '-' : ''}${integer || '0'}${fraction ? `.${fraction}` : ''}`;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
/**
|
|
1146
|
+
* Converts numerical wei to a string representation of gwei.
|
|
1147
|
+
*
|
|
1148
|
+
* - Docs: https://viem.sh/docs/utilities/formatGwei
|
|
1149
|
+
*
|
|
1150
|
+
* @example
|
|
1151
|
+
* import { formatGwei } from 'viem'
|
|
1152
|
+
*
|
|
1153
|
+
* formatGwei(1000000000n)
|
|
1154
|
+
* // '1'
|
|
1155
|
+
*/
|
|
1156
|
+
function formatGwei(wei, unit = 'wei') {
|
|
1157
|
+
return formatUnits(wei, gweiUnits[unit]);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
function prettyPrint(args) {
|
|
1161
|
+
const entries = Object.entries(args)
|
|
1162
|
+
.map(([key, value]) => {
|
|
1163
|
+
if (value === undefined || value === false)
|
|
1164
|
+
return null;
|
|
1165
|
+
return [key, value];
|
|
1166
|
+
})
|
|
1167
|
+
.filter(Boolean);
|
|
1168
|
+
const maxLength = entries.reduce((acc, [key]) => Math.max(acc, key.length), 0);
|
|
1169
|
+
return entries
|
|
1170
|
+
.map(([key, value]) => ` ${`${key}:`.padEnd(maxLength + 1)} ${value}`)
|
|
1171
|
+
.join('\n');
|
|
1172
|
+
}
|
|
1173
|
+
class InvalidLegacyVError extends BaseError {
|
|
1174
|
+
constructor({ v }) {
|
|
1175
|
+
super(`Invalid \`v\` value "${v}". Expected 27 or 28.`, {
|
|
1176
|
+
name: 'InvalidLegacyVError',
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
class InvalidSerializableTransactionError extends BaseError {
|
|
1181
|
+
constructor({ transaction }) {
|
|
1182
|
+
super('Cannot infer a transaction type from provided transaction.', {
|
|
1183
|
+
metaMessages: [
|
|
1184
|
+
'Provided Transaction:',
|
|
1185
|
+
'{',
|
|
1186
|
+
prettyPrint(transaction),
|
|
1187
|
+
'}',
|
|
1188
|
+
'',
|
|
1189
|
+
'To infer the type, either provide:',
|
|
1190
|
+
'- a `type` to the Transaction, or',
|
|
1191
|
+
'- an EIP-1559 Transaction with `maxFeePerGas`, or',
|
|
1192
|
+
'- an EIP-2930 Transaction with `gasPrice` & `accessList`, or',
|
|
1193
|
+
'- an EIP-4844 Transaction with `blobs`, `blobVersionedHashes`, `sidecars`, or',
|
|
1194
|
+
'- an EIP-7702 Transaction with `authorizationList`, or',
|
|
1195
|
+
'- a Legacy Transaction with `gasPrice`',
|
|
1196
|
+
],
|
|
1197
|
+
name: 'InvalidSerializableTransactionError',
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
class InvalidStorageKeySizeError extends BaseError {
|
|
1202
|
+
constructor({ storageKey }) {
|
|
1203
|
+
super(`Size for storage key "${storageKey}" is invalid. Expected 32 bytes. Got ${Math.floor((storageKey.length - 2) / 2)} bytes.`, { name: 'InvalidStorageKeySizeError' });
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
/*
|
|
1208
|
+
* Serializes an EIP-7702 authorization list.
|
|
1209
|
+
*/
|
|
1210
|
+
function serializeAuthorizationList(authorizationList) {
|
|
1211
|
+
if (!authorizationList || authorizationList.length === 0)
|
|
1212
|
+
return [];
|
|
1213
|
+
const serializedAuthorizationList = [];
|
|
1214
|
+
for (const authorization of authorizationList) {
|
|
1215
|
+
const { chainId, nonce, ...signature } = authorization;
|
|
1216
|
+
const contractAddress = authorization.address;
|
|
1217
|
+
serializedAuthorizationList.push([
|
|
1218
|
+
chainId ? toHex(chainId) : '0x',
|
|
1219
|
+
contractAddress,
|
|
1220
|
+
nonce ? toHex(nonce) : '0x',
|
|
1221
|
+
...toYParitySignatureArray({}, signature),
|
|
1222
|
+
]);
|
|
1223
|
+
}
|
|
1224
|
+
return serializedAuthorizationList;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
/**
|
|
1228
|
+
* Compute commitments from a list of blobs.
|
|
1229
|
+
*
|
|
1230
|
+
* @example
|
|
1231
|
+
* ```ts
|
|
1232
|
+
* import { blobsToCommitments, toBlobs } from 'viem'
|
|
1233
|
+
* import { kzg } from './kzg'
|
|
1234
|
+
*
|
|
1235
|
+
* const blobs = toBlobs({ data: '0x1234' })
|
|
1236
|
+
* const commitments = blobsToCommitments({ blobs, kzg })
|
|
1237
|
+
* ```
|
|
1238
|
+
*/
|
|
1239
|
+
function blobsToCommitments(parameters) {
|
|
1240
|
+
const { kzg } = parameters;
|
|
1241
|
+
const to = parameters.to ?? (typeof parameters.blobs[0] === 'string' ? 'hex' : 'bytes');
|
|
1242
|
+
const blobs = (typeof parameters.blobs[0] === 'string'
|
|
1243
|
+
? parameters.blobs.map((x) => hexToBytes(x))
|
|
1244
|
+
: parameters.blobs);
|
|
1245
|
+
const commitments = [];
|
|
1246
|
+
for (const blob of blobs)
|
|
1247
|
+
commitments.push(Uint8Array.from(kzg.blobToKzgCommitment(blob)));
|
|
1248
|
+
return (to === 'bytes'
|
|
1249
|
+
? commitments
|
|
1250
|
+
: commitments.map((x) => bytesToHex(x)));
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/**
|
|
1254
|
+
* Compute the proofs for a list of blobs and their commitments.
|
|
1255
|
+
*
|
|
1256
|
+
* @example
|
|
1257
|
+
* ```ts
|
|
1258
|
+
* import {
|
|
1259
|
+
* blobsToCommitments,
|
|
1260
|
+
* toBlobs
|
|
1261
|
+
* } from 'viem'
|
|
1262
|
+
* import { kzg } from './kzg'
|
|
1263
|
+
*
|
|
1264
|
+
* const blobs = toBlobs({ data: '0x1234' })
|
|
1265
|
+
* const commitments = blobsToCommitments({ blobs, kzg })
|
|
1266
|
+
* const proofs = blobsToProofs({ blobs, commitments, kzg })
|
|
1267
|
+
* ```
|
|
1268
|
+
*/
|
|
1269
|
+
function blobsToProofs(parameters) {
|
|
1270
|
+
const { kzg } = parameters;
|
|
1271
|
+
const to = parameters.to ?? (typeof parameters.blobs[0] === 'string' ? 'hex' : 'bytes');
|
|
1272
|
+
const blobs = (typeof parameters.blobs[0] === 'string'
|
|
1273
|
+
? parameters.blobs.map((x) => hexToBytes(x))
|
|
1274
|
+
: parameters.blobs);
|
|
1275
|
+
const commitments = (typeof parameters.commitments[0] === 'string'
|
|
1276
|
+
? parameters.commitments.map((x) => hexToBytes(x))
|
|
1277
|
+
: parameters.commitments);
|
|
1278
|
+
const proofs = [];
|
|
1279
|
+
for (let i = 0; i < blobs.length; i++) {
|
|
1280
|
+
const blob = blobs[i];
|
|
1281
|
+
const commitment = commitments[i];
|
|
1282
|
+
proofs.push(Uint8Array.from(kzg.computeBlobKzgProof(blob, commitment)));
|
|
1283
|
+
}
|
|
1284
|
+
return (to === 'bytes'
|
|
1285
|
+
? proofs
|
|
1286
|
+
: proofs.map((x) => bytesToHex(x)));
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
/**
|
|
1290
|
+
* Utilities for hex, bytes, CSPRNG.
|
|
1291
|
+
* @module
|
|
1292
|
+
*/
|
|
1293
|
+
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
1294
|
+
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
|
|
1295
|
+
// node.js versions earlier than v19 don't declare it in global scope.
|
|
1296
|
+
// For node.js, package.json#exports field mapping rewrites import
|
|
1297
|
+
// from `crypto` to `cryptoNode`, which imports native module.
|
|
1298
|
+
// Makes the utils un-importable in browsers without a bundler.
|
|
1299
|
+
// Once node.js 18 is deprecated (2025-04-30), we can just drop the import.
|
|
1300
|
+
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
|
|
1301
|
+
function isBytes(a) {
|
|
1302
|
+
return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
|
|
1303
|
+
}
|
|
1304
|
+
/** Asserts something is positive integer. */
|
|
1305
|
+
function anumber(n) {
|
|
1306
|
+
if (!Number.isSafeInteger(n) || n < 0)
|
|
1307
|
+
throw new Error('positive integer expected, got ' + n);
|
|
1308
|
+
}
|
|
1309
|
+
/** Asserts something is Uint8Array. */
|
|
1310
|
+
function abytes(b, ...lengths) {
|
|
1311
|
+
if (!isBytes(b))
|
|
1312
|
+
throw new Error('Uint8Array expected');
|
|
1313
|
+
if (lengths.length > 0 && !lengths.includes(b.length))
|
|
1314
|
+
throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length);
|
|
1315
|
+
}
|
|
1316
|
+
/** Asserts a hash instance has not been destroyed / finished */
|
|
1317
|
+
function aexists(instance, checkFinished = true) {
|
|
1318
|
+
if (instance.destroyed)
|
|
1319
|
+
throw new Error('Hash instance has been destroyed');
|
|
1320
|
+
if (checkFinished && instance.finished)
|
|
1321
|
+
throw new Error('Hash#digest() has already been called');
|
|
1322
|
+
}
|
|
1323
|
+
/** Asserts output is properly-sized byte array */
|
|
1324
|
+
function aoutput(out, instance) {
|
|
1325
|
+
abytes(out);
|
|
1326
|
+
const min = instance.outputLen;
|
|
1327
|
+
if (out.length < min) {
|
|
1328
|
+
throw new Error('digestInto() expects output buffer of length at least ' + min);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
/** Cast u8 / u16 / u32 to u32. */
|
|
1332
|
+
function u32(arr) {
|
|
1333
|
+
return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
|
1334
|
+
}
|
|
1335
|
+
/** Zeroize a byte array. Warning: JS provides no guarantees. */
|
|
1336
|
+
function clean(...arrays) {
|
|
1337
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
1338
|
+
arrays[i].fill(0);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
/** Create DataView of an array for easy byte-level manipulation. */
|
|
1342
|
+
function createView(arr) {
|
|
1343
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
1344
|
+
}
|
|
1345
|
+
/** The rotate right (circular right shift) operation for uint32 */
|
|
1346
|
+
function rotr(word, shift) {
|
|
1347
|
+
return (word << (32 - shift)) | (word >>> shift);
|
|
1348
|
+
}
|
|
1349
|
+
/** Is current platform little-endian? Most are. Big-Endian platform: IBM */
|
|
1350
|
+
const isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();
|
|
1351
|
+
/** The byte swap operation for uint32 */
|
|
1352
|
+
function byteSwap(word) {
|
|
1353
|
+
return (((word << 24) & 0xff000000) |
|
|
1354
|
+
((word << 8) & 0xff0000) |
|
|
1355
|
+
((word >>> 8) & 0xff00) |
|
|
1356
|
+
((word >>> 24) & 0xff));
|
|
1357
|
+
}
|
|
1358
|
+
/** In place byte swap for Uint32Array */
|
|
1359
|
+
function byteSwap32(arr) {
|
|
1360
|
+
for (let i = 0; i < arr.length; i++) {
|
|
1361
|
+
arr[i] = byteSwap(arr[i]);
|
|
1362
|
+
}
|
|
1363
|
+
return arr;
|
|
1364
|
+
}
|
|
1365
|
+
const swap32IfBE = isLE
|
|
1366
|
+
? (u) => u
|
|
1367
|
+
: byteSwap32;
|
|
1368
|
+
/**
|
|
1369
|
+
* Converts string to bytes using UTF8 encoding.
|
|
1370
|
+
* @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])
|
|
1371
|
+
*/
|
|
1372
|
+
function utf8ToBytes(str) {
|
|
1373
|
+
if (typeof str !== 'string')
|
|
1374
|
+
throw new Error('string expected');
|
|
1375
|
+
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
|
|
1379
|
+
* Warning: when Uint8Array is passed, it would NOT get copied.
|
|
1380
|
+
* Keep in mind for future mutable operations.
|
|
1381
|
+
*/
|
|
1382
|
+
function toBytes(data) {
|
|
1383
|
+
if (typeof data === 'string')
|
|
1384
|
+
data = utf8ToBytes(data);
|
|
1385
|
+
abytes(data);
|
|
1386
|
+
return data;
|
|
1387
|
+
}
|
|
1388
|
+
/** For runtime check if class implements interface */
|
|
1389
|
+
class Hash {
|
|
1390
|
+
}
|
|
1391
|
+
/** Wraps hash function, creating an interface on top of it */
|
|
1392
|
+
function createHasher(hashCons) {
|
|
1393
|
+
const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
|
|
1394
|
+
const tmp = hashCons();
|
|
1395
|
+
hashC.outputLen = tmp.outputLen;
|
|
1396
|
+
hashC.blockLen = tmp.blockLen;
|
|
1397
|
+
hashC.create = () => hashCons();
|
|
1398
|
+
return hashC;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
/**
|
|
1402
|
+
* Internal Merkle-Damgard hash utils.
|
|
1403
|
+
* @module
|
|
1404
|
+
*/
|
|
1405
|
+
/** Polyfill for Safari 14. https://caniuse.com/mdn-javascript_builtins_dataview_setbiguint64 */
|
|
1406
|
+
function setBigUint64(view, byteOffset, value, isLE) {
|
|
1407
|
+
if (typeof view.setBigUint64 === 'function')
|
|
1408
|
+
return view.setBigUint64(byteOffset, value, isLE);
|
|
1409
|
+
const _32n = BigInt(32);
|
|
1410
|
+
const _u32_max = BigInt(0xffffffff);
|
|
1411
|
+
const wh = Number((value >> _32n) & _u32_max);
|
|
1412
|
+
const wl = Number(value & _u32_max);
|
|
1413
|
+
const h = isLE ? 4 : 0;
|
|
1414
|
+
const l = isLE ? 0 : 4;
|
|
1415
|
+
view.setUint32(byteOffset + h, wh, isLE);
|
|
1416
|
+
view.setUint32(byteOffset + l, wl, isLE);
|
|
1417
|
+
}
|
|
1418
|
+
/** Choice: a ? b : c */
|
|
1419
|
+
function Chi(a, b, c) {
|
|
1420
|
+
return (a & b) ^ (~a & c);
|
|
1421
|
+
}
|
|
1422
|
+
/** Majority function, true if any two inputs is true. */
|
|
1423
|
+
function Maj(a, b, c) {
|
|
1424
|
+
return (a & b) ^ (a & c) ^ (b & c);
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* Merkle-Damgard hash construction base class.
|
|
1428
|
+
* Could be used to create MD5, RIPEMD, SHA1, SHA2.
|
|
1429
|
+
*/
|
|
1430
|
+
class HashMD extends Hash {
|
|
1431
|
+
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
1432
|
+
super();
|
|
1433
|
+
this.finished = false;
|
|
1434
|
+
this.length = 0;
|
|
1435
|
+
this.pos = 0;
|
|
1436
|
+
this.destroyed = false;
|
|
1437
|
+
this.blockLen = blockLen;
|
|
1438
|
+
this.outputLen = outputLen;
|
|
1439
|
+
this.padOffset = padOffset;
|
|
1440
|
+
this.isLE = isLE;
|
|
1441
|
+
this.buffer = new Uint8Array(blockLen);
|
|
1442
|
+
this.view = createView(this.buffer);
|
|
1443
|
+
}
|
|
1444
|
+
update(data) {
|
|
1445
|
+
aexists(this);
|
|
1446
|
+
data = toBytes(data);
|
|
1447
|
+
abytes(data);
|
|
1448
|
+
const { view, buffer, blockLen } = this;
|
|
1449
|
+
const len = data.length;
|
|
1450
|
+
for (let pos = 0; pos < len;) {
|
|
1451
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
1452
|
+
// Fast path: we have at least one block in input, cast it to view and process
|
|
1453
|
+
if (take === blockLen) {
|
|
1454
|
+
const dataView = createView(data);
|
|
1455
|
+
for (; blockLen <= len - pos; pos += blockLen)
|
|
1456
|
+
this.process(dataView, pos);
|
|
1457
|
+
continue;
|
|
1458
|
+
}
|
|
1459
|
+
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
1460
|
+
this.pos += take;
|
|
1461
|
+
pos += take;
|
|
1462
|
+
if (this.pos === blockLen) {
|
|
1463
|
+
this.process(view, 0);
|
|
1464
|
+
this.pos = 0;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
this.length += data.length;
|
|
1468
|
+
this.roundClean();
|
|
1469
|
+
return this;
|
|
1470
|
+
}
|
|
1471
|
+
digestInto(out) {
|
|
1472
|
+
aexists(this);
|
|
1473
|
+
aoutput(out, this);
|
|
1474
|
+
this.finished = true;
|
|
1475
|
+
// Padding
|
|
1476
|
+
// We can avoid allocation of buffer for padding completely if it
|
|
1477
|
+
// was previously not allocated here. But it won't change performance.
|
|
1478
|
+
const { buffer, view, blockLen, isLE } = this;
|
|
1479
|
+
let { pos } = this;
|
|
1480
|
+
// append the bit '1' to the message
|
|
1481
|
+
buffer[pos++] = 0b10000000;
|
|
1482
|
+
clean(this.buffer.subarray(pos));
|
|
1483
|
+
// we have less than padOffset left in buffer, so we cannot put length in
|
|
1484
|
+
// current block, need process it and pad again
|
|
1485
|
+
if (this.padOffset > blockLen - pos) {
|
|
1486
|
+
this.process(view, 0);
|
|
1487
|
+
pos = 0;
|
|
1488
|
+
}
|
|
1489
|
+
// Pad until full block byte with zeros
|
|
1490
|
+
for (let i = pos; i < blockLen; i++)
|
|
1491
|
+
buffer[i] = 0;
|
|
1492
|
+
// Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that
|
|
1493
|
+
// You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.
|
|
1494
|
+
// So we just write lowest 64 bits of that value.
|
|
1495
|
+
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
|
|
1496
|
+
this.process(view, 0);
|
|
1497
|
+
const oview = createView(out);
|
|
1498
|
+
const len = this.outputLen;
|
|
1499
|
+
// NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT
|
|
1500
|
+
if (len % 4)
|
|
1501
|
+
throw new Error('_sha2: outputLen should be aligned to 32bit');
|
|
1502
|
+
const outLen = len / 4;
|
|
1503
|
+
const state = this.get();
|
|
1504
|
+
if (outLen > state.length)
|
|
1505
|
+
throw new Error('_sha2: outputLen bigger than state');
|
|
1506
|
+
for (let i = 0; i < outLen; i++)
|
|
1507
|
+
oview.setUint32(4 * i, state[i], isLE);
|
|
1508
|
+
}
|
|
1509
|
+
digest() {
|
|
1510
|
+
const { buffer, outputLen } = this;
|
|
1511
|
+
this.digestInto(buffer);
|
|
1512
|
+
const res = buffer.slice(0, outputLen);
|
|
1513
|
+
this.destroy();
|
|
1514
|
+
return res;
|
|
1515
|
+
}
|
|
1516
|
+
_cloneInto(to) {
|
|
1517
|
+
to || (to = new this.constructor());
|
|
1518
|
+
to.set(...this.get());
|
|
1519
|
+
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
1520
|
+
to.destroyed = destroyed;
|
|
1521
|
+
to.finished = finished;
|
|
1522
|
+
to.length = length;
|
|
1523
|
+
to.pos = pos;
|
|
1524
|
+
if (length % blockLen)
|
|
1525
|
+
to.buffer.set(buffer);
|
|
1526
|
+
return to;
|
|
1527
|
+
}
|
|
1528
|
+
clone() {
|
|
1529
|
+
return this._cloneInto();
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
/**
|
|
1533
|
+
* Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.
|
|
1534
|
+
* Check out `test/misc/sha2-gen-iv.js` for recomputation guide.
|
|
1535
|
+
*/
|
|
1536
|
+
/** Initial SHA256 state. Bits 0..32 of frac part of sqrt of primes 2..19 */
|
|
1537
|
+
const SHA256_IV = /* @__PURE__ */ Uint32Array.from([
|
|
1538
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
|
1539
|
+
]);
|
|
1540
|
+
|
|
1541
|
+
/**
|
|
1542
|
+
* Internal helpers for u64. BigUint64Array is too slow as per 2025, so we implement it using Uint32Array.
|
|
1543
|
+
* @todo re-check https://issues.chromium.org/issues/42212588
|
|
1544
|
+
* @module
|
|
1545
|
+
*/
|
|
1546
|
+
const U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
|
|
1547
|
+
const _32n = /* @__PURE__ */ BigInt(32);
|
|
1548
|
+
function fromBig(n, le = false) {
|
|
1549
|
+
if (le)
|
|
1550
|
+
return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };
|
|
1551
|
+
return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
|
|
1552
|
+
}
|
|
1553
|
+
function split(lst, le = false) {
|
|
1554
|
+
const len = lst.length;
|
|
1555
|
+
let Ah = new Uint32Array(len);
|
|
1556
|
+
let Al = new Uint32Array(len);
|
|
1557
|
+
for (let i = 0; i < len; i++) {
|
|
1558
|
+
const { h, l } = fromBig(lst[i], le);
|
|
1559
|
+
[Ah[i], Al[i]] = [h, l];
|
|
1560
|
+
}
|
|
1561
|
+
return [Ah, Al];
|
|
1562
|
+
}
|
|
1563
|
+
// Left rotate for Shift in [1, 32)
|
|
1564
|
+
const rotlSH = (h, l, s) => (h << s) | (l >>> (32 - s));
|
|
1565
|
+
const rotlSL = (h, l, s) => (l << s) | (h >>> (32 - s));
|
|
1566
|
+
// Left rotate for Shift in (32, 64), NOTE: 32 is special case.
|
|
1567
|
+
const rotlBH = (h, l, s) => (l << (s - 32)) | (h >>> (64 - s));
|
|
1568
|
+
const rotlBL = (h, l, s) => (h << (s - 32)) | (l >>> (64 - s));
|
|
1569
|
+
|
|
1570
|
+
/**
|
|
1571
|
+
* SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.
|
|
1572
|
+
* SHA256 is the fastest hash implementable in JS, even faster than Blake3.
|
|
1573
|
+
* Check out [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and
|
|
1574
|
+
* [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
|
|
1575
|
+
* @module
|
|
1576
|
+
*/
|
|
1577
|
+
/**
|
|
1578
|
+
* Round constants:
|
|
1579
|
+
* First 32 bits of fractional parts of the cube roots of the first 64 primes 2..311)
|
|
1580
|
+
*/
|
|
1581
|
+
// prettier-ignore
|
|
1582
|
+
const SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
1583
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
1584
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
1585
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
1586
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
1587
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
1588
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
1589
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
1590
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
|
1591
|
+
]);
|
|
1592
|
+
/** Reusable temporary buffer. "W" comes straight from spec. */
|
|
1593
|
+
const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|
1594
|
+
class SHA256 extends HashMD {
|
|
1595
|
+
constructor(outputLen = 32) {
|
|
1596
|
+
super(64, outputLen, 8, false);
|
|
1597
|
+
// We cannot use array here since array allows indexing by variable
|
|
1598
|
+
// which means optimizer/compiler cannot use registers.
|
|
1599
|
+
this.A = SHA256_IV[0] | 0;
|
|
1600
|
+
this.B = SHA256_IV[1] | 0;
|
|
1601
|
+
this.C = SHA256_IV[2] | 0;
|
|
1602
|
+
this.D = SHA256_IV[3] | 0;
|
|
1603
|
+
this.E = SHA256_IV[4] | 0;
|
|
1604
|
+
this.F = SHA256_IV[5] | 0;
|
|
1605
|
+
this.G = SHA256_IV[6] | 0;
|
|
1606
|
+
this.H = SHA256_IV[7] | 0;
|
|
1607
|
+
}
|
|
1608
|
+
get() {
|
|
1609
|
+
const { A, B, C, D, E, F, G, H } = this;
|
|
1610
|
+
return [A, B, C, D, E, F, G, H];
|
|
1611
|
+
}
|
|
1612
|
+
// prettier-ignore
|
|
1613
|
+
set(A, B, C, D, E, F, G, H) {
|
|
1614
|
+
this.A = A | 0;
|
|
1615
|
+
this.B = B | 0;
|
|
1616
|
+
this.C = C | 0;
|
|
1617
|
+
this.D = D | 0;
|
|
1618
|
+
this.E = E | 0;
|
|
1619
|
+
this.F = F | 0;
|
|
1620
|
+
this.G = G | 0;
|
|
1621
|
+
this.H = H | 0;
|
|
1622
|
+
}
|
|
1623
|
+
process(view, offset) {
|
|
1624
|
+
// Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array
|
|
1625
|
+
for (let i = 0; i < 16; i++, offset += 4)
|
|
1626
|
+
SHA256_W[i] = view.getUint32(offset, false);
|
|
1627
|
+
for (let i = 16; i < 64; i++) {
|
|
1628
|
+
const W15 = SHA256_W[i - 15];
|
|
1629
|
+
const W2 = SHA256_W[i - 2];
|
|
1630
|
+
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);
|
|
1631
|
+
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);
|
|
1632
|
+
SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;
|
|
1633
|
+
}
|
|
1634
|
+
// Compression function main loop, 64 rounds
|
|
1635
|
+
let { A, B, C, D, E, F, G, H } = this;
|
|
1636
|
+
for (let i = 0; i < 64; i++) {
|
|
1637
|
+
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
|
|
1638
|
+
const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;
|
|
1639
|
+
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
|
|
1640
|
+
const T2 = (sigma0 + Maj(A, B, C)) | 0;
|
|
1641
|
+
H = G;
|
|
1642
|
+
G = F;
|
|
1643
|
+
F = E;
|
|
1644
|
+
E = (D + T1) | 0;
|
|
1645
|
+
D = C;
|
|
1646
|
+
C = B;
|
|
1647
|
+
B = A;
|
|
1648
|
+
A = (T1 + T2) | 0;
|
|
1649
|
+
}
|
|
1650
|
+
// Add the compressed chunk to the current hash value
|
|
1651
|
+
A = (A + this.A) | 0;
|
|
1652
|
+
B = (B + this.B) | 0;
|
|
1653
|
+
C = (C + this.C) | 0;
|
|
1654
|
+
D = (D + this.D) | 0;
|
|
1655
|
+
E = (E + this.E) | 0;
|
|
1656
|
+
F = (F + this.F) | 0;
|
|
1657
|
+
G = (G + this.G) | 0;
|
|
1658
|
+
H = (H + this.H) | 0;
|
|
1659
|
+
this.set(A, B, C, D, E, F, G, H);
|
|
1660
|
+
}
|
|
1661
|
+
roundClean() {
|
|
1662
|
+
clean(SHA256_W);
|
|
1663
|
+
}
|
|
1664
|
+
destroy() {
|
|
1665
|
+
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
|
1666
|
+
clean(this.buffer);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
/**
|
|
1670
|
+
* SHA2-256 hash function from RFC 4634.
|
|
1671
|
+
*
|
|
1672
|
+
* It is the fastest JS hash, even faster than Blake3.
|
|
1673
|
+
* To break sha256 using birthday attack, attackers need to try 2^128 hashes.
|
|
1674
|
+
* BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
|
|
1675
|
+
*/
|
|
1676
|
+
const sha256$2 = /* @__PURE__ */ createHasher(() => new SHA256());
|
|
1677
|
+
|
|
1678
|
+
/**
|
|
1679
|
+
* SHA2-256 a.k.a. sha256. In JS, it is the fastest hash, even faster than Blake3.
|
|
1680
|
+
*
|
|
1681
|
+
* To break sha256 using birthday attack, attackers need to try 2^128 hashes.
|
|
1682
|
+
* BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
|
|
1683
|
+
*
|
|
1684
|
+
* Check out [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
|
|
1685
|
+
* @module
|
|
1686
|
+
* @deprecated
|
|
1687
|
+
*/
|
|
1688
|
+
/** @deprecated Use import from `noble/hashes/sha2` module */
|
|
1689
|
+
const sha256$1 = sha256$2;
|
|
1690
|
+
|
|
1691
|
+
function sha256(value, to_) {
|
|
1692
|
+
const bytes = sha256$1(isHex(value, { strict: false }) ? toBytes$1(value) : value);
|
|
1693
|
+
return bytes;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
/**
|
|
1697
|
+
* Transform a commitment to it's versioned hash.
|
|
1698
|
+
*
|
|
1699
|
+
* @example
|
|
1700
|
+
* ```ts
|
|
1701
|
+
* import {
|
|
1702
|
+
* blobsToCommitments,
|
|
1703
|
+
* commitmentToVersionedHash,
|
|
1704
|
+
* toBlobs
|
|
1705
|
+
* } from 'viem'
|
|
1706
|
+
* import { kzg } from './kzg'
|
|
1707
|
+
*
|
|
1708
|
+
* const blobs = toBlobs({ data: '0x1234' })
|
|
1709
|
+
* const [commitment] = blobsToCommitments({ blobs, kzg })
|
|
1710
|
+
* const versionedHash = commitmentToVersionedHash({ commitment })
|
|
1711
|
+
* ```
|
|
1712
|
+
*/
|
|
1713
|
+
function commitmentToVersionedHash(parameters) {
|
|
1714
|
+
const { commitment, version = 1 } = parameters;
|
|
1715
|
+
const to = parameters.to ?? (typeof commitment === 'string' ? 'hex' : 'bytes');
|
|
1716
|
+
const versionedHash = sha256(commitment);
|
|
1717
|
+
versionedHash.set([version], 0);
|
|
1718
|
+
return (to === 'bytes' ? versionedHash : bytesToHex(versionedHash));
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
/**
|
|
1722
|
+
* Transform a list of commitments to their versioned hashes.
|
|
1723
|
+
*
|
|
1724
|
+
* @example
|
|
1725
|
+
* ```ts
|
|
1726
|
+
* import {
|
|
1727
|
+
* blobsToCommitments,
|
|
1728
|
+
* commitmentsToVersionedHashes,
|
|
1729
|
+
* toBlobs
|
|
1730
|
+
* } from 'viem'
|
|
1731
|
+
* import { kzg } from './kzg'
|
|
1732
|
+
*
|
|
1733
|
+
* const blobs = toBlobs({ data: '0x1234' })
|
|
1734
|
+
* const commitments = blobsToCommitments({ blobs, kzg })
|
|
1735
|
+
* const versionedHashes = commitmentsToVersionedHashes({ commitments })
|
|
1736
|
+
* ```
|
|
1737
|
+
*/
|
|
1738
|
+
function commitmentsToVersionedHashes(parameters) {
|
|
1739
|
+
const { commitments, version } = parameters;
|
|
1740
|
+
const to = parameters.to ?? (typeof commitments[0] === 'string' ? 'hex' : 'bytes');
|
|
1741
|
+
const hashes = [];
|
|
1742
|
+
for (const commitment of commitments) {
|
|
1743
|
+
hashes.push(commitmentToVersionedHash({
|
|
1744
|
+
commitment,
|
|
1745
|
+
to,
|
|
1746
|
+
version,
|
|
1747
|
+
}));
|
|
1748
|
+
}
|
|
1749
|
+
return hashes;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#parameters
|
|
1753
|
+
/** Blob limit per transaction. */
|
|
1754
|
+
const blobsPerTransaction = 6;
|
|
1755
|
+
/** The number of bytes in a BLS scalar field element. */
|
|
1756
|
+
const bytesPerFieldElement = 32;
|
|
1757
|
+
/** The number of field elements in a blob. */
|
|
1758
|
+
const fieldElementsPerBlob = 4096;
|
|
1759
|
+
/** The number of bytes in a blob. */
|
|
1760
|
+
const bytesPerBlob = bytesPerFieldElement * fieldElementsPerBlob;
|
|
1761
|
+
/** Blob bytes limit per transaction. */
|
|
1762
|
+
const maxBytesPerTransaction = bytesPerBlob * blobsPerTransaction -
|
|
1763
|
+
// terminator byte (0x80).
|
|
1764
|
+
1 -
|
|
1765
|
+
// zero byte (0x00) appended to each field element.
|
|
1766
|
+
1 * fieldElementsPerBlob * blobsPerTransaction;
|
|
1767
|
+
|
|
1768
|
+
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#parameters
|
|
1769
|
+
const versionedHashVersionKzg = 1;
|
|
1770
|
+
|
|
1771
|
+
class BlobSizeTooLargeError extends BaseError {
|
|
1772
|
+
constructor({ maxSize, size }) {
|
|
1773
|
+
super('Blob size is too large.', {
|
|
1774
|
+
metaMessages: [`Max: ${maxSize} bytes`, `Given: ${size} bytes`],
|
|
1775
|
+
name: 'BlobSizeTooLargeError',
|
|
1776
|
+
});
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
class EmptyBlobError extends BaseError {
|
|
1780
|
+
constructor() {
|
|
1781
|
+
super('Blob data must not be empty.', { name: 'EmptyBlobError' });
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
class InvalidVersionedHashSizeError extends BaseError {
|
|
1785
|
+
constructor({ hash, size, }) {
|
|
1786
|
+
super(`Versioned hash "${hash}" size is invalid.`, {
|
|
1787
|
+
metaMessages: ['Expected: 32', `Received: ${size}`],
|
|
1788
|
+
name: 'InvalidVersionedHashSizeError',
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
class InvalidVersionedHashVersionError extends BaseError {
|
|
1793
|
+
constructor({ hash, version, }) {
|
|
1794
|
+
super(`Versioned hash "${hash}" version is invalid.`, {
|
|
1795
|
+
metaMessages: [
|
|
1796
|
+
`Expected: ${versionedHashVersionKzg}`,
|
|
1797
|
+
`Received: ${version}`,
|
|
1798
|
+
],
|
|
1799
|
+
name: 'InvalidVersionedHashVersionError',
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
/**
|
|
1805
|
+
* Transforms arbitrary data to blobs.
|
|
1806
|
+
*
|
|
1807
|
+
* @example
|
|
1808
|
+
* ```ts
|
|
1809
|
+
* import { toBlobs, stringToHex } from 'viem'
|
|
1810
|
+
*
|
|
1811
|
+
* const blobs = toBlobs({ data: stringToHex('hello world') })
|
|
1812
|
+
* ```
|
|
1813
|
+
*/
|
|
1814
|
+
function toBlobs(parameters) {
|
|
1815
|
+
const to = parameters.to ?? (typeof parameters.data === 'string' ? 'hex' : 'bytes');
|
|
1816
|
+
const data = (typeof parameters.data === 'string'
|
|
1817
|
+
? hexToBytes(parameters.data)
|
|
1818
|
+
: parameters.data);
|
|
1819
|
+
const size_ = size(data);
|
|
1820
|
+
if (!size_)
|
|
1821
|
+
throw new EmptyBlobError();
|
|
1822
|
+
if (size_ > maxBytesPerTransaction)
|
|
1823
|
+
throw new BlobSizeTooLargeError({
|
|
1824
|
+
maxSize: maxBytesPerTransaction,
|
|
1825
|
+
size: size_,
|
|
1826
|
+
});
|
|
1827
|
+
const blobs = [];
|
|
1828
|
+
let active = true;
|
|
1829
|
+
let position = 0;
|
|
1830
|
+
while (active) {
|
|
1831
|
+
const blob = createCursor(new Uint8Array(bytesPerBlob));
|
|
1832
|
+
let size = 0;
|
|
1833
|
+
while (size < fieldElementsPerBlob) {
|
|
1834
|
+
const bytes = data.slice(position, position + (bytesPerFieldElement - 1));
|
|
1835
|
+
// Push a zero byte so the field element doesn't overflow the BLS modulus.
|
|
1836
|
+
blob.pushByte(0x00);
|
|
1837
|
+
// Push the current segment of data bytes.
|
|
1838
|
+
blob.pushBytes(bytes);
|
|
1839
|
+
// If we detect that the current segment of data bytes is less than 31 bytes,
|
|
1840
|
+
// we can stop processing and push a terminator byte to indicate the end of the blob.
|
|
1841
|
+
if (bytes.length < 31) {
|
|
1842
|
+
blob.pushByte(0x80);
|
|
1843
|
+
active = false;
|
|
1844
|
+
break;
|
|
1845
|
+
}
|
|
1846
|
+
size++;
|
|
1847
|
+
position += 31;
|
|
1848
|
+
}
|
|
1849
|
+
blobs.push(blob);
|
|
1850
|
+
}
|
|
1851
|
+
return (to === 'bytes'
|
|
1852
|
+
? blobs.map((x) => x.bytes)
|
|
1853
|
+
: blobs.map((x) => bytesToHex(x.bytes)));
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
/**
|
|
1857
|
+
* Transforms arbitrary data (or blobs, commitments, & proofs) into a sidecar array.
|
|
1858
|
+
*
|
|
1859
|
+
* @example
|
|
1860
|
+
* ```ts
|
|
1861
|
+
* import { toBlobSidecars, stringToHex } from 'viem'
|
|
1862
|
+
*
|
|
1863
|
+
* const sidecars = toBlobSidecars({ data: stringToHex('hello world') })
|
|
1864
|
+
* ```
|
|
1865
|
+
*
|
|
1866
|
+
* @example
|
|
1867
|
+
* ```ts
|
|
1868
|
+
* import {
|
|
1869
|
+
* blobsToCommitments,
|
|
1870
|
+
* toBlobs,
|
|
1871
|
+
* blobsToProofs,
|
|
1872
|
+
* toBlobSidecars,
|
|
1873
|
+
* stringToHex
|
|
1874
|
+
* } from 'viem'
|
|
1875
|
+
*
|
|
1876
|
+
* const blobs = toBlobs({ data: stringToHex('hello world') })
|
|
1877
|
+
* const commitments = blobsToCommitments({ blobs, kzg })
|
|
1878
|
+
* const proofs = blobsToProofs({ blobs, commitments, kzg })
|
|
1879
|
+
*
|
|
1880
|
+
* const sidecars = toBlobSidecars({ blobs, commitments, proofs })
|
|
1881
|
+
* ```
|
|
1882
|
+
*/
|
|
1883
|
+
function toBlobSidecars(parameters) {
|
|
1884
|
+
const { data, kzg, to } = parameters;
|
|
1885
|
+
const blobs = parameters.blobs ?? toBlobs({ data: data, to });
|
|
1886
|
+
const commitments = parameters.commitments ?? blobsToCommitments({ blobs, kzg: kzg, to });
|
|
1887
|
+
const proofs = parameters.proofs ?? blobsToProofs({ blobs, commitments, kzg: kzg, to });
|
|
1888
|
+
const sidecars = [];
|
|
1889
|
+
for (let i = 0; i < blobs.length; i++)
|
|
1890
|
+
sidecars.push({
|
|
1891
|
+
blob: blobs[i],
|
|
1892
|
+
commitment: commitments[i],
|
|
1893
|
+
proof: proofs[i],
|
|
1894
|
+
});
|
|
1895
|
+
return sidecars;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
class InvalidAddressError extends BaseError {
|
|
1899
|
+
constructor({ address }) {
|
|
1900
|
+
super(`Address "${address}" is invalid.`, {
|
|
1901
|
+
metaMessages: [
|
|
1902
|
+
'- Address must be a hex value of 20 bytes (40 hex characters).',
|
|
1903
|
+
'- Address must match its checksum counterpart.',
|
|
1904
|
+
],
|
|
1905
|
+
name: 'InvalidAddressError',
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
class InvalidChainIdError extends BaseError {
|
|
1911
|
+
constructor({ chainId }) {
|
|
1912
|
+
super(typeof chainId === 'number'
|
|
1913
|
+
? `Chain ID "${chainId}" is invalid.`
|
|
1914
|
+
: 'Chain ID is invalid.', { name: 'InvalidChainIdError' });
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
class FeeCapTooHighError extends BaseError {
|
|
1919
|
+
constructor({ cause, maxFeePerGas, } = {}) {
|
|
1920
|
+
super(`The fee cap (\`maxFeePerGas\`${maxFeePerGas ? ` = ${formatGwei(maxFeePerGas)} gwei` : ''}) cannot be higher than the maximum allowed value (2^256-1).`, {
|
|
1921
|
+
cause,
|
|
1922
|
+
name: 'FeeCapTooHighError',
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
Object.defineProperty(FeeCapTooHighError, "nodeMessage", {
|
|
1927
|
+
enumerable: true,
|
|
1928
|
+
configurable: true,
|
|
1929
|
+
writable: true,
|
|
1930
|
+
value: /max fee per gas higher than 2\^256-1|fee cap higher than 2\^256-1/
|
|
1931
|
+
});
|
|
1932
|
+
class TipAboveFeeCapError extends BaseError {
|
|
1933
|
+
constructor({ cause, maxPriorityFeePerGas, maxFeePerGas, } = {}) {
|
|
1934
|
+
super([
|
|
1935
|
+
`The provided tip (\`maxPriorityFeePerGas\`${maxPriorityFeePerGas
|
|
1936
|
+
? ` = ${formatGwei(maxPriorityFeePerGas)} gwei`
|
|
1937
|
+
: ''}) cannot be higher than the fee cap (\`maxFeePerGas\`${maxFeePerGas ? ` = ${formatGwei(maxFeePerGas)} gwei` : ''}).`,
|
|
1938
|
+
].join('\n'), {
|
|
1939
|
+
cause,
|
|
1940
|
+
name: 'TipAboveFeeCapError',
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
Object.defineProperty(TipAboveFeeCapError, "nodeMessage", {
|
|
1945
|
+
enumerable: true,
|
|
1946
|
+
configurable: true,
|
|
1947
|
+
writable: true,
|
|
1948
|
+
value: /max priority fee per gas higher than max fee per gas|tip higher than fee cap/
|
|
1949
|
+
});
|
|
1950
|
+
|
|
1951
|
+
/**
|
|
1952
|
+
* Map with a LRU (Least recently used) policy.
|
|
1953
|
+
*
|
|
1954
|
+
* @link https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU
|
|
1955
|
+
*/
|
|
1956
|
+
class LruMap extends Map {
|
|
1957
|
+
constructor(size) {
|
|
1958
|
+
super();
|
|
1959
|
+
Object.defineProperty(this, "maxSize", {
|
|
1960
|
+
enumerable: true,
|
|
1961
|
+
configurable: true,
|
|
1962
|
+
writable: true,
|
|
1963
|
+
value: void 0
|
|
1964
|
+
});
|
|
1965
|
+
this.maxSize = size;
|
|
1966
|
+
}
|
|
1967
|
+
get(key) {
|
|
1968
|
+
const value = super.get(key);
|
|
1969
|
+
if (super.has(key)) {
|
|
1970
|
+
super.delete(key);
|
|
1971
|
+
super.set(key, value);
|
|
1972
|
+
}
|
|
1973
|
+
return value;
|
|
1974
|
+
}
|
|
1975
|
+
set(key, value) {
|
|
1976
|
+
if (super.has(key))
|
|
1977
|
+
super.delete(key);
|
|
1978
|
+
super.set(key, value);
|
|
1979
|
+
if (this.maxSize && this.size > this.maxSize) {
|
|
1980
|
+
const firstKey = super.keys().next().value;
|
|
1981
|
+
if (firstKey !== undefined)
|
|
1982
|
+
super.delete(firstKey);
|
|
1983
|
+
}
|
|
1984
|
+
return this;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
/**
|
|
1989
|
+
* SHA3 (keccak) hash function, based on a new "Sponge function" design.
|
|
1990
|
+
* Different from older hashes, the internal state is bigger than output size.
|
|
1991
|
+
*
|
|
1992
|
+
* Check out [FIPS-202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf),
|
|
1993
|
+
* [Website](https://keccak.team/keccak.html),
|
|
1994
|
+
* [the differences between SHA-3 and Keccak](https://crypto.stackexchange.com/questions/15727/what-are-the-key-differences-between-the-draft-sha-3-standard-and-the-keccak-sub).
|
|
1995
|
+
*
|
|
1996
|
+
* Check out `sha3-addons` module for cSHAKE, k12, and others.
|
|
1997
|
+
* @module
|
|
1998
|
+
*/
|
|
1999
|
+
// No __PURE__ annotations in sha3 header:
|
|
2000
|
+
// EVERYTHING is in fact used on every export.
|
|
2001
|
+
// Various per round constants calculations
|
|
2002
|
+
const _0n = BigInt(0);
|
|
2003
|
+
const _1n = BigInt(1);
|
|
2004
|
+
const _2n = BigInt(2);
|
|
2005
|
+
const _7n = BigInt(7);
|
|
2006
|
+
const _256n = BigInt(256);
|
|
2007
|
+
const _0x71n = BigInt(0x71);
|
|
2008
|
+
const SHA3_PI = [];
|
|
2009
|
+
const SHA3_ROTL = [];
|
|
2010
|
+
const _SHA3_IOTA = [];
|
|
2011
|
+
for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
|
|
2012
|
+
// Pi
|
|
2013
|
+
[x, y] = [y, (2 * x + 3 * y) % 5];
|
|
2014
|
+
SHA3_PI.push(2 * (5 * y + x));
|
|
2015
|
+
// Rotational
|
|
2016
|
+
SHA3_ROTL.push((((round + 1) * (round + 2)) / 2) % 64);
|
|
2017
|
+
// Iota
|
|
2018
|
+
let t = _0n;
|
|
2019
|
+
for (let j = 0; j < 7; j++) {
|
|
2020
|
+
R = ((R << _1n) ^ ((R >> _7n) * _0x71n)) % _256n;
|
|
2021
|
+
if (R & _2n)
|
|
2022
|
+
t ^= _1n << ((_1n << /* @__PURE__ */ BigInt(j)) - _1n);
|
|
2023
|
+
}
|
|
2024
|
+
_SHA3_IOTA.push(t);
|
|
2025
|
+
}
|
|
2026
|
+
const IOTAS = split(_SHA3_IOTA, true);
|
|
2027
|
+
const SHA3_IOTA_H = IOTAS[0];
|
|
2028
|
+
const SHA3_IOTA_L = IOTAS[1];
|
|
2029
|
+
// Left rotation (without 0, 32, 64)
|
|
2030
|
+
const rotlH = (h, l, s) => (s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s));
|
|
2031
|
+
const rotlL = (h, l, s) => (s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s));
|
|
2032
|
+
/** `keccakf1600` internal function, additionally allows to adjust round count. */
|
|
2033
|
+
function keccakP(s, rounds = 24) {
|
|
2034
|
+
const B = new Uint32Array(5 * 2);
|
|
2035
|
+
// NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js)
|
|
2036
|
+
for (let round = 24 - rounds; round < 24; round++) {
|
|
2037
|
+
// Theta θ
|
|
2038
|
+
for (let x = 0; x < 10; x++)
|
|
2039
|
+
B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
|
|
2040
|
+
for (let x = 0; x < 10; x += 2) {
|
|
2041
|
+
const idx1 = (x + 8) % 10;
|
|
2042
|
+
const idx0 = (x + 2) % 10;
|
|
2043
|
+
const B0 = B[idx0];
|
|
2044
|
+
const B1 = B[idx0 + 1];
|
|
2045
|
+
const Th = rotlH(B0, B1, 1) ^ B[idx1];
|
|
2046
|
+
const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
|
|
2047
|
+
for (let y = 0; y < 50; y += 10) {
|
|
2048
|
+
s[x + y] ^= Th;
|
|
2049
|
+
s[x + y + 1] ^= Tl;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
// Rho (ρ) and Pi (π)
|
|
2053
|
+
let curH = s[2];
|
|
2054
|
+
let curL = s[3];
|
|
2055
|
+
for (let t = 0; t < 24; t++) {
|
|
2056
|
+
const shift = SHA3_ROTL[t];
|
|
2057
|
+
const Th = rotlH(curH, curL, shift);
|
|
2058
|
+
const Tl = rotlL(curH, curL, shift);
|
|
2059
|
+
const PI = SHA3_PI[t];
|
|
2060
|
+
curH = s[PI];
|
|
2061
|
+
curL = s[PI + 1];
|
|
2062
|
+
s[PI] = Th;
|
|
2063
|
+
s[PI + 1] = Tl;
|
|
2064
|
+
}
|
|
2065
|
+
// Chi (χ)
|
|
2066
|
+
for (let y = 0; y < 50; y += 10) {
|
|
2067
|
+
for (let x = 0; x < 10; x++)
|
|
2068
|
+
B[x] = s[y + x];
|
|
2069
|
+
for (let x = 0; x < 10; x++)
|
|
2070
|
+
s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
|
|
2071
|
+
}
|
|
2072
|
+
// Iota (ι)
|
|
2073
|
+
s[0] ^= SHA3_IOTA_H[round];
|
|
2074
|
+
s[1] ^= SHA3_IOTA_L[round];
|
|
2075
|
+
}
|
|
2076
|
+
clean(B);
|
|
2077
|
+
}
|
|
2078
|
+
/** Keccak sponge function. */
|
|
2079
|
+
class Keccak extends Hash {
|
|
2080
|
+
// NOTE: we accept arguments in bytes instead of bits here.
|
|
2081
|
+
constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) {
|
|
2082
|
+
super();
|
|
2083
|
+
this.pos = 0;
|
|
2084
|
+
this.posOut = 0;
|
|
2085
|
+
this.finished = false;
|
|
2086
|
+
this.destroyed = false;
|
|
2087
|
+
this.enableXOF = false;
|
|
2088
|
+
this.blockLen = blockLen;
|
|
2089
|
+
this.suffix = suffix;
|
|
2090
|
+
this.outputLen = outputLen;
|
|
2091
|
+
this.enableXOF = enableXOF;
|
|
2092
|
+
this.rounds = rounds;
|
|
2093
|
+
// Can be passed from user as dkLen
|
|
2094
|
+
anumber(outputLen);
|
|
2095
|
+
// 1600 = 5x5 matrix of 64bit. 1600 bits === 200 bytes
|
|
2096
|
+
// 0 < blockLen < 200
|
|
2097
|
+
if (!(0 < blockLen && blockLen < 200))
|
|
2098
|
+
throw new Error('only keccak-f1600 function is supported');
|
|
2099
|
+
this.state = new Uint8Array(200);
|
|
2100
|
+
this.state32 = u32(this.state);
|
|
2101
|
+
}
|
|
2102
|
+
clone() {
|
|
2103
|
+
return this._cloneInto();
|
|
2104
|
+
}
|
|
2105
|
+
keccak() {
|
|
2106
|
+
swap32IfBE(this.state32);
|
|
2107
|
+
keccakP(this.state32, this.rounds);
|
|
2108
|
+
swap32IfBE(this.state32);
|
|
2109
|
+
this.posOut = 0;
|
|
2110
|
+
this.pos = 0;
|
|
2111
|
+
}
|
|
2112
|
+
update(data) {
|
|
2113
|
+
aexists(this);
|
|
2114
|
+
data = toBytes(data);
|
|
2115
|
+
abytes(data);
|
|
2116
|
+
const { blockLen, state } = this;
|
|
2117
|
+
const len = data.length;
|
|
2118
|
+
for (let pos = 0; pos < len;) {
|
|
2119
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
2120
|
+
for (let i = 0; i < take; i++)
|
|
2121
|
+
state[this.pos++] ^= data[pos++];
|
|
2122
|
+
if (this.pos === blockLen)
|
|
2123
|
+
this.keccak();
|
|
2124
|
+
}
|
|
2125
|
+
return this;
|
|
2126
|
+
}
|
|
2127
|
+
finish() {
|
|
2128
|
+
if (this.finished)
|
|
2129
|
+
return;
|
|
2130
|
+
this.finished = true;
|
|
2131
|
+
const { state, suffix, pos, blockLen } = this;
|
|
2132
|
+
// Do the padding
|
|
2133
|
+
state[pos] ^= suffix;
|
|
2134
|
+
if ((suffix & 0x80) !== 0 && pos === blockLen - 1)
|
|
2135
|
+
this.keccak();
|
|
2136
|
+
state[blockLen - 1] ^= 0x80;
|
|
2137
|
+
this.keccak();
|
|
2138
|
+
}
|
|
2139
|
+
writeInto(out) {
|
|
2140
|
+
aexists(this, false);
|
|
2141
|
+
abytes(out);
|
|
2142
|
+
this.finish();
|
|
2143
|
+
const bufferOut = this.state;
|
|
2144
|
+
const { blockLen } = this;
|
|
2145
|
+
for (let pos = 0, len = out.length; pos < len;) {
|
|
2146
|
+
if (this.posOut >= blockLen)
|
|
2147
|
+
this.keccak();
|
|
2148
|
+
const take = Math.min(blockLen - this.posOut, len - pos);
|
|
2149
|
+
out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
|
|
2150
|
+
this.posOut += take;
|
|
2151
|
+
pos += take;
|
|
2152
|
+
}
|
|
2153
|
+
return out;
|
|
2154
|
+
}
|
|
2155
|
+
xofInto(out) {
|
|
2156
|
+
// Sha3/Keccak usage with XOF is probably mistake, only SHAKE instances can do XOF
|
|
2157
|
+
if (!this.enableXOF)
|
|
2158
|
+
throw new Error('XOF is not possible for this instance');
|
|
2159
|
+
return this.writeInto(out);
|
|
2160
|
+
}
|
|
2161
|
+
xof(bytes) {
|
|
2162
|
+
anumber(bytes);
|
|
2163
|
+
return this.xofInto(new Uint8Array(bytes));
|
|
2164
|
+
}
|
|
2165
|
+
digestInto(out) {
|
|
2166
|
+
aoutput(out, this);
|
|
2167
|
+
if (this.finished)
|
|
2168
|
+
throw new Error('digest() was already called');
|
|
2169
|
+
this.writeInto(out);
|
|
2170
|
+
this.destroy();
|
|
2171
|
+
return out;
|
|
2172
|
+
}
|
|
2173
|
+
digest() {
|
|
2174
|
+
return this.digestInto(new Uint8Array(this.outputLen));
|
|
2175
|
+
}
|
|
2176
|
+
destroy() {
|
|
2177
|
+
this.destroyed = true;
|
|
2178
|
+
clean(this.state);
|
|
2179
|
+
}
|
|
2180
|
+
_cloneInto(to) {
|
|
2181
|
+
const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
|
|
2182
|
+
to || (to = new Keccak(blockLen, suffix, outputLen, enableXOF, rounds));
|
|
2183
|
+
to.state32.set(this.state32);
|
|
2184
|
+
to.pos = this.pos;
|
|
2185
|
+
to.posOut = this.posOut;
|
|
2186
|
+
to.finished = this.finished;
|
|
2187
|
+
to.rounds = rounds;
|
|
2188
|
+
// Suffix can change in cSHAKE
|
|
2189
|
+
to.suffix = suffix;
|
|
2190
|
+
to.outputLen = outputLen;
|
|
2191
|
+
to.enableXOF = enableXOF;
|
|
2192
|
+
to.destroyed = this.destroyed;
|
|
2193
|
+
return to;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
const gen = (suffix, blockLen, outputLen) => createHasher(() => new Keccak(blockLen, suffix, outputLen));
|
|
2197
|
+
/** keccak-256 hash function. Different from SHA3-256. */
|
|
2198
|
+
const keccak_256 = /* @__PURE__ */ (() => gen(0x01, 136, 256 / 8))();
|
|
2199
|
+
|
|
2200
|
+
function keccak256(value, to_) {
|
|
2201
|
+
const bytes = keccak_256(isHex(value, { strict: false }) ? toBytes$1(value) : value);
|
|
2202
|
+
return bytes;
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
const checksumAddressCache = /*#__PURE__*/ new LruMap(8192);
|
|
2206
|
+
function checksumAddress(address_,
|
|
2207
|
+
/**
|
|
2208
|
+
* Warning: EIP-1191 checksum addresses are generally not backwards compatible with the
|
|
2209
|
+
* wider Ethereum ecosystem, meaning it will break when validated against an application/tool
|
|
2210
|
+
* that relies on EIP-55 checksum encoding (checksum without chainId).
|
|
2211
|
+
*
|
|
2212
|
+
* It is highly recommended to not use this feature unless you
|
|
2213
|
+
* know what you are doing.
|
|
2214
|
+
*
|
|
2215
|
+
* See more: https://github.com/ethereum/EIPs/issues/1121
|
|
2216
|
+
*/
|
|
2217
|
+
chainId) {
|
|
2218
|
+
if (checksumAddressCache.has(`${address_}.${chainId}`))
|
|
2219
|
+
return checksumAddressCache.get(`${address_}.${chainId}`);
|
|
2220
|
+
const hexAddress = address_.substring(2).toLowerCase();
|
|
2221
|
+
const hash = keccak256(stringToBytes(hexAddress));
|
|
2222
|
+
const address = (hexAddress).split('');
|
|
2223
|
+
for (let i = 0; i < 40; i += 2) {
|
|
2224
|
+
if (hash[i >> 1] >> 4 >= 8 && address[i]) {
|
|
2225
|
+
address[i] = address[i].toUpperCase();
|
|
2226
|
+
}
|
|
2227
|
+
if ((hash[i >> 1] & 0x0f) >= 8 && address[i + 1]) {
|
|
2228
|
+
address[i + 1] = address[i + 1].toUpperCase();
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
const result = `0x${address.join('')}`;
|
|
2232
|
+
checksumAddressCache.set(`${address_}.${chainId}`, result);
|
|
2233
|
+
return result;
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
const addressRegex = /^0x[a-fA-F0-9]{40}$/;
|
|
2237
|
+
/** @internal */
|
|
2238
|
+
const isAddressCache = /*#__PURE__*/ new LruMap(8192);
|
|
2239
|
+
function isAddress(address, options) {
|
|
2240
|
+
const { strict = true } = options ?? {};
|
|
2241
|
+
const cacheKey = `${address}.${strict}`;
|
|
2242
|
+
if (isAddressCache.has(cacheKey))
|
|
2243
|
+
return isAddressCache.get(cacheKey);
|
|
2244
|
+
const result = (() => {
|
|
2245
|
+
if (!addressRegex.test(address))
|
|
2246
|
+
return false;
|
|
2247
|
+
if (address.toLowerCase() === address)
|
|
2248
|
+
return true;
|
|
2249
|
+
if (strict)
|
|
2250
|
+
return checksumAddress(address) === address;
|
|
2251
|
+
return true;
|
|
2252
|
+
})();
|
|
2253
|
+
isAddressCache.set(cacheKey, result);
|
|
2254
|
+
return result;
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
/**
|
|
2258
|
+
* @description Returns a section of the hex or byte array given a start/end bytes offset.
|
|
2259
|
+
*
|
|
2260
|
+
* @param value The hex or byte array to slice.
|
|
2261
|
+
* @param start The start offset (in bytes).
|
|
2262
|
+
* @param end The end offset (in bytes).
|
|
2263
|
+
*/
|
|
2264
|
+
function slice(value, start, end, { strict } = {}) {
|
|
2265
|
+
if (isHex(value, { strict: false }))
|
|
2266
|
+
return sliceHex(value, start, end, {
|
|
2267
|
+
strict,
|
|
2268
|
+
});
|
|
2269
|
+
return sliceBytes(value, start, end, {
|
|
2270
|
+
strict,
|
|
2271
|
+
});
|
|
2272
|
+
}
|
|
2273
|
+
function assertEndOffset(value, start, end) {
|
|
2274
|
+
if (size(value) !== end - start) {
|
|
2275
|
+
throw new SliceOffsetOutOfBoundsError({
|
|
2276
|
+
offset: end,
|
|
2277
|
+
position: 'end',
|
|
2278
|
+
size: size(value),
|
|
2279
|
+
});
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
/**
|
|
2283
|
+
* @description Returns a section of the byte array given a start/end bytes offset.
|
|
2284
|
+
*
|
|
2285
|
+
* @param value The byte array to slice.
|
|
2286
|
+
* @param start The start offset (in bytes).
|
|
2287
|
+
* @param end The end offset (in bytes).
|
|
2288
|
+
*/
|
|
2289
|
+
function sliceBytes(value_, start, end, { strict } = {}) {
|
|
2290
|
+
const value = value_.slice(start, end);
|
|
2291
|
+
if (strict)
|
|
2292
|
+
assertEndOffset(value, start, end);
|
|
2293
|
+
return value;
|
|
2294
|
+
}
|
|
2295
|
+
/**
|
|
2296
|
+
* @description Returns a section of the hex value given a start/end bytes offset.
|
|
2297
|
+
*
|
|
2298
|
+
* @param value The hex value to slice.
|
|
2299
|
+
* @param start The start offset (in bytes).
|
|
2300
|
+
* @param end The end offset (in bytes).
|
|
2301
|
+
*/
|
|
2302
|
+
function sliceHex(value_, start, end, { strict } = {}) {
|
|
2303
|
+
const value = `0x${value_
|
|
2304
|
+
.replace('0x', '')
|
|
2305
|
+
.slice((start) * 2, (end) * 2)}`;
|
|
2306
|
+
if (strict)
|
|
2307
|
+
assertEndOffset(value, start, end);
|
|
2308
|
+
return value;
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
function assertTransactionEIP7702(transaction) {
|
|
2312
|
+
const { authorizationList } = transaction;
|
|
2313
|
+
if (authorizationList) {
|
|
2314
|
+
for (const authorization of authorizationList) {
|
|
2315
|
+
const { chainId } = authorization;
|
|
2316
|
+
const address = authorization.address;
|
|
2317
|
+
if (!isAddress(address))
|
|
2318
|
+
throw new InvalidAddressError({ address });
|
|
2319
|
+
if (chainId < 0)
|
|
2320
|
+
throw new InvalidChainIdError({ chainId });
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
assertTransactionEIP1559(transaction);
|
|
2324
|
+
}
|
|
2325
|
+
function assertTransactionEIP4844(transaction) {
|
|
2326
|
+
const { blobVersionedHashes } = transaction;
|
|
2327
|
+
if (blobVersionedHashes) {
|
|
2328
|
+
if (blobVersionedHashes.length === 0)
|
|
2329
|
+
throw new EmptyBlobError();
|
|
2330
|
+
for (const hash of blobVersionedHashes) {
|
|
2331
|
+
const size_ = size(hash);
|
|
2332
|
+
const version = hexToNumber(slice(hash, 0, 1));
|
|
2333
|
+
if (size_ !== 32)
|
|
2334
|
+
throw new InvalidVersionedHashSizeError({ hash, size: size_ });
|
|
2335
|
+
if (version !== versionedHashVersionKzg)
|
|
2336
|
+
throw new InvalidVersionedHashVersionError({
|
|
2337
|
+
hash,
|
|
2338
|
+
version,
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
assertTransactionEIP1559(transaction);
|
|
2343
|
+
}
|
|
2344
|
+
function assertTransactionEIP1559(transaction) {
|
|
2345
|
+
const { chainId, maxPriorityFeePerGas, maxFeePerGas, to } = transaction;
|
|
2346
|
+
if (chainId <= 0)
|
|
2347
|
+
throw new InvalidChainIdError({ chainId });
|
|
2348
|
+
if (to && !isAddress(to))
|
|
2349
|
+
throw new InvalidAddressError({ address: to });
|
|
2350
|
+
if (maxFeePerGas && maxFeePerGas > maxUint256)
|
|
2351
|
+
throw new FeeCapTooHighError({ maxFeePerGas });
|
|
2352
|
+
if (maxPriorityFeePerGas &&
|
|
2353
|
+
maxFeePerGas &&
|
|
2354
|
+
maxPriorityFeePerGas > maxFeePerGas)
|
|
2355
|
+
throw new TipAboveFeeCapError({ maxFeePerGas, maxPriorityFeePerGas });
|
|
2356
|
+
}
|
|
2357
|
+
function assertTransactionEIP2930(transaction) {
|
|
2358
|
+
const { chainId, maxPriorityFeePerGas, gasPrice, maxFeePerGas, to } = transaction;
|
|
2359
|
+
if (chainId <= 0)
|
|
2360
|
+
throw new InvalidChainIdError({ chainId });
|
|
2361
|
+
if (to && !isAddress(to))
|
|
2362
|
+
throw new InvalidAddressError({ address: to });
|
|
2363
|
+
if (maxPriorityFeePerGas || maxFeePerGas)
|
|
2364
|
+
throw new BaseError('`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid EIP-2930 Transaction attribute.');
|
|
2365
|
+
if (gasPrice && gasPrice > maxUint256)
|
|
2366
|
+
throw new FeeCapTooHighError({ maxFeePerGas: gasPrice });
|
|
2367
|
+
}
|
|
2368
|
+
function assertTransactionLegacy(transaction) {
|
|
2369
|
+
const { chainId, maxPriorityFeePerGas, gasPrice, maxFeePerGas, to } = transaction;
|
|
2370
|
+
if (to && !isAddress(to))
|
|
2371
|
+
throw new InvalidAddressError({ address: to });
|
|
2372
|
+
if (typeof chainId !== 'undefined' && chainId <= 0)
|
|
2373
|
+
throw new InvalidChainIdError({ chainId });
|
|
2374
|
+
if (maxPriorityFeePerGas || maxFeePerGas)
|
|
2375
|
+
throw new BaseError('`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid Legacy Transaction attribute.');
|
|
2376
|
+
if (gasPrice && gasPrice > maxUint256)
|
|
2377
|
+
throw new FeeCapTooHighError({ maxFeePerGas: gasPrice });
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
function getTransactionType(transaction) {
|
|
2381
|
+
if (transaction.type)
|
|
2382
|
+
return transaction.type;
|
|
2383
|
+
if (typeof transaction.authorizationList !== 'undefined')
|
|
2384
|
+
return 'eip7702';
|
|
2385
|
+
if (typeof transaction.blobs !== 'undefined' ||
|
|
2386
|
+
typeof transaction.blobVersionedHashes !== 'undefined' ||
|
|
2387
|
+
typeof transaction.maxFeePerBlobGas !== 'undefined' ||
|
|
2388
|
+
typeof transaction.sidecars !== 'undefined')
|
|
2389
|
+
return 'eip4844';
|
|
2390
|
+
if (typeof transaction.maxFeePerGas !== 'undefined' ||
|
|
2391
|
+
typeof transaction.maxPriorityFeePerGas !== 'undefined') {
|
|
2392
|
+
return 'eip1559';
|
|
2393
|
+
}
|
|
2394
|
+
if (typeof transaction.gasPrice !== 'undefined') {
|
|
2395
|
+
if (typeof transaction.accessList !== 'undefined')
|
|
2396
|
+
return 'eip2930';
|
|
2397
|
+
return 'legacy';
|
|
2398
|
+
}
|
|
2399
|
+
throw new InvalidSerializableTransactionError({ transaction });
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
/*
|
|
2403
|
+
* Serialize an EIP-2930 access list
|
|
2404
|
+
* @remarks
|
|
2405
|
+
* Use to create a transaction serializer with support for EIP-2930 access lists
|
|
2406
|
+
*
|
|
2407
|
+
* @param accessList - Array of objects of address and arrays of Storage Keys
|
|
2408
|
+
* @throws InvalidAddressError, InvalidStorageKeySizeError
|
|
2409
|
+
* @returns Array of hex strings
|
|
2410
|
+
*/
|
|
2411
|
+
function serializeAccessList(accessList) {
|
|
2412
|
+
if (!accessList || accessList.length === 0)
|
|
2413
|
+
return [];
|
|
2414
|
+
const serializedAccessList = [];
|
|
2415
|
+
for (let i = 0; i < accessList.length; i++) {
|
|
2416
|
+
const { address, storageKeys } = accessList[i];
|
|
2417
|
+
for (let j = 0; j < storageKeys.length; j++) {
|
|
2418
|
+
if (storageKeys[j].length - 2 !== 64) {
|
|
2419
|
+
throw new InvalidStorageKeySizeError({ storageKey: storageKeys[j] });
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
if (!isAddress(address, { strict: false })) {
|
|
2423
|
+
throw new InvalidAddressError({ address });
|
|
2424
|
+
}
|
|
2425
|
+
serializedAccessList.push([address, storageKeys]);
|
|
2426
|
+
}
|
|
2427
|
+
return serializedAccessList;
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
function serializeTransaction$1(transaction, signature) {
|
|
2431
|
+
const type = getTransactionType(transaction);
|
|
2432
|
+
if (type === 'eip1559')
|
|
2433
|
+
return serializeTransactionEIP1559(transaction, signature);
|
|
2434
|
+
if (type === 'eip2930')
|
|
2435
|
+
return serializeTransactionEIP2930(transaction, signature);
|
|
2436
|
+
if (type === 'eip4844')
|
|
2437
|
+
return serializeTransactionEIP4844(transaction, signature);
|
|
2438
|
+
if (type === 'eip7702')
|
|
2439
|
+
return serializeTransactionEIP7702(transaction, signature);
|
|
2440
|
+
return serializeTransactionLegacy(transaction, signature);
|
|
2441
|
+
}
|
|
2442
|
+
function serializeTransactionEIP7702(transaction, signature) {
|
|
2443
|
+
const { authorizationList, chainId, gas, nonce, to, value, maxFeePerGas, maxPriorityFeePerGas, accessList, data, } = transaction;
|
|
2444
|
+
assertTransactionEIP7702(transaction);
|
|
2445
|
+
const serializedAccessList = serializeAccessList(accessList);
|
|
2446
|
+
const serializedAuthorizationList = serializeAuthorizationList(authorizationList);
|
|
2447
|
+
return concatHex([
|
|
2448
|
+
'0x04',
|
|
2449
|
+
toRlp([
|
|
2450
|
+
numberToHex(chainId),
|
|
2451
|
+
nonce ? numberToHex(nonce) : '0x',
|
|
2452
|
+
maxPriorityFeePerGas ? numberToHex(maxPriorityFeePerGas) : '0x',
|
|
2453
|
+
maxFeePerGas ? numberToHex(maxFeePerGas) : '0x',
|
|
2454
|
+
gas ? numberToHex(gas) : '0x',
|
|
2455
|
+
to ?? '0x',
|
|
2456
|
+
value ? numberToHex(value) : '0x',
|
|
2457
|
+
data ?? '0x',
|
|
2458
|
+
serializedAccessList,
|
|
2459
|
+
serializedAuthorizationList,
|
|
2460
|
+
...toYParitySignatureArray(transaction, signature),
|
|
2461
|
+
]),
|
|
2462
|
+
]);
|
|
2463
|
+
}
|
|
2464
|
+
function serializeTransactionEIP4844(transaction, signature) {
|
|
2465
|
+
const { chainId, gas, nonce, to, value, maxFeePerBlobGas, maxFeePerGas, maxPriorityFeePerGas, accessList, data, } = transaction;
|
|
2466
|
+
assertTransactionEIP4844(transaction);
|
|
2467
|
+
let blobVersionedHashes = transaction.blobVersionedHashes;
|
|
2468
|
+
let sidecars = transaction.sidecars;
|
|
2469
|
+
// If `blobs` are passed, we will need to compute the KZG commitments & proofs.
|
|
2470
|
+
if (transaction.blobs &&
|
|
2471
|
+
(typeof blobVersionedHashes === 'undefined' ||
|
|
2472
|
+
typeof sidecars === 'undefined')) {
|
|
2473
|
+
const blobs = (typeof transaction.blobs[0] === 'string'
|
|
2474
|
+
? transaction.blobs
|
|
2475
|
+
: transaction.blobs.map((x) => bytesToHex(x)));
|
|
2476
|
+
const kzg = transaction.kzg;
|
|
2477
|
+
const commitments = blobsToCommitments({
|
|
2478
|
+
blobs,
|
|
2479
|
+
kzg,
|
|
2480
|
+
});
|
|
2481
|
+
if (typeof blobVersionedHashes === 'undefined')
|
|
2482
|
+
blobVersionedHashes = commitmentsToVersionedHashes({
|
|
2483
|
+
commitments,
|
|
2484
|
+
});
|
|
2485
|
+
if (typeof sidecars === 'undefined') {
|
|
2486
|
+
const proofs = blobsToProofs({ blobs, commitments, kzg });
|
|
2487
|
+
sidecars = toBlobSidecars({ blobs, commitments, proofs });
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
const serializedAccessList = serializeAccessList(accessList);
|
|
2491
|
+
const serializedTransaction = [
|
|
2492
|
+
numberToHex(chainId),
|
|
2493
|
+
nonce ? numberToHex(nonce) : '0x',
|
|
2494
|
+
maxPriorityFeePerGas ? numberToHex(maxPriorityFeePerGas) : '0x',
|
|
2495
|
+
maxFeePerGas ? numberToHex(maxFeePerGas) : '0x',
|
|
2496
|
+
gas ? numberToHex(gas) : '0x',
|
|
2497
|
+
to ?? '0x',
|
|
2498
|
+
value ? numberToHex(value) : '0x',
|
|
2499
|
+
data ?? '0x',
|
|
2500
|
+
serializedAccessList,
|
|
2501
|
+
maxFeePerBlobGas ? numberToHex(maxFeePerBlobGas) : '0x',
|
|
2502
|
+
blobVersionedHashes ?? [],
|
|
2503
|
+
...toYParitySignatureArray(transaction, signature),
|
|
2504
|
+
];
|
|
2505
|
+
const blobs = [];
|
|
2506
|
+
const commitments = [];
|
|
2507
|
+
const proofs = [];
|
|
2508
|
+
if (sidecars)
|
|
2509
|
+
for (let i = 0; i < sidecars.length; i++) {
|
|
2510
|
+
const { blob, commitment, proof } = sidecars[i];
|
|
2511
|
+
blobs.push(blob);
|
|
2512
|
+
commitments.push(commitment);
|
|
2513
|
+
proofs.push(proof);
|
|
2514
|
+
}
|
|
2515
|
+
return concatHex([
|
|
2516
|
+
'0x03',
|
|
2517
|
+
sidecars
|
|
2518
|
+
? // If sidecars are enabled, envelope turns into a "wrapper":
|
|
2519
|
+
toRlp([serializedTransaction, blobs, commitments, proofs])
|
|
2520
|
+
: // If sidecars are disabled, standard envelope is used:
|
|
2521
|
+
toRlp(serializedTransaction),
|
|
2522
|
+
]);
|
|
2523
|
+
}
|
|
2524
|
+
function serializeTransactionEIP1559(transaction, signature) {
|
|
2525
|
+
const { chainId, gas, nonce, to, value, maxFeePerGas, maxPriorityFeePerGas, accessList, data, } = transaction;
|
|
2526
|
+
assertTransactionEIP1559(transaction);
|
|
2527
|
+
const serializedAccessList = serializeAccessList(accessList);
|
|
2528
|
+
const serializedTransaction = [
|
|
2529
|
+
numberToHex(chainId),
|
|
2530
|
+
nonce ? numberToHex(nonce) : '0x',
|
|
2531
|
+
maxPriorityFeePerGas ? numberToHex(maxPriorityFeePerGas) : '0x',
|
|
2532
|
+
maxFeePerGas ? numberToHex(maxFeePerGas) : '0x',
|
|
2533
|
+
gas ? numberToHex(gas) : '0x',
|
|
2534
|
+
to ?? '0x',
|
|
2535
|
+
value ? numberToHex(value) : '0x',
|
|
2536
|
+
data ?? '0x',
|
|
2537
|
+
serializedAccessList,
|
|
2538
|
+
...toYParitySignatureArray(transaction, signature),
|
|
2539
|
+
];
|
|
2540
|
+
return concatHex([
|
|
2541
|
+
'0x02',
|
|
2542
|
+
toRlp(serializedTransaction),
|
|
2543
|
+
]);
|
|
2544
|
+
}
|
|
2545
|
+
function serializeTransactionEIP2930(transaction, signature) {
|
|
2546
|
+
const { chainId, gas, data, nonce, to, value, accessList, gasPrice } = transaction;
|
|
2547
|
+
assertTransactionEIP2930(transaction);
|
|
2548
|
+
const serializedAccessList = serializeAccessList(accessList);
|
|
2549
|
+
const serializedTransaction = [
|
|
2550
|
+
numberToHex(chainId),
|
|
2551
|
+
nonce ? numberToHex(nonce) : '0x',
|
|
2552
|
+
gasPrice ? numberToHex(gasPrice) : '0x',
|
|
2553
|
+
gas ? numberToHex(gas) : '0x',
|
|
2554
|
+
to ?? '0x',
|
|
2555
|
+
value ? numberToHex(value) : '0x',
|
|
2556
|
+
data ?? '0x',
|
|
2557
|
+
serializedAccessList,
|
|
2558
|
+
...toYParitySignatureArray(transaction, signature),
|
|
2559
|
+
];
|
|
2560
|
+
return concatHex([
|
|
2561
|
+
'0x01',
|
|
2562
|
+
toRlp(serializedTransaction),
|
|
2563
|
+
]);
|
|
2564
|
+
}
|
|
2565
|
+
function serializeTransactionLegacy(transaction, signature) {
|
|
2566
|
+
const { chainId = 0, gas, data, nonce, to, value, gasPrice } = transaction;
|
|
2567
|
+
assertTransactionLegacy(transaction);
|
|
2568
|
+
let serializedTransaction = [
|
|
2569
|
+
nonce ? numberToHex(nonce) : '0x',
|
|
2570
|
+
gasPrice ? numberToHex(gasPrice) : '0x',
|
|
2571
|
+
gas ? numberToHex(gas) : '0x',
|
|
2572
|
+
to ?? '0x',
|
|
2573
|
+
value ? numberToHex(value) : '0x',
|
|
2574
|
+
data ?? '0x',
|
|
2575
|
+
];
|
|
2576
|
+
if (signature) {
|
|
2577
|
+
const v = (() => {
|
|
2578
|
+
// EIP-155 (inferred chainId)
|
|
2579
|
+
if (signature.v >= 35n) {
|
|
2580
|
+
const inferredChainId = (signature.v - 35n) / 2n;
|
|
2581
|
+
if (inferredChainId > 0)
|
|
2582
|
+
return signature.v;
|
|
2583
|
+
return 27n + (signature.v === 35n ? 0n : 1n);
|
|
2584
|
+
}
|
|
2585
|
+
// EIP-155 (explicit chainId)
|
|
2586
|
+
if (chainId > 0)
|
|
2587
|
+
return BigInt(chainId * 2) + BigInt(35n + signature.v - 27n);
|
|
2588
|
+
// Pre-EIP-155 (no chainId)
|
|
2589
|
+
const v = 27n + (signature.v === 27n ? 0n : 1n);
|
|
2590
|
+
if (signature.v !== v)
|
|
2591
|
+
throw new InvalidLegacyVError({ v: signature.v });
|
|
2592
|
+
return v;
|
|
2593
|
+
})();
|
|
2594
|
+
const r = trim(signature.r);
|
|
2595
|
+
const s = trim(signature.s);
|
|
2596
|
+
serializedTransaction = [
|
|
2597
|
+
...serializedTransaction,
|
|
2598
|
+
numberToHex(v),
|
|
2599
|
+
r === '0x00' ? '0x' : r,
|
|
2600
|
+
s === '0x00' ? '0x' : s,
|
|
2601
|
+
];
|
|
2602
|
+
}
|
|
2603
|
+
else if (chainId > 0) {
|
|
2604
|
+
serializedTransaction = [
|
|
2605
|
+
...serializedTransaction,
|
|
2606
|
+
numberToHex(chainId),
|
|
2607
|
+
'0x',
|
|
2608
|
+
'0x',
|
|
2609
|
+
];
|
|
2610
|
+
}
|
|
2611
|
+
return toRlp(serializedTransaction);
|
|
2612
|
+
}
|
|
2613
|
+
function toYParitySignatureArray(transaction, signature_) {
|
|
2614
|
+
const signature = signature_ ?? transaction;
|
|
2615
|
+
const { v, yParity } = signature;
|
|
2616
|
+
if (typeof signature.r === 'undefined')
|
|
2617
|
+
return [];
|
|
2618
|
+
if (typeof signature.s === 'undefined')
|
|
2619
|
+
return [];
|
|
2620
|
+
if (typeof v === 'undefined' && typeof yParity === 'undefined')
|
|
2621
|
+
return [];
|
|
2622
|
+
const r = trim(signature.r);
|
|
2623
|
+
const s = trim(signature.s);
|
|
2624
|
+
const yParity_ = (() => {
|
|
2625
|
+
if (typeof yParity === 'number')
|
|
2626
|
+
return yParity ? numberToHex(1) : '0x';
|
|
2627
|
+
if (v === 0n)
|
|
2628
|
+
return '0x';
|
|
2629
|
+
if (v === 1n)
|
|
2630
|
+
return numberToHex(1);
|
|
2631
|
+
return v === 27n ? '0x' : numberToHex(1);
|
|
2632
|
+
})();
|
|
2633
|
+
return [yParity_, r === '0x00' ? '0x' : r, s === '0x00' ? '0x' : s];
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
/**
|
|
2637
|
+
* Predeploy contracts for OP Stack.
|
|
2638
|
+
* @see https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md
|
|
2639
|
+
*/
|
|
2640
|
+
const contracts = {
|
|
2641
|
+
gasPriceOracle: { address: '0x420000000000000000000000000000000000000F' },
|
|
2642
|
+
l1Block: { address: '0x4200000000000000000000000000000000000015' },
|
|
2643
|
+
l2CrossDomainMessenger: {
|
|
2644
|
+
address: '0x4200000000000000000000000000000000000007',
|
|
2645
|
+
},
|
|
2646
|
+
l2Erc721Bridge: { address: '0x4200000000000000000000000000000000000014' },
|
|
2647
|
+
l2StandardBridge: { address: '0x4200000000000000000000000000000000000010' },
|
|
2648
|
+
l2ToL1MessagePasser: {
|
|
2649
|
+
address: '0x4200000000000000000000000000000000000016',
|
|
2650
|
+
},
|
|
2651
|
+
};
|
|
2652
|
+
|
|
2653
|
+
const formatters = {
|
|
2654
|
+
block: /*#__PURE__*/ defineBlock({
|
|
2655
|
+
format(args) {
|
|
2656
|
+
const transactions = args.transactions?.map((transaction) => {
|
|
2657
|
+
if (typeof transaction === 'string')
|
|
2658
|
+
return transaction;
|
|
2659
|
+
const formatted = formatTransaction(transaction);
|
|
2660
|
+
if (formatted.typeHex === '0x7e') {
|
|
2661
|
+
formatted.isSystemTx = transaction.isSystemTx;
|
|
2662
|
+
formatted.mint = transaction.mint
|
|
2663
|
+
? hexToBigInt(transaction.mint)
|
|
2664
|
+
: undefined;
|
|
2665
|
+
formatted.sourceHash = transaction.sourceHash;
|
|
2666
|
+
formatted.type = 'deposit';
|
|
2667
|
+
}
|
|
2668
|
+
return formatted;
|
|
2669
|
+
});
|
|
2670
|
+
return {
|
|
2671
|
+
transactions,
|
|
2672
|
+
stateRoot: args.stateRoot,
|
|
2673
|
+
};
|
|
2674
|
+
},
|
|
2675
|
+
}),
|
|
2676
|
+
transaction: /*#__PURE__*/ defineTransaction({
|
|
2677
|
+
format(args) {
|
|
2678
|
+
const transaction = {};
|
|
2679
|
+
if (args.type === '0x7e') {
|
|
2680
|
+
transaction.isSystemTx = args.isSystemTx;
|
|
2681
|
+
transaction.mint = args.mint ? hexToBigInt(args.mint) : undefined;
|
|
2682
|
+
transaction.sourceHash = args.sourceHash;
|
|
2683
|
+
transaction.type = 'deposit';
|
|
2684
|
+
}
|
|
2685
|
+
return transaction;
|
|
2686
|
+
},
|
|
2687
|
+
}),
|
|
2688
|
+
transactionReceipt: /*#__PURE__*/ defineTransactionReceipt({
|
|
2689
|
+
format(args) {
|
|
2690
|
+
return {
|
|
2691
|
+
l1GasPrice: args.l1GasPrice ? hexToBigInt(args.l1GasPrice) : null,
|
|
2692
|
+
l1GasUsed: args.l1GasUsed ? hexToBigInt(args.l1GasUsed) : null,
|
|
2693
|
+
l1Fee: args.l1Fee ? hexToBigInt(args.l1Fee) : null,
|
|
2694
|
+
l1FeeScalar: args.l1FeeScalar ? Number(args.l1FeeScalar) : null,
|
|
2695
|
+
};
|
|
2696
|
+
},
|
|
2697
|
+
}),
|
|
2698
|
+
};
|
|
2699
|
+
|
|
2700
|
+
function serializeTransaction(transaction, signature) {
|
|
2701
|
+
if (isDeposit(transaction))
|
|
2702
|
+
return serializeTransactionDeposit(transaction);
|
|
2703
|
+
return serializeTransaction$1(transaction, signature);
|
|
2704
|
+
}
|
|
2705
|
+
const serializers = {
|
|
2706
|
+
transaction: serializeTransaction,
|
|
2707
|
+
};
|
|
2708
|
+
function serializeTransactionDeposit(transaction) {
|
|
2709
|
+
assertTransactionDeposit(transaction);
|
|
2710
|
+
const { sourceHash, data, from, gas, isSystemTx, mint, to, value } = transaction;
|
|
2711
|
+
const serializedTransaction = [
|
|
2712
|
+
sourceHash,
|
|
2713
|
+
from,
|
|
2714
|
+
to ?? '0x',
|
|
2715
|
+
mint ? toHex(mint) : '0x',
|
|
2716
|
+
value ? toHex(value) : '0x',
|
|
2717
|
+
gas ? toHex(gas) : '0x',
|
|
2718
|
+
isSystemTx ? '0x1' : '0x',
|
|
2719
|
+
data ?? '0x',
|
|
2720
|
+
];
|
|
2721
|
+
return concatHex([
|
|
2722
|
+
'0x7e',
|
|
2723
|
+
toRlp(serializedTransaction),
|
|
2724
|
+
]);
|
|
2725
|
+
}
|
|
2726
|
+
function isDeposit(transaction) {
|
|
2727
|
+
if (transaction.type === 'deposit')
|
|
2728
|
+
return true;
|
|
2729
|
+
if (typeof transaction.sourceHash !== 'undefined')
|
|
2730
|
+
return true;
|
|
2731
|
+
return false;
|
|
2732
|
+
}
|
|
2733
|
+
function assertTransactionDeposit(transaction) {
|
|
2734
|
+
const { from, to } = transaction;
|
|
2735
|
+
if (from && !isAddress(from))
|
|
2736
|
+
throw new InvalidAddressError({ address: from });
|
|
2737
|
+
if (to && !isAddress(to))
|
|
2738
|
+
throw new InvalidAddressError({ address: to });
|
|
2739
|
+
}
|
|
2740
|
+
|
|
2741
|
+
const chainConfig = {
|
|
2742
|
+
blockTime: 2_000,
|
|
2743
|
+
contracts,
|
|
2744
|
+
formatters,
|
|
2745
|
+
serializers,
|
|
2746
|
+
};
|
|
2747
|
+
|
|
2748
|
+
const sourceId$1 = 1; // mainnet
|
|
2749
|
+
const base = /*#__PURE__*/ defineChain({
|
|
2750
|
+
...chainConfig,
|
|
2751
|
+
id: 8453,
|
|
2752
|
+
name: 'Base',
|
|
2753
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
2754
|
+
rpcUrls: {
|
|
2755
|
+
default: {
|
|
2756
|
+
http: ['https://mainnet.base.org'],
|
|
2757
|
+
},
|
|
2758
|
+
},
|
|
2759
|
+
blockExplorers: {
|
|
2760
|
+
default: {
|
|
2761
|
+
name: 'Basescan',
|
|
2762
|
+
url: 'https://basescan.org',
|
|
2763
|
+
apiUrl: 'https://api.basescan.org/api',
|
|
2764
|
+
},
|
|
2765
|
+
},
|
|
2766
|
+
contracts: {
|
|
2767
|
+
...chainConfig.contracts,
|
|
2768
|
+
disputeGameFactory: {
|
|
2769
|
+
[sourceId$1]: {
|
|
2770
|
+
address: '0x43edB88C4B80fDD2AdFF2412A7BebF9dF42cB40e',
|
|
2771
|
+
},
|
|
2772
|
+
},
|
|
2773
|
+
l2OutputOracle: {
|
|
2774
|
+
[sourceId$1]: {
|
|
2775
|
+
address: '0x56315b90c40730925ec5485cf004d835058518A0',
|
|
2776
|
+
},
|
|
2777
|
+
},
|
|
2778
|
+
multicall3: {
|
|
2779
|
+
address: '0xca11bde05977b3631167028862be2a173976ca11',
|
|
2780
|
+
blockCreated: 5022,
|
|
2781
|
+
},
|
|
2782
|
+
portal: {
|
|
2783
|
+
[sourceId$1]: {
|
|
2784
|
+
address: '0x49048044D57e1C92A77f79988d21Fa8fAF74E97e',
|
|
2785
|
+
blockCreated: 17482143,
|
|
2786
|
+
},
|
|
2787
|
+
},
|
|
2788
|
+
l1StandardBridge: {
|
|
2789
|
+
[sourceId$1]: {
|
|
2790
|
+
address: '0x3154Cf16ccdb4C6d922629664174b904d80F2C35',
|
|
2791
|
+
blockCreated: 17482143,
|
|
2792
|
+
},
|
|
2793
|
+
},
|
|
2794
|
+
},
|
|
2795
|
+
sourceId: sourceId$1,
|
|
2796
|
+
});
|
|
2797
|
+
/*#__PURE__*/ defineChain({
|
|
2798
|
+
...base,
|
|
2799
|
+
experimental_preconfirmationTime: 200,
|
|
2800
|
+
rpcUrls: {
|
|
2801
|
+
default: {
|
|
2802
|
+
http: ['https://mainnet-preconf.base.org'],
|
|
2803
|
+
},
|
|
2804
|
+
},
|
|
2805
|
+
});
|
|
2806
|
+
|
|
2807
|
+
const sourceId = 11_155_111; // sepolia
|
|
2808
|
+
const baseSepolia = /*#__PURE__*/ defineChain({
|
|
2809
|
+
...chainConfig,
|
|
2810
|
+
id: 84532,
|
|
2811
|
+
network: 'base-sepolia',
|
|
2812
|
+
name: 'Base Sepolia',
|
|
2813
|
+
nativeCurrency: { name: 'Sepolia Ether', symbol: 'ETH', decimals: 18 },
|
|
2814
|
+
rpcUrls: {
|
|
2815
|
+
default: {
|
|
2816
|
+
http: ['https://sepolia.base.org'],
|
|
2817
|
+
},
|
|
2818
|
+
},
|
|
2819
|
+
blockExplorers: {
|
|
2820
|
+
default: {
|
|
2821
|
+
name: 'Basescan',
|
|
2822
|
+
url: 'https://sepolia.basescan.org',
|
|
2823
|
+
apiUrl: 'https://api-sepolia.basescan.org/api',
|
|
2824
|
+
},
|
|
2825
|
+
},
|
|
2826
|
+
contracts: {
|
|
2827
|
+
...chainConfig.contracts,
|
|
2828
|
+
disputeGameFactory: {
|
|
2829
|
+
[sourceId]: {
|
|
2830
|
+
address: '0xd6E6dBf4F7EA0ac412fD8b65ED297e64BB7a06E1',
|
|
2831
|
+
},
|
|
2832
|
+
},
|
|
2833
|
+
l2OutputOracle: {
|
|
2834
|
+
[sourceId]: {
|
|
2835
|
+
address: '0x84457ca9D0163FbC4bbfe4Dfbb20ba46e48DF254',
|
|
2836
|
+
},
|
|
2837
|
+
},
|
|
2838
|
+
portal: {
|
|
2839
|
+
[sourceId]: {
|
|
2840
|
+
address: '0x49f53e41452c74589e85ca1677426ba426459e85',
|
|
2841
|
+
blockCreated: 4446677,
|
|
2842
|
+
},
|
|
2843
|
+
},
|
|
2844
|
+
l1StandardBridge: {
|
|
2845
|
+
[sourceId]: {
|
|
2846
|
+
address: '0xfd0Bf71F60660E2f608ed56e1659C450eB113120',
|
|
2847
|
+
blockCreated: 4446677,
|
|
2848
|
+
},
|
|
2849
|
+
},
|
|
2850
|
+
multicall3: {
|
|
2851
|
+
address: '0xca11bde05977b3631167028862be2a173976ca11',
|
|
2852
|
+
blockCreated: 1059647,
|
|
2853
|
+
},
|
|
2854
|
+
},
|
|
2855
|
+
testnet: true,
|
|
2856
|
+
sourceId,
|
|
2857
|
+
});
|
|
2858
|
+
/*#__PURE__*/ defineChain({
|
|
2859
|
+
...baseSepolia,
|
|
2860
|
+
experimental_preconfirmationTime: 200,
|
|
2861
|
+
rpcUrls: {
|
|
2862
|
+
default: {
|
|
2863
|
+
http: ['https://sepolia-preconf.base.org'],
|
|
2864
|
+
},
|
|
2865
|
+
},
|
|
2866
|
+
});
|
|
2867
|
+
|
|
2868
|
+
const mainnet = /*#__PURE__*/ defineChain({
|
|
2869
|
+
id: 1,
|
|
2870
|
+
name: 'Ethereum',
|
|
2871
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
2872
|
+
blockTime: 12_000,
|
|
2873
|
+
rpcUrls: {
|
|
2874
|
+
default: {
|
|
2875
|
+
http: ['https://eth.merkle.io'],
|
|
2876
|
+
},
|
|
2877
|
+
},
|
|
2878
|
+
blockExplorers: {
|
|
2879
|
+
default: {
|
|
2880
|
+
name: 'Etherscan',
|
|
2881
|
+
url: 'https://etherscan.io',
|
|
2882
|
+
apiUrl: 'https://api.etherscan.io/api',
|
|
2883
|
+
},
|
|
2884
|
+
},
|
|
2885
|
+
contracts: {
|
|
2886
|
+
ensUniversalResolver: {
|
|
2887
|
+
address: '0xeeeeeeee14d718c2b47d9923deab1335e144eeee',
|
|
2888
|
+
blockCreated: 23_085_558,
|
|
2889
|
+
},
|
|
2890
|
+
multicall3: {
|
|
2891
|
+
address: '0xca11bde05977b3631167028862be2a173976ca11',
|
|
2892
|
+
blockCreated: 14_353_601,
|
|
2893
|
+
},
|
|
2894
|
+
},
|
|
2895
|
+
});
|
|
2896
|
+
|
|
2897
|
+
const sepolia = /*#__PURE__*/ defineChain({
|
|
2898
|
+
id: 11_155_111,
|
|
2899
|
+
name: 'Sepolia',
|
|
2900
|
+
nativeCurrency: { name: 'Sepolia Ether', symbol: 'ETH', decimals: 18 },
|
|
2901
|
+
rpcUrls: {
|
|
2902
|
+
default: {
|
|
2903
|
+
http: ['https://11155111.rpc.thirdweb.com'],
|
|
2904
|
+
},
|
|
2905
|
+
},
|
|
2906
|
+
blockExplorers: {
|
|
2907
|
+
default: {
|
|
2908
|
+
name: 'Etherscan',
|
|
2909
|
+
url: 'https://sepolia.etherscan.io',
|
|
2910
|
+
apiUrl: 'https://api-sepolia.etherscan.io/api',
|
|
2911
|
+
},
|
|
2912
|
+
},
|
|
2913
|
+
contracts: {
|
|
2914
|
+
multicall3: {
|
|
2915
|
+
address: '0xca11bde05977b3631167028862be2a173976ca11',
|
|
2916
|
+
blockCreated: 751532,
|
|
2917
|
+
},
|
|
2918
|
+
ensUniversalResolver: {
|
|
2919
|
+
address: '0xeeeeeeee14d718c2b47d9923deab1335e144eeee',
|
|
2920
|
+
blockCreated: 8_928_790,
|
|
2921
|
+
},
|
|
2922
|
+
},
|
|
2923
|
+
testnet: true,
|
|
2924
|
+
});
|
|
2925
|
+
|
|
2926
|
+
/**
|
|
2927
|
+
* Maps AgentKit network IDs to Lombard SDK ChainId values.
|
|
2928
|
+
*
|
|
2929
|
+
* Only includes networks where Lombard contracts are deployed.
|
|
2930
|
+
*/ const NETWORK_ID_TO_LOMBARD_CHAIN = {
|
|
2931
|
+
"ethereum-mainnet": ChainId.ethereum,
|
|
2932
|
+
"ethereum-sepolia": ChainId.sepolia,
|
|
2933
|
+
"base-mainnet": ChainId.base,
|
|
2934
|
+
"base-sepolia": ChainId.baseSepoliaTestnet
|
|
2935
|
+
};
|
|
2936
|
+
/**
|
|
2937
|
+
* Maps AgentKit network IDs to Lombard SDK Env values.
|
|
2938
|
+
*/ const NETWORK_ID_TO_ENV = {
|
|
2939
|
+
"ethereum-mainnet": Env.prod,
|
|
2940
|
+
"ethereum-sepolia": Env.testnet,
|
|
2941
|
+
"base-mainnet": Env.prod,
|
|
2942
|
+
"base-sepolia": Env.testnet
|
|
2943
|
+
};
|
|
2944
|
+
/**
|
|
2945
|
+
* Maps AgentKit network IDs to viem `Chain` objects, for callers that need
|
|
2946
|
+
* to construct a wallet/public client outside of the action provider.
|
|
2947
|
+
*/ const NETWORK_ID_TO_VIEM_CHAIN = {
|
|
2948
|
+
"ethereum-mainnet": mainnet,
|
|
2949
|
+
"ethereum-sepolia": sepolia,
|
|
2950
|
+
"base-mainnet": base,
|
|
2951
|
+
"base-sepolia": baseSepolia
|
|
2952
|
+
};
|
|
2953
|
+
/**
|
|
2954
|
+
* Resolves an AgentKit Network to Lombard SDK chain parameters.
|
|
2955
|
+
*
|
|
2956
|
+
* @returns ResolvedNetwork or null if the network is not supported
|
|
2957
|
+
*/ function resolveNetwork(network) {
|
|
2958
|
+
const networkId = network.networkId;
|
|
2959
|
+
if (!networkId) return null;
|
|
2960
|
+
const chainId = NETWORK_ID_TO_LOMBARD_CHAIN[networkId];
|
|
2961
|
+
const env = NETWORK_ID_TO_ENV[networkId];
|
|
2962
|
+
if (chainId === undefined || !env) return null;
|
|
2963
|
+
return {
|
|
2964
|
+
chainId,
|
|
2965
|
+
env,
|
|
2966
|
+
networkId
|
|
2967
|
+
};
|
|
2968
|
+
}
|
|
2969
|
+
/**
|
|
2970
|
+
* Resolves a user-supplied chain name string to Lombard SDK chain parameters.
|
|
2971
|
+
*
|
|
2972
|
+
* Accepts both AgentKit network IDs (e.g., "ethereum-mainnet") and
|
|
2973
|
+
* friendly names (e.g., "ethereum", "base-sepolia").
|
|
2974
|
+
*/ function resolveChainName(chainName) {
|
|
2975
|
+
const normalized = chainName.toLowerCase().trim();
|
|
2976
|
+
// Try direct match on network ID
|
|
2977
|
+
if (NETWORK_ID_TO_LOMBARD_CHAIN[normalized] !== undefined) {
|
|
2978
|
+
return {
|
|
2979
|
+
chainId: NETWORK_ID_TO_LOMBARD_CHAIN[normalized],
|
|
2980
|
+
env: NETWORK_ID_TO_ENV[normalized],
|
|
2981
|
+
networkId: normalized
|
|
2982
|
+
};
|
|
2983
|
+
}
|
|
2984
|
+
// Friendly name aliases
|
|
2985
|
+
const aliases = {
|
|
2986
|
+
ethereum: "ethereum-mainnet",
|
|
2987
|
+
eth: "ethereum-mainnet",
|
|
2988
|
+
mainnet: "ethereum-mainnet",
|
|
2989
|
+
sepolia: "ethereum-sepolia",
|
|
2990
|
+
base: "base-mainnet",
|
|
2991
|
+
"base-sep": "base-sepolia"
|
|
2992
|
+
};
|
|
2993
|
+
const resolved = aliases[normalized];
|
|
2994
|
+
if (resolved && NETWORK_ID_TO_LOMBARD_CHAIN[resolved] !== undefined) {
|
|
2995
|
+
return {
|
|
2996
|
+
chainId: NETWORK_ID_TO_LOMBARD_CHAIN[resolved],
|
|
2997
|
+
env: NETWORK_ID_TO_ENV[resolved],
|
|
2998
|
+
networkId: resolved
|
|
2999
|
+
};
|
|
3000
|
+
}
|
|
3001
|
+
return null;
|
|
3002
|
+
}
|
|
3003
|
+
/**
|
|
3004
|
+
* Checks if a given AgentKit Network is supported by the Lombard provider.
|
|
3005
|
+
*/ function isLombardSupportedNetwork(network) {
|
|
3006
|
+
return resolveNetwork(network) !== null;
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
const StakeBtcbToLbtcSchema = z.object({
|
|
3010
|
+
amount: z.string().describe("Amount of BTC.b to stake in human-readable format (e.g. '0.1' for 0.1 BTC.b). Will be converted to LBTC.").refine((v)=>/^\d+(\.\d+)?$/.test(v), "Amount must be a numeric string").refine((v)=>parseFloat(v) > 0, "Amount must be positive").refine((v)=>parseFloat(v) < 1000, "Amount must be under 1000 BTC")
|
|
3011
|
+
});
|
|
3012
|
+
const UnstakeLbtcSchema = z.object({
|
|
3013
|
+
amount: z.string().describe("Amount of LBTC to unstake in human-readable format (e.g. '0.1')").refine((v)=>/^\d+(\.\d+)?$/.test(v), "Amount must be a numeric string").refine((v)=>parseFloat(v) > 0, "Amount must be positive").refine((v)=>parseFloat(v) < 1000, "Amount must be under 1000 BTC"),
|
|
3014
|
+
recipient: z.string().describe("Destination address. For BTC output, use a Bitcoin address (e.g. bc1q...). For BTCb output, use an EVM address (e.g. 0x...).").refine((v)=>/^0x[a-fA-F0-9]{40}$/.test(v) || /^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,62}$/.test(v), "Invalid address: must be an EVM address (0x...) or a Bitcoin address (bc1.../1.../3...)"),
|
|
3015
|
+
outputAsset: z.enum([
|
|
3016
|
+
"BTC",
|
|
3017
|
+
"BTCb"
|
|
3018
|
+
]).describe("Output asset: 'BTC' for native Bitcoin (cross-chain, takes longer), 'BTCb' for wrapped BTC on the same EVM chain (faster)")
|
|
3019
|
+
});
|
|
3020
|
+
const RedeemLbtcToBtcbSchema = z.object({
|
|
3021
|
+
amount: z.string().describe("Amount of LBTC to redeem in human-readable format (e.g. '0.1'). Will be converted to BTC.b on the same chain.").refine((v)=>/^\d+(\.\d+)?$/.test(v), "Amount must be a numeric string").refine((v)=>parseFloat(v) > 0, "Amount must be positive").refine((v)=>parseFloat(v) < 1000, "Amount must be under 1000 BTC")
|
|
3022
|
+
});
|
|
3023
|
+
const DeployToDefiSchema = z.object({
|
|
3024
|
+
amount: z.string().describe("Amount to deploy in human-readable format (e.g. '0.1')").refine((v)=>/^\d+(\.\d+)?$/.test(v), "Amount must be a numeric string").refine((v)=>parseFloat(v) > 0, "Amount must be positive").refine((v)=>parseFloat(v) < 1000, "Amount must be under 1000 BTC")
|
|
3025
|
+
});
|
|
3026
|
+
const ClaimDepositSchema = z.object({
|
|
3027
|
+
depositTxHash: z.string().describe("Transaction hash of the original deposit to claim. Use get_deposit_status first to check if the deposit is claimable.")
|
|
3028
|
+
});
|
|
3029
|
+
const GetLbtcBalanceSchema = z.object({
|
|
3030
|
+
address: z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid EVM address").optional().describe("EVM address to check balance for. If omitted, uses the connected wallet address.")
|
|
3031
|
+
});
|
|
3032
|
+
const GetBtcbBalanceSchema = z.object({
|
|
3033
|
+
address: z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid EVM address").optional().describe("EVM address to check balance for. If omitted, uses the connected wallet address.")
|
|
3034
|
+
});
|
|
3035
|
+
const GetLbtcExchangeRateSchema = z.object({});
|
|
3036
|
+
const GetDepositStatusSchema = z.object({
|
|
3037
|
+
address: z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid EVM address").optional().describe("EVM address to check deposits for. If omitted, uses the connected wallet address.")
|
|
3038
|
+
});
|
|
3039
|
+
const GetUnstakeStatusSchema = z.object({
|
|
3040
|
+
address: z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid EVM address").optional().describe("EVM address to check unstakes for. If omitted, uses the connected wallet address.")
|
|
3041
|
+
});
|
|
3042
|
+
|
|
3043
|
+
/**
|
|
3044
|
+
* Adapts a Coinbase AgentKit EvmWalletProvider into a viem-compatible
|
|
3045
|
+
* EIP1193Provider that the Lombard SDK contract functions expect.
|
|
3046
|
+
*
|
|
3047
|
+
* The adapter implements the `request` method by delegating to the
|
|
3048
|
+
* appropriate EvmWalletProvider method for each JSON-RPC call.
|
|
3049
|
+
*/ function toEIP1193Provider(walletProvider, chainId) {
|
|
3050
|
+
const chain = CHAIN_ID_TO_VIEM_CHAIN_MAP[chainId];
|
|
3051
|
+
// Use the SDK's makePublicClient for reliable RPC calls
|
|
3052
|
+
const publicClient = chain ? makePublicClient({
|
|
3053
|
+
chainId
|
|
3054
|
+
}) : null;
|
|
3055
|
+
// Use a Proxy to implement the EIP1193 request interface.
|
|
3056
|
+
// This avoids complex viem type gymnastics while correctly routing
|
|
3057
|
+
// wallet operations to the AgentKit provider and reads to a public client.
|
|
3058
|
+
const handler = async ({ method, params })=>{
|
|
3059
|
+
switch(method){
|
|
3060
|
+
case "eth_accounts":
|
|
3061
|
+
return [
|
|
3062
|
+
walletProvider.getAddress()
|
|
3063
|
+
];
|
|
3064
|
+
case "eth_chainId":
|
|
3065
|
+
return `0x${chainId.toString(16)}`;
|
|
3066
|
+
case "eth_sendTransaction":
|
|
3067
|
+
{
|
|
3068
|
+
if (!params || params.length === 0) {
|
|
3069
|
+
throw new Error("eth_sendTransaction requires transaction parameter");
|
|
3070
|
+
}
|
|
3071
|
+
const tx = params[0];
|
|
3072
|
+
return walletProvider.sendTransaction({
|
|
3073
|
+
to: tx.to,
|
|
3074
|
+
value: tx.value ? BigInt(tx.value) : undefined,
|
|
3075
|
+
data: tx.data,
|
|
3076
|
+
gas: tx.gas ? BigInt(tx.gas) : undefined
|
|
3077
|
+
});
|
|
3078
|
+
}
|
|
3079
|
+
case "personal_sign":
|
|
3080
|
+
{
|
|
3081
|
+
const [message] = params;
|
|
3082
|
+
return walletProvider.signMessage(message);
|
|
3083
|
+
}
|
|
3084
|
+
case "eth_signTypedData_v4":
|
|
3085
|
+
{
|
|
3086
|
+
const [, typedDataJson] = params;
|
|
3087
|
+
const typedData = typeof typedDataJson === "string" ? JSON.parse(typedDataJson) : typedDataJson;
|
|
3088
|
+
return walletProvider.signTypedData(typedData);
|
|
3089
|
+
}
|
|
3090
|
+
default:
|
|
3091
|
+
{
|
|
3092
|
+
if (!publicClient) {
|
|
3093
|
+
throw new Error(`Unsupported RPC method: ${method}`);
|
|
3094
|
+
}
|
|
3095
|
+
return publicClient.request({
|
|
3096
|
+
method,
|
|
3097
|
+
params
|
|
3098
|
+
});
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
};
|
|
3102
|
+
return {
|
|
3103
|
+
request: handler
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
/**
|
|
3107
|
+
* Reads an ERC20 token balance using the AgentKit wallet provider's
|
|
3108
|
+
* readContract method directly (avoids needing EIP1193 adapter for reads).
|
|
3109
|
+
*/ async function getTokenBalance(walletProvider, token, chainId, address, env) {
|
|
3110
|
+
const tokenInfo = await getTokenContractInfo(token, chainId, env);
|
|
3111
|
+
const tokenAddress = tokenInfo.address;
|
|
3112
|
+
const balance = await walletProvider.readContract({
|
|
3113
|
+
address: tokenAddress,
|
|
3114
|
+
abi: [
|
|
3115
|
+
{
|
|
3116
|
+
name: "balanceOf",
|
|
3117
|
+
type: "function",
|
|
3118
|
+
stateMutability: "view",
|
|
3119
|
+
inputs: [
|
|
3120
|
+
{
|
|
3121
|
+
name: "account",
|
|
3122
|
+
type: "address"
|
|
3123
|
+
}
|
|
3124
|
+
],
|
|
3125
|
+
outputs: [
|
|
3126
|
+
{
|
|
3127
|
+
name: "",
|
|
3128
|
+
type: "uint256"
|
|
3129
|
+
}
|
|
3130
|
+
]
|
|
3131
|
+
}
|
|
3132
|
+
],
|
|
3133
|
+
functionName: "balanceOf",
|
|
3134
|
+
args: [
|
|
3135
|
+
address
|
|
3136
|
+
]
|
|
3137
|
+
});
|
|
3138
|
+
// Use decimals from token info when available, default to 8 for BTC-like tokens
|
|
3139
|
+
const decimals = "decimals" in tokenInfo && typeof tokenInfo.decimals === "number" ? tokenInfo.decimals : 8;
|
|
3140
|
+
const formatted = formatUnits$1(balance, decimals);
|
|
3141
|
+
return {
|
|
3142
|
+
balance: balance,
|
|
3143
|
+
formatted,
|
|
3144
|
+
decimals
|
|
3145
|
+
};
|
|
3146
|
+
}
|
|
3147
|
+
/**
|
|
3148
|
+
* Format a result string for AgentKit action responses.
|
|
3149
|
+
*/ function formatSuccess(action, details) {
|
|
3150
|
+
return JSON.stringify({
|
|
3151
|
+
success: true,
|
|
3152
|
+
action,
|
|
3153
|
+
...details
|
|
3154
|
+
});
|
|
3155
|
+
}
|
|
3156
|
+
function formatError(action, error) {
|
|
3157
|
+
let message;
|
|
3158
|
+
if (error instanceof Error) {
|
|
3159
|
+
// Strip RPC URLs and internal details
|
|
3160
|
+
message = error.message.replace(/https?:\/\/[^\s]+/g, "[redacted-url]").replace(/0x[a-fA-F0-9]{64,}/g, "[redacted-data]");
|
|
3161
|
+
} else {
|
|
3162
|
+
message = String(error);
|
|
3163
|
+
}
|
|
3164
|
+
return JSON.stringify({
|
|
3165
|
+
success: false,
|
|
3166
|
+
action,
|
|
3167
|
+
error: message
|
|
3168
|
+
});
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
3172
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3173
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
3174
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
3175
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
3176
|
+
}
|
|
3177
|
+
function _ts_metadata(k, v) {
|
|
3178
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
3179
|
+
}
|
|
3180
|
+
/**
|
|
3181
|
+
* LombardActionProvider exposes Lombard protocol operations
|
|
3182
|
+
* (stake, unstake, redeem, deploy, claim) as Coinbase AgentKit actions.
|
|
3183
|
+
*
|
|
3184
|
+
* SECURITY: This provider executes real transactions. Configure your wallet
|
|
3185
|
+
* provider with appropriate spending limits. All amounts are validated to be
|
|
3186
|
+
* positive numeric strings under 1000 BTC equivalent.
|
|
3187
|
+
*
|
|
3188
|
+
* Usage:
|
|
3189
|
+
* ```ts
|
|
3190
|
+
* import { lombardActionProvider } from '@lombard.finance/sdk-agentkit';
|
|
3191
|
+
*
|
|
3192
|
+
* const agentkit = await AgentKit.from({
|
|
3193
|
+
* walletProvider,
|
|
3194
|
+
* actionProviders: [lombardActionProvider()],
|
|
3195
|
+
* });
|
|
3196
|
+
* ```
|
|
3197
|
+
*/ class LombardActionProvider extends ActionProvider {
|
|
3198
|
+
constructor(){
|
|
3199
|
+
super("lombard", []);
|
|
3200
|
+
}
|
|
3201
|
+
supportsNetwork = (network)=>{
|
|
3202
|
+
return isLombardSupportedNetwork(network);
|
|
3203
|
+
};
|
|
3204
|
+
// ─── Write Actions ──────────────────────────────────────────────────
|
|
3205
|
+
async stakeBtcbToLbtc(walletProvider, args) {
|
|
3206
|
+
try {
|
|
3207
|
+
const resolved = resolveNetwork(walletProvider.getNetwork());
|
|
3208
|
+
if (!resolved) {
|
|
3209
|
+
return formatError("stake_btcb_to_lbtc", "Current network is not supported by Lombard");
|
|
3210
|
+
}
|
|
3211
|
+
const { chainId, env } = resolved;
|
|
3212
|
+
const account = walletProvider.getAddress();
|
|
3213
|
+
const provider = toEIP1193Provider(walletProvider, chainId);
|
|
3214
|
+
// Handle fee authorization for chains that require it
|
|
3215
|
+
await this.ensureFeeAuthorization(walletProvider, chainId, env, account, provider);
|
|
3216
|
+
// Check allowance and approve if needed
|
|
3217
|
+
const routerAddress = await getAssetRouterAddress({
|
|
3218
|
+
tokenIn: Token.BTCb,
|
|
3219
|
+
chainId,
|
|
3220
|
+
env
|
|
3221
|
+
});
|
|
3222
|
+
const allowance = await getTokenAllowance({
|
|
3223
|
+
token: Token.BTCb,
|
|
3224
|
+
owner: account,
|
|
3225
|
+
spender: routerAddress,
|
|
3226
|
+
chainId,
|
|
3227
|
+
env
|
|
3228
|
+
});
|
|
3229
|
+
if (allowance.isLessThan(args.amount)) {
|
|
3230
|
+
await approveToken({
|
|
3231
|
+
token: Token.BTCb,
|
|
3232
|
+
spender: routerAddress,
|
|
3233
|
+
amount: args.amount,
|
|
3234
|
+
account,
|
|
3235
|
+
chainId,
|
|
3236
|
+
provider,
|
|
3237
|
+
env
|
|
3238
|
+
});
|
|
3239
|
+
}
|
|
3240
|
+
const txHash = await depositToken({
|
|
3241
|
+
amount: args.amount,
|
|
3242
|
+
tokenIn: Token.BTCb,
|
|
3243
|
+
tokenOut: Token.LBTC,
|
|
3244
|
+
account,
|
|
3245
|
+
chainId,
|
|
3246
|
+
provider,
|
|
3247
|
+
env
|
|
3248
|
+
});
|
|
3249
|
+
return formatSuccess("stake_btcb_to_lbtc", {
|
|
3250
|
+
txHash,
|
|
3251
|
+
amount: args.amount,
|
|
3252
|
+
from: "BTC.b",
|
|
3253
|
+
to: "LBTC"
|
|
3254
|
+
});
|
|
3255
|
+
} catch (error) {
|
|
3256
|
+
return formatError("stake_btcb_to_lbtc", error);
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
async unstakeLbtc(walletProvider, args) {
|
|
3260
|
+
try {
|
|
3261
|
+
const resolved = resolveNetwork(walletProvider.getNetwork());
|
|
3262
|
+
if (!resolved) {
|
|
3263
|
+
return formatError("unstake_lbtc", "Current network is not supported by Lombard");
|
|
3264
|
+
}
|
|
3265
|
+
const { chainId, env } = resolved;
|
|
3266
|
+
const account = walletProvider.getAddress();
|
|
3267
|
+
const provider = toEIP1193Provider(walletProvider, chainId);
|
|
3268
|
+
// Handle fee authorization for chains that require it
|
|
3269
|
+
await this.ensureFeeAuthorization(walletProvider, chainId, env, account, provider);
|
|
3270
|
+
if (args.outputAsset === "BTC") {
|
|
3271
|
+
const txHash = await unstakeLBTC({
|
|
3272
|
+
amount: args.amount,
|
|
3273
|
+
btcAddress: args.recipient,
|
|
3274
|
+
tokenIn: Token.LBTC,
|
|
3275
|
+
account,
|
|
3276
|
+
chainId,
|
|
3277
|
+
provider,
|
|
3278
|
+
env
|
|
3279
|
+
});
|
|
3280
|
+
return formatSuccess("unstake_lbtc", {
|
|
3281
|
+
txHash,
|
|
3282
|
+
amount: args.amount,
|
|
3283
|
+
to: "BTC",
|
|
3284
|
+
recipient: args.recipient,
|
|
3285
|
+
note: "Cross-chain unstake initiated. BTC will arrive after processing (may take hours)."
|
|
3286
|
+
});
|
|
3287
|
+
}
|
|
3288
|
+
// BTCb output - same chain redeem (always sends to caller's address)
|
|
3289
|
+
const txHash = await redeemToken({
|
|
3290
|
+
amount: args.amount,
|
|
3291
|
+
tokenIn: Token.LBTC,
|
|
3292
|
+
tokenOut: Token.BTCb,
|
|
3293
|
+
account,
|
|
3294
|
+
chainId,
|
|
3295
|
+
provider,
|
|
3296
|
+
env
|
|
3297
|
+
});
|
|
3298
|
+
const result = {
|
|
3299
|
+
txHash,
|
|
3300
|
+
amount: args.amount,
|
|
3301
|
+
to: "BTC.b"
|
|
3302
|
+
};
|
|
3303
|
+
if (args.recipient && args.recipient.toLowerCase() !== account.toLowerCase()) {
|
|
3304
|
+
result.warning = "BTC.b redemption always sends to the connected wallet. The recipient address was ignored.";
|
|
3305
|
+
}
|
|
3306
|
+
return formatSuccess("unstake_lbtc", result);
|
|
3307
|
+
} catch (error) {
|
|
3308
|
+
return formatError("unstake_lbtc", error);
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
async redeemLbtcToBtcb(walletProvider, args) {
|
|
3312
|
+
try {
|
|
3313
|
+
const resolved = resolveNetwork(walletProvider.getNetwork());
|
|
3314
|
+
if (!resolved) {
|
|
3315
|
+
return formatError("redeem_lbtc_to_btcb", "Current network is not supported by Lombard");
|
|
3316
|
+
}
|
|
3317
|
+
const { chainId, env } = resolved;
|
|
3318
|
+
const account = walletProvider.getAddress();
|
|
3319
|
+
const provider = toEIP1193Provider(walletProvider, chainId);
|
|
3320
|
+
await this.ensureFeeAuthorization(walletProvider, chainId, env, account, provider);
|
|
3321
|
+
const txHash = await redeemToken({
|
|
3322
|
+
amount: args.amount,
|
|
3323
|
+
tokenIn: Token.LBTC,
|
|
3324
|
+
tokenOut: Token.BTCb,
|
|
3325
|
+
account,
|
|
3326
|
+
chainId,
|
|
3327
|
+
provider,
|
|
3328
|
+
env
|
|
3329
|
+
});
|
|
3330
|
+
return formatSuccess("redeem_lbtc_to_btcb", {
|
|
3331
|
+
txHash,
|
|
3332
|
+
amount: args.amount,
|
|
3333
|
+
from: "LBTC",
|
|
3334
|
+
to: "BTC.b"
|
|
3335
|
+
});
|
|
3336
|
+
} catch (error) {
|
|
3337
|
+
return formatError("redeem_lbtc_to_btcb", error);
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
async deployToDefi(walletProvider, args) {
|
|
3341
|
+
try {
|
|
3342
|
+
const resolved = resolveNetwork(walletProvider.getNetwork());
|
|
3343
|
+
if (!resolved) {
|
|
3344
|
+
return formatError("deploy_to_defi", "Current network is not supported by Lombard");
|
|
3345
|
+
}
|
|
3346
|
+
const { chainId } = resolved;
|
|
3347
|
+
const account = walletProvider.getAddress();
|
|
3348
|
+
const provider = toEIP1193Provider(walletProvider, chainId);
|
|
3349
|
+
const txHash = await depositEarn({
|
|
3350
|
+
amount: args.amount,
|
|
3351
|
+
approve: true,
|
|
3352
|
+
token: Token.LBTC,
|
|
3353
|
+
account,
|
|
3354
|
+
chainId,
|
|
3355
|
+
provider,
|
|
3356
|
+
rpcUrl: undefined,
|
|
3357
|
+
env: resolved.env
|
|
3358
|
+
});
|
|
3359
|
+
return formatSuccess("deploy_to_defi", {
|
|
3360
|
+
txHash,
|
|
3361
|
+
amount: args.amount,
|
|
3362
|
+
asset: "LBTC"
|
|
3363
|
+
});
|
|
3364
|
+
} catch (error) {
|
|
3365
|
+
return formatError("deploy_to_defi", error);
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
async claimDeposit(walletProvider, args) {
|
|
3369
|
+
try {
|
|
3370
|
+
const resolved = resolveNetwork(walletProvider.getNetwork());
|
|
3371
|
+
if (!resolved) {
|
|
3372
|
+
return formatError("claim_deposit", "Current network is not supported by Lombard");
|
|
3373
|
+
}
|
|
3374
|
+
const { chainId, env } = resolved;
|
|
3375
|
+
const account = walletProvider.getAddress();
|
|
3376
|
+
const provider = toEIP1193Provider(walletProvider, chainId);
|
|
3377
|
+
// Find the claimable deposit by tx hash
|
|
3378
|
+
const deposits = await getDepositsByAddress({
|
|
3379
|
+
address: account,
|
|
3380
|
+
env
|
|
3381
|
+
});
|
|
3382
|
+
const deposit = deposits.find((d)=>d.txHash === args.depositTxHash);
|
|
3383
|
+
if (!deposit) {
|
|
3384
|
+
return formatError("claim_deposit", `No deposit found with txHash: ${args.depositTxHash}`);
|
|
3385
|
+
}
|
|
3386
|
+
const status = getDepositStatus(deposit);
|
|
3387
|
+
if (status !== "claimable") {
|
|
3388
|
+
const display = getDepositStatusDisplay(status);
|
|
3389
|
+
return formatError("claim_deposit", `Deposit is not claimable. Current status: ${display.label} - ${display.description}`);
|
|
3390
|
+
}
|
|
3391
|
+
if (!deposit.rawPayload || !deposit.proof) {
|
|
3392
|
+
return formatError("claim_deposit", "Deposit proof data is not yet available");
|
|
3393
|
+
}
|
|
3394
|
+
const txHash = await claimLBTC({
|
|
3395
|
+
data: deposit.rawPayload,
|
|
3396
|
+
proofSignature: deposit.proof,
|
|
3397
|
+
account,
|
|
3398
|
+
chainId,
|
|
3399
|
+
provider,
|
|
3400
|
+
env
|
|
3401
|
+
});
|
|
3402
|
+
return formatSuccess("claim_deposit", {
|
|
3403
|
+
txHash,
|
|
3404
|
+
depositTxHash: args.depositTxHash
|
|
3405
|
+
});
|
|
3406
|
+
} catch (error) {
|
|
3407
|
+
return formatError("claim_deposit", error);
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
3410
|
+
// ─── Read Actions ───────────────────────────────────────────────────
|
|
3411
|
+
async getLbtcBalance(walletProvider, args) {
|
|
3412
|
+
try {
|
|
3413
|
+
const resolved = resolveNetwork(walletProvider.getNetwork());
|
|
3414
|
+
if (!resolved) {
|
|
3415
|
+
return formatError("get_lbtc_balance", "Current network is not supported by Lombard");
|
|
3416
|
+
}
|
|
3417
|
+
const { chainId, env } = resolved;
|
|
3418
|
+
const address = args.address || walletProvider.getAddress();
|
|
3419
|
+
const { formatted } = await getTokenBalance(walletProvider, Token.LBTC, chainId, address, env);
|
|
3420
|
+
return formatSuccess("get_lbtc_balance", {
|
|
3421
|
+
address,
|
|
3422
|
+
balance: formatted,
|
|
3423
|
+
token: "LBTC",
|
|
3424
|
+
chain: resolved.networkId
|
|
3425
|
+
});
|
|
3426
|
+
} catch (error) {
|
|
3427
|
+
return formatError("get_lbtc_balance", error);
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
async getBtcbBalance(walletProvider, args) {
|
|
3431
|
+
try {
|
|
3432
|
+
const resolved = resolveNetwork(walletProvider.getNetwork());
|
|
3433
|
+
if (!resolved) {
|
|
3434
|
+
return formatError("get_btcb_balance", "Current network is not supported by Lombard");
|
|
3435
|
+
}
|
|
3436
|
+
const { chainId, env } = resolved;
|
|
3437
|
+
const address = args.address || walletProvider.getAddress();
|
|
3438
|
+
const { formatted } = await getTokenBalance(walletProvider, Token.BTCb, chainId, address, env);
|
|
3439
|
+
return formatSuccess("get_btcb_balance", {
|
|
3440
|
+
address,
|
|
3441
|
+
balance: formatted,
|
|
3442
|
+
token: "BTC.b",
|
|
3443
|
+
chain: resolved.networkId
|
|
3444
|
+
});
|
|
3445
|
+
} catch (error) {
|
|
3446
|
+
return formatError("get_btcb_balance", error);
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
async getLbtcExchangeRate(_walletProvider, _args) {
|
|
3450
|
+
try {
|
|
3451
|
+
const resolved = resolveNetwork(_walletProvider.getNetwork());
|
|
3452
|
+
if (!resolved) {
|
|
3453
|
+
return formatError("get_lbtc_exchange_rate", "Current network is not supported by Lombard");
|
|
3454
|
+
}
|
|
3455
|
+
const env = resolved.env;
|
|
3456
|
+
const rate = await getLBTCExchangeRate({
|
|
3457
|
+
env
|
|
3458
|
+
});
|
|
3459
|
+
return formatSuccess("get_lbtc_exchange_rate", {
|
|
3460
|
+
exchangeRate: rate.exchangeRate,
|
|
3461
|
+
minStakeAmountBtc: fromSatoshi(rate.minAmount).toString()
|
|
3462
|
+
});
|
|
3463
|
+
} catch (error) {
|
|
3464
|
+
return formatError("get_lbtc_exchange_rate", error);
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
async getDepositStatusAction(walletProvider, args) {
|
|
3468
|
+
try {
|
|
3469
|
+
const resolved = resolveNetwork(walletProvider.getNetwork());
|
|
3470
|
+
if (!resolved) {
|
|
3471
|
+
return formatError("get_deposit_status", "Current network is not supported by Lombard");
|
|
3472
|
+
}
|
|
3473
|
+
const { env } = resolved;
|
|
3474
|
+
const address = args.address || walletProvider.getAddress();
|
|
3475
|
+
const deposits = await getDepositsByAddress({
|
|
3476
|
+
address,
|
|
3477
|
+
env
|
|
3478
|
+
});
|
|
3479
|
+
if (deposits.length === 0) {
|
|
3480
|
+
return formatSuccess("get_deposit_status", {
|
|
3481
|
+
address,
|
|
3482
|
+
deposits: [],
|
|
3483
|
+
message: "No deposits found for this address"
|
|
3484
|
+
});
|
|
3485
|
+
}
|
|
3486
|
+
const summaries = deposits.map((d)=>{
|
|
3487
|
+
const status = getDepositStatus(d);
|
|
3488
|
+
const display = getDepositStatusDisplay(status);
|
|
3489
|
+
return {
|
|
3490
|
+
txHash: d.txHash,
|
|
3491
|
+
amount: d.amount?.toString(),
|
|
3492
|
+
status,
|
|
3493
|
+
statusLabel: display.label,
|
|
3494
|
+
description: display.description,
|
|
3495
|
+
requiresAction: display.requiresAction,
|
|
3496
|
+
isTerminal: display.isTerminal,
|
|
3497
|
+
claimTxHash: d.claimTxHash || null
|
|
3498
|
+
};
|
|
3499
|
+
});
|
|
3500
|
+
return formatSuccess("get_deposit_status", {
|
|
3501
|
+
address,
|
|
3502
|
+
totalDeposits: deposits.length,
|
|
3503
|
+
deposits: summaries
|
|
3504
|
+
});
|
|
3505
|
+
} catch (error) {
|
|
3506
|
+
return formatError("get_deposit_status", error);
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
async getUnstakeStatus(walletProvider, args) {
|
|
3510
|
+
try {
|
|
3511
|
+
const resolved = resolveNetwork(walletProvider.getNetwork());
|
|
3512
|
+
if (!resolved) {
|
|
3513
|
+
return formatError("get_unstake_status", "Current network is not supported by Lombard");
|
|
3514
|
+
}
|
|
3515
|
+
const { env } = resolved;
|
|
3516
|
+
const address = args.address || walletProvider.getAddress();
|
|
3517
|
+
const unstakes = await getUnstakesByAddress({
|
|
3518
|
+
address,
|
|
3519
|
+
env
|
|
3520
|
+
});
|
|
3521
|
+
if (unstakes.length === 0) {
|
|
3522
|
+
return formatSuccess("get_unstake_status", {
|
|
3523
|
+
address,
|
|
3524
|
+
unstakes: [],
|
|
3525
|
+
message: "No unstakes found for this address"
|
|
3526
|
+
});
|
|
3527
|
+
}
|
|
3528
|
+
const summaries = unstakes.map((u)=>({
|
|
3529
|
+
txHash: u.txHash,
|
|
3530
|
+
amount: u.amount?.toString(),
|
|
3531
|
+
payoutStatus: u.payoutTxStatus,
|
|
3532
|
+
payoutTxHash: u.payoutTxHash || null,
|
|
3533
|
+
toAddress: u.toAddress || null
|
|
3534
|
+
}));
|
|
3535
|
+
return formatSuccess("get_unstake_status", {
|
|
3536
|
+
address,
|
|
3537
|
+
totalUnstakes: unstakes.length,
|
|
3538
|
+
unstakes: summaries
|
|
3539
|
+
});
|
|
3540
|
+
} catch (error) {
|
|
3541
|
+
return formatError("get_unstake_status", error);
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
// ─── Private Helpers ────────────────────────────────────────────────
|
|
3545
|
+
/**
|
|
3546
|
+
* Ensures fee authorization is in place for chains that require it.
|
|
3547
|
+
* On Ethereum and Sepolia, staking/unstaking requires an EIP-712 fee
|
|
3548
|
+
* signature stored with the backend before the transaction can proceed.
|
|
3549
|
+
*/ async ensureFeeAuthorization(_walletProvider, chainId, env, account, provider) {
|
|
3550
|
+
if (!requiresAutoMintFee(chainId)) {
|
|
3551
|
+
return;
|
|
3552
|
+
}
|
|
3553
|
+
// Check if a valid fee signature already exists
|
|
3554
|
+
const existing = await getNetworkFeeSignature({
|
|
3555
|
+
address: account,
|
|
3556
|
+
chainId,
|
|
3557
|
+
env
|
|
3558
|
+
});
|
|
3559
|
+
if (existing.hasSignature) {
|
|
3560
|
+
// Check expiration
|
|
3561
|
+
const expirationMs = new Date(existing.expirationDate).getTime();
|
|
3562
|
+
const bufferMs = 60 * 60 * 1000; // 1 hour buffer
|
|
3563
|
+
if (expirationMs > Date.now() + bufferMs) {
|
|
3564
|
+
return; // Existing signature is still valid
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
// Need to sign a new fee authorization
|
|
3568
|
+
const mintingFee = await getLBTCMintingFee({
|
|
3569
|
+
chainId,
|
|
3570
|
+
env
|
|
3571
|
+
});
|
|
3572
|
+
const { signature, typedData } = await signNetworkFee({
|
|
3573
|
+
fee: mintingFee,
|
|
3574
|
+
account,
|
|
3575
|
+
chainId,
|
|
3576
|
+
provider,
|
|
3577
|
+
env
|
|
3578
|
+
});
|
|
3579
|
+
await storeNetworkFeeSignature({
|
|
3580
|
+
signature,
|
|
3581
|
+
typedData,
|
|
3582
|
+
address: account,
|
|
3583
|
+
env
|
|
3584
|
+
});
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
_ts_decorate([
|
|
3588
|
+
CreateAction({
|
|
3589
|
+
name: "stake_btcb_to_lbtc",
|
|
3590
|
+
description: "Stake BTC.b (cross-chain Bitcoin) to receive LBTC (Lombard Staked Bitcoin). " + "This converts BTC.b into LBTC which earns staking yield. " + "Handles token approval and fee authorization automatically.",
|
|
3591
|
+
schema: StakeBtcbToLbtcSchema
|
|
3592
|
+
}),
|
|
3593
|
+
_ts_metadata("design:type", Function),
|
|
3594
|
+
_ts_metadata("design:paramtypes", [
|
|
3595
|
+
typeof EvmWalletProvider === "undefined" ? Object : EvmWalletProvider,
|
|
3596
|
+
typeof z === "undefined" || typeof z.infer === "undefined" ? Object : z.infer
|
|
3597
|
+
]),
|
|
3598
|
+
_ts_metadata("design:returntype", Promise)
|
|
3599
|
+
], LombardActionProvider.prototype, "stakeBtcbToLbtc", null);
|
|
3600
|
+
_ts_decorate([
|
|
3601
|
+
CreateAction({
|
|
3602
|
+
name: "unstake_lbtc",
|
|
3603
|
+
description: "Unstake LBTC (Lombard Staked Bitcoin). " + "Output can be native BTC (cross-chain, requires a Bitcoin address) or BTC.b (same EVM chain). " + "BTC unstaking takes longer as it crosses chains. BTC.b is faster, same-chain.",
|
|
3604
|
+
schema: UnstakeLbtcSchema
|
|
3605
|
+
}),
|
|
3606
|
+
_ts_metadata("design:type", Function),
|
|
3607
|
+
_ts_metadata("design:paramtypes", [
|
|
3608
|
+
typeof EvmWalletProvider === "undefined" ? Object : EvmWalletProvider,
|
|
3609
|
+
typeof z === "undefined" || typeof z.infer === "undefined" ? Object : z.infer
|
|
3610
|
+
]),
|
|
3611
|
+
_ts_metadata("design:returntype", Promise)
|
|
3612
|
+
], LombardActionProvider.prototype, "unstakeLbtc", null);
|
|
3613
|
+
_ts_decorate([
|
|
3614
|
+
CreateAction({
|
|
3615
|
+
name: "redeem_lbtc_to_btcb",
|
|
3616
|
+
description: "Use this for simple same-chain LBTC to BTC.b conversion. " + "For cross-chain unstaking to native BTC, use unstake_lbtc instead.",
|
|
3617
|
+
schema: RedeemLbtcToBtcbSchema
|
|
3618
|
+
}),
|
|
3619
|
+
_ts_metadata("design:type", Function),
|
|
3620
|
+
_ts_metadata("design:paramtypes", [
|
|
3621
|
+
typeof EvmWalletProvider === "undefined" ? Object : EvmWalletProvider,
|
|
3622
|
+
typeof z === "undefined" || typeof z.infer === "undefined" ? Object : z.infer
|
|
3623
|
+
]),
|
|
3624
|
+
_ts_metadata("design:returntype", Promise)
|
|
3625
|
+
], LombardActionProvider.prototype, "redeemLbtcToBtcb", null);
|
|
3626
|
+
_ts_decorate([
|
|
3627
|
+
CreateAction({
|
|
3628
|
+
name: "deploy_to_defi",
|
|
3629
|
+
description: "Deploy LBTC into a DeFi vault to earn additional yield. " + "Currently supports the Veda vault. " + "Handles approval automatically.",
|
|
3630
|
+
schema: DeployToDefiSchema
|
|
3631
|
+
}),
|
|
3632
|
+
_ts_metadata("design:type", Function),
|
|
3633
|
+
_ts_metadata("design:paramtypes", [
|
|
3634
|
+
typeof EvmWalletProvider === "undefined" ? Object : EvmWalletProvider,
|
|
3635
|
+
typeof z === "undefined" || typeof z.infer === "undefined" ? Object : z.infer
|
|
3636
|
+
]),
|
|
3637
|
+
_ts_metadata("design:returntype", Promise)
|
|
3638
|
+
], LombardActionProvider.prototype, "deployToDefi", null);
|
|
3639
|
+
_ts_decorate([
|
|
3640
|
+
CreateAction({
|
|
3641
|
+
name: "claim_deposit",
|
|
3642
|
+
description: "Claim a notarized deposit to mint LBTC. " + "Use get_deposit_status first to check if a deposit is claimable. " + 'Only deposits with status "claimable" can be claimed.',
|
|
3643
|
+
schema: ClaimDepositSchema
|
|
3644
|
+
}),
|
|
3645
|
+
_ts_metadata("design:type", Function),
|
|
3646
|
+
_ts_metadata("design:paramtypes", [
|
|
3647
|
+
typeof EvmWalletProvider === "undefined" ? Object : EvmWalletProvider,
|
|
3648
|
+
typeof z === "undefined" || typeof z.infer === "undefined" ? Object : z.infer
|
|
3649
|
+
]),
|
|
3650
|
+
_ts_metadata("design:returntype", Promise)
|
|
3651
|
+
], LombardActionProvider.prototype, "claimDeposit", null);
|
|
3652
|
+
_ts_decorate([
|
|
3653
|
+
CreateAction({
|
|
3654
|
+
name: "get_lbtc_balance",
|
|
3655
|
+
description: "Check the LBTC (Lombard Staked Bitcoin) balance for an address on the current chain.",
|
|
3656
|
+
schema: GetLbtcBalanceSchema
|
|
3657
|
+
}),
|
|
3658
|
+
_ts_metadata("design:type", Function),
|
|
3659
|
+
_ts_metadata("design:paramtypes", [
|
|
3660
|
+
typeof EvmWalletProvider === "undefined" ? Object : EvmWalletProvider,
|
|
3661
|
+
typeof z === "undefined" || typeof z.infer === "undefined" ? Object : z.infer
|
|
3662
|
+
]),
|
|
3663
|
+
_ts_metadata("design:returntype", Promise)
|
|
3664
|
+
], LombardActionProvider.prototype, "getLbtcBalance", null);
|
|
3665
|
+
_ts_decorate([
|
|
3666
|
+
CreateAction({
|
|
3667
|
+
name: "get_btcb_balance",
|
|
3668
|
+
description: "Check the BTC.b (cross-chain Bitcoin) balance for an address on the current chain.",
|
|
3669
|
+
schema: GetBtcbBalanceSchema
|
|
3670
|
+
}),
|
|
3671
|
+
_ts_metadata("design:type", Function),
|
|
3672
|
+
_ts_metadata("design:paramtypes", [
|
|
3673
|
+
typeof EvmWalletProvider === "undefined" ? Object : EvmWalletProvider,
|
|
3674
|
+
typeof z === "undefined" || typeof z.infer === "undefined" ? Object : z.infer
|
|
3675
|
+
]),
|
|
3676
|
+
_ts_metadata("design:returntype", Promise)
|
|
3677
|
+
], LombardActionProvider.prototype, "getBtcbBalance", null);
|
|
3678
|
+
_ts_decorate([
|
|
3679
|
+
CreateAction({
|
|
3680
|
+
name: "get_lbtc_exchange_rate",
|
|
3681
|
+
description: "Get the current LBTC/BTC exchange rate and minimum stake amount. " + "LBTC is a rebasing token, so the rate is typically close to 1:1.",
|
|
3682
|
+
schema: GetLbtcExchangeRateSchema
|
|
3683
|
+
}),
|
|
3684
|
+
_ts_metadata("design:type", Function),
|
|
3685
|
+
_ts_metadata("design:paramtypes", [
|
|
3686
|
+
typeof EvmWalletProvider === "undefined" ? Object : EvmWalletProvider,
|
|
3687
|
+
typeof z === "undefined" || typeof z.infer === "undefined" ? Object : z.infer
|
|
3688
|
+
]),
|
|
3689
|
+
_ts_metadata("design:returntype", Promise)
|
|
3690
|
+
], LombardActionProvider.prototype, "getLbtcExchangeRate", null);
|
|
3691
|
+
_ts_decorate([
|
|
3692
|
+
CreateAction({
|
|
3693
|
+
name: "get_deposit_status",
|
|
3694
|
+
description: "Check the status of all deposits for an address. " + "Shows pending, claimable, claimed, and failed deposits. " + "Use this to find claimable deposits before calling claim_deposit.",
|
|
3695
|
+
schema: GetDepositStatusSchema
|
|
3696
|
+
}),
|
|
3697
|
+
_ts_metadata("design:type", Function),
|
|
3698
|
+
_ts_metadata("design:paramtypes", [
|
|
3699
|
+
typeof EvmWalletProvider === "undefined" ? Object : EvmWalletProvider,
|
|
3700
|
+
typeof z === "undefined" || typeof z.infer === "undefined" ? Object : z.infer
|
|
3701
|
+
]),
|
|
3702
|
+
_ts_metadata("design:returntype", Promise)
|
|
3703
|
+
], LombardActionProvider.prototype, "getDepositStatusAction", null);
|
|
3704
|
+
_ts_decorate([
|
|
3705
|
+
CreateAction({
|
|
3706
|
+
name: "get_unstake_status",
|
|
3707
|
+
description: "Check the status of all unstake/redeem operations for an address. " + "Shows pending and completed unstakes with payout transaction details.",
|
|
3708
|
+
schema: GetUnstakeStatusSchema
|
|
3709
|
+
}),
|
|
3710
|
+
_ts_metadata("design:type", Function),
|
|
3711
|
+
_ts_metadata("design:paramtypes", [
|
|
3712
|
+
typeof EvmWalletProvider === "undefined" ? Object : EvmWalletProvider,
|
|
3713
|
+
typeof z === "undefined" || typeof z.infer === "undefined" ? Object : z.infer
|
|
3714
|
+
]),
|
|
3715
|
+
_ts_metadata("design:returntype", Promise)
|
|
3716
|
+
], LombardActionProvider.prototype, "getUnstakeStatus", null);
|
|
3717
|
+
const lombardActionProvider = ()=>new LombardActionProvider();
|
|
3718
|
+
|
|
3719
|
+
export { ClaimDepositSchema, DeployToDefiSchema, GetBtcbBalanceSchema, GetDepositStatusSchema, GetLbtcBalanceSchema, GetLbtcExchangeRateSchema, GetUnstakeStatusSchema, LombardActionProvider, NETWORK_ID_TO_VIEM_CHAIN, RedeemLbtcToBtcbSchema, StakeBtcbToLbtcSchema, UnstakeLbtcSchema, isLombardSupportedNetwork, lombardActionProvider, resolveChainName, resolveNetwork };
|