@mysten/bcs
Advanced tools
Comparing version 0.0.0-experimental-20231204220336 to 0.0.0-experimental-20231205235231
# Change Log | ||
## 0.0.0-experimental-20231204220336 | ||
## 0.0.0-experimental-20231205235231 | ||
@@ -5,0 +5,0 @@ ### Minor Changes |
2014
dist/index.js
@@ -1,1992 +0,22 @@ | ||
"use strict"; | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __export = (target, all) => { | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __copyProps = (to, from, except, desc) => { | ||
if (from && typeof from === "object" || typeof from === "function") { | ||
for (let key of __getOwnPropNames(from)) | ||
if (!__hasOwnProp.call(to, key) && key !== except) | ||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
} | ||
return to; | ||
}; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
// If the importer is in node compatibility mode or this is not an ESM | ||
// file that has been converted to a CommonJS file using a Babel- | ||
// compatible transform (i.e. "__esModule" has not been set), then set | ||
// "default" to the CommonJS "module.exports" for node compatibility. | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
mod | ||
)); | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
var __accessCheck = (obj, member, msg) => { | ||
if (!member.has(obj)) | ||
throw TypeError("Cannot " + msg); | ||
}; | ||
var __privateGet = (obj, member, getter) => { | ||
__accessCheck(obj, member, "read from private field"); | ||
return getter ? getter.call(obj) : member.get(obj); | ||
}; | ||
var __privateAdd = (obj, member, value) => { | ||
if (member.has(obj)) | ||
throw TypeError("Cannot add the same private member more than once"); | ||
member instanceof WeakSet ? member.add(obj) : member.set(obj, value); | ||
}; | ||
var __privateSet = (obj, member, value, setter) => { | ||
__accessCheck(obj, member, "write to private field"); | ||
setter ? setter.call(obj, value) : member.set(obj, value); | ||
return value; | ||
}; | ||
// src/index.ts | ||
var src_exports = {}; | ||
__export(src_exports, { | ||
BCS: () => BCS, | ||
BcsReader: () => BcsReader, | ||
BcsType: () => BcsType, | ||
BcsWriter: () => BcsWriter, | ||
SerializedBcs: () => SerializedBcs, | ||
bcs: () => bcs, | ||
decodeStr: () => decodeStr, | ||
encodeStr: () => encodeStr, | ||
fromB58: () => fromB58, | ||
fromB64: () => fromB64, | ||
fromHEX: () => fromHEX, | ||
getRustConfig: () => getRustConfig, | ||
getSuiMoveConfig: () => getSuiMoveConfig, | ||
isSerializedBcs: () => isSerializedBcs, | ||
registerPrimitives: () => registerPrimitives, | ||
splitGenericParameters: () => splitGenericParameters, | ||
toB58: () => toB58, | ||
toB64: () => toB64, | ||
toHEX: () => toHEX | ||
}); | ||
module.exports = __toCommonJS(src_exports); | ||
// src/b58.ts | ||
var import_bs58 = __toESM(require("bs58")); | ||
var toB58 = (buffer) => import_bs58.default.encode(buffer); | ||
var fromB58 = (str) => import_bs58.default.decode(str); | ||
// src/b64.ts | ||
function b64ToUint6(nChr) { | ||
return nChr > 64 && nChr < 91 ? nChr - 65 : nChr > 96 && nChr < 123 ? nChr - 71 : nChr > 47 && nChr < 58 ? nChr + 4 : nChr === 43 ? 62 : nChr === 47 ? 63 : 0; | ||
} | ||
function fromB64(sBase64, nBlocksSize) { | ||
var sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, ""), nInLen = sB64Enc.length, nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen); | ||
for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { | ||
nMod4 = nInIdx & 3; | ||
nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 6 * (3 - nMod4); | ||
if (nMod4 === 3 || nInLen - nInIdx === 1) { | ||
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { | ||
taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; | ||
} | ||
nUint24 = 0; | ||
} | ||
} | ||
return taBytes; | ||
} | ||
function uint6ToB64(nUint6) { | ||
return nUint6 < 26 ? nUint6 + 65 : nUint6 < 52 ? nUint6 + 71 : nUint6 < 62 ? nUint6 - 4 : nUint6 === 62 ? 43 : nUint6 === 63 ? 47 : 65; | ||
} | ||
function toB64(aBytes) { | ||
var nMod3 = 2, sB64Enc = ""; | ||
for (var nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { | ||
nMod3 = nIdx % 3; | ||
nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24); | ||
if (nMod3 === 2 || aBytes.length - nIdx === 1) { | ||
sB64Enc += String.fromCodePoint( | ||
uint6ToB64(nUint24 >>> 18 & 63), | ||
uint6ToB64(nUint24 >>> 12 & 63), | ||
uint6ToB64(nUint24 >>> 6 & 63), | ||
uint6ToB64(nUint24 & 63) | ||
); | ||
nUint24 = 0; | ||
} | ||
} | ||
return sB64Enc.slice(0, sB64Enc.length - 2 + nMod3) + (nMod3 === 2 ? "" : nMod3 === 1 ? "=" : "=="); | ||
} | ||
// src/hex.ts | ||
function fromHEX(hexStr) { | ||
const normalized = hexStr.startsWith("0x") ? hexStr.slice(2) : hexStr; | ||
const padded = normalized.length % 2 === 0 ? normalized : `0${normalized}}`; | ||
const intArr = padded.match(/.{2}/g)?.map((byte) => parseInt(byte, 16)) ?? []; | ||
return Uint8Array.from(intArr); | ||
} | ||
function toHEX(bytes) { | ||
return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""); | ||
} | ||
// src/uleb.ts | ||
function ulebEncode(num) { | ||
let arr = []; | ||
let len = 0; | ||
if (num === 0) { | ||
return [0]; | ||
} | ||
while (num > 0) { | ||
arr[len] = num & 127; | ||
if (num >>= 7) { | ||
arr[len] |= 128; | ||
} | ||
len += 1; | ||
} | ||
return arr; | ||
} | ||
function ulebDecode(arr) { | ||
let total = 0; | ||
let shift = 0; | ||
let len = 0; | ||
while (true) { | ||
let byte = arr[len]; | ||
len += 1; | ||
total |= (byte & 127) << shift; | ||
if ((byte & 128) === 0) { | ||
break; | ||
} | ||
shift += 7; | ||
} | ||
return { | ||
value: total, | ||
length: len | ||
}; | ||
} | ||
// src/reader.ts | ||
var BcsReader = class { | ||
/** | ||
* @param {Uint8Array} data Data to use as a buffer. | ||
*/ | ||
constructor(data) { | ||
this.bytePosition = 0; | ||
this.dataView = new DataView(data.buffer); | ||
} | ||
/** | ||
* Shift current cursor position by `bytes`. | ||
* | ||
* @param {Number} bytes Number of bytes to | ||
* @returns {this} Self for possible chaining. | ||
*/ | ||
shift(bytes) { | ||
this.bytePosition += bytes; | ||
return this; | ||
} | ||
/** | ||
* Read U8 value from the buffer and shift cursor by 1. | ||
* @returns | ||
*/ | ||
read8() { | ||
let value = this.dataView.getUint8(this.bytePosition); | ||
this.shift(1); | ||
return value; | ||
} | ||
/** | ||
* Read U16 value from the buffer and shift cursor by 2. | ||
* @returns | ||
*/ | ||
read16() { | ||
let value = this.dataView.getUint16(this.bytePosition, true); | ||
this.shift(2); | ||
return value; | ||
} | ||
/** | ||
* Read U32 value from the buffer and shift cursor by 4. | ||
* @returns | ||
*/ | ||
read32() { | ||
let value = this.dataView.getUint32(this.bytePosition, true); | ||
this.shift(4); | ||
return value; | ||
} | ||
/** | ||
* Read U64 value from the buffer and shift cursor by 8. | ||
* @returns | ||
*/ | ||
read64() { | ||
let value1 = this.read32(); | ||
let value2 = this.read32(); | ||
let result = value2.toString(16) + value1.toString(16).padStart(8, "0"); | ||
return BigInt("0x" + result).toString(10); | ||
} | ||
/** | ||
* Read U128 value from the buffer and shift cursor by 16. | ||
*/ | ||
read128() { | ||
let value1 = BigInt(this.read64()); | ||
let value2 = BigInt(this.read64()); | ||
let result = value2.toString(16) + value1.toString(16).padStart(16, "0"); | ||
return BigInt("0x" + result).toString(10); | ||
} | ||
/** | ||
* Read U128 value from the buffer and shift cursor by 32. | ||
* @returns | ||
*/ | ||
read256() { | ||
let value1 = BigInt(this.read128()); | ||
let value2 = BigInt(this.read128()); | ||
let result = value2.toString(16) + value1.toString(16).padStart(32, "0"); | ||
return BigInt("0x" + result).toString(10); | ||
} | ||
/** | ||
* Read `num` number of bytes from the buffer and shift cursor by `num`. | ||
* @param num Number of bytes to read. | ||
*/ | ||
readBytes(num) { | ||
let start = this.bytePosition + this.dataView.byteOffset; | ||
let value = new Uint8Array(this.dataView.buffer, start, num); | ||
this.shift(num); | ||
return value; | ||
} | ||
/** | ||
* Read ULEB value - an integer of varying size. Used for enum indexes and | ||
* vector lengths. | ||
* @returns {Number} The ULEB value. | ||
*/ | ||
readULEB() { | ||
let start = this.bytePosition + this.dataView.byteOffset; | ||
let buffer = new Uint8Array(this.dataView.buffer, start); | ||
let { value, length } = ulebDecode(buffer); | ||
this.shift(length); | ||
return value; | ||
} | ||
/** | ||
* Read a BCS vector: read a length and then apply function `cb` X times | ||
* where X is the length of the vector, defined as ULEB in BCS bytes. | ||
* @param cb Callback to process elements of vector. | ||
* @returns {Array<Any>} Array of the resulting values, returned by callback. | ||
*/ | ||
readVec(cb) { | ||
let length = this.readULEB(); | ||
let result = []; | ||
for (let i = 0; i < length; i++) { | ||
result.push(cb(this, i, length)); | ||
} | ||
return result; | ||
} | ||
}; | ||
// src/utils.ts | ||
function encodeStr(data, encoding) { | ||
switch (encoding) { | ||
case "base58": | ||
return toB58(data); | ||
case "base64": | ||
return toB64(data); | ||
case "hex": | ||
return toHEX(data); | ||
default: | ||
throw new Error("Unsupported encoding, supported values are: base64, hex"); | ||
} | ||
} | ||
function decodeStr(data, encoding) { | ||
switch (encoding) { | ||
case "base58": | ||
return fromB58(data); | ||
case "base64": | ||
return fromB64(data); | ||
case "hex": | ||
return fromHEX(data); | ||
default: | ||
throw new Error("Unsupported encoding, supported values are: base64, hex"); | ||
} | ||
} | ||
function splitGenericParameters(str, genericSeparators = ["<", ">"]) { | ||
const [left, right] = genericSeparators; | ||
const tok = []; | ||
let word = ""; | ||
let nestedAngleBrackets = 0; | ||
for (let i = 0; i < str.length; i++) { | ||
const char = str[i]; | ||
if (char === left) { | ||
nestedAngleBrackets++; | ||
} | ||
if (char === right) { | ||
nestedAngleBrackets--; | ||
} | ||
if (nestedAngleBrackets === 0 && char === ",") { | ||
tok.push(word.trim()); | ||
word = ""; | ||
continue; | ||
} | ||
word += char; | ||
} | ||
tok.push(word.trim()); | ||
return tok; | ||
} | ||
// src/writer.ts | ||
var BcsWriter = class { | ||
constructor({ size = 1024, maxSize, allocateSize = 1024 } = {}) { | ||
this.bytePosition = 0; | ||
this.size = size; | ||
this.maxSize = maxSize || size; | ||
this.allocateSize = allocateSize; | ||
this.dataView = new DataView(new ArrayBuffer(size)); | ||
} | ||
ensureSizeOrGrow(bytes) { | ||
const requiredSize = this.bytePosition + bytes; | ||
if (requiredSize > this.size) { | ||
const nextSize = Math.min(this.maxSize, this.size + this.allocateSize); | ||
if (requiredSize > nextSize) { | ||
throw new Error( | ||
`Attempting to serialize to BCS, but buffer does not have enough size. Allocated size: ${this.size}, Max size: ${this.maxSize}, Required size: ${requiredSize}` | ||
); | ||
} | ||
this.size = nextSize; | ||
const nextBuffer = new ArrayBuffer(this.size); | ||
new Uint8Array(nextBuffer).set(new Uint8Array(this.dataView.buffer)); | ||
this.dataView = new DataView(nextBuffer); | ||
} | ||
} | ||
/** | ||
* Shift current cursor position by `bytes`. | ||
* | ||
* @param {Number} bytes Number of bytes to | ||
* @returns {this} Self for possible chaining. | ||
*/ | ||
shift(bytes) { | ||
this.bytePosition += bytes; | ||
return this; | ||
} | ||
/** | ||
* Write a U8 value into a buffer and shift cursor position by 1. | ||
* @param {Number} value Value to write. | ||
* @returns {this} | ||
*/ | ||
write8(value) { | ||
this.ensureSizeOrGrow(1); | ||
this.dataView.setUint8(this.bytePosition, Number(value)); | ||
return this.shift(1); | ||
} | ||
/** | ||
* Write a U16 value into a buffer and shift cursor position by 2. | ||
* @param {Number} value Value to write. | ||
* @returns {this} | ||
*/ | ||
write16(value) { | ||
this.ensureSizeOrGrow(2); | ||
this.dataView.setUint16(this.bytePosition, Number(value), true); | ||
return this.shift(2); | ||
} | ||
/** | ||
* Write a U32 value into a buffer and shift cursor position by 4. | ||
* @param {Number} value Value to write. | ||
* @returns {this} | ||
*/ | ||
write32(value) { | ||
this.ensureSizeOrGrow(4); | ||
this.dataView.setUint32(this.bytePosition, Number(value), true); | ||
return this.shift(4); | ||
} | ||
/** | ||
* Write a U64 value into a buffer and shift cursor position by 8. | ||
* @param {bigint} value Value to write. | ||
* @returns {this} | ||
*/ | ||
write64(value) { | ||
toLittleEndian(BigInt(value), 8).forEach((el) => this.write8(el)); | ||
return this; | ||
} | ||
/** | ||
* Write a U128 value into a buffer and shift cursor position by 16. | ||
* | ||
* @param {bigint} value Value to write. | ||
* @returns {this} | ||
*/ | ||
write128(value) { | ||
toLittleEndian(BigInt(value), 16).forEach((el) => this.write8(el)); | ||
return this; | ||
} | ||
/** | ||
* Write a U256 value into a buffer and shift cursor position by 16. | ||
* | ||
* @param {bigint} value Value to write. | ||
* @returns {this} | ||
*/ | ||
write256(value) { | ||
toLittleEndian(BigInt(value), 32).forEach((el) => this.write8(el)); | ||
return this; | ||
} | ||
/** | ||
* Write a ULEB value into a buffer and shift cursor position by number of bytes | ||
* written. | ||
* @param {Number} value Value to write. | ||
* @returns {this} | ||
*/ | ||
writeULEB(value) { | ||
ulebEncode(value).forEach((el) => this.write8(el)); | ||
return this; | ||
} | ||
/** | ||
* Write a vector into a buffer by first writing the vector length and then calling | ||
* a callback on each passed value. | ||
* | ||
* @param {Array<Any>} vector Array of elements to write. | ||
* @param {WriteVecCb} cb Callback to call on each element of the vector. | ||
* @returns {this} | ||
*/ | ||
writeVec(vector, cb) { | ||
this.writeULEB(vector.length); | ||
Array.from(vector).forEach((el, i) => cb(this, el, i, vector.length)); | ||
return this; | ||
} | ||
/** | ||
* Adds support for iterations over the object. | ||
* @returns {Uint8Array} | ||
*/ | ||
*[Symbol.iterator]() { | ||
for (let i = 0; i < this.bytePosition; i++) { | ||
yield this.dataView.getUint8(i); | ||
} | ||
return this.toBytes(); | ||
} | ||
/** | ||
* Get underlying buffer taking only value bytes (in case initial buffer size was bigger). | ||
* @returns {Uint8Array} Resulting bcs. | ||
*/ | ||
toBytes() { | ||
return new Uint8Array(this.dataView.buffer.slice(0, this.bytePosition)); | ||
} | ||
/** | ||
* Represent data as 'hex' or 'base64' | ||
* @param encoding Encoding to use: 'base64' or 'hex' | ||
*/ | ||
toString(encoding) { | ||
return encodeStr(this.toBytes(), encoding); | ||
} | ||
}; | ||
function toLittleEndian(bigint, size) { | ||
let result = new Uint8Array(size); | ||
let i = 0; | ||
while (bigint > 0) { | ||
result[i] = Number(bigint % BigInt(256)); | ||
bigint = bigint / BigInt(256); | ||
i += 1; | ||
} | ||
return result; | ||
} | ||
// src/bcs-type.ts | ||
var _write, _serialize; | ||
var _BcsType = class _BcsType { | ||
constructor(options) { | ||
__privateAdd(this, _write, void 0); | ||
__privateAdd(this, _serialize, void 0); | ||
this.name = options.name; | ||
this.read = options.read; | ||
this.serializedSize = options.serializedSize ?? (() => null); | ||
__privateSet(this, _write, options.write); | ||
__privateSet(this, _serialize, options.serialize ?? ((value, options2) => { | ||
const writer = new BcsWriter({ size: this.serializedSize(value) ?? void 0, ...options2 }); | ||
__privateGet(this, _write).call(this, value, writer); | ||
return writer.toBytes(); | ||
})); | ||
this.validate = options.validate ?? (() => { | ||
}); | ||
} | ||
write(value, writer) { | ||
this.validate(value); | ||
__privateGet(this, _write).call(this, value, writer); | ||
} | ||
serialize(value, options) { | ||
this.validate(value); | ||
return new SerializedBcs(this, __privateGet(this, _serialize).call(this, value, options)); | ||
} | ||
parse(bytes) { | ||
const reader = new BcsReader(bytes); | ||
return this.read(reader); | ||
} | ||
transform({ | ||
name, | ||
input, | ||
output | ||
}) { | ||
return new _BcsType({ | ||
name: name ?? this.name, | ||
read: (reader) => output(this.read(reader)), | ||
write: (value, writer) => __privateGet(this, _write).call(this, input(value), writer), | ||
serializedSize: (value) => this.serializedSize(input(value)), | ||
serialize: (value, options) => __privateGet(this, _serialize).call(this, input(value), options), | ||
validate: (value) => this.validate(input(value)) | ||
}); | ||
} | ||
}; | ||
_write = new WeakMap(); | ||
_serialize = new WeakMap(); | ||
var BcsType = _BcsType; | ||
var SERIALIZED_BCS_BRAND = Symbol.for("@mysten/serialized-bcs"); | ||
function isSerializedBcs(obj) { | ||
return !!obj && typeof obj === "object" && obj[SERIALIZED_BCS_BRAND] === true; | ||
} | ||
var _schema, _bytes; | ||
var SerializedBcs = class { | ||
constructor(type, schema) { | ||
__privateAdd(this, _schema, void 0); | ||
__privateAdd(this, _bytes, void 0); | ||
__privateSet(this, _schema, type); | ||
__privateSet(this, _bytes, schema); | ||
} | ||
// Used to brand SerializedBcs so that they can be identified, even between multiple copies | ||
// of the @mysten/bcs package are installed | ||
get [SERIALIZED_BCS_BRAND]() { | ||
return true; | ||
} | ||
toBytes() { | ||
return __privateGet(this, _bytes); | ||
} | ||
toHex() { | ||
return toHEX(__privateGet(this, _bytes)); | ||
} | ||
toBase64() { | ||
return toB64(__privateGet(this, _bytes)); | ||
} | ||
toBase58() { | ||
return toB58(__privateGet(this, _bytes)); | ||
} | ||
parse() { | ||
return __privateGet(this, _schema).parse(__privateGet(this, _bytes)); | ||
} | ||
}; | ||
_schema = new WeakMap(); | ||
_bytes = new WeakMap(); | ||
function fixedSizeBcsType({ | ||
size, | ||
...options | ||
}) { | ||
return new BcsType({ | ||
...options, | ||
serializedSize: () => size | ||
}); | ||
} | ||
function uIntBcsType({ | ||
readMethod, | ||
writeMethod, | ||
...options | ||
}) { | ||
return fixedSizeBcsType({ | ||
...options, | ||
read: (reader) => reader[readMethod](), | ||
write: (value, writer) => writer[writeMethod](value), | ||
validate: (value) => { | ||
if (value < 0 || value > options.maxValue) { | ||
throw new TypeError( | ||
`Invalid ${options.name} value: ${value}. Expected value in range 0-${options.maxValue}` | ||
); | ||
} | ||
options.validate?.(value); | ||
} | ||
}); | ||
} | ||
function bigUIntBcsType({ | ||
readMethod, | ||
writeMethod, | ||
...options | ||
}) { | ||
return fixedSizeBcsType({ | ||
...options, | ||
read: (reader) => reader[readMethod](), | ||
write: (value, writer) => writer[writeMethod](BigInt(value)), | ||
validate: (val) => { | ||
const value = BigInt(val); | ||
if (value < 0 || value > options.maxValue) { | ||
throw new TypeError( | ||
`Invalid ${options.name} value: ${value}. Expected value in range 0-${options.maxValue}` | ||
); | ||
} | ||
options.validate?.(value); | ||
} | ||
}); | ||
} | ||
function dynamicSizeBcsType({ | ||
serialize, | ||
...options | ||
}) { | ||
const type = new BcsType({ | ||
...options, | ||
serialize, | ||
write: (value, writer) => { | ||
for (const byte of type.serialize(value).toBytes()) { | ||
writer.write8(byte); | ||
} | ||
} | ||
}); | ||
return type; | ||
} | ||
function stringLikeBcsType({ | ||
toBytes, | ||
fromBytes, | ||
...options | ||
}) { | ||
return new BcsType({ | ||
...options, | ||
read: (reader) => { | ||
const length = reader.readULEB(); | ||
const bytes = reader.readBytes(length); | ||
return fromBytes(bytes); | ||
}, | ||
write: (hex, writer) => { | ||
const bytes = toBytes(hex); | ||
writer.writeULEB(bytes.length); | ||
for (let i = 0; i < bytes.length; i++) { | ||
writer.write8(bytes[i]); | ||
} | ||
}, | ||
serialize: (value) => { | ||
const bytes = toBytes(value); | ||
const size = ulebEncode(bytes.length); | ||
const result = new Uint8Array(size.length + bytes.length); | ||
result.set(size, 0); | ||
result.set(bytes, size.length); | ||
return result; | ||
}, | ||
validate: (value) => { | ||
if (typeof value !== "string") { | ||
throw new TypeError(`Invalid ${options.name} value: ${value}. Expected string`); | ||
} | ||
options.validate?.(value); | ||
} | ||
}); | ||
} | ||
function lazyBcsType(cb) { | ||
let lazyType = null; | ||
function getType() { | ||
if (!lazyType) { | ||
lazyType = cb(); | ||
} | ||
return lazyType; | ||
} | ||
return new BcsType({ | ||
name: "lazy", | ||
read: (data) => getType().read(data), | ||
serializedSize: (value) => getType().serializedSize(value), | ||
write: (value, writer) => getType().write(value, writer), | ||
serialize: (value, options) => getType().serialize(value, options).toBytes() | ||
}); | ||
} | ||
// src/bcs.ts | ||
var bcs = { | ||
/** | ||
* Creates a BcsType that can be used to read and write an 8-bit unsigned integer. | ||
* @example | ||
* bcs.u8().serialize(255).toBytes() // Uint8Array [ 255 ] | ||
*/ | ||
u8(options) { | ||
return uIntBcsType({ | ||
name: "u8", | ||
readMethod: "read8", | ||
writeMethod: "write8", | ||
size: 1, | ||
maxValue: 2 ** 8 - 1, | ||
...options | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType that can be used to read and write a 16-bit unsigned integer. | ||
* @example | ||
* bcs.u16().serialize(65535).toBytes() // Uint8Array [ 255, 255 ] | ||
*/ | ||
u16(options) { | ||
return uIntBcsType({ | ||
name: "u16", | ||
readMethod: "read16", | ||
writeMethod: "write16", | ||
size: 2, | ||
maxValue: 2 ** 16 - 1, | ||
...options | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType that can be used to read and write a 32-bit unsigned integer. | ||
* @example | ||
* bcs.u32().serialize(4294967295).toBytes() // Uint8Array [ 255, 255, 255, 255 ] | ||
*/ | ||
u32(options) { | ||
return uIntBcsType({ | ||
name: "u32", | ||
readMethod: "read32", | ||
writeMethod: "write32", | ||
size: 4, | ||
maxValue: 2 ** 32 - 1, | ||
...options | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType that can be used to read and write a 64-bit unsigned integer. | ||
* @example | ||
* bcs.u64().serialize(1).toBytes() // Uint8Array [ 1, 0, 0, 0, 0, 0, 0, 0 ] | ||
*/ | ||
u64(options) { | ||
return bigUIntBcsType({ | ||
name: "u64", | ||
readMethod: "read64", | ||
writeMethod: "write64", | ||
size: 8, | ||
maxValue: 2n ** 64n - 1n, | ||
...options | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType that can be used to read and write a 128-bit unsigned integer. | ||
* @example | ||
* bcs.u128().serialize(1).toBytes() // Uint8Array [ 1, ..., 0 ] | ||
*/ | ||
u128(options) { | ||
return bigUIntBcsType({ | ||
name: "u128", | ||
readMethod: "read128", | ||
writeMethod: "write128", | ||
size: 16, | ||
maxValue: 2n ** 128n - 1n, | ||
...options | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType that can be used to read and write a 256-bit unsigned integer. | ||
* @example | ||
* bcs.u256().serialize(1).toBytes() // Uint8Array [ 1, ..., 0 ] | ||
*/ | ||
u256(options) { | ||
return bigUIntBcsType({ | ||
name: "u256", | ||
readMethod: "read256", | ||
writeMethod: "write256", | ||
size: 32, | ||
maxValue: 2n ** 256n - 1n, | ||
...options | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType that can be used to read and write boolean values. | ||
* @example | ||
* bcs.bool().serialize(true).toBytes() // Uint8Array [ 1 ] | ||
*/ | ||
bool(options) { | ||
return fixedSizeBcsType({ | ||
name: "bool", | ||
size: 1, | ||
read: (reader) => reader.read8() === 1, | ||
write: (value, writer) => writer.write8(value ? 1 : 0), | ||
...options, | ||
validate: (value) => { | ||
options?.validate?.(value); | ||
if (typeof value !== "boolean") { | ||
throw new TypeError(`Expected boolean, found ${typeof value}`); | ||
} | ||
} | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType that can be used to read and write unsigned LEB encoded integers | ||
* @example | ||
* | ||
*/ | ||
uleb128(options) { | ||
return dynamicSizeBcsType({ | ||
name: "uleb128", | ||
read: (reader) => reader.readULEB(), | ||
serialize: (value) => { | ||
return Uint8Array.from(ulebEncode(value)); | ||
}, | ||
...options | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType representing a fixed length byte array | ||
* @param size The number of bytes this types represents | ||
* @example | ||
* bcs.bytes(3).serialize(new Uint8Array([1, 2, 3])).toBytes() // Uint8Array [1, 2, 3] | ||
*/ | ||
bytes(size, options) { | ||
return fixedSizeBcsType({ | ||
name: `bytes[${size}]`, | ||
size, | ||
read: (reader) => reader.readBytes(size), | ||
write: (value, writer) => { | ||
for (let i = 0; i < size; i++) { | ||
writer.write8(value[i] ?? 0); | ||
} | ||
}, | ||
...options, | ||
validate: (value) => { | ||
options?.validate?.(value); | ||
if (!("length" in value)) { | ||
throw new TypeError(`Expected array, found ${typeof value}`); | ||
} | ||
if (value.length !== size) { | ||
throw new TypeError(`Expected array of length ${size}, found ${value.length}`); | ||
} | ||
} | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType that can ser/de string values. Strings will be UTF-8 encoded | ||
* @example | ||
* bcs.string().serialize('a').toBytes() // Uint8Array [ 1, 97 ] | ||
*/ | ||
string(options) { | ||
return stringLikeBcsType({ | ||
name: "string", | ||
toBytes: (value) => new TextEncoder().encode(value), | ||
fromBytes: (bytes) => new TextDecoder().decode(bytes), | ||
...options | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType that represents a fixed length array of a given type | ||
* @param size The number of elements in the array | ||
* @param type The BcsType of each element in the array | ||
* @example | ||
* bcs.fixedArray(3, bcs.u8()).serialize([1, 2, 3]).toBytes() // Uint8Array [ 1, 2, 3 ] | ||
*/ | ||
fixedArray(size, type, options) { | ||
return new BcsType({ | ||
name: `${type.name}[${size}]`, | ||
read: (reader) => { | ||
const result = new Array(size); | ||
for (let i = 0; i < size; i++) { | ||
result[i] = type.read(reader); | ||
} | ||
return result; | ||
}, | ||
write: (value, writer) => { | ||
for (const item of value) { | ||
type.write(item, writer); | ||
} | ||
}, | ||
...options, | ||
validate: (value) => { | ||
options?.validate?.(value); | ||
if (!("length" in value)) { | ||
throw new TypeError(`Expected array, found ${typeof value}`); | ||
} | ||
if (value.length !== size) { | ||
throw new TypeError(`Expected array of length ${size}, found ${value.length}`); | ||
} | ||
} | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType representing an optional value | ||
* @param type The BcsType of the optional value | ||
* @example | ||
* bcs.option(bcs.u8()).serialize(null).toBytes() // Uint8Array [ 0 ] | ||
* bcs.option(bcs.u8()).serialize(1).toBytes() // Uint8Array [ 1, 1 ] | ||
*/ | ||
option(type) { | ||
return bcs.enum(`Option<${type.name}>`, { | ||
None: null, | ||
Some: type | ||
}).transform({ | ||
input: (value) => { | ||
if (value == null) { | ||
return { None: true }; | ||
} | ||
return { Some: value }; | ||
}, | ||
output: (value) => { | ||
if ("Some" in value) { | ||
return value.Some; | ||
} | ||
return null; | ||
} | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType representing a variable length vector of a given type | ||
* @param type The BcsType of each element in the vector | ||
* | ||
* @example | ||
* bcs.vector(bcs.u8()).toBytes([1, 2, 3]) // Uint8Array [ 3, 1, 2, 3 ] | ||
*/ | ||
vector(type, options) { | ||
return new BcsType({ | ||
name: `vector<${type.name}>`, | ||
read: (reader) => { | ||
const length = reader.readULEB(); | ||
const result = new Array(length); | ||
for (let i = 0; i < length; i++) { | ||
result[i] = type.read(reader); | ||
} | ||
return result; | ||
}, | ||
write: (value, writer) => { | ||
writer.writeULEB(value.length); | ||
for (const item of value) { | ||
type.write(item, writer); | ||
} | ||
}, | ||
...options, | ||
validate: (value) => { | ||
options?.validate?.(value); | ||
if (!("length" in value)) { | ||
throw new TypeError(`Expected array, found ${typeof value}`); | ||
} | ||
} | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType representing a tuple of a given set of types | ||
* @param types The BcsTypes for each element in the tuple | ||
* | ||
* @example | ||
* const tuple = bcs.tuple([bcs.u8(), bcs.string(), bcs.bool()]) | ||
* tuple.serialize([1, 'a', true]).toBytes() // Uint8Array [ 1, 1, 97, 1 ] | ||
*/ | ||
tuple(types, options) { | ||
return new BcsType({ | ||
name: `(${types.map((t) => t.name).join(", ")})`, | ||
serializedSize: (values) => { | ||
let total = 0; | ||
for (let i = 0; i < types.length; i++) { | ||
const size = types[i].serializedSize(values[i]); | ||
if (size == null) { | ||
return null; | ||
} | ||
total += size; | ||
} | ||
return total; | ||
}, | ||
read: (reader) => { | ||
const result = []; | ||
for (const type of types) { | ||
result.push(type.read(reader)); | ||
} | ||
return result; | ||
}, | ||
write: (value, writer) => { | ||
for (let i = 0; i < types.length; i++) { | ||
types[i].write(value[i], writer); | ||
} | ||
}, | ||
...options, | ||
validate: (value) => { | ||
options?.validate?.(value); | ||
if (!Array.isArray(value)) { | ||
throw new TypeError(`Expected array, found ${typeof value}`); | ||
} | ||
if (value.length !== types.length) { | ||
throw new TypeError(`Expected array of length ${types.length}, found ${value.length}`); | ||
} | ||
} | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType representing a struct of a given set of fields | ||
* @param name The name of the struct | ||
* @param fields The fields of the struct. The order of the fields affects how data is serialized and deserialized | ||
* | ||
* @example | ||
* const struct = bcs.struct('MyStruct', { | ||
* a: bcs.u8(), | ||
* b: bcs.string(), | ||
* }) | ||
* struct.serialize({ a: 1, b: 'a' }).toBytes() // Uint8Array [ 1, 1, 97 ] | ||
*/ | ||
struct(name, fields, options) { | ||
const canonicalOrder = Object.entries(fields); | ||
return new BcsType({ | ||
name, | ||
serializedSize: (values) => { | ||
let total = 0; | ||
for (const [field, type] of canonicalOrder) { | ||
const size = type.serializedSize(values[field]); | ||
if (size == null) { | ||
return null; | ||
} | ||
total += size; | ||
} | ||
return total; | ||
}, | ||
read: (reader) => { | ||
const result = {}; | ||
for (const [field, type] of canonicalOrder) { | ||
result[field] = type.read(reader); | ||
} | ||
return result; | ||
}, | ||
write: (value, writer) => { | ||
for (const [field, type] of canonicalOrder) { | ||
type.write(value[field], writer); | ||
} | ||
}, | ||
...options, | ||
validate: (value) => { | ||
options?.validate?.(value); | ||
if (typeof value !== "object" || value == null) { | ||
throw new TypeError(`Expected object, found ${typeof value}`); | ||
} | ||
} | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType representing an enum of a given set of options | ||
* @param name The name of the enum | ||
* @param values The values of the enum. The order of the values affects how data is serialized and deserialized. | ||
* null can be used to represent a variant with no data. | ||
* | ||
* @example | ||
* const enum = bcs.enum('MyEnum', { | ||
* A: bcs.u8(), | ||
* B: bcs.string(), | ||
* C: null, | ||
* }) | ||
* enum.serialize({ A: 1 }).toBytes() // Uint8Array [ 0, 1 ] | ||
* enum.serialize({ B: 'a' }).toBytes() // Uint8Array [ 1, 1, 97 ] | ||
* enum.serialize({ C: true }).toBytes() // Uint8Array [ 2 ] | ||
*/ | ||
enum(name, values, options) { | ||
const canonicalOrder = Object.entries(values); | ||
return new BcsType({ | ||
name, | ||
read: (reader) => { | ||
const index = reader.readULEB(); | ||
const [name2, type] = canonicalOrder[index]; | ||
return { | ||
[name2]: type?.read(reader) ?? true | ||
}; | ||
}, | ||
write: (value, writer) => { | ||
const [name2, val] = Object.entries(value)[0]; | ||
for (let i = 0; i < canonicalOrder.length; i++) { | ||
const [optionName, optionType] = canonicalOrder[i]; | ||
if (optionName === name2) { | ||
writer.writeULEB(i); | ||
optionType?.write(val, writer); | ||
return; | ||
} | ||
} | ||
}, | ||
...options, | ||
validate: (value) => { | ||
options?.validate?.(value); | ||
if (typeof value !== "object" || value == null) { | ||
throw new TypeError(`Expected object, found ${typeof value}`); | ||
} | ||
const keys = Object.keys(value); | ||
if (keys.length !== 1) { | ||
throw new TypeError(`Expected object with one key, found ${keys.length}`); | ||
} | ||
const [name2] = keys; | ||
if (!Object.hasOwn(values, name2)) { | ||
throw new TypeError(`Invalid enum variant ${name2}`); | ||
} | ||
} | ||
}); | ||
}, | ||
/** | ||
* Creates a BcsType representing a map of a given key and value type | ||
* @param keyType The BcsType of the key | ||
* @param valueType The BcsType of the value | ||
* @example | ||
* const map = bcs.map(bcs.u8(), bcs.string()) | ||
* map.serialize(new Map([[2, 'a']])).toBytes() // Uint8Array [ 1, 2, 1, 97 ] | ||
*/ | ||
map(keyType, valueType) { | ||
return bcs.vector(bcs.tuple([keyType, valueType])).transform({ | ||
name: `Map<${keyType.name}, ${valueType.name}>`, | ||
input: (value) => { | ||
return [...value.entries()]; | ||
}, | ||
output: (value) => { | ||
const result = /* @__PURE__ */ new Map(); | ||
for (const [key, val] of value) { | ||
result.set(key, val); | ||
} | ||
return result; | ||
} | ||
}); | ||
}, | ||
/** | ||
* @deprecated | ||
* | ||
* Generics should be implemented as generic typescript functions instead: | ||
* | ||
* ```ts | ||
* function VecMap<K, V>, (K: BcsType<K>, V: BcsType<V>) { | ||
* return bcs.struct('VecMap<K, V>', { | ||
* keys: bcs.vector(K), | ||
* values: bcs.vector(V), | ||
* }) | ||
* } | ||
* ``` | ||
*/ | ||
generic(names, cb) { | ||
return (...types) => { | ||
return cb(...types).transform({ | ||
name: `${cb.name}<${types.map((t) => t.name).join(", ")}>`, | ||
input: (value) => value, | ||
output: (value) => value | ||
}); | ||
}; | ||
}, | ||
/** | ||
* Creates a BcsType that wraps another BcsType which is lazily evaluated. This is useful for creating recursive types. | ||
* @param cb A callback that returns the BcsType | ||
*/ | ||
lazy(cb) { | ||
return lazyBcsType(cb); | ||
} | ||
}; | ||
// src/legacy-registry.ts | ||
var SUI_ADDRESS_LENGTH = 32; | ||
var _BCS = class _BCS { | ||
/** | ||
* Construct a BCS instance with a prepared schema. | ||
* | ||
* @param schema A prepared schema with type definitions | ||
* @param withPrimitives Whether to register primitive types by default | ||
*/ | ||
constructor(schema) { | ||
/** | ||
* Map of kind `TypeName => TypeInterface`. Holds all | ||
* callbacks for (de)serialization of every registered type. | ||
* | ||
* If the value stored is a string, it is treated as an alias. | ||
*/ | ||
this.types = /* @__PURE__ */ new Map(); | ||
/** | ||
* Count temp keys to generate a new one when requested. | ||
*/ | ||
this.counter = 0; | ||
if (schema instanceof _BCS) { | ||
this.schema = schema.schema; | ||
this.types = new Map(schema.types); | ||
return; | ||
} | ||
this.schema = schema; | ||
this.registerAddressType(_BCS.ADDRESS, schema.addressLength, schema.addressEncoding); | ||
this.registerVectorType(schema.vectorType); | ||
if (schema.types && schema.types.structs) { | ||
for (let name of Object.keys(schema.types.structs)) { | ||
this.registerStructType(name, schema.types.structs[name]); | ||
} | ||
} | ||
if (schema.types && schema.types.enums) { | ||
for (let name of Object.keys(schema.types.enums)) { | ||
this.registerEnumType(name, schema.types.enums[name]); | ||
} | ||
} | ||
if (schema.types && schema.types.aliases) { | ||
for (let name of Object.keys(schema.types.aliases)) { | ||
this.registerAlias(name, schema.types.aliases[name]); | ||
} | ||
} | ||
if (schema.withPrimitives !== false) { | ||
registerPrimitives(this); | ||
} | ||
} | ||
/** | ||
* Name of the key to use for temporary struct definitions. | ||
* Returns a temp key + index (for a case when multiple temp | ||
* structs are processed). | ||
*/ | ||
tempKey() { | ||
return `bcs-struct-${++this.counter}`; | ||
} | ||
/** | ||
* Serialize data into bcs. | ||
* | ||
* @example | ||
* bcs.registerVectorType('vector<u8>', 'u8'); | ||
* | ||
* let serialized = BCS | ||
* .set('vector<u8>', [1,2,3,4,5,6]) | ||
* .toBytes(); | ||
* | ||
* console.assert(toHex(serialized) === '06010203040506'); | ||
* | ||
* @param type Name of the type to serialize (must be registered) or a struct type. | ||
* @param data Data to serialize. | ||
* @param size Serialization buffer size. Default 1024 = 1KB. | ||
* @return A BCS reader instance. Usually you'd want to call `.toBytes()` | ||
*/ | ||
ser(type, data, options) { | ||
if (typeof type === "string" || Array.isArray(type)) { | ||
const { name, params } = this.parseTypeName(type); | ||
return this.getTypeInterface(name).encode(this, data, options, params); | ||
} | ||
if (typeof type === "object") { | ||
const key = this.tempKey(); | ||
const temp = new _BCS(this); | ||
return temp.registerStructType(key, type).ser(key, data, options); | ||
} | ||
throw new Error(`Incorrect type passed into the '.ser()' function. | ||
${JSON.stringify(type)}`); | ||
} | ||
/** | ||
* Deserialize BCS into a JS type. | ||
* | ||
* @example | ||
* let num = bcs.ser('u64', '4294967295').toString('hex'); | ||
* let deNum = bcs.de('u64', num, 'hex'); | ||
* console.assert(deNum.toString(10) === '4294967295'); | ||
* | ||
* @param type Name of the type to deserialize (must be registered) or a struct type definition. | ||
* @param data Data to deserialize. | ||
* @param encoding Optional - encoding to use if data is of type String | ||
* @return Deserialized data. | ||
*/ | ||
de(type, data, encoding) { | ||
if (typeof data === "string") { | ||
if (encoding) { | ||
data = decodeStr(data, encoding); | ||
} else { | ||
throw new Error("To pass a string to `bcs.de`, specify encoding"); | ||
} | ||
} | ||
if (typeof type === "string" || Array.isArray(type)) { | ||
const { name, params } = this.parseTypeName(type); | ||
return this.getTypeInterface(name).decode(this, data, params); | ||
} | ||
if (typeof type === "object") { | ||
const temp = new _BCS(this); | ||
const key = this.tempKey(); | ||
return temp.registerStructType(key, type).de(key, data, encoding); | ||
} | ||
throw new Error(`Incorrect type passed into the '.de()' function. | ||
${JSON.stringify(type)}`); | ||
} | ||
/** | ||
* Check whether a `TypeInterface` has been loaded for a `type`. | ||
* @param type Name of the type to check. | ||
* @returns | ||
*/ | ||
hasType(type) { | ||
return this.types.has(type); | ||
} | ||
/** | ||
* Create an alias for a type. | ||
* WARNING: this can potentially lead to recursion | ||
* @param name Alias to use | ||
* @param forType Type to reference | ||
* @returns | ||
* | ||
* @example | ||
* ``` | ||
* let bcs = new BCS(getSuiMoveConfig()); | ||
* bcs.registerAlias('ObjectDigest', BCS.BASE58); | ||
* let b58_digest = bcs.de('ObjectDigest', '<digest_bytes>', 'base64'); | ||
* ``` | ||
*/ | ||
registerAlias(name, forType) { | ||
this.types.set(name, forType); | ||
return this; | ||
} | ||
/** | ||
* Method to register new types for BCS internal representation. | ||
* For each registered type 2 callbacks must be specified and one is optional: | ||
* | ||
* - encodeCb(writer, data) - write a way to serialize data with BcsWriter; | ||
* - decodeCb(reader) - write a way to deserialize data with BcsReader; | ||
* - validateCb(data) - validate data - either return bool or throw an error | ||
* | ||
* @example | ||
* // our type would be a string that consists only of numbers | ||
* bcs.registerType('number_string', | ||
* (writer, data) => writer.writeVec(data, (w, el) => w.write8(el)), | ||
* (reader) => reader.readVec((r) => r.read8()).join(''), // read each value as u8 | ||
* (value) => /[0-9]+/.test(value) // test that it has at least one digit | ||
* ); | ||
* console.log(Array.from(bcs.ser('number_string', '12345').toBytes()) == [5,1,2,3,4,5]); | ||
* | ||
* @param name | ||
* @param encodeCb Callback to encode a value. | ||
* @param decodeCb Callback to decode a value. | ||
* @param validateCb Optional validator Callback to check type before serialization. | ||
*/ | ||
registerType(typeName, encodeCb, decodeCb, validateCb = () => true) { | ||
const { name, params: generics } = this.parseTypeName(typeName); | ||
this.types.set(name, { | ||
encode(self, data, options, typeParams) { | ||
const typeMap = generics.reduce((acc, value, index) => { | ||
return Object.assign(acc, { [value]: typeParams[index] }); | ||
}, {}); | ||
return this._encodeRaw.call(self, new BcsWriter(options), data, typeParams, typeMap); | ||
}, | ||
decode(self, data, typeParams) { | ||
const typeMap = generics.reduce((acc, value, index) => { | ||
return Object.assign(acc, { [value]: typeParams[index] }); | ||
}, {}); | ||
return this._decodeRaw.call(self, new BcsReader(data), typeParams, typeMap); | ||
}, | ||
// these methods should always be used with caution as they require pre-defined | ||
// reader and writer and mainly exist to allow multi-field (de)serialization; | ||
_encodeRaw(writer, data, typeParams, typeMap) { | ||
if (validateCb(data)) { | ||
return encodeCb.call(this, writer, data, typeParams, typeMap); | ||
} else { | ||
throw new Error(`Validation failed for type ${name}, data: ${data}`); | ||
} | ||
}, | ||
_decodeRaw(reader, typeParams, typeMap) { | ||
return decodeCb.call(this, reader, typeParams, typeMap); | ||
} | ||
}); | ||
return this; | ||
} | ||
/** | ||
* Method to register BcsType instances to the registry | ||
* Types are registered with a callback that provides BcsType instances for each generic | ||
* passed to the type. | ||
* | ||
* - createType(...generics) - Return a BcsType instance | ||
* | ||
* @example | ||
* // our type would be a string that consists only of numbers | ||
* bcs.registerType('Box<T>', (T) => { | ||
* return bcs.struct({ | ||
* value: T | ||
* }); | ||
* }); | ||
* console.log(Array.from(bcs.ser('Box<string>', '12345').toBytes()) == [5,1,2,3,4,5]); | ||
* | ||
* @param name | ||
* @param createType a Callback to create the BcsType with any passed in generics | ||
*/ | ||
registerBcsType(typeName, createType) { | ||
this.registerType( | ||
typeName, | ||
(writer, data, typeParams) => { | ||
const generics = typeParams.map( | ||
(param) => new BcsType({ | ||
name: String(param), | ||
write: (data2, writer2) => { | ||
const { name, params } = this.parseTypeName(param); | ||
const typeInterface = this.getTypeInterface(name); | ||
const typeMap = params.reduce((acc, value, index) => { | ||
return Object.assign(acc, { [value]: typeParams[index] }); | ||
}, {}); | ||
return typeInterface._encodeRaw.call(this, writer2, data2, params, typeMap); | ||
}, | ||
read: () => { | ||
throw new Error("Not implemented"); | ||
} | ||
}) | ||
); | ||
createType(...generics).write(data, writer); | ||
return writer; | ||
}, | ||
(reader, typeParams) => { | ||
const generics = typeParams.map( | ||
(param) => new BcsType({ | ||
name: String(param), | ||
write: (data, writer) => { | ||
throw new Error("Not implemented"); | ||
}, | ||
read: (reader2) => { | ||
const { name, params } = this.parseTypeName(param); | ||
const typeInterface = this.getTypeInterface(name); | ||
const typeMap = params.reduce((acc, value, index) => { | ||
return Object.assign(acc, { [value]: typeParams[index] }); | ||
}, {}); | ||
return typeInterface._decodeRaw.call(this, reader2, params, typeMap); | ||
} | ||
}) | ||
); | ||
return createType(...generics).read(reader); | ||
} | ||
); | ||
return this; | ||
} | ||
/** | ||
* Register an address type which is a sequence of U8s of specified length. | ||
* @example | ||
* bcs.registerAddressType('address', SUI_ADDRESS_LENGTH); | ||
* let addr = bcs.de('address', 'c3aca510c785c7094ac99aeaa1e69d493122444df50bb8a99dfa790c654a79af'); | ||
* | ||
* @param name Name of the address type. | ||
* @param length Byte length of the address. | ||
* @param encoding Encoding to use for the address type | ||
* @returns | ||
*/ | ||
registerAddressType(name, length, encoding = "hex") { | ||
switch (encoding) { | ||
case "base64": | ||
return this.registerType( | ||
name, | ||
function encodeAddress(writer, data) { | ||
return fromB64(data).reduce((writer2, el) => writer2.write8(el), writer); | ||
}, | ||
function decodeAddress(reader) { | ||
return toB64(reader.readBytes(length)); | ||
} | ||
); | ||
case "hex": | ||
return this.registerType( | ||
name, | ||
function encodeAddress(writer, data) { | ||
return fromHEX(data).reduce((writer2, el) => writer2.write8(el), writer); | ||
}, | ||
function decodeAddress(reader) { | ||
return toHEX(reader.readBytes(length)); | ||
} | ||
); | ||
default: | ||
throw new Error("Unsupported encoding! Use either hex or base64"); | ||
} | ||
} | ||
/** | ||
* Register custom vector type inside the bcs. | ||
* | ||
* @example | ||
* bcs.registerVectorType('vector<T>'); // generic registration | ||
* let array = bcs.de('vector<u8>', '06010203040506', 'hex'); // [1,2,3,4,5,6]; | ||
* let again = bcs.ser('vector<u8>', [1,2,3,4,5,6]).toString('hex'); | ||
* | ||
* @param name Name of the type to register | ||
* @param elementType Optional name of the inner type of the vector | ||
* @return Returns self for chaining. | ||
*/ | ||
registerVectorType(typeName) { | ||
let { name, params } = this.parseTypeName(typeName); | ||
if (params.length > 1) { | ||
throw new Error("Vector can have only one type parameter; got " + name); | ||
} | ||
return this.registerType( | ||
typeName, | ||
function encodeVector(writer, data, typeParams, typeMap) { | ||
return writer.writeVec(data, (writer2, el) => { | ||
let elementType = typeParams[0]; | ||
if (!elementType) { | ||
throw new Error(`Incorrect number of type parameters passed a to vector '${typeName}'`); | ||
} | ||
let { name: name2, params: params2 } = this.parseTypeName(elementType); | ||
if (this.hasType(name2)) { | ||
return this.getTypeInterface(name2)._encodeRaw.call(this, writer2, el, params2, typeMap); | ||
} | ||
if (!(name2 in typeMap)) { | ||
throw new Error( | ||
`Unable to find a matching type definition for ${name2} in vector; make sure you passed a generic` | ||
); | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name2]); | ||
return this.getTypeInterface(innerName)._encodeRaw.call( | ||
this, | ||
writer2, | ||
el, | ||
innerParams, | ||
typeMap | ||
); | ||
}); | ||
}, | ||
function decodeVector(reader, typeParams, typeMap) { | ||
return reader.readVec((reader2) => { | ||
let elementType = typeParams[0]; | ||
if (!elementType) { | ||
throw new Error(`Incorrect number of type parameters passed to a vector '${typeName}'`); | ||
} | ||
let { name: name2, params: params2 } = this.parseTypeName(elementType); | ||
if (this.hasType(name2)) { | ||
return this.getTypeInterface(name2)._decodeRaw.call(this, reader2, params2, typeMap); | ||
} | ||
if (!(name2 in typeMap)) { | ||
throw new Error( | ||
`Unable to find a matching type definition for ${name2} in vector; make sure you passed a generic` | ||
); | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name2]); | ||
return this.getTypeInterface(innerName)._decodeRaw.call( | ||
this, | ||
reader2, | ||
innerParams, | ||
typeMap | ||
); | ||
}); | ||
} | ||
); | ||
} | ||
/** | ||
* Safe method to register a custom Move struct. The first argument is a name of the | ||
* struct which is only used on the FrontEnd and has no affect on serialization results, | ||
* and the second is a struct description passed as an Object. | ||
* | ||
* The description object MUST have the same order on all of the platforms (ie in Move | ||
* or in Rust). | ||
* | ||
* @example | ||
* // Move / Rust struct | ||
* // struct Coin { | ||
* // value: u64, | ||
* // owner: vector<u8>, // name // Vec<u8> in Rust | ||
* // is_locked: bool, | ||
* // } | ||
* | ||
* bcs.registerStructType('Coin', { | ||
* value: bcs.U64, | ||
* owner: bcs.STRING, | ||
* is_locked: bcs.BOOL | ||
* }); | ||
* | ||
* // Created in Rust with diem/bcs | ||
* // let rust_bcs_str = '80d1b105600000000e4269672057616c6c65742047757900'; | ||
* let rust_bcs_str = [ // using an Array here as BCS works with Uint8Array | ||
* 128, 209, 177, 5, 96, 0, 0, | ||
* 0, 14, 66, 105, 103, 32, 87, | ||
* 97, 108, 108, 101, 116, 32, 71, | ||
* 117, 121, 0 | ||
* ]; | ||
* | ||
* // Let's encode the value as well | ||
* let test_set = bcs.ser('Coin', { | ||
* owner: 'Big Wallet Guy', | ||
* value: '412412400000', | ||
* is_locked: false, | ||
* }); | ||
* | ||
* console.assert(Array.from(test_set.toBytes()) === rust_bcs_str, 'Whoopsie, result mismatch'); | ||
* | ||
* @param name Name of the type to register. | ||
* @param fields Fields of the struct. Must be in the correct order. | ||
* @return Returns BCS for chaining. | ||
*/ | ||
registerStructType(typeName, fields) { | ||
for (let key in fields) { | ||
let internalName = this.tempKey(); | ||
let value = fields[key]; | ||
if (!Array.isArray(value) && typeof value !== "string") { | ||
fields[key] = internalName; | ||
this.registerStructType(internalName, value); | ||
} | ||
} | ||
let struct = Object.freeze(fields); | ||
let canonicalOrder = Object.keys(struct); | ||
let { name: structName, params: generics } = this.parseTypeName(typeName); | ||
return this.registerType( | ||
typeName, | ||
function encodeStruct(writer, data, typeParams, typeMap) { | ||
if (!data || data.constructor !== Object) { | ||
throw new Error(`Expected ${structName} to be an Object, got: ${data}`); | ||
} | ||
if (typeParams.length !== generics.length) { | ||
throw new Error( | ||
`Incorrect number of generic parameters passed; expected: ${generics.length}, got: ${typeParams.length}` | ||
); | ||
} | ||
for (let key of canonicalOrder) { | ||
if (!(key in data)) { | ||
throw new Error(`Struct ${structName} requires field ${key}:${struct[key]}`); | ||
} | ||
const { name: fieldType, params: fieldParams } = this.parseTypeName( | ||
struct[key] | ||
); | ||
if (!generics.includes(fieldType)) { | ||
this.getTypeInterface(fieldType)._encodeRaw.call( | ||
this, | ||
writer, | ||
data[key], | ||
fieldParams, | ||
typeMap | ||
); | ||
} else { | ||
const paramIdx = generics.indexOf(fieldType); | ||
let { name, params } = this.parseTypeName(typeParams[paramIdx]); | ||
if (this.hasType(name)) { | ||
this.getTypeInterface(name)._encodeRaw.call( | ||
this, | ||
writer, | ||
data[key], | ||
params, | ||
typeMap | ||
); | ||
continue; | ||
} | ||
if (!(name in typeMap)) { | ||
throw new Error( | ||
`Unable to find a matching type definition for ${name} in ${structName}; make sure you passed a generic` | ||
); | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name]); | ||
this.getTypeInterface(innerName)._encodeRaw.call( | ||
this, | ||
writer, | ||
data[key], | ||
innerParams, | ||
typeMap | ||
); | ||
} | ||
} | ||
return writer; | ||
}, | ||
function decodeStruct(reader, typeParams, typeMap) { | ||
if (typeParams.length !== generics.length) { | ||
throw new Error( | ||
`Incorrect number of generic parameters passed; expected: ${generics.length}, got: ${typeParams.length}` | ||
); | ||
} | ||
let result = {}; | ||
for (let key of canonicalOrder) { | ||
const { name: fieldName, params: fieldParams } = this.parseTypeName( | ||
struct[key] | ||
); | ||
if (!generics.includes(fieldName)) { | ||
result[key] = this.getTypeInterface(fieldName)._decodeRaw.call( | ||
this, | ||
reader, | ||
fieldParams, | ||
typeMap | ||
); | ||
} else { | ||
const paramIdx = generics.indexOf(fieldName); | ||
let { name, params } = this.parseTypeName(typeParams[paramIdx]); | ||
if (this.hasType(name)) { | ||
result[key] = this.getTypeInterface(name)._decodeRaw.call( | ||
this, | ||
reader, | ||
params, | ||
typeMap | ||
); | ||
continue; | ||
} | ||
if (!(name in typeMap)) { | ||
throw new Error( | ||
`Unable to find a matching type definition for ${name} in ${structName}; make sure you passed a generic` | ||
); | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name]); | ||
result[key] = this.getTypeInterface(innerName)._decodeRaw.call( | ||
this, | ||
reader, | ||
innerParams, | ||
typeMap | ||
); | ||
} | ||
} | ||
return result; | ||
} | ||
); | ||
} | ||
/** | ||
* Safe method to register custom enum type where each invariant holds the value of another type. | ||
* @example | ||
* bcs.registerStructType('Coin', { value: 'u64' }); | ||
* bcs.registerEnumType('MyEnum', { | ||
* single: 'Coin', | ||
* multi: 'vector<Coin>', | ||
* empty: null | ||
* }); | ||
* | ||
* console.log( | ||
* bcs.de('MyEnum', 'AICWmAAAAAAA', 'base64'), // { single: { value: 10000000 } } | ||
* bcs.de('MyEnum', 'AQIBAAAAAAAAAAIAAAAAAAAA', 'base64') // { multi: [ { value: 1 }, { value: 2 } ] } | ||
* ) | ||
* | ||
* // and serialization | ||
* bcs.ser('MyEnum', { single: { value: 10000000 } }).toBytes(); | ||
* bcs.ser('MyEnum', { multi: [ { value: 1 }, { value: 2 } ] }); | ||
* | ||
* @param name | ||
* @param variants | ||
*/ | ||
registerEnumType(typeName, variants) { | ||
for (let key in variants) { | ||
let internalName = this.tempKey(); | ||
let value = variants[key]; | ||
if (value !== null && !Array.isArray(value) && typeof value !== "string") { | ||
variants[key] = internalName; | ||
this.registerStructType(internalName, value); | ||
} | ||
} | ||
let struct = Object.freeze(variants); | ||
let canonicalOrder = Object.keys(struct); | ||
let { name, params: canonicalTypeParams } = this.parseTypeName(typeName); | ||
return this.registerType( | ||
typeName, | ||
function encodeEnum(writer, data, typeParams, typeMap) { | ||
if (!data) { | ||
throw new Error(`Unable to write enum "${name}", missing data. | ||
Received: "${data}"`); | ||
} | ||
if (typeof data !== "object") { | ||
throw new Error( | ||
`Incorrect data passed into enum "${name}", expected object with properties: "${canonicalOrder.join( | ||
" | " | ||
)}". | ||
Received: "${JSON.stringify(data)}"` | ||
); | ||
} | ||
let key = Object.keys(data)[0]; | ||
if (key === void 0) { | ||
throw new Error(`Empty object passed as invariant of the enum "${name}"`); | ||
} | ||
let orderByte = canonicalOrder.indexOf(key); | ||
if (orderByte === -1) { | ||
throw new Error( | ||
`Unknown invariant of the enum "${name}", allowed values: "${canonicalOrder.join( | ||
" | " | ||
)}"; received "${key}"` | ||
); | ||
} | ||
let invariant = canonicalOrder[orderByte]; | ||
let invariantType = struct[invariant]; | ||
writer.write8(orderByte); | ||
if (invariantType === null) { | ||
return writer; | ||
} | ||
let paramIndex = canonicalTypeParams.indexOf(invariantType); | ||
let typeOrParam = paramIndex === -1 ? invariantType : typeParams[paramIndex]; | ||
{ | ||
let { name: name2, params } = this.parseTypeName(typeOrParam); | ||
return this.getTypeInterface(name2)._encodeRaw.call( | ||
this, | ||
writer, | ||
data[key], | ||
params, | ||
typeMap | ||
); | ||
} | ||
}, | ||
function decodeEnum(reader, typeParams, typeMap) { | ||
let orderByte = reader.readULEB(); | ||
let invariant = canonicalOrder[orderByte]; | ||
let invariantType = struct[invariant]; | ||
if (orderByte === -1) { | ||
throw new Error( | ||
`Decoding type mismatch, expected enum "${name}" invariant index, received "${orderByte}"` | ||
); | ||
} | ||
if (invariantType === null) { | ||
return { [invariant]: true }; | ||
} | ||
let paramIndex = canonicalTypeParams.indexOf(invariantType); | ||
let typeOrParam = paramIndex === -1 ? invariantType : typeParams[paramIndex]; | ||
{ | ||
let { name: name2, params } = this.parseTypeName(typeOrParam); | ||
return { | ||
[invariant]: this.getTypeInterface(name2)._decodeRaw.call(this, reader, params, typeMap) | ||
}; | ||
} | ||
} | ||
); | ||
} | ||
/** | ||
* Get a set of encoders/decoders for specific type. | ||
* Mainly used to define custom type de/serialization logic. | ||
* | ||
* @param type | ||
* @returns {TypeInterface} | ||
*/ | ||
getTypeInterface(type) { | ||
let typeInterface = this.types.get(type); | ||
if (typeof typeInterface === "string") { | ||
let chain = []; | ||
while (typeof typeInterface === "string") { | ||
if (chain.includes(typeInterface)) { | ||
throw new Error(`Recursive definition found: ${chain.join(" -> ")} -> ${typeInterface}`); | ||
} | ||
chain.push(typeInterface); | ||
typeInterface = this.types.get(typeInterface); | ||
} | ||
} | ||
if (typeInterface === void 0) { | ||
throw new Error(`Type ${type} is not registered`); | ||
} | ||
return typeInterface; | ||
} | ||
/** | ||
* Parse a type name and get the type's generics. | ||
* @example | ||
* let { typeName, typeParams } = parseTypeName('Option<Coin<SUI>>'); | ||
* // typeName: Option | ||
* // typeParams: [ 'Coin<SUI>' ] | ||
* | ||
* @param name Name of the type to process | ||
* @returns Object with typeName and typeParams listed as Array | ||
*/ | ||
parseTypeName(name) { | ||
if (Array.isArray(name)) { | ||
let [typeName2, ...params2] = name; | ||
return { name: typeName2, params: params2 }; | ||
} | ||
if (typeof name !== "string") { | ||
throw new Error(`Illegal type passed as a name of the type: ${name}`); | ||
} | ||
let [left, right] = this.schema.genericSeparators || ["<", ">"]; | ||
let l_bound = name.indexOf(left); | ||
let r_bound = Array.from(name).reverse().indexOf(right); | ||
if (l_bound === -1 && r_bound === -1) { | ||
return { name, params: [] }; | ||
} | ||
if (l_bound === -1 || r_bound === -1) { | ||
throw new Error(`Unclosed generic in name '${name}'`); | ||
} | ||
let typeName = name.slice(0, l_bound); | ||
let params = splitGenericParameters( | ||
name.slice(l_bound + 1, name.length - r_bound - 1), | ||
this.schema.genericSeparators | ||
); | ||
return { name: typeName, params }; | ||
} | ||
}; | ||
// Predefined types constants | ||
_BCS.U8 = "u8"; | ||
_BCS.U16 = "u16"; | ||
_BCS.U32 = "u32"; | ||
_BCS.U64 = "u64"; | ||
_BCS.U128 = "u128"; | ||
_BCS.U256 = "u256"; | ||
_BCS.BOOL = "bool"; | ||
_BCS.VECTOR = "vector"; | ||
_BCS.ADDRESS = "address"; | ||
_BCS.STRING = "string"; | ||
_BCS.HEX = "hex-string"; | ||
_BCS.BASE58 = "base58-string"; | ||
_BCS.BASE64 = "base64-string"; | ||
var BCS = _BCS; | ||
function registerPrimitives(bcs2) { | ||
bcs2.registerType( | ||
BCS.U8, | ||
function(writer, data) { | ||
return writer.write8(data); | ||
}, | ||
function(reader) { | ||
return reader.read8(); | ||
}, | ||
(u8) => u8 < 256 | ||
); | ||
bcs2.registerType( | ||
BCS.U16, | ||
function(writer, data) { | ||
return writer.write16(data); | ||
}, | ||
function(reader) { | ||
return reader.read16(); | ||
}, | ||
(u16) => u16 < 65536 | ||
); | ||
bcs2.registerType( | ||
BCS.U32, | ||
function(writer, data) { | ||
return writer.write32(data); | ||
}, | ||
function(reader) { | ||
return reader.read32(); | ||
}, | ||
(u32) => u32 <= 4294967296n | ||
); | ||
bcs2.registerType( | ||
BCS.U64, | ||
function(writer, data) { | ||
return writer.write64(data); | ||
}, | ||
function(reader) { | ||
return reader.read64(); | ||
} | ||
); | ||
bcs2.registerType( | ||
BCS.U128, | ||
function(writer, data) { | ||
return writer.write128(data); | ||
}, | ||
function(reader) { | ||
return reader.read128(); | ||
} | ||
); | ||
bcs2.registerType( | ||
BCS.U256, | ||
function(writer, data) { | ||
return writer.write256(data); | ||
}, | ||
function(reader) { | ||
return reader.read256(); | ||
} | ||
); | ||
bcs2.registerType( | ||
BCS.BOOL, | ||
function(writer, data) { | ||
return writer.write8(data); | ||
}, | ||
function(reader) { | ||
return reader.read8().toString(10) === "1"; | ||
} | ||
); | ||
bcs2.registerType( | ||
BCS.STRING, | ||
function(writer, data) { | ||
return writer.writeVec(Array.from(data), (writer2, el) => writer2.write8(el.charCodeAt(0))); | ||
}, | ||
function(reader) { | ||
return reader.readVec((reader2) => reader2.read8()).map((el) => String.fromCharCode(Number(el))).join(""); | ||
}, | ||
(_str) => true | ||
); | ||
bcs2.registerType( | ||
BCS.HEX, | ||
function(writer, data) { | ||
return writer.writeVec(Array.from(fromHEX(data)), (writer2, el) => writer2.write8(el)); | ||
}, | ||
function(reader) { | ||
let bytes = reader.readVec((reader2) => reader2.read8()); | ||
return toHEX(new Uint8Array(bytes)); | ||
} | ||
); | ||
bcs2.registerType( | ||
BCS.BASE58, | ||
function(writer, data) { | ||
return writer.writeVec(Array.from(fromB58(data)), (writer2, el) => writer2.write8(el)); | ||
}, | ||
function(reader) { | ||
let bytes = reader.readVec((reader2) => reader2.read8()); | ||
return toB58(new Uint8Array(bytes)); | ||
} | ||
); | ||
bcs2.registerType( | ||
BCS.BASE64, | ||
function(writer, data) { | ||
return writer.writeVec(Array.from(fromB64(data)), (writer2, el) => writer2.write8(el)); | ||
}, | ||
function(reader) { | ||
let bytes = reader.readVec((reader2) => reader2.read8()); | ||
return toB64(new Uint8Array(bytes)); | ||
} | ||
); | ||
} | ||
function getRustConfig() { | ||
return { | ||
genericSeparators: ["<", ">"], | ||
vectorType: "Vec", | ||
addressLength: SUI_ADDRESS_LENGTH, | ||
addressEncoding: "hex" | ||
}; | ||
} | ||
function getSuiMoveConfig() { | ||
return { | ||
genericSeparators: ["<", ">"], | ||
vectorType: "vector", | ||
addressLength: SUI_ADDRESS_LENGTH, | ||
addressEncoding: "hex" | ||
}; | ||
} | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = { | ||
BCS, | ||
BcsReader, | ||
BcsType, | ||
BcsWriter, | ||
SerializedBcs, | ||
bcs, | ||
decodeStr, | ||
encodeStr, | ||
fromB58, | ||
fromB64, | ||
fromHEX, | ||
getRustConfig, | ||
getSuiMoveConfig, | ||
isSerializedBcs, | ||
registerPrimitives, | ||
splitGenericParameters, | ||
toB58, | ||
toB64, | ||
toHEX | ||
}); | ||
//# sourceMappingURL=index.js.map | ||
// Copyright (c) Mysten Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
/* | ||
* BCS implementation {@see https://github.com/diem/bcs } for JavaScript. | ||
* Intended to be used for Move applications; supports both NodeJS and browser. | ||
* | ||
* For more details and examples {@see README.md }. | ||
* | ||
* @module bcs | ||
* @property {BcsReader} | ||
*/ | ||
import { fromB58, toB58 } from './b58.js'; | ||
import { fromB64, toB64 } from './b64.js'; | ||
import { BcsType, isSerializedBcs, SerializedBcs } from './bcs-type.js'; | ||
import { bcs } from './bcs.js'; | ||
import { fromHEX, toHEX } from './hex.js'; | ||
import { BcsReader } from './reader.js'; | ||
import { decodeStr, encodeStr, splitGenericParameters } from './utils.js'; | ||
import { BcsWriter } from './writer.js'; | ||
export * from './legacy-registry.js'; | ||
// Re-export all encoding dependencies. | ||
export { bcs, BcsType, SerializedBcs, isSerializedBcs, toB58, fromB58, toB64, fromB64, fromHEX, toHEX, encodeStr, decodeStr, splitGenericParameters, BcsReader, BcsWriter, }; |
{ | ||
"name": "@mysten/bcs", | ||
"version": "0.0.0-experimental-20231204220336", | ||
"version": "0.0.0-experimental-20231205235231", | ||
"description": "BCS - Canonical Binary Serialization implementation for JavaScript", | ||
@@ -5,0 +5,0 @@ "license": "Apache-2.0", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
504177
7176