@0xfutbol/id
Version:
React component library with shared providers for 0xFutbol ID
364 lines (363 loc) • 14.4 kB
JavaScript
;var eth_sendRawTransaction=require('./eth_sendRawTransaction-BernmwlE.js'),index=require('./index-DwkZmKdT.js'),concatHex=require('./concat-hex-DJMxTlMl.js'),sha256$1=require('./sha256-CnhWMqX6.js');function toRlp(bytes, to = 'hex') {
const encodable = getEncodable(bytes);
const cursor = index.b0(new Uint8Array(encodable.length));
encodable.encode(cursor);
if (to === 'hex')
return index.e(cursor.bytes);
return cursor.bytes;
}
function getEncodable(bytes) {
if (Array.isArray(bytes))
return getEncodableList(bytes.map((x) => getEncodable(x)));
return getEncodableBytes(bytes);
}
function getEncodableList(list) {
const bodyLength = list.reduce((acc, x) => acc + x.length, 0);
const sizeOfBodyLength = getSizeOfLength(bodyLength);
const length = (() => {
if (bodyLength <= 55)
return 1 + bodyLength;
return 1 + sizeOfBodyLength + bodyLength;
})();
return {
length,
encode(cursor) {
if (bodyLength <= 55) {
cursor.pushByte(0xc0 + bodyLength);
}
else {
cursor.pushByte(0xc0 + 55 + sizeOfBodyLength);
if (sizeOfBodyLength === 1)
cursor.pushUint8(bodyLength);
else if (sizeOfBodyLength === 2)
cursor.pushUint16(bodyLength);
else if (sizeOfBodyLength === 3)
cursor.pushUint24(bodyLength);
else
cursor.pushUint32(bodyLength);
}
for (const { encode } of list) {
encode(cursor);
}
},
};
}
function getEncodableBytes(bytesOrHex) {
const bytes = typeof bytesOrHex === 'string' ? index.b1(bytesOrHex) : bytesOrHex;
const sizeOfBytesLength = getSizeOfLength(bytes.length);
const length = (() => {
if (bytes.length === 1 && bytes[0] < 0x80)
return 1;
if (bytes.length <= 55)
return 1 + bytes.length;
return 1 + sizeOfBytesLength + bytes.length;
})();
return {
length,
encode(cursor) {
if (bytes.length === 1 && bytes[0] < 0x80) {
cursor.pushBytes(bytes);
}
else if (bytes.length <= 55) {
cursor.pushByte(0x80 + bytes.length);
cursor.pushBytes(bytes);
}
else {
cursor.pushByte(0x80 + 55 + sizeOfBytesLength);
if (sizeOfBytesLength === 1)
cursor.pushUint8(bytes.length);
else if (sizeOfBytesLength === 2)
cursor.pushUint16(bytes.length);
else if (sizeOfBytesLength === 3)
cursor.pushUint24(bytes.length);
else
cursor.pushUint32(bytes.length);
cursor.pushBytes(bytes);
}
},
};
}
function getSizeOfLength(length) {
if (length < 2 ** 8)
return 1;
if (length < 2 ** 16)
return 2;
if (length < 2 ** 24)
return 3;
if (length < 2 ** 32)
return 4;
throw new index.B('Length is too large.');
}const maxUint16 = 2n ** 16n - 1n;function sha256(value, to_) {
const bytes = sha256$1.s(index.V(value, { strict: false }) ? index.a0(value) : value);
return index.ak(bytes);
}/**
* Provides error checking on string or number bigint inputs.
* @param value - A possibly integer-like string, number, or bigint.
* @returns The bigint representation of the input.
* @example
* ```ts
* toBigInt("2")
* // 2n
*/
function toBigInt(value) {
if (["string", "number"].includes(typeof value) &&
!Number.isInteger(Number(value))) {
throw new Error(`Expected value to be an integer to convert to a bigint, got ${value} of type ${typeof value}`);
}
if (value instanceof Uint8Array) {
return BigInt(index.N(value));
}
return BigInt(value);
}
const replaceBigInts = (obj, replacer) => {
if (typeof obj === "bigint")
return replacer(obj);
if (Array.isArray(obj))
return obj.map((x) => replaceBigInts(x, replacer));
if (obj && typeof obj === "object")
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, replaceBigInts(v, replacer)]));
return obj;
};const maxBytecodeSize = maxUint16 * 32n;class BytecodeLengthExceedsMaxSizeError extends index.B {
constructor({ givenLength, maxBytecodeSize, }) {
super(`Bytecode cannot be longer than ${maxBytecodeSize} bytes. Given length: ${givenLength}`, { name: 'BytecodeLengthExceedsMaxSizeError' });
}
}
class BytecodeLengthInWordsMustBeOddError extends index.B {
constructor({ givenLengthInWords }) {
super(`Bytecode length in 32-byte words must be odd. Given length in words: ${givenLengthInWords}`, { name: 'BytecodeLengthInWordsMustBeOddError' });
}
}
class BytecodeLengthMustBeDivisibleBy32Error extends index.B {
constructor({ givenLength }) {
super(`The bytecode length in bytes must be divisible by 32. Given length: ${givenLength}`, { name: 'BytecodeLengthMustBeDivisibleBy32Error' });
}
}function hashBytecode(bytecode) {
const bytecodeBytes = index.a0(bytecode);
if (bytecodeBytes.length % 32 !== 0)
throw new BytecodeLengthMustBeDivisibleBy32Error({
givenLength: bytecodeBytes.length,
});
if (bytecodeBytes.length > maxBytecodeSize)
throw new BytecodeLengthExceedsMaxSizeError({
givenLength: bytecodeBytes.length,
maxBytecodeSize,
});
const hashStr = sha256(bytecodeBytes);
const hash = index.a0(hashStr);
// Note that the length of the bytecode
// should be provided in 32-byte words.
const bytecodeLengthInWords = bytecodeBytes.length / 32;
if (bytecodeLengthInWords % 2 === 0) {
throw new BytecodeLengthInWordsMustBeOddError({
givenLengthInWords: bytecodeLengthInWords,
});
}
const bytecodeLength = index.a0(bytecodeLengthInWords);
// The bytecode should always take the first 2 bytes of the bytecode hash,
// so we pad it from the left in case the length is smaller than 2 bytes.
const bytecodeLengthPadded = index.aj(bytecodeLength, { size: 2 });
const codeHashVersion = new Uint8Array([1, 0]);
hash.set(codeHashVersion, 0);
hash.set(bytecodeLengthPadded, 2);
return hash;
}const gasPerPubdataDefault = 50000n;
const getEip712Domain = (transaction) => {
const message = transactionToMessage(transaction);
return {
domain: {
name: "zkSync",
version: "2",
chainId: transaction.chainId,
},
types: {
Transaction: [
{ name: "txType", type: "uint256" },
{ name: "from", type: "uint256" },
{ name: "to", type: "uint256" },
{ name: "gasLimit", type: "uint256" },
{ name: "gasPerPubdataByteLimit", type: "uint256" },
{ name: "maxFeePerGas", type: "uint256" },
{ name: "maxPriorityFeePerGas", type: "uint256" },
{ name: "paymaster", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "value", type: "uint256" },
{ name: "data", type: "bytes" },
{ name: "factoryDeps", type: "bytes32[]" },
{ name: "paymasterInput", type: "bytes" },
],
},
primaryType: "Transaction",
message: message,
};
};
function transactionToMessage(transaction) {
const { gas, nonce, to, from, value, maxFeePerGas, maxPriorityFeePerGas, paymaster, paymasterInput, gasPerPubdata, data, factoryDeps, } = transaction;
return {
txType: 113n,
from: BigInt(from),
to: to ? BigInt(to) : 0n,
gasLimit: gas ?? 0n,
gasPerPubdataByteLimit: gasPerPubdata ?? gasPerPubdataDefault,
maxFeePerGas: maxFeePerGas ?? 0n,
maxPriorityFeePerGas: maxPriorityFeePerGas ?? 0n,
paymaster: paymaster ? BigInt(paymaster) : 0n,
nonce: nonce ? BigInt(nonce) : 0n,
value: value ?? 0n,
data: data ? data : "0x0",
factoryDeps: factoryDeps?.map((dep) => index.i(hashBytecode(dep))) ?? [],
paymasterInput: paymasterInput ? paymasterInput : "0x",
};
}/**
* Sends a transaction using the provided wallet.
* @param options - The options for sending the transaction.
* @returns A promise that resolves to the transaction hash.
* @throws An error if the wallet is not connected.
* @transaction
* @example
* ```ts
* import { sendTransaction } from "thirdweb";
*
* const { transactionHash } = await sendTransaction({
* account,
* transaction
* });
* ```
*/
async function sendEip712Transaction(options) {
const { account, transaction } = options;
const eip712Transaction = await populateEip712Transaction(options);
const hash = await signEip712Transaction({
account,
eip712Transaction,
chainId: transaction.chain.id,
});
const rpc = index.f(transaction);
const result = await eth_sendRawTransaction.e(rpc, hash);
return {
transactionHash: result,
chain: transaction.chain,
client: transaction.client,
};
}
async function signEip712Transaction(options) {
const { account, eip712Transaction, chainId } = options;
// EIP712 signing of the serialized tx
const eip712Domain = getEip712Domain(eip712Transaction);
const customSignature = await account.signTypedData({
// biome-ignore lint/suspicious/noExplicitAny: TODO type properly
...eip712Domain,
});
return serializeTransactionEIP712({
...eip712Transaction,
chainId,
customSignature,
});
}
/**
* Populate a prepared transaction to be serialized as a EIP712 transaction
* @param options
* @internal
*/
async function populateEip712Transaction(options) {
const { account, transaction } = options;
const { gas, maxFeePerGas, maxPriorityFeePerGas, gasPerPubdata } = await getZkGasFees({ transaction, from: index.d(account.address) });
// serialize the transaction (with fees, gas, nonce)
const serializableTransaction = await index.aH({
transaction: {
...transaction,
gas,
maxFeePerGas,
maxPriorityFeePerGas,
},
from: account.address,
});
return {
...serializableTransaction,
...transaction.eip712,
gasPerPubdata,
from: account.address,
};
}
function serializeTransactionEIP712(transaction) {
const { chainId, gas, nonce, to, from, value, maxFeePerGas, maxPriorityFeePerGas, customSignature, factoryDeps, paymaster, paymasterInput, gasPerPubdata, data, } = transaction;
const serializedTransaction = [
nonce ? index.i(nonce) : "0x",
maxPriorityFeePerGas ? index.i(maxPriorityFeePerGas) : "0x",
maxFeePerGas ? index.i(maxFeePerGas) : "0x",
gas ? index.i(gas) : "0x",
to ?? "0x",
value ? index.i(value) : "0x",
data ?? "0x0",
index.i(chainId),
index.i(""),
index.i(""),
index.i(chainId),
from ?? "0x",
gasPerPubdata ? index.i(gasPerPubdata) : index.i(gasPerPubdataDefault),
factoryDeps ?? [],
customSignature ?? "0x", // EIP712 signature
paymaster && paymasterInput ? [paymaster, paymasterInput] : [],
];
// @ts-ignore - TODO: fix types
return concatHex.concatHex(["0x71", toRlp(serializedTransaction)]);
}
async function getZkGasFees(args) {
const { transaction, from } = args;
let [gas, maxFeePerGas, maxPriorityFeePerGas, eip712] = await Promise.all([
index.aq(transaction.gas),
index.aq(transaction.maxFeePerGas),
index.aq(transaction.maxPriorityFeePerGas),
index.aq(transaction.eip712),
]);
let gasPerPubdata = eip712?.gasPerPubdata;
if (gas === undefined ||
maxFeePerGas === undefined ||
maxPriorityFeePerGas === undefined) {
const rpc = index.f(transaction);
const params = await formatTransaction({ transaction, from });
const result = (await rpc({
// biome-ignore lint/suspicious/noExplicitAny: TODO add to RPC method types
method: "zks_estimateFee",
// biome-ignore lint/suspicious/noExplicitAny: TODO add to RPC method types
params: [replaceBigInts(params, index.i)],
}));
gas = toBigInt(result.gas_limit) * 2n; // overestimating to avoid issues when not accounting for paymaster extra gas ( we should really pass the paymaster input above for better accuracy )
const baseFee = toBigInt(result.max_fee_per_gas);
maxFeePerGas = baseFee * 2n; // bumping the base fee per gas to ensure fast inclusion
maxPriorityFeePerGas = toBigInt(result.max_priority_fee_per_gas) || 1n;
gasPerPubdata = toBigInt(result.gas_per_pubdata_limit) * 2n; // doubling for fast inclusion;
if (gasPerPubdata < 50000n) {
// enforce a minimum gas per pubdata limit
gasPerPubdata = 50000n;
}
}
return {
gas,
maxFeePerGas,
maxPriorityFeePerGas,
gasPerPubdata,
};
}
async function formatTransaction(args) {
const { transaction, from } = args;
const [data, to, value, eip712] = await Promise.all([
index.ap(transaction),
index.aq(transaction.to),
index.aq(transaction.value),
index.aq(transaction.eip712),
]);
const gasPerPubdata = eip712?.gasPerPubdata;
return {
from,
to,
data,
value,
gasPerPubdata,
eip712Meta: {
...eip712,
gasPerPubdata: gasPerPubdata || 50000n,
factoryDeps: eip712?.factoryDeps?.map((dep) => Array.from(index.b1(dep))),
},
type: "0x71",
};
}var sendEip712Transaction$1=/*#__PURE__*/Object.freeze({__proto__:null,getZkGasFees:getZkGasFees,populateEip712Transaction:populateEip712Transaction,sendEip712Transaction:sendEip712Transaction,signEip712Transaction:signEip712Transaction});exports.a=sendEip712Transaction$1;exports.p=populateEip712Transaction;exports.s=signEip712Transaction;exports.t=toBigInt;//# sourceMappingURL=send-eip712-transaction-B3DIfS-8.js.map