@patchworkdev/pdk
Version:
Patchwork Development Kit
146 lines (145 loc) • 4.82 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.unpackMetadata = unpackMetadata;
exports.splitPackedValues = splitPackedValues;
exports.getBitLength = getBitLength;
exports.convertValue = convertValue;
/*
* Given a parsed contract JSON schema and packed metadata (uint256 array), walks through each field and
* attemps to unpack its corresponding metadata from the packed array
*/
async function unpackMetadata(schema, packedMetadata, excludedKeys = [], excludedTypes = []) {
if (!schema?.fields?.length)
throw new Error('Schema is empty');
const processedFields = {};
for (const field of schema.fields) {
// Skip fields based on key or type
if (excludedKeys.includes(field.key))
continue;
if (excludedTypes.includes(field.type))
continue;
const values = splitPackedValues(packedMetadata, Number(field.slot), Number(field.arrayLength || 1), getBitLength(field.type), Number(field.offset));
processedFields[field.key] =
field.arrayLength === 1 || field.arrayLength === undefined
? convertValue(values[0], field.type) // Single value
: values.map((v) => convertValue(v, field.type)); // Array of values
}
// Cast processedFields to InferSchemaType<S> safely
return processedFields;
}
/*
* Given a bit window and full array of bitpacked uint256 bigints, walks through the array and attempts to split
* relevant bits into a new array of unpacked bigints of various lengths for the specific field
*/
function splitPackedValues(uint256array, startingslot, fieldlength, bitlength, offset) {
const values = [];
for (let i = 0; i < fieldlength; i++) {
const value = (uint256array[startingslot] >> BigInt(offset)) & ((1n << BigInt(bitlength)) - 1n);
values.push(value);
offset += bitlength;
if (offset >= 256) {
startingslot++;
offset = 0;
}
}
return values;
}
/*
* Given a field type from a JSON contract schema, returns bit length of the field
*/
function getBitLength(fieldType) {
switch (fieldType) {
case 'bool':
return 1;
case 'int8':
case 'uint8':
return 8;
case 'int16':
case 'uint16':
return 16;
case 'int32':
case 'uint32':
return 32;
case 'int64':
case 'uint64':
return 64;
case 'int128':
case 'uint128':
return 128;
case 'int256':
case 'uint256':
return 256;
case 'char8':
return 8 * 8;
case 'char16':
return 16 * 8;
case 'char32':
return 32 * 8;
case 'char64':
return 64 * 8;
case 'bytes8':
return 8 * 8;
case 'bytes16':
return 16 * 8;
case 'bytes32':
return 32 * 8;
case 'literef':
return 64;
case 'address':
return 160;
case 'string':
throw new Error('string type is not supported for packed metadata');
default:
throw new Error(`Unsupported field type: ${fieldType}`);
}
}
/*
* Given a raw bigint and a field type from a JSON contract schema, returns the converted value
*/
function convertValue(value, fieldType) {
switch (fieldType) {
case 'bool':
return value !== 0n;
case 'int8':
case 'int16':
case 'int32':
case 'uint8':
case 'uint16':
case 'uint32':
case 'literef':
return Number(value);
case 'int64':
case 'int128':
case 'int256':
case 'uint64':
case 'uint128':
case 'uint256':
return BigInt(value);
case 'char8':
case 'char16':
case 'char32':
case 'char64':
// Convert bigint to a buffer and then to a string
const hexString = value.toString(16);
const utf8String = Buffer.from(hexString, 'hex')
.toString('utf8')
.replace(/[\0]+$/g, '');
const jankyCharacters = /[\uFFFD\u0000-\u001F]/;
if (jankyCharacters.test(utf8String)) {
return hexString;
}
else {
return utf8String;
}
case 'bytes8':
case 'bytes16':
case 'bytes32':
return value.toString(16);
case 'address':
return value.toString(16).padStart(40, '0');
case 'string':
throw new Error('STRING type is not supported for packed metadata');
default:
throw new Error(`Unsupported field type for conversion: ${fieldType}`);
}
}