UNPKG

@patchworkdev/common

Version:

Patchwork Development Kit

150 lines (149 loc) 8.14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PackFuncGen = void 0; const generator_1 = require("../generator"); class PackFuncGen { gen(schema) { const packMetadataFunction = generatePackMetadataFunction(schema.storage.fields); function generatePackMetadataFunction(entries) { let slots = []; for (let slotIdx = 0; slotIdx < schema.storage.slots.length; slotIdx++) { let slotEntries = []; for (let fieldIdx = 0; fieldIdx < schema.storage.slots[slotIdx].fieldIDs.length; fieldIdx++) { let fieldID = schema.storage.slots[slotIdx].fieldIDs[fieldIdx]; let field = schema.getField(fieldID); if (field.arrayLength === 0) { continue; } if (field.totalBits === 0) { continue; } let elementBits = field.elementBits; let arrayElementsPerSlot = 256 / elementBits; let arrayIdxStartInSlot = field.arrayLength == 1 ? 0 : (slotIdx - field.slot) * arrayElementsPerSlot; let arrayIdxEndInSlot = field.arrayLength == 1 ? 1 : arrayIdxStartInSlot + arrayElementsPerSlot; let offsetInSlot = field.slot == slotIdx ? field.offset : 0; for (let arrayIdx = arrayIdxStartInSlot; arrayIdx < arrayIdxEndInSlot && arrayIdx < field.arrayLength; arrayIdx++) { // the starting offset in this slot - if not the starting slot for the field then 0 let arrayIdxStr = field.arrayLength > 1 ? `[${arrayIdx}]` : ''; let conversion = `data.${field.key}`; if (field.isString) { const stringOffsetBits = 256 - elementBits; const stringOffset = stringOffsetBits > 0 ? ` >> ${stringOffsetBits}` : ''; conversion = `PatchworkUtils.strToUint256(${conversion}${arrayIdxStr})${stringOffset}`; } else if (field.type == `address`) { conversion = `uint256(uint160(${conversion}${arrayIdxStr}))`; } else if (field.type == `bool`) { conversion = `uint256(${conversion}${arrayIdxStr} ? 1 : 0)`; } else if (['int8', 'int16', 'int32', 'int64', 'int128', 'int256', 'bytes8', 'bytes16'].indexOf(field.type) >= 0) { conversion = `uint256(uint${elementBits}(${conversion}${arrayIdxStr}))`; } else { conversion = `uint256(${conversion}${arrayIdxStr})`; } if (offsetInSlot > 0 || arrayIdx > 0) { let offset = field.offset + elementBits * (arrayIdx % arrayElementsPerSlot); if (offset !== 0) { conversion += ` << ${offset}`; } } slotEntries.push(conversion); } } // Will run out of stack, max 8 const maxArrayPerSlot = 8; if (slotEntries.length > maxArrayPerSlot) { let index = 0; while (index < slotEntries.length) { const group = slotEntries.slice(index, index + maxArrayPerSlot); if (index === 0) { slots.push(`uint256 slot${slotIdx} = ` + group.join(' | ') + ';'); } else if (index + maxArrayPerSlot < slotEntries.length) { slots.push(`slot${slotIdx} = slot${slotIdx} | ` + group.join(' | ') + ';'); } else { slots.push(`slots[${slotIdx}] = slot${slotIdx} | ` + group.join(' | ') + ';'); } index += maxArrayPerSlot; } } else { slots.push(`slots[${slotIdx}] = ` + slotEntries.join(' | ') + ';'); } } return (`` + `function packMetadata(${schema.getMetadataStructName()} memory data) public pure returns (uint256[] memory slots) {\n` + (0, generator_1.ind)(4, `slots = new uint256[](${schema.storage.slots.length});\n`) + (0, generator_1.ind)(4, `${slots.join('\n')}\n`) + (0, generator_1.ind)(4, `return slots;\n`) + `}\n`); } function generateUnpackMetadataFunction(entries) { let slotIndex = 0; let unpackLines = []; function unpackField(field, arrayIdx) { if (field.arrayLength === 0) { return; } if (field.totalBits === 0) { return; } const naiveOffset = field.offset + field.elementBits * arrayIdx; const slot = field.slot + Math.floor((field.offset + field.elementBits * arrayIdx) / 256); const offset = naiveOffset - (slot - field.slot) * 256; if (slot > slotIndex) { slotIndex = slot; unpackLines.push(`slot = slots[${slotIndex}];`); } let arrayIdxStr = field.arrayLength > 1 ? `[${arrayIdx}]` : ''; let shift = offset === 0 ? `` : ` >> ${offset}`; if (field.isString) { let strBytes = field.elementBits / 8; let slotExpr = `slot${shift}`; if (field.elementBits != 256) { slotExpr = `uint${field.elementBits}(${slotExpr})`; } unpackLines.push(`data.${field.key}${arrayIdxStr} = PatchworkUtils.toString${strBytes}(${slotExpr});`); } else if (field.type == `address`) { unpackLines.push(`data.${field.key}${arrayIdxStr} = address(uint160(slot${shift}));`); } else if (field.type == `bool`) { unpackLines.push(`data.${field.key}${arrayIdxStr} = slot${shift} & 1 == 1;`); } else if (['int8', 'int16', 'int32', 'int64', 'int128', 'int256', 'bytes8', 'bytes16'].indexOf(field.type) >= 0) { let unpackedValue = `${field.solidityType}(uint${field.elementBits}(slot${shift}))`; unpackLines.push(`data.${field.key}${arrayIdxStr} = ${unpackedValue};`); } else { let unpackedValue = `${field.solidityType}(slot${shift})`; unpackLines.push(`data.${field.key}${arrayIdxStr} = ${unpackedValue};`); } } unpackLines.push(`uint256 slot = slots[0];`); for (let i = 0; i < entries.length; i++) { let entry = entries[i]; if (entry.arrayLength > 0) { for (let j = 0; j < entry.arrayLength; j++) { unpackField(entry, j); } } else { unpackField(entry, 0); } } return (`` + `function unpackMetadata(uint256[] memory slots) public pure returns (${schema.getMetadataStructName()} memory data) {\n` + ` ${unpackLines.join('\n ')}\n` + ` return data;\n` + `}\n`); } const unpackMetadataFunction = generateUnpackMetadataFunction(schema.storage.fields); return (0, generator_1.ind)(4, `` + `${packMetadataFunction}\n` + `${unpackMetadataFunction}\n`); } } exports.PackFuncGen = PackFuncGen;