@patchworkdev/common
Version:
Patchwork Development Kit
150 lines (149 loc) • 8.14 kB
JavaScript
;
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;