UNPKG

@patchworkdev/pdk

Version:

Patchwork Development Kit

146 lines (145 loc) 4.82 kB
"use strict"; 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}`); } }