@atomiqlabs/chain-starknet
Version:
Starknet specific base implementation
258 lines (257 loc) • 10.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.findLastIndex = exports.parseInitFunctionCalldata = exports.poseidonHashRange = exports.bufferToByteArray = exports.bufferToBytes31Span = exports.bytes31SpanToBuffer = exports.toBigInt = exports.bigNumberishToBuffer = exports.u32ReverseEndianness = exports.bufferToU32Array = exports.u32ArrayToBuffer = exports.calculateHash = exports.toHex = exports.tryWithRetries = exports.getLogger = exports.onceAsync = exports.timeoutPromise = exports.isUint256 = void 0;
const starknet_types_07_1 = require("starknet-types-07");
const starknet_1 = require("starknet");
const buffer_1 = require("buffer");
const StarknetSwapData_1 = require("../starknet/swaps/StarknetSwapData");
function isUint256(val) {
return val.low != null && val.high != null;
}
exports.isUint256 = isUint256;
function timeoutPromise(timeoutMillis, abortSignal) {
return new Promise((resolve, reject) => {
const timeout = setTimeout(resolve, timeoutMillis);
if (abortSignal != null)
abortSignal.addEventListener("abort", () => {
clearTimeout(timeout);
reject(new Error("Aborted"));
});
});
}
exports.timeoutPromise = timeoutPromise;
function onceAsync(executor) {
let promise;
return () => {
if (promise == null) {
promise = executor();
return promise;
}
else {
return promise.catch(() => promise = executor());
}
};
}
exports.onceAsync = onceAsync;
function getLogger(prefix) {
return {
debug: (msg, ...args) => console.debug(prefix + msg, ...args),
info: (msg, ...args) => console.info(prefix + msg, ...args),
warn: (msg, ...args) => console.warn(prefix + msg, ...args),
error: (msg, ...args) => console.error(prefix + msg, ...args)
};
}
exports.getLogger = getLogger;
const logger = getLogger("Utils: ");
async function tryWithRetries(func, retryPolicy, errorAllowed, abortSignal) {
retryPolicy = retryPolicy || {};
retryPolicy.maxRetries = retryPolicy.maxRetries || 5;
retryPolicy.delay = retryPolicy.delay || 500;
retryPolicy.exponential = retryPolicy.exponential == null ? true : retryPolicy.exponential;
let err = null;
for (let i = 0; i < retryPolicy.maxRetries; i++) {
try {
const resp = await func();
return resp;
}
catch (e) {
if (errorAllowed != null && errorAllowed(e))
throw e;
err = e;
logger.error("tryWithRetries(): error on try number: " + i, e);
}
if (abortSignal != null && abortSignal.aborted)
throw new Error("Aborted");
if (i !== retryPolicy.maxRetries - 1) {
await timeoutPromise(retryPolicy.exponential ? retryPolicy.delay * Math.pow(2, i) : retryPolicy.delay, abortSignal);
}
}
throw err;
}
exports.tryWithRetries = tryWithRetries;
function toHex(value, length = 64) {
if (value == null)
return null;
switch (typeof (value)) {
case "string":
if (value.startsWith("0x")) {
return "0x" + value.slice(2).padStart(length, "0");
}
else {
return "0x" + BigInt(value).toString(16).padStart(length, "0");
}
case "number":
case "bigint":
return "0x" + value.toString(16).padStart(length, "0");
}
return "0x" + value.toString("hex").padStart(length, "0");
}
exports.toHex = toHex;
function calculateHash(tx) {
const commonData = {
version: tx.details.version,
maxFee: tx.details.maxFee,
chainId: tx.details.chainId,
nonce: tx.details.nonce,
accountDeploymentData: tx.details.version === "0x3" ? tx.details.accountDeploymentData : null,
nonceDataAvailabilityMode: tx.details.version === "0x3" ? starknet_types_07_1.EDAMode[tx.details.nonceDataAvailabilityMode] : null,
feeDataAvailabilityMode: tx.details.version === "0x3" ? starknet_types_07_1.EDAMode[tx.details.feeDataAvailabilityMode] : null,
resourceBounds: tx.details.version === "0x3" ? tx.details.resourceBounds : null,
tip: tx.details.version === "0x3" ? tx.details.tip : null,
paymasterData: tx.details.version === "0x3" ? tx.details.paymasterData : null
};
switch (tx.type) {
case "INVOKE":
const invokeData = starknet_1.CallData.compile(tx.signed.calldata);
return tx.txId = starknet_1.hash.calculateInvokeTransactionHash({
senderAddress: tx.details.walletAddress,
compiledCalldata: invokeData,
...commonData
});
case "DEPLOY_ACCOUNT":
const deployAccountData = starknet_1.CallData.compile(tx.signed.constructorCalldata);
return tx.txId = starknet_1.hash.calculateDeployAccountTransactionHash({
contractAddress: tx.tx.contractAddress,
classHash: tx.signed.classHash,
constructorCalldata: deployAccountData,
compiledConstructorCalldata: deployAccountData,
salt: tx.signed.addressSalt,
...commonData
});
default:
throw new Error("Unsupported tx type!");
}
}
exports.calculateHash = calculateHash;
function u32ArrayToBuffer(arr) {
const buffer = buffer_1.Buffer.alloc(4 * arr.length);
for (let i = 0; i < arr.length; i++) {
buffer.writeUInt32BE(Number(arr[i]), i * 4);
}
return buffer;
}
exports.u32ArrayToBuffer = u32ArrayToBuffer;
function bufferToU32Array(buffer) {
const result = [];
for (let i = 0; i < buffer.length; i += 4) {
result.push(buffer.readUInt32BE(i));
}
return result;
}
exports.bufferToU32Array = bufferToU32Array;
function u32ReverseEndianness(value) {
const valueBN = BigInt(value);
return Number(((valueBN & 0xffn) << 24n) |
((valueBN & 0xff00n) << 8n) |
((valueBN >> 8n) & 0xff00n) |
((valueBN >> 24n) & 0xffn));
}
exports.u32ReverseEndianness = u32ReverseEndianness;
function bigNumberishToBuffer(value, length) {
if (isUint256(value)) {
return buffer_1.Buffer.concat([bigNumberishToBuffer(value.high, 16), bigNumberishToBuffer(value.low, 16)]);
}
if (typeof (value) === "string") {
if (value.startsWith("0x")) {
value = value.slice(2);
}
else {
value = BigInt(value).toString(16);
}
}
else {
value = value.toString(16);
}
if (length != null)
value = value.padStart(length * 2, "0");
const buff = buffer_1.Buffer.from(value, "hex");
if (buff.length > length)
return buff.slice(buff.length - length);
return buff;
}
exports.bigNumberishToBuffer = bigNumberishToBuffer;
function toBigInt(value) {
if (value == null)
return null;
if (isUint256(value)) {
return (toBigInt(value.high) << 128n) | toBigInt(value.low);
}
if (typeof (value) === "string") {
if (!value.startsWith("0x"))
value = "0x" + value;
return BigInt(value);
}
if (typeof (value) === "bigint") {
return value;
}
return BigInt(value);
}
exports.toBigInt = toBigInt;
function bytes31SpanToBuffer(span, length) {
const buffers = [];
const numFullBytes31 = Math.floor(length / 31);
const additionalBytes = length - (numFullBytes31 * 31);
const requiredSpanLength = numFullBytes31 + (additionalBytes === 0 ? 0 : 1);
if (span.length < requiredSpanLength)
throw new Error("Not enough bytes in the felt array!");
let i = 0;
for (; i < numFullBytes31; i++) {
buffers.push(bigNumberishToBuffer(span[i], 31));
}
if (additionalBytes !== 0)
buffers.push(bigNumberishToBuffer(span[i], additionalBytes));
return buffer_1.Buffer.concat(buffers);
}
exports.bytes31SpanToBuffer = bytes31SpanToBuffer;
function bufferToBytes31Span(buffer, startIndex = 0, endIndex = buffer.length) {
const values = [];
for (let i = startIndex + 31; i < endIndex; i += 31) {
values.push(BigInt("0x" + buffer.slice(i - 31, i).toString("hex")));
}
if (endIndex > startIndex + (values.length * 31))
values.push(BigInt("0x" + buffer.slice(startIndex + (values.length * 31), endIndex).toString("hex")));
return values;
}
exports.bufferToBytes31Span = bufferToBytes31Span;
function bufferToByteArray(buffer, startIndex = 0, endIndex = buffer.length) {
const values = [];
for (let i = startIndex + 31; i < endIndex; i += 31) {
values.push(BigInt("0x" + buffer.slice(i - 31, i).toString("hex")));
}
let pendingWord = BigInt(0);
let pendingWordLen = BigInt(endIndex - (startIndex + (values.length * 31)));
if (pendingWordLen !== BigInt(0)) {
pendingWord = BigInt("0x" + buffer.slice(startIndex + (values.length * 31), endIndex).toString("hex"));
}
return [
BigInt(values.length),
...values,
pendingWord,
pendingWordLen
];
}
exports.bufferToByteArray = bufferToByteArray;
function poseidonHashRange(buffer, startIndex = 0, endIndex = buffer.length) {
return starknet_1.hash.computePoseidonHashOnElements(bufferToBytes31Span(buffer, startIndex, endIndex));
}
exports.poseidonHashRange = poseidonHashRange;
function parseInitFunctionCalldata(calldata, claimHandler) {
const escrow = StarknetSwapData_1.StarknetSwapData.fromSerializedFeltArray(calldata, claimHandler);
const signatureLen = Number(toBigInt(calldata.shift()));
const signature = calldata.splice(0, signatureLen);
const timeout = toBigInt(calldata.shift());
const extraDataLen = Number(toBigInt(calldata.shift()));
const extraData = calldata.splice(0, extraDataLen);
if (calldata.length !== 0)
throw new Error("Calldata not read fully!");
return { escrow, signature, timeout, extraData };
}
exports.parseInitFunctionCalldata = parseInitFunctionCalldata;
function findLastIndex(array, callback) {
for (let i = array.length - 1; i >= 0; i--) {
if (callback(array[i], i))
return i;
}
return -1;
}
exports.findLastIndex = findLastIndex;