UNPKG

near-safe

Version:

An SDK for controlling Ethereum Smart Accounts via ERC4337 from a Near Account.

165 lines (164 loc) 7.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.packGas = exports.PLACEHOLDER_SIG = void 0; exports.packSignature = packSignature; exports.packPaymasterData = packPaymasterData; exports.containsValue = containsValue; exports.isContract = isContract; exports.getClient = getClient; exports.metaTransactionsFromRequest = metaTransactionsFromRequest; exports.saltNonceFromMessage = saltNonceFromMessage; exports.signatureFromTxHash = signatureFromTxHash; exports.raceToFirstResolve = raceToFirstResolve; exports.assertUnique = assertUnique; exports.userOpTransactionCost = userOpTransactionCost; const near_ca_1 = require("near-ca"); const viem_1 = require("viem"); exports.PLACEHOLDER_SIG = (0, viem_1.encodePacked)(["uint48", "uint48"], [0, 0]); const packGas = (hi, lo) => (0, viem_1.encodePacked)(["uint128", "uint128"], [BigInt(hi), BigInt(lo)]); exports.packGas = packGas; function packSignature(signature, validFrom = 0, validTo = 0) { return (0, viem_1.encodePacked)(["uint48", "uint48", "bytes"], [validFrom, validTo, signature]); } function packPaymasterData(data) { return (data.paymaster ? (0, viem_1.concatHex)([ data.paymaster, (0, viem_1.toHex)(BigInt(data.paymasterVerificationGasLimit || 0n), { size: 16 }), (0, viem_1.toHex)(BigInt(data.paymasterPostOpGasLimit || 0n), { size: 16 }), data.paymasterData || "0x", ]) : "0x"); } function containsValue(transactions) { return transactions.some((tx) => BigInt(tx.value) !== 0n); } async function isContract(address, chainId) { return (await getClient(chainId).getCode({ address })) !== undefined; } function getClient(chainId, rpcUrl) { // Caution: rpcUrl might not be aligned with chainId! const options = rpcUrl ? { rpcUrl } : {}; return near_ca_1.Network.fromChainId(chainId, options).client; } function metaTransactionsFromRequest(params) { let transactions; if ((0, viem_1.isHex)(params)) { // TODO: Consider deprecating this route. // If RLP hex is given, decode the transaction and build EthTransactionParams const tx = (0, viem_1.parseTransaction)(params); transactions = [ { from: viem_1.zeroAddress, // TODO: This is a hack - but its unused. to: tx.to, value: tx.value ? (0, viem_1.toHex)(tx.value) : "0x00", data: tx.data || "0x", }, ]; } else { // TODO: add type guard here. transactions = params; } return transactions.map((tx) => ({ to: tx.to, value: tx.value || "0x00", data: tx.data || "0x", })); } function saltNonceFromMessage(input) { // Convert the string to bytes (UTF-8 encoding) // Compute the keccak256 hash of the input bytes // Convert the resulting hash (which is in hex) to a BigInt // Return string for readability and transport. return BigInt((0, viem_1.keccak256)((0, viem_1.toBytes)(input))).toString(); } /** * Fetches the signature for a NEAR transaction hash. If an `accountId` is provided, * it fetches the signature from the appropriate network. Otherwise, it races across * both `testnet` and `mainnet`. * * @param {string} txHash - The NEAR transaction hash for which to fetch the signature. * @param {string} [accountId] - (Optional) The account ID associated with the transaction. * Providing this will reduce dangling promises as the network is determined by the account. * * @returns {Promise<Hex>} A promise that resolves to the hex-encoded signature. * * @throws Will throw an error if no signature is found for the given transaction hash. */ async function signatureFromTxHash(txHash, accountId) { if (accountId) { const signature = await (0, near_ca_1.signatureFromTxHash)(`https://archival-rpc.${(0, near_ca_1.getNetworkId)(accountId)}.near.org`, txHash, accountId); return packSignature((0, viem_1.serializeSignature)(signature)); } try { const signature = await raceToFirstResolve(["testnet", "mainnet"].map((network) => (0, near_ca_1.signatureFromTxHash)(archiveNode(network), txHash))); return packSignature((0, viem_1.serializeSignature)(signature)); } catch { throw new Error(`No signature found for txHash ${txHash}`); } } /** * Utility function to construct an archive node URL for a given NEAR network. * * @param {string} networkId - The ID of the NEAR network (e.g., 'testnet', 'mainnet'). * * @returns {string} The full URL of the archival RPC node for the specified network. */ const archiveNode = (networkId) => `https://archival-rpc.${networkId}.near.org`; /** * Races an array of promises and resolves with the first promise that fulfills. * If all promises reject, the function will reject with an error. * * @template T * @param {Promise<T>[]} promises - An array of promises to race. Each promise should resolve to type `T`. * * @returns {Promise<T>} A promise that resolves to the value of the first successfully resolved promise. * * @throws Will throw an error if all promises reject with the message "All promises rejected". */ async function raceToFirstResolve(promises) { return new Promise((resolve, reject) => { let rejectionCount = 0; const totalPromises = promises.length; promises.forEach((promise) => { // Wrap each promise so it only resolves when fulfilled Promise.resolve(promise) .then(resolve) // Resolve when any promise resolves .catch(() => { rejectionCount++; // If all promises reject, reject the race with an error if (rejectionCount === totalPromises) { reject(new Error("All promises rejected")); } }); }); }); } function assertUnique(iterable, errorMessage = "The collection contains more than one distinct element.") { const uniqueValues = new Set(iterable); if (uniqueValues.size > 1) { throw new Error(errorMessage); } } function userOpTransactionCost(userOp) { // Convert values from hex to decimal const preVerificationGas = BigInt(userOp.preVerificationGas); const verificationGasLimit = BigInt(userOp.verificationGasLimit); const callGasLimit = BigInt(userOp.callGasLimit); const paymasterVerificationGasLimit = BigInt(userOp.paymasterVerificationGasLimit || "0x0"); const paymasterPostOpGasLimit = BigInt(userOp.paymasterPostOpGasLimit || "0x0"); // Sum total gas const totalGasUsed = preVerificationGas + verificationGasLimit + callGasLimit + paymasterVerificationGasLimit + paymasterPostOpGasLimit; // Convert maxFeePerGas from hex to decimal const maxFeePerGas = BigInt(userOp.maxFeePerGas); // Calculate total cost in wei const totalCostInWei = totalGasUsed * maxFeePerGas; // Convert to Ether for a human-readable value return totalCostInWei; }