UNPKG

@axiom-crypto/tools

Version:

Useful data, field, and byte manipulation tools for Axiom.

100 lines (99 loc) 3.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isAssignedSlot = exports.unpackSlot = exports.packSlot = exports.checkFitsInSlot = exports.getSlotForArray = exports.getSlotForMapping = void 0; const ethers_1 = require("ethers"); const byteLength_1 = require("./byteLength"); const byteStringReader_1 = require("./byteStringReader"); function getSlotForMapping(mappingSlot, keyType, key) { const packed = ethers_1.AbiCoder.defaultAbiCoder().encode([keyType, "uint256"], [key, mappingSlot]); return ethers_1.ethers.keccak256(packed); } exports.getSlotForMapping = getSlotForMapping; function getSlotForArray(arraySlot, arrayType, arrayIdx) { const packed = ethers_1.AbiCoder.defaultAbiCoder().encode(["uint256"], [arraySlot]); const baseSlot = ethers_1.ethers.keccak256(packed); const typeSize = (0, byteLength_1.getByteLength)(arrayType); let slot = BigInt(baseSlot) + BigInt(arrayIdx.valueOf()) / BigInt(Math.floor(32 / typeSize)); return "0x" + slot.toString(16); } exports.getSlotForArray = getSlotForArray; function checkFitsInSlot(types) { const lengths = types .map((type) => (0, byteLength_1.getByteLength)(type)) .reduce((a, b) => a + b, 0); if (lengths > 32) { return false; } return true; } exports.checkFitsInSlot = checkFitsInSlot; /** * Packs a 32-byte EVM storage slot from left to right with the given types and data * @param types Array of fixed-length Solidity types * @param data Array of data * @returns 32-byte hex string of packed data */ function packSlot(types, data) { if (!checkFitsInSlot(types)) { throw new Error("Data does not fit in slot"); } const packed = ethers_1.ethers.solidityPacked(types, data); return ethers_1.ethers.zeroPadValue(packed, 32); } exports.packSlot = packSlot; /** * Unpacks a 32-byte EVM storage slot into an array of data, read from left to right * @param types Array of fixed-length Solidity types * @param slot 32-byte hex string of slot * @returns Array of data */ function unpackSlot(types, slot) { if (!checkFitsInSlot(types)) { throw new Error("Data does not fit in slot"); } if (!slot.startsWith("0x")) { throw new Error("Invalid slot value (needs to start with 0x)"); } if (slot.length % 2 !== 0) { throw new Error("Invalid slot value (length must be even)"); } const dataLength = types .map((type) => (0, byteLength_1.getByteLength)(type)) .reduce((a, b) => a + b, 0); const leftZeroes = (32 - dataLength) * 2; const reader = new byteStringReader_1.ByteStringReader(slot); reader.setIndex(leftZeroes); let values = []; for (let i = 0; i < types.length; i++) { const value = reader.readBytes((0, byteLength_1.getByteLength)(types[i])); values.push(value); } return values; } exports.unpackSlot = unpackSlot; function isAssignedSlot(key, proof) { const keyHash = ethers_1.ethers.keccak256(key); let claimedKeyHash = "0x"; let keyIdx = 0; for (let i = 0; i < proof.length; i += 1) { const parsedNode = ethers_1.ethers.decodeRlp(proof[i]); if (parsedNode.length === 17) { claimedKeyHash += keyHash[keyIdx + 2]; keyIdx += 1; } else { const prefix = parsedNode[0][2]; if (prefix === "1" || prefix === "3") { claimedKeyHash += parsedNode[0].slice(3); keyIdx = keyIdx + parsedNode[0].length - 3; } else { claimedKeyHash += parsedNode[0].slice(4); keyIdx = keyIdx + parsedNode[0].length - 4; } } } return keyHash === claimedKeyHash; } exports.isAssignedSlot = isAssignedSlot;