UNPKG

near-safe

Version:

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

76 lines (75 loc) 3.31 kB
import { decodeFunctionData, encodeFunctionData, encodePacked, getAddress, parseAbi, size, toHex, } from "viem"; import { OperationType } from "../types"; export const MULTI_SEND_ABI = ["function multiSend(bytes memory transactions)"]; const MULTISEND_141 = "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526"; const MULTISEND_CALLONLY_141 = "0x9641d764fc13c8B624c04430C7356C1C7C8102e2"; /// Encodes the transaction as packed bytes of: /// - `operation` as a `uint8` with `0` for a `call` or `1` for a `delegatecall` (=> 1 byte), /// - `to` as an `address` (=> 20 bytes), /// - `value` as a `uint256` (=> 32 bytes), /// - length of `data` as a `uint256` (=> 32 bytes), /// - `data` as `bytes`. const encodeMetaTx = (tx) => encodePacked(["uint8", "address", "uint256", "uint256", "bytes"], [ tx.operation || OperationType.Call, tx.to, BigInt(tx.value), BigInt(size(tx.data)), tx.data, ]); const remove0x = (hexString) => hexString.slice(2); // Encodes a batch of module transactions into a single multiSend module transaction. // A module transaction is an object with fields corresponding to a Gnosis Safe's (i.e., Zodiac IAvatar's) `execTransactionFromModule` method parameters. export function encodeMulti(transactions, multiSendContractAddress = transactions.some((t) => t.operation === OperationType.DelegateCall) ? MULTISEND_141 : MULTISEND_CALLONLY_141) { const encodedTransactions = "0x" + transactions.map(encodeMetaTx).map(remove0x).join(""); return { operation: OperationType.DelegateCall, to: multiSendContractAddress, value: "0x00", data: encodeFunctionData({ abi: parseAbi(MULTI_SEND_ABI), functionName: "multiSend", args: [encodedTransactions], }), }; } export function isMultisendTx(args) { const to = args[0].toLowerCase(); return (to === MULTISEND_141.toLowerCase() || to === MULTISEND_CALLONLY_141.toLowerCase()); } function unpack(packed, startIndex) { // read operation from first 8 bits (= 2 hex digits) const operation = parseInt(packed.substring(startIndex, startIndex + 2), 16); // the next 40 characters are the to address const to = getAddress(`0x${packed.substring(startIndex + 2, startIndex + 42)}`); // then comes the uint256 value (= 64 hex digits) const value = toHex(BigInt(`0x${packed.substring(startIndex + 42, startIndex + 106)}`)); // and the uint256 data length (= 64 hex digits) const hexDataLength = parseInt(packed.substring(startIndex + 106, startIndex + 170), 16); const endIndex = startIndex + 170 + hexDataLength * 2; // * 2 because each hex item is represented with 2 digits const data = `0x${packed.substring(startIndex + 170, endIndex)}`; return { operation, to, value, data, endIndex, }; } export function decodeMulti(data) { const tx = decodeFunctionData({ abi: parseAbi(MULTI_SEND_ABI), data, }); const [transactionsEncoded] = tx.args; const result = []; let startIndex = 2; // skip over 0x while (startIndex < transactionsEncoded.length) { const { endIndex, ...tx } = unpack(transactionsEncoded, startIndex); result.push(tx); startIndex = endIndex; } return result; }