UNPKG

@debridge-finance/solana-utils

Version:

Common utils package to power communication with Solana contracts at deBridge

165 lines 5.45 kB
import BN from "bn.js"; import { hexToBuffer } from "./helpers"; /** * All functions are copied from ethers. This is optimization to reduce dependencies */ function concat(items) { const objects = items.map((item) => arrayify(item)); const length = objects.reduce((accum, item) => accum + item.length, 0); const result = new Uint8Array(length); objects.reduce((offset, object) => { result.set(object, offset); return offset + object.length; }, 0); return result; } function arrayify(value) { if (typeof value === "number") { const numValue = value; const result = []; while (value) { result.unshift(numValue & 0xff); value = parseInt(String(numValue / 256)); } if (result.length === 0) { result.push(0); } return new Uint8Array(result); } if (BN.isBN(value)) { value = `0x${value.toString("hex")}`; } if (typeof value === "string" && value.startsWith("0x")) { return hexToBuffer(value); } if (Array.isArray(value) || ArrayBuffer.isView(value)) { return new Uint8Array(value); } throw new Error(`unknown value: ${value}`); } // http://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array function toUtf8Bytes(str) { let result = []; for (let i = 0; i < str.length; i++) { const c = str.charCodeAt(i); if (c < 0x80) { result.push(c); } else if (c < 0x800) { result.push((c >> 6) | 0xc0); result.push((c & 0x3f) | 0x80); } else if ((c & 0xfc00) == 0xd800) { i++; const c2 = str.charCodeAt(i); if (i >= str.length || (c2 & 0xfc00) !== 0xdc00) { throw new Error("invalid utf-8 string"); } // Surrogate Pair const pair = 0x10000 + ((c & 0x03ff) << 10) + (c2 & 0x03ff); result.push((pair >> 18) | 0xf0); result.push(((pair >> 12) & 0x3f) | 0x80); result.push(((pair >> 6) & 0x3f) | 0x80); result.push((pair & 0x3f) | 0x80); } else { result.push((c >> 12) | 0xe0); result.push(((c >> 6) & 0x3f) | 0x80); result.push((c & 0x3f) | 0x80); } } return arrayify(result); } const zeroPad = (value, length) => { const bufValue = arrayify(value); if (bufValue.length > length) { throw new Error("value out of range"); } const result = new Uint8Array(length); result.set(bufValue, length - bufValue.length); return result; }; const isHexString = (data) => typeof data === "string" && data.startsWith("0x"); const _pack = (type, value, isArray) => { const regexBytes = new RegExp("^bytes([0-9]+)$"); const regexNumber = new RegExp("^(u?int)([0-9]*)$"); const regexArray = new RegExp("^(.*)\\[([0-9]*)\\]$"); const Zeros = "0000000000000000000000000000000000000000000000000000000000000000"; switch (type) { case "address": if (isArray) { return zeroPad(value, 32); } return arrayify(value); case "string": return toUtf8Bytes(value); case "bytes": return arrayify(value); case "bool": value = value ? "0x01" : "0x00"; if (isArray) { return zeroPad(value, 32); } return arrayify(value); } let match = type.match(regexNumber); if (match) { //let signed = (match[1] === "int") let size = parseInt(match[2] || "256"); if ((match[2] && String(size) !== match[2]) || size % 8 !== 0 || size === 0 || size > 256) { throw new Error("invalid number type"); } if (isArray) { size = 256; } if (isHexString(value)) { value = new BN(value.slice(2), "hex").toTwos(size); } else { value = new BN(value).toTwos(size); } return zeroPad(value, size / 8); } match = type.match(regexBytes); if (match) { const size = parseInt(match[1]); if (String(size) !== match[1] || size === 0 || size > 32) { throw new Error("invalid bytes type"); } if (arrayify(value).byteLength !== size) { throw new Error(`invalid value for ${type}`); } if (isArray) { return arrayify((value + Zeros).substring(0, 66)); } return value; } match = type.match(regexArray); if (match && Array.isArray(value)) { const baseType = match[1]; const count = parseInt(match[2] || String(value.length)); if (count != value.length) { throw new Error(`invalid array length for ${type}`); } const result = []; value.forEach(function (value) { result.push(_pack(baseType, value, true)); }); return concat(result); } throw new Error("invalid type"); }; export function solidityPack(types, values) { if (types.length != values.length) { throw new Error("types and values length mismatch"); } const tight = []; types.forEach((type, index) => { tight.push(_pack(type, values[index])); }); return concat(tight); } //# sourceMappingURL=solidityPack.js.map