@mysten/bcs
Advanced tools
Comparing version 0.0.0-experimental-20230615203750 to 0.0.0-experimental-20230616000145
@@ -76,5 +76,2 @@ "use strict"; | ||
nMod3 = nIdx % 3; | ||
if (nIdx > 0 && nIdx * 4 / 3 % 76 === 0) { | ||
sB64Enc += ""; | ||
} | ||
nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24); | ||
@@ -103,6 +100,3 @@ if (nMod3 === 2 || aBytes.length - nIdx === 1) { | ||
function toHEX(bytes) { | ||
return bytes.reduce( | ||
(str, byte) => str + byte.toString(16).padStart(2, "0"), | ||
"" | ||
); | ||
return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""); | ||
} | ||
@@ -237,7 +231,3 @@ | ||
var BcsWriter = class { | ||
constructor({ | ||
size = 1024, | ||
maxSize, | ||
allocateSize = 1024 | ||
} = {}) { | ||
constructor({ size = 1024, maxSize, allocateSize = 1024 } = {}) { | ||
this.bytePosition = 0; | ||
@@ -439,7 +429,3 @@ this.size = size; | ||
this.schema = schema; | ||
this.registerAddressType( | ||
_BCS.ADDRESS, | ||
schema.addressLength, | ||
schema.addressEncoding | ||
); | ||
this.registerAddressType(_BCS.ADDRESS, schema.addressLength, schema.addressEncoding); | ||
this.registerVectorType(schema.vectorType); | ||
@@ -493,8 +479,3 @@ if (schema.types && schema.types.structs) { | ||
const { name, params } = this.parseTypeName(type); | ||
return this.getTypeInterface(name).encode( | ||
this, | ||
data, | ||
options, | ||
params | ||
); | ||
return this.getTypeInterface(name).encode(this, data, options, params); | ||
} | ||
@@ -506,8 +487,4 @@ if (typeof type === "object") { | ||
} | ||
throw new Error( | ||
`Incorrect type passed into the '.ser()' function. | ||
${JSON.stringify( | ||
type | ||
)}` | ||
); | ||
throw new Error(`Incorrect type passed into the '.ser()' function. | ||
${JSON.stringify(type)}`); | ||
} | ||
@@ -544,8 +521,4 @@ /** | ||
} | ||
throw new Error( | ||
`Incorrect type passed into the '.de()' function. | ||
${JSON.stringify( | ||
type | ||
)}` | ||
); | ||
throw new Error(`Incorrect type passed into the '.de()' function. | ||
${JSON.stringify(type)}`); | ||
} | ||
@@ -604,29 +577,12 @@ /** | ||
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 | ||
); | ||
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 | ||
); | ||
const typeMap = generics.reduce((acc, value, index) => { | ||
return Object.assign(acc, { [value]: typeParams[index] }); | ||
}, {}); | ||
return this._decodeRaw.call(self, new BcsReader(data), typeParams, typeMap); | ||
}, | ||
@@ -665,6 +621,3 @@ // these methods should always be used with caution as they require pre-defined | ||
function encodeAddress(writer, data) { | ||
return fromB64(data).reduce( | ||
(writer2, el) => writer2.write8(el), | ||
writer | ||
); | ||
return fromB64(data).reduce((writer2, el) => writer2.write8(el), writer); | ||
}, | ||
@@ -679,6 +632,3 @@ function decodeAddress(reader) { | ||
function encodeAddress(writer, data) { | ||
return fromHEX(data).reduce( | ||
(writer2, el) => writer2.write8(el), | ||
writer | ||
); | ||
return fromHEX(data).reduce((writer2, el) => writer2.write8(el), writer); | ||
}, | ||
@@ -716,15 +666,7 @@ function decodeAddress(reader) { | ||
if (!elementType) { | ||
throw new Error( | ||
`Incorrect number of type parameters passed a to vector '${typeName}'` | ||
); | ||
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 | ||
); | ||
return this.getTypeInterface(name2)._encodeRaw.call(this, writer2, el, params2, typeMap); | ||
} | ||
@@ -736,5 +678,3 @@ if (!(name2 in typeMap)) { | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName( | ||
typeMap[name2] | ||
); | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name2]); | ||
return this.getTypeInterface(innerName)._encodeRaw.call( | ||
@@ -753,14 +693,7 @@ this, | ||
if (!elementType) { | ||
throw new Error( | ||
`Incorrect number of type parameters passed to a vector '${typeName}'` | ||
); | ||
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 | ||
); | ||
return this.getTypeInterface(name2)._decodeRaw.call(this, reader2, params2, typeMap); | ||
} | ||
@@ -772,5 +705,3 @@ if (!(name2 in typeMap)) { | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName( | ||
typeMap[name2] | ||
); | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name2]); | ||
return this.getTypeInterface(innerName)._decodeRaw.call( | ||
@@ -846,5 +777,3 @@ this, | ||
if (!data || data.constructor !== Object) { | ||
throw new Error( | ||
`Expected ${structName} to be an Object, got: ${data}` | ||
); | ||
throw new Error(`Expected ${structName} to be an Object, got: ${data}`); | ||
} | ||
@@ -858,5 +787,3 @@ if (typeParams.length !== generics.length) { | ||
if (!(key in data)) { | ||
throw new Error( | ||
`Struct ${structName} requires field ${key}:${struct[key]}` | ||
); | ||
throw new Error(`Struct ${structName} requires field ${key}:${struct[key]}`); | ||
} | ||
@@ -892,5 +819,3 @@ const { name: fieldType, params: fieldParams } = this.parseTypeName( | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName( | ||
typeMap[name] | ||
); | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name]); | ||
this.getTypeInterface(innerName)._encodeRaw.call( | ||
@@ -942,5 +867,3 @@ this, | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName( | ||
typeMap[name] | ||
); | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name]); | ||
result[key] = this.getTypeInterface(innerName)._decodeRaw.call( | ||
@@ -996,6 +919,4 @@ this, | ||
if (!data) { | ||
throw new Error( | ||
`Unable to write enum "${name}", missing data. | ||
Received: "${data}"` | ||
); | ||
throw new Error(`Unable to write enum "${name}", missing data. | ||
Received: "${data}"`); | ||
} | ||
@@ -1012,5 +933,3 @@ if (typeof data !== "object") { | ||
if (key === void 0) { | ||
throw new Error( | ||
`Empty object passed as invariant of the enum "${name}"` | ||
); | ||
throw new Error(`Empty object passed as invariant of the enum "${name}"`); | ||
} | ||
@@ -1061,8 +980,3 @@ let orderByte = canonicalOrder.indexOf(key); | ||
return { | ||
[invariant]: this.getTypeInterface(name2)._decodeRaw.call( | ||
this, | ||
reader, | ||
params, | ||
typeMap | ||
) | ||
[invariant]: this.getTypeInterface(name2)._decodeRaw.call(this, reader, params, typeMap) | ||
}; | ||
@@ -1086,7 +1000,3 @@ } | ||
if (chain.includes(typeInterface)) { | ||
throw new Error( | ||
`Recursive definition found: ${chain.join( | ||
" -> " | ||
)} -> ${typeInterface}` | ||
); | ||
throw new Error(`Recursive definition found: ${chain.join(" -> ")} -> ${typeInterface}`); | ||
} | ||
@@ -1161,5 +1071,3 @@ chain.push(typeInterface); | ||
default: | ||
throw new Error( | ||
"Unsupported encoding, supported values are: base64, hex" | ||
); | ||
throw new Error("Unsupported encoding, supported values are: base64, hex"); | ||
} | ||
@@ -1176,5 +1084,3 @@ } | ||
default: | ||
throw new Error( | ||
"Unsupported encoding, supported values are: base64, hex" | ||
); | ||
throw new Error("Unsupported encoding, supported values are: base64, hex"); | ||
} | ||
@@ -1252,6 +1158,3 @@ } | ||
function(writer, data) { | ||
return writer.writeVec( | ||
Array.from(data), | ||
(writer2, el) => writer2.write8(el.charCodeAt(0)) | ||
); | ||
return writer.writeVec(Array.from(data), (writer2, el) => writer2.write8(el.charCodeAt(0))); | ||
}, | ||
@@ -1266,6 +1169,3 @@ function(reader) { | ||
function(writer, data) { | ||
return writer.writeVec( | ||
Array.from(fromHEX(data)), | ||
(writer2, el) => writer2.write8(el) | ||
); | ||
return writer.writeVec(Array.from(fromHEX(data)), (writer2, el) => writer2.write8(el)); | ||
}, | ||
@@ -1280,6 +1180,3 @@ function(reader) { | ||
function(writer, data) { | ||
return writer.writeVec( | ||
Array.from(fromB58(data)), | ||
(writer2, el) => writer2.write8(el) | ||
); | ||
return writer.writeVec(Array.from(fromB58(data)), (writer2, el) => writer2.write8(el)); | ||
}, | ||
@@ -1294,6 +1191,3 @@ function(reader) { | ||
function(writer, data) { | ||
return writer.writeVec( | ||
Array.from(fromB64(data)), | ||
(writer2, el) => writer2.write8(el) | ||
); | ||
return writer.writeVec(Array.from(fromB64(data)), (writer2, el) => writer2.write8(el)); | ||
}, | ||
@@ -1300,0 +1194,0 @@ function(reader) { |
104
CHANGELOG.md
# Change Log | ||
## 0.0.0-experimental-20230615203750 | ||
## 0.0.0-experimental-20230616000145 | ||
### Patch Changes | ||
- 36f2edff31: Fix an issue with parsing struct types with nested type parameters | ||
- 36f2edff31: Fix an issue with parsing struct types with nested type parameters | ||
@@ -13,4 +13,4 @@ ## 0.7.2 | ||
- ca5c72815d: Fix a bcs decoding bug for u128 and u256 values | ||
- fdb569464e: Fixes an issue with a top level generic in a nested vector | ||
- ca5c72815d: Fix a bcs decoding bug for u128 and u256 values | ||
- fdb569464e: Fixes an issue with a top level generic in a nested vector | ||
@@ -21,3 +21,3 @@ ## 0.7.1 | ||
- b4f0bfc76: Fix type definitions for package exports. | ||
- b4f0bfc76: Fix type definitions for package exports. | ||
@@ -28,30 +28,30 @@ ## 0.7.0 | ||
- 19b567f21: Unified self- and delegated staking flows. Removed fields from `Validator` (`stake_amount`, `pending_stake`, and `pending_withdraw`) and renamed `delegation_staking_pool` to `staking_pool`. Additionally removed the `validator_stake` and `delegated_stake` fields in the `ValidatorSet` type and replaced them with a `total_stake` field. | ||
- 5c3b00cde: Add object id to staking pool and pool id to staked sui. | ||
- 3d9a04648: Adds `deactivation_epoch` to staking pool object, and adds `inactive_pools` to the validator set object. | ||
- a8049d159: Fixes the issue with deep nested generics by introducing array type names | ||
- 19b567f21: Unified self- and delegated staking flows. Removed fields from `Validator` (`stake_amount`, `pending_stake`, and `pending_withdraw`) and renamed `delegation_staking_pool` to `staking_pool`. Additionally removed the `validator_stake` and `delegated_stake` fields in the `ValidatorSet` type and replaced them with a `total_stake` field. | ||
- 5c3b00cde: Add object id to staking pool and pool id to staked sui. | ||
- 3d9a04648: Adds `deactivation_epoch` to staking pool object, and adds `inactive_pools` to the validator set object. | ||
- a8049d159: Fixes the issue with deep nested generics by introducing array type names | ||
- all of the methods (except for aliasing) now allow passing in arrays instead | ||
of strings to allow for easier composition of generics and avoid using template | ||
strings | ||
- all of the methods (except for aliasing) now allow passing in arrays instead | ||
of strings to allow for easier composition of generics and avoid using template | ||
strings | ||
```js | ||
// new syntax | ||
bcs.registerStructType(['VecMap', 'K', 'V'], { | ||
keys: ['vector', 'K'], | ||
values: ['vector', 'V'], | ||
}); | ||
```js | ||
// new syntax | ||
bcs.registerStructType(['VecMap', 'K', 'V'], { | ||
keys: ['vector', 'K'], | ||
values: ['vector', 'V'], | ||
}); | ||
// is identical to an old string definition | ||
bcs.registerStructType('VecMap<K, V>', { | ||
keys: 'vector<K>', | ||
values: 'vector<V>', | ||
}); | ||
``` | ||
// is identical to an old string definition | ||
bcs.registerStructType('VecMap<K, V>', { | ||
keys: 'vector<K>', | ||
values: 'vector<V>', | ||
}); | ||
``` | ||
Similar approach applies to `bcs.ser()` and `bcs.de()` as well as to other register\* methods | ||
Similar approach applies to `bcs.ser()` and `bcs.de()` as well as to other register\* methods | ||
- a0955c479: Switch from 20 to 32-byte address. Match Secp256k1.deriveKeypair with Ed25519. | ||
- 0a7b42a6d: This changes almost all occurences of "delegate", "delegation" (and various capitalizations/forms) to their equivalent "stake"-based name. Function names, function argument names, RPC endpoints, Move functions, and object fields have been updated with this new naming convention. | ||
- 77bdf907f: When parsing u64, u128, and u256 values with bcs, they are now string encoded. | ||
- a0955c479: Switch from 20 to 32-byte address. Match Secp256k1.deriveKeypair with Ed25519. | ||
- 0a7b42a6d: This changes almost all occurences of "delegate", "delegation" (and various capitalizations/forms) to their equivalent "stake"-based name. Function names, function argument names, RPC endpoints, Move functions, and object fields have been updated with this new naming convention. | ||
- 77bdf907f: When parsing u64, u128, and u256 values with bcs, they are now string encoded. | ||
@@ -62,3 +62,3 @@ ## 0.6.1 | ||
- 0e202a543: Remove pending delegation switches. | ||
- 0e202a543: Remove pending delegation switches. | ||
@@ -70,4 +70,4 @@ ## 0.6.0 | ||
bcs.registerStructType(['VecMap', 'K', 'V'], { | ||
keys: ['vector', 'K'], | ||
values: ['vector', 'V'], | ||
keys: ['vector', 'K'], | ||
values: ['vector', 'V'], | ||
}); | ||
@@ -77,4 +77,4 @@ | ||
bcs.registerStructType('VecMap<K, V>', { | ||
keys: 'vector<K>', | ||
values: 'vector<V>', | ||
keys: 'vector<K>', | ||
values: 'vector<V>', | ||
}); | ||
@@ -85,14 +85,14 @@ ``` | ||
- 598f106ef: Adds base58 encoding support to bcs | ||
- 598f106ef: Adds base58 encoding support to bcs | ||
- two functions added: `fromB58` and `toB58` similar to existing encodings | ||
- `Reader.toString` and `de/encodeStr` methods support new `base58` value | ||
- adds a 3 built-in types "hex-string", "base58-string" and "base64-string" | ||
- adds constants for the built-ins: `BCS.BASE64`, `BCS.BASE58` and `BCS.HEX` | ||
- two functions added: `fromB58` and `toB58` similar to existing encodings | ||
- `Reader.toString` and `de/encodeStr` methods support new `base58` value | ||
- adds a 3 built-in types "hex-string", "base58-string" and "base64-string" | ||
- adds constants for the built-ins: `BCS.BASE64`, `BCS.BASE58` and `BCS.HEX` | ||
```js | ||
bcs.registerStructType('TestStruct', { | ||
hex: BCS.HEX, | ||
base58: BCS.BASE58, | ||
base64: BCS.BASE64, | ||
hex: BCS.HEX, | ||
base58: BCS.BASE58, | ||
base64: BCS.BASE64, | ||
}); | ||
@@ -103,4 +103,4 @@ ``` | ||
- adds new `registerAlias` function which allows type aliases and tracks basic recursion | ||
- adds support for inline definitions in the `.de()` and `.ser()` methods | ||
- adds new `registerAlias` function which allows type aliases and tracks basic recursion | ||
- adds support for inline definitions in the `.de()` and `.ser()` methods | ||
@@ -128,3 +128,3 @@ ### Examples | ||
- 1a0968636: Remove usage of bn.js, and use native bigints instead. | ||
- 1a0968636: Remove usage of bn.js, and use native bigints instead. | ||
@@ -135,7 +135,7 @@ ## 0.4.0 | ||
- 1591726e8: Support multiple instances of BCS | ||
- 1591726e8: Support multiple instances of BCS | ||
### Patch Changes | ||
- 1591726e8: Add support for generic types | ||
- 1591726e8: Add support for generic types | ||
@@ -146,3 +146,3 @@ ## 0.3.0 | ||
- d343b67e: Re-release packages | ||
- d343b67e: Re-release packages | ||
@@ -153,4 +153,4 @@ ## 0.2.1 | ||
- c5e4851b: Updated build process from TSDX to tsup. | ||
- e2aa08e9: Fix missing built files for packages. | ||
- c5e4851b: Updated build process from TSDX to tsup. | ||
- e2aa08e9: Fix missing built files for packages. | ||
@@ -161,5 +161,5 @@ Version history from v0.1.0 to this day. | ||
- `bcs.de(...)` now supports strings if encoding is passed as the last argument | ||
- `BCS` (upper) -> `bcs` (lower) renaming | ||
- Improved documentation, checked documentation examples for failures | ||
- `bcs.de(...)` now supports strings if encoding is passed as the last argument | ||
- `BCS` (upper) -> `bcs` (lower) renaming | ||
- Improved documentation, checked documentation examples for failures | ||
@@ -166,0 +166,0 @@ ## v0.1.0 |
@@ -1,3 +0,3 @@ | ||
import { toB64, fromB64 } from "./b64"; | ||
import { toHEX, fromHEX } from "./hex"; | ||
import { toB64, fromB64 } from './b64'; | ||
import { toHEX, fromHEX } from './hex'; | ||
declare const toB58: (buffer: Uint8Array) => string; | ||
@@ -10,3 +10,3 @@ declare const fromB58: (str: string) => Uint8Array; | ||
*/ | ||
export type Encoding = "base58" | "base64" | "hex"; | ||
export type Encoding = 'base58' | 'base64' | 'hex'; | ||
/** | ||
@@ -141,3 +141,3 @@ * Allows for array definitions for names. | ||
private allocateSize; | ||
constructor({ size, maxSize, allocateSize, }?: BcsWriterOptions); | ||
constructor({ size, maxSize, allocateSize }?: BcsWriterOptions); | ||
private ensureSizeOrGrow; | ||
@@ -204,3 +204,3 @@ /** | ||
*/ | ||
writeVec(vector: any[], cb: (writer: BcsWriter, el: any, i: number, len: number) => {}): this; | ||
writeVec(vector: any[], cb: (writer: BcsWriter, el: any, i: number, len: number) => void): this; | ||
/** | ||
@@ -277,3 +277,3 @@ * Adds support for iterations over the object. | ||
*/ | ||
addressEncoding?: "hex" | "base64"; | ||
addressEncoding?: 'hex' | 'base64'; | ||
/** | ||
@@ -280,0 +280,0 @@ * Opening and closing symbol for type parameters. Can be |
@@ -76,5 +76,2 @@ "use strict"; | ||
nMod3 = nIdx % 3; | ||
if (nIdx > 0 && nIdx * 4 / 3 % 76 === 0) { | ||
sB64Enc += ""; | ||
} | ||
nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24); | ||
@@ -103,6 +100,3 @@ if (nMod3 === 2 || aBytes.length - nIdx === 1) { | ||
function toHEX(bytes) { | ||
return bytes.reduce( | ||
(str, byte) => str + byte.toString(16).padStart(2, "0"), | ||
"" | ||
); | ||
return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""); | ||
} | ||
@@ -237,7 +231,3 @@ | ||
var BcsWriter = class { | ||
constructor({ | ||
size = 1024, | ||
maxSize, | ||
allocateSize = 1024 | ||
} = {}) { | ||
constructor({ size = 1024, maxSize, allocateSize = 1024 } = {}) { | ||
this.bytePosition = 0; | ||
@@ -439,7 +429,3 @@ this.size = size; | ||
this.schema = schema; | ||
this.registerAddressType( | ||
_BCS.ADDRESS, | ||
schema.addressLength, | ||
schema.addressEncoding | ||
); | ||
this.registerAddressType(_BCS.ADDRESS, schema.addressLength, schema.addressEncoding); | ||
this.registerVectorType(schema.vectorType); | ||
@@ -493,8 +479,3 @@ if (schema.types && schema.types.structs) { | ||
const { name, params } = this.parseTypeName(type); | ||
return this.getTypeInterface(name).encode( | ||
this, | ||
data, | ||
options, | ||
params | ||
); | ||
return this.getTypeInterface(name).encode(this, data, options, params); | ||
} | ||
@@ -506,8 +487,4 @@ if (typeof type === "object") { | ||
} | ||
throw new Error( | ||
`Incorrect type passed into the '.ser()' function. | ||
${JSON.stringify( | ||
type | ||
)}` | ||
); | ||
throw new Error(`Incorrect type passed into the '.ser()' function. | ||
${JSON.stringify(type)}`); | ||
} | ||
@@ -544,8 +521,4 @@ /** | ||
} | ||
throw new Error( | ||
`Incorrect type passed into the '.de()' function. | ||
${JSON.stringify( | ||
type | ||
)}` | ||
); | ||
throw new Error(`Incorrect type passed into the '.de()' function. | ||
${JSON.stringify(type)}`); | ||
} | ||
@@ -604,29 +577,12 @@ /** | ||
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 | ||
); | ||
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 | ||
); | ||
const typeMap = generics.reduce((acc, value, index) => { | ||
return Object.assign(acc, { [value]: typeParams[index] }); | ||
}, {}); | ||
return this._decodeRaw.call(self, new BcsReader(data), typeParams, typeMap); | ||
}, | ||
@@ -665,6 +621,3 @@ // these methods should always be used with caution as they require pre-defined | ||
function encodeAddress(writer, data) { | ||
return fromB64(data).reduce( | ||
(writer2, el) => writer2.write8(el), | ||
writer | ||
); | ||
return fromB64(data).reduce((writer2, el) => writer2.write8(el), writer); | ||
}, | ||
@@ -679,6 +632,3 @@ function decodeAddress(reader) { | ||
function encodeAddress(writer, data) { | ||
return fromHEX(data).reduce( | ||
(writer2, el) => writer2.write8(el), | ||
writer | ||
); | ||
return fromHEX(data).reduce((writer2, el) => writer2.write8(el), writer); | ||
}, | ||
@@ -716,15 +666,7 @@ function decodeAddress(reader) { | ||
if (!elementType) { | ||
throw new Error( | ||
`Incorrect number of type parameters passed a to vector '${typeName}'` | ||
); | ||
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 | ||
); | ||
return this.getTypeInterface(name2)._encodeRaw.call(this, writer2, el, params2, typeMap); | ||
} | ||
@@ -736,5 +678,3 @@ if (!(name2 in typeMap)) { | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName( | ||
typeMap[name2] | ||
); | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name2]); | ||
return this.getTypeInterface(innerName)._encodeRaw.call( | ||
@@ -753,14 +693,7 @@ this, | ||
if (!elementType) { | ||
throw new Error( | ||
`Incorrect number of type parameters passed to a vector '${typeName}'` | ||
); | ||
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 | ||
); | ||
return this.getTypeInterface(name2)._decodeRaw.call(this, reader2, params2, typeMap); | ||
} | ||
@@ -772,5 +705,3 @@ if (!(name2 in typeMap)) { | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName( | ||
typeMap[name2] | ||
); | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name2]); | ||
return this.getTypeInterface(innerName)._decodeRaw.call( | ||
@@ -846,5 +777,3 @@ this, | ||
if (!data || data.constructor !== Object) { | ||
throw new Error( | ||
`Expected ${structName} to be an Object, got: ${data}` | ||
); | ||
throw new Error(`Expected ${structName} to be an Object, got: ${data}`); | ||
} | ||
@@ -858,5 +787,3 @@ if (typeParams.length !== generics.length) { | ||
if (!(key in data)) { | ||
throw new Error( | ||
`Struct ${structName} requires field ${key}:${struct[key]}` | ||
); | ||
throw new Error(`Struct ${structName} requires field ${key}:${struct[key]}`); | ||
} | ||
@@ -892,5 +819,3 @@ const { name: fieldType, params: fieldParams } = this.parseTypeName( | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName( | ||
typeMap[name] | ||
); | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name]); | ||
this.getTypeInterface(innerName)._encodeRaw.call( | ||
@@ -942,5 +867,3 @@ this, | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName( | ||
typeMap[name] | ||
); | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name]); | ||
result[key] = this.getTypeInterface(innerName)._decodeRaw.call( | ||
@@ -996,6 +919,4 @@ this, | ||
if (!data) { | ||
throw new Error( | ||
`Unable to write enum "${name}", missing data. | ||
Received: "${data}"` | ||
); | ||
throw new Error(`Unable to write enum "${name}", missing data. | ||
Received: "${data}"`); | ||
} | ||
@@ -1012,5 +933,3 @@ if (typeof data !== "object") { | ||
if (key === void 0) { | ||
throw new Error( | ||
`Empty object passed as invariant of the enum "${name}"` | ||
); | ||
throw new Error(`Empty object passed as invariant of the enum "${name}"`); | ||
} | ||
@@ -1061,8 +980,3 @@ let orderByte = canonicalOrder.indexOf(key); | ||
return { | ||
[invariant]: this.getTypeInterface(name2)._decodeRaw.call( | ||
this, | ||
reader, | ||
params, | ||
typeMap | ||
) | ||
[invariant]: this.getTypeInterface(name2)._decodeRaw.call(this, reader, params, typeMap) | ||
}; | ||
@@ -1086,7 +1000,3 @@ } | ||
if (chain.includes(typeInterface)) { | ||
throw new Error( | ||
`Recursive definition found: ${chain.join( | ||
" -> " | ||
)} -> ${typeInterface}` | ||
); | ||
throw new Error(`Recursive definition found: ${chain.join(" -> ")} -> ${typeInterface}`); | ||
} | ||
@@ -1161,5 +1071,3 @@ chain.push(typeInterface); | ||
default: | ||
throw new Error( | ||
"Unsupported encoding, supported values are: base64, hex" | ||
); | ||
throw new Error("Unsupported encoding, supported values are: base64, hex"); | ||
} | ||
@@ -1176,5 +1084,3 @@ } | ||
default: | ||
throw new Error( | ||
"Unsupported encoding, supported values are: base64, hex" | ||
); | ||
throw new Error("Unsupported encoding, supported values are: base64, hex"); | ||
} | ||
@@ -1252,6 +1158,3 @@ } | ||
function(writer, data) { | ||
return writer.writeVec( | ||
Array.from(data), | ||
(writer2, el) => writer2.write8(el.charCodeAt(0)) | ||
); | ||
return writer.writeVec(Array.from(data), (writer2, el) => writer2.write8(el.charCodeAt(0))); | ||
}, | ||
@@ -1266,6 +1169,3 @@ function(reader) { | ||
function(writer, data) { | ||
return writer.writeVec( | ||
Array.from(fromHEX(data)), | ||
(writer2, el) => writer2.write8(el) | ||
); | ||
return writer.writeVec(Array.from(fromHEX(data)), (writer2, el) => writer2.write8(el)); | ||
}, | ||
@@ -1280,6 +1180,3 @@ function(reader) { | ||
function(writer, data) { | ||
return writer.writeVec( | ||
Array.from(fromB58(data)), | ||
(writer2, el) => writer2.write8(el) | ||
); | ||
return writer.writeVec(Array.from(fromB58(data)), (writer2, el) => writer2.write8(el)); | ||
}, | ||
@@ -1294,6 +1191,3 @@ function(reader) { | ||
function(writer, data) { | ||
return writer.writeVec( | ||
Array.from(fromB64(data)), | ||
(writer2, el) => writer2.write8(el) | ||
); | ||
return writer.writeVec(Array.from(fromB64(data)), (writer2, el) => writer2.write8(el)); | ||
}, | ||
@@ -1300,0 +1194,0 @@ function(reader) { |
{ | ||
"name": "@mysten/bcs", | ||
"version": "0.0.0-experimental-20230615203750", | ||
"version": "0.0.0-experimental-20230616000145", | ||
"description": "BCS - Canonical Binary Serialization implementation for JavaScript", | ||
@@ -66,4 +66,10 @@ "license": "Apache-2.0", | ||
"test": "vitest run", | ||
"test:watch": "vitest" | ||
"test:watch": "vitest", | ||
"prettier:check": "prettier -c --ignore-unknown .", | ||
"prettier:fix": "prettier -w --ignore-unknown .", | ||
"eslint:check": "eslint --max-warnings=0 .", | ||
"eslint:fix": "pnpm run eslint:check --fix", | ||
"lint": "pnpm run eslint:check && pnpm run prettier:check", | ||
"lint:fix": "pnpm run eslint:fix && pnpm run prettier:fix" | ||
} | ||
} |
269
README.md
@@ -16,3 +16,3 @@ # BCS - Binary Canonical Serialization | ||
```ts | ||
import { BCS, getSuiMoveConfig } from "@mysten/bcs"; | ||
import { BCS, getSuiMoveConfig } from '@mysten/bcs'; | ||
@@ -22,10 +22,10 @@ const bcs = new BCS(getSuiMoveConfig()); | ||
// registering types so we can use them | ||
bcs.registerAlias("UID", BCS.ADDRESS); | ||
bcs.registerEnumType("Option<T>", { | ||
none: null, | ||
some: "T", | ||
bcs.registerAlias('UID', BCS.ADDRESS); | ||
bcs.registerEnumType('Option<T>', { | ||
none: null, | ||
some: 'T', | ||
}); | ||
bcs.registerStructType("Coin", { | ||
id: "UID", | ||
value: BCS.U64, | ||
bcs.registerStructType('Coin', { | ||
id: 'UID', | ||
value: BCS.U64, | ||
}); | ||
@@ -35,11 +35,11 @@ | ||
let bcsBytes = bcs | ||
.ser("Coin", { | ||
id: "0000000000000000000000000000000000000000000000000000000000000001", | ||
value: 1000000n, | ||
}) | ||
.toBytes(); | ||
let coin = bcs.de("Coin", bcsBytes, "hex"); | ||
.ser('Coin', { | ||
id: '0000000000000000000000000000000000000000000000000000000000000001', | ||
value: 1000000n, | ||
}) | ||
.toBytes(); | ||
let coin = bcs.de('Coin', bcsBytes, 'hex'); | ||
// serialization: Object into bytes - an Option with <T = Coin> | ||
let data = bcs.ser("Option<Coin>", { some: coin }).toString("hex"); | ||
let data = bcs.ser('Option<Coin>', { some: coin }).toString('hex'); | ||
@@ -68,25 +68,25 @@ console.log(data); | ||
// Example: All options used | ||
import { BCS } from "@mysten/bcs"; | ||
import { BCS } from '@mysten/bcs'; | ||
const SUI_ADDRESS_LENGTH = 32; | ||
const bcs = new BCS({ | ||
vectorType: "vector<T>", | ||
addressLength: SUI_ADDRESS_LENGTH, | ||
addressEncoding: "hex", | ||
genericSeparators: ["<", ">"], | ||
types: { | ||
// define schema in the initializer | ||
structs: { | ||
User: { | ||
name: BCS.STRING, | ||
age: BCS.U8, | ||
}, | ||
}, | ||
enums: {}, | ||
aliases: { hex: BCS.HEX }, | ||
}, | ||
withPrimitives: true, | ||
vectorType: 'vector<T>', | ||
addressLength: SUI_ADDRESS_LENGTH, | ||
addressEncoding: 'hex', | ||
genericSeparators: ['<', '>'], | ||
types: { | ||
// define schema in the initializer | ||
structs: { | ||
User: { | ||
name: BCS.STRING, | ||
age: BCS.U8, | ||
}, | ||
}, | ||
enums: {}, | ||
aliases: { hex: BCS.HEX }, | ||
}, | ||
withPrimitives: true, | ||
}); | ||
let bytes = bcs.ser("User", { name: "Adam", age: "30" }).toString("base64"); | ||
let bytes = bcs.ser('User', { name: 'Adam', age: '30' }).toString('base64'); | ||
@@ -100,3 +100,3 @@ console.log(bytes); | ||
// Example: Sui Move Config | ||
import { BCS, getSuiMoveConfig } from "@mysten/bcs"; | ||
import { BCS, getSuiMoveConfig } from '@mysten/bcs'; | ||
@@ -107,6 +107,6 @@ const bcs = new BCS(getSuiMoveConfig()); | ||
const val = [1, 2, 3, 4]; | ||
const ser = bcs.ser(["vector", BCS.U8], val).toBytes(); | ||
const ser = bcs.ser(['vector', BCS.U8], val).toBytes(); | ||
// use bcs.de() to deserialize data | ||
const res = bcs.de(["vector", BCS.U8], ser); | ||
const res = bcs.de(['vector', BCS.U8], ser); | ||
@@ -120,8 +120,8 @@ console.assert(res.toString() === val.toString()); | ||
// Example: Rust Config | ||
import { BCS, getRustConfig } from "@mysten/bcs"; | ||
import { BCS, getRustConfig } from '@mysten/bcs'; | ||
const bcs = new BCS(getRustConfig()); | ||
const val = [1, 2, 3, 4]; | ||
const ser = bcs.ser(["Vec", BCS.U8], val).toBytes(); | ||
const res = bcs.de(["Vec", BCS.U8], ser); | ||
const ser = bcs.ser(['Vec', BCS.U8], val).toBytes(); | ||
const res = bcs.de(['Vec', BCS.U8], ser); | ||
@@ -154,3 +154,3 @@ console.assert(res.toString() === val.toString()); | ||
// Example: Primitive types | ||
import { BCS, getSuiMoveConfig } from "@mysten/bcs"; | ||
import { BCS, getSuiMoveConfig } from '@mysten/bcs'; | ||
const bcs = new BCS(getSuiMoveConfig()); | ||
@@ -160,27 +160,23 @@ | ||
let _u8 = bcs.ser(BCS.U8, 100).toBytes(); | ||
let _u64 = bcs.ser(BCS.U64, 1000000n).toString("hex"); | ||
let _u128 = bcs.ser(BCS.U128, "100000010000001000000").toString("base64"); | ||
let _u64 = bcs.ser(BCS.U64, 1000000n).toString('hex'); | ||
let _u128 = bcs.ser(BCS.U128, '100000010000001000000').toString('base64'); | ||
// Other types | ||
let _bool = bcs.ser(BCS.BOOL, true).toString("hex"); | ||
let _addr = bcs | ||
.ser(BCS.ADDRESS, "0000000000000000000000000000000000000001") | ||
.toBytes(); | ||
let _str = bcs.ser(BCS.STRING, "this is an ascii string").toBytes(); | ||
let _bool = bcs.ser(BCS.BOOL, true).toString('hex'); | ||
let _addr = bcs.ser(BCS.ADDRESS, '0000000000000000000000000000000000000001').toBytes(); | ||
let _str = bcs.ser(BCS.STRING, 'this is an ascii string').toBytes(); | ||
// Vectors (vector<T>) | ||
let _u8_vec = bcs.ser(["vector", BCS.U8], [1, 2, 3, 4, 5, 6, 7]).toBytes(); | ||
let _bool_vec = bcs.ser(["vector", BCS.BOOL], [true, true, false]).toBytes(); | ||
let _str_vec = bcs | ||
.ser("vector<bool>", ["string1", "string2", "string3"]) | ||
.toBytes(); | ||
let _u8_vec = bcs.ser(['vector', BCS.U8], [1, 2, 3, 4, 5, 6, 7]).toBytes(); | ||
let _bool_vec = bcs.ser(['vector', BCS.BOOL], [true, true, false]).toBytes(); | ||
let _str_vec = bcs.ser('vector<bool>', ['string1', 'string2', 'string3']).toBytes(); | ||
// Even vector of vector (...of vector) is an option | ||
let _matrix = bcs | ||
.ser("vector<vector<u8>>", [ | ||
[0, 0, 0], | ||
[1, 1, 1], | ||
[2, 2, 2], | ||
]) | ||
.toBytes(); | ||
.ser('vector<vector<u8>>', [ | ||
[0, 0, 0], | ||
[1, 1, 1], | ||
[2, 2, 2], | ||
]) | ||
.toBytes(); | ||
``` | ||
@@ -194,7 +190,7 @@ | ||
// Example: Ser/de and Encoding | ||
import { BCS, getSuiMoveConfig, BcsWriter } from "@mysten/bcs"; | ||
import { BCS, getSuiMoveConfig, BcsWriter } from '@mysten/bcs'; | ||
const bcs = new BCS(getSuiMoveConfig()); | ||
// bcs.ser() returns an instance of BcsWriter which can be converted to bytes or a string | ||
let bcsWriter: BcsWriter = bcs.ser(BCS.STRING, "this is a string"); | ||
let bcsWriter: BcsWriter = bcs.ser(BCS.STRING, 'this is a string'); | ||
@@ -205,5 +201,5 @@ // writer.toBytes() returns a Uint8Array | ||
// custom encodings can be chosen when needed (just like Buffer) | ||
let hex: string = bcsWriter.toString("hex"); | ||
let base64: string = bcsWriter.toString("base64"); | ||
let base58: string = bcsWriter.toString("base58"); | ||
let hex: string = bcsWriter.toString('hex'); | ||
let base64: string = bcsWriter.toString('base64'); | ||
let base58: string = bcsWriter.toString('base58'); | ||
@@ -215,7 +211,7 @@ // bcs.de() reads BCS data and returns the value | ||
// alternatively, an encoding of input can be specified | ||
let str2 = bcs.de(BCS.STRING, hex, "hex"); | ||
let str3 = bcs.de(BCS.STRING, base64, "base64"); | ||
let str4 = bcs.de(BCS.STRING, base58, "base58"); | ||
let str2 = bcs.de(BCS.STRING, hex, 'hex'); | ||
let str3 = bcs.de(BCS.STRING, base64, 'base64'); | ||
let str4 = bcs.de(BCS.STRING, base58, 'base58'); | ||
console.assert((str1 == str2) == (str3 == str4), "Result is the same"); | ||
console.assert((str1 == str2) == (str3 == str4), 'Result is the same'); | ||
``` | ||
@@ -233,14 +229,14 @@ | ||
// Example: Alias | ||
import { BCS, getSuiMoveConfig } from "@mysten/bcs"; | ||
import { BCS, getSuiMoveConfig } from '@mysten/bcs'; | ||
const bcs = new BCS(getSuiMoveConfig()); | ||
bcs.registerAlias("ObjectDigest", BCS.BASE58); | ||
bcs.registerAlias('ObjectDigest', BCS.BASE58); | ||
// ObjectDigest is now treated as base58 string | ||
let _b58 = bcs.ser("ObjectDigest", "Ldp").toBytes(); | ||
let _b58 = bcs.ser('ObjectDigest', 'Ldp').toBytes(); | ||
// we can override already existing definition to make it a HEX string | ||
bcs.registerAlias("ObjectDigest", BCS.HEX); | ||
bcs.registerAlias('ObjectDigest', BCS.HEX); | ||
let _hex = bcs.ser("ObjectDigest", "C0FFEE").toBytes(); | ||
let _hex = bcs.ser('ObjectDigest', 'C0FFEE').toBytes(); | ||
``` | ||
@@ -254,14 +250,14 @@ | ||
// Example: Struct | ||
import { BCS, getSuiMoveConfig } from "@mysten/bcs"; | ||
import { BCS, getSuiMoveConfig } from '@mysten/bcs'; | ||
const bcs = new BCS(getSuiMoveConfig()); | ||
// register a custom type (it becomes available for using) | ||
bcs.registerStructType("Balance", { | ||
value: BCS.U64, | ||
bcs.registerStructType('Balance', { | ||
value: BCS.U64, | ||
}); | ||
bcs.registerStructType("Coin", { | ||
id: BCS.ADDRESS, | ||
// reference another registered type | ||
balance: "Balance", | ||
bcs.registerStructType('Coin', { | ||
id: BCS.ADDRESS, | ||
// reference another registered type | ||
balance: 'Balance', | ||
}); | ||
@@ -272,9 +268,9 @@ | ||
let _bytes = bcs | ||
.ser("Coin", { | ||
id: "0x0000000000000000000000000000000000000000000000000000000000000005", | ||
balance: { | ||
value: 100000000n, | ||
}, | ||
}) | ||
.toBytes(); | ||
.ser('Coin', { | ||
id: '0x0000000000000000000000000000000000000000000000000000000000000005', | ||
balance: { | ||
value: 100000000n, | ||
}, | ||
}) | ||
.toBytes(); | ||
``` | ||
@@ -285,5 +281,6 @@ | ||
To define a generic struct or an enum, pass the type parameters. It can either be done as a part of a string or as an Array. See below: | ||
```ts | ||
// Example: Generics | ||
import { BCS, getSuiMoveConfig } from "@mysten/bcs"; | ||
import { BCS, getSuiMoveConfig } from '@mysten/bcs'; | ||
const bcs = new BCS(getSuiMoveConfig()); | ||
@@ -294,28 +291,32 @@ | ||
// If you're not familiar with generics, treat them as type Templates | ||
bcs.registerStructType(["Container", "T"], { | ||
contents: "T" | ||
bcs.registerStructType(['Container', 'T'], { | ||
contents: 'T', | ||
}); | ||
// When serializing, we have to pass the type to use for `T` | ||
bcs.ser(["Container", BCS.U8], { | ||
contents: 100 | ||
}).toString("hex"); | ||
bcs | ||
.ser(['Container', BCS.U8], { | ||
contents: 100, | ||
}) | ||
.toString('hex'); | ||
// Reusing the same Container type with different contents. | ||
// Mind that generics need to be passed as Array after the main type. | ||
bcs.ser(["Container", [ "vector", BCS.BOOL ]], { | ||
contents: [ true, false, true ] | ||
}).toString("hex"); | ||
bcs | ||
.ser(['Container', ['vector', BCS.BOOL]], { | ||
contents: [true, false, true], | ||
}) | ||
.toString('hex'); | ||
// Using multiple generics - you can use any string for convenience and | ||
// readability. See how we also use array notation for a field definition. | ||
bcs.registerStructType(["VecMap", "Key", "Val"], { | ||
keys: ["vector", "Key"], | ||
values: ["vector", "Val"] | ||
bcs.registerStructType(['VecMap', 'Key', 'Val'], { | ||
keys: ['vector', 'Key'], | ||
values: ['vector', 'Val'], | ||
}); | ||
// To serialize VecMap, we can use: | ||
bcs.ser(["VecMap", BCS.STRING, BCS.STRING], { | ||
keys: [ "key1", "key2", "key3" ], | ||
values: [ "value1", "value2", "value3" ] | ||
bcs.ser(['VecMap', BCS.STRING, BCS.STRING], { | ||
keys: ['key1', 'key2', 'key3'], | ||
values: ['value1', 'value2', 'value3'], | ||
}); | ||
@@ -330,35 +331,35 @@ ``` | ||
// Example: Enum | ||
import { BCS, getSuiMoveConfig } from "@mysten/bcs"; | ||
import { BCS, getSuiMoveConfig } from '@mysten/bcs'; | ||
const bcs = new BCS(getSuiMoveConfig()); | ||
bcs.registerEnumType("Option<T>", { | ||
none: null, | ||
some: "T", | ||
bcs.registerEnumType('Option<T>', { | ||
none: null, | ||
some: 'T', | ||
}); | ||
bcs.registerEnumType("TransactionType", { | ||
single: "vector<u8>", | ||
batch: "vector<vector<u8>>", | ||
bcs.registerEnumType('TransactionType', { | ||
single: 'vector<u8>', | ||
batch: 'vector<vector<u8>>', | ||
}); | ||
// any truthy value marks empty in struct value | ||
let _optionNone = bcs.ser("Option<TransactionType>", { | ||
none: true, | ||
let _optionNone = bcs.ser('Option<TransactionType>', { | ||
none: true, | ||
}); | ||
// some now contains a value of type TransactionType | ||
let _optionTx = bcs.ser("Option<TransactionType>", { | ||
some: { | ||
single: [1, 2, 3, 4, 5, 6], | ||
}, | ||
let _optionTx = bcs.ser('Option<TransactionType>', { | ||
some: { | ||
single: [1, 2, 3, 4, 5, 6], | ||
}, | ||
}); | ||
// same type signature but a different enum invariant - batch | ||
let _optionTxBatch = bcs.ser("Option<TransactionType>", { | ||
some: { | ||
batch: [ | ||
[1, 2, 3, 4, 5, 6], | ||
[1, 2, 3, 4, 5, 6], | ||
], | ||
}, | ||
let _optionTxBatch = bcs.ser('Option<TransactionType>', { | ||
some: { | ||
batch: [ | ||
[1, 2, 3, 4, 5, 6], | ||
[1, 2, 3, 4, 5, 6], | ||
], | ||
}, | ||
}); | ||
@@ -375,3 +376,3 @@ ``` | ||
// Example: Inline Struct | ||
import { BCS, getSuiMoveConfig } from "@mysten/bcs"; | ||
import { BCS, getSuiMoveConfig } from '@mysten/bcs'; | ||
const bcs = new BCS(getSuiMoveConfig()); | ||
@@ -381,4 +382,4 @@ | ||
const coin = { | ||
id: "0000000000000000000000000000000000000000000000000000000000000005", | ||
value: 1111333333222n, | ||
id: '0000000000000000000000000000000000000000000000000000000000000005', | ||
value: 1111333333222n, | ||
}; | ||
@@ -392,4 +393,4 @@ | ||
console.assert(coin.id == coin_restored.id, "`id` must match"); | ||
console.assert(coin.value == coin_restored.value, "`value` must match"); | ||
console.assert(coin.id == coin_restored.id, '`id` must match'); | ||
console.assert(coin.value == coin_restored.value, '`value` must match'); | ||
``` | ||
@@ -427,3 +428,3 @@ | ||
// Example: Simplifying UID | ||
import { BCS, getSuiMoveConfig } from "@mysten/bcs"; | ||
import { BCS, getSuiMoveConfig } from '@mysten/bcs'; | ||
const bcs = new BCS(getSuiMoveConfig()); | ||
@@ -433,14 +434,14 @@ | ||
// structure and use only the value. | ||
bcs.registerAlias("UID", BCS.ADDRESS); | ||
bcs.registerAlias('UID', BCS.ADDRESS); | ||
// Simply follow the definition onchain | ||
bcs.registerStructType("Metadata", { | ||
name: BCS.STRING, | ||
bcs.registerStructType('Metadata', { | ||
name: BCS.STRING, | ||
}); | ||
// Same for the main object that we intend to read | ||
bcs.registerStructType("ChainObject", { | ||
id: "UID", | ||
owner: BCS.ADDRESS, | ||
meta: "Metadata", | ||
bcs.registerStructType('ChainObject', { | ||
id: 'UID', | ||
owner: BCS.ADDRESS, | ||
meta: 'Metadata', | ||
}); | ||
@@ -447,0 +448,0 @@ ``` |
122
src/b64.ts
@@ -12,39 +12,35 @@ // Copyright (c) Mysten Labs, Inc. | ||
function b64ToUint6(nChr: number) { | ||
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; | ||
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; | ||
} | ||
export function fromB64(sBase64: string, nBlocksSize?: number): Uint8Array { | ||
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); | ||
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; | ||
} | ||
} | ||
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; | ||
return taBytes; | ||
} | ||
@@ -55,40 +51,36 @@ | ||
function uint6ToB64(nUint6: number) { | ||
return nUint6 < 26 | ||
? nUint6 + 65 | ||
: nUint6 < 52 | ||
? nUint6 + 71 | ||
: nUint6 < 62 | ||
? nUint6 - 4 | ||
: nUint6 === 62 | ||
? 43 | ||
: nUint6 === 63 | ||
? 47 | ||
: 65; | ||
return nUint6 < 26 | ||
? nUint6 + 65 | ||
: nUint6 < 52 | ||
? nUint6 + 71 | ||
: nUint6 < 62 | ||
? nUint6 - 4 | ||
: nUint6 === 62 | ||
? 43 | ||
: nUint6 === 63 | ||
? 47 | ||
: 65; | ||
} | ||
export function toB64(aBytes: Uint8Array): string { | ||
var nMod3 = 2, | ||
sB64Enc = ''; | ||
var nMod3 = 2, | ||
sB64Enc = ''; | ||
for (var nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { | ||
nMod3 = nIdx % 3; | ||
if (nIdx > 0 && ((nIdx * 4) / 3) % 76 === 0) { | ||
sB64Enc += ''; | ||
} | ||
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; | ||
} | ||
} | ||
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 ? '=' : '==') | ||
); | ||
return ( | ||
sB64Enc.slice(0, sB64Enc.length - 2 + nMod3) + (nMod3 === 2 ? '' : nMod3 === 1 ? '=' : '==') | ||
); | ||
} |
@@ -5,20 +5,17 @@ // Copyright (c) Mysten Labs, Inc. | ||
export function fromHEX(hexStr: string): Uint8Array { | ||
// @ts-ignore | ||
let intArr = hexStr | ||
.replace('0x', '') | ||
.match(/.{1,2}/g) | ||
.map(byte => parseInt(byte, 16)); | ||
// @ts-ignore | ||
let intArr = hexStr | ||
.replace('0x', '') | ||
.match(/.{1,2}/g) | ||
.map((byte) => parseInt(byte, 16)); | ||
if (intArr === null) { | ||
throw new Error(`Unable to parse HEX: ${hexStr}`); | ||
} | ||
if (intArr === null) { | ||
throw new Error(`Unable to parse HEX: ${hexStr}`); | ||
} | ||
return Uint8Array.from(intArr); | ||
return Uint8Array.from(intArr); | ||
} | ||
export function toHEX(bytes: Uint8Array): string { | ||
return bytes.reduce( | ||
(str, byte) => str + byte.toString(16).padStart(2, '0'), | ||
'' | ||
); | ||
return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), ''); | ||
} |
2595
src/index.ts
@@ -14,5 +14,5 @@ // Copyright (c) Mysten Labs, Inc. | ||
import { toB64, fromB64 } from "./b64"; | ||
import { toHEX, fromHEX } from "./hex"; | ||
import bs58 from "bs58"; | ||
import { toB64, fromB64 } from './b64'; | ||
import { toHEX, fromHEX } from './hex'; | ||
import bs58 from 'bs58'; | ||
@@ -22,10 +22,10 @@ const SUI_ADDRESS_LENGTH = 32; | ||
function toLittleEndian(bigint: bigint, size: number) { | ||
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; | ||
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; | ||
} | ||
@@ -43,3 +43,3 @@ | ||
*/ | ||
export type Encoding = "base58" | "base64" | "hex"; | ||
export type Encoding = 'base58' | 'base64' | 'hex'; | ||
@@ -88,121 +88,121 @@ /** | ||
export class BcsReader { | ||
private dataView: DataView; | ||
private bytePosition: number = 0; | ||
private dataView: DataView; | ||
private bytePosition: number = 0; | ||
/** | ||
* @param {Uint8Array} data Data to use as a buffer. | ||
*/ | ||
constructor(data: Uint8Array) { | ||
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: number) { | ||
this.bytePosition += bytes; | ||
return this; | ||
} | ||
/** | ||
* Read U8 value from the buffer and shift cursor by 1. | ||
* @returns | ||
*/ | ||
read8(): number { | ||
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(): number { | ||
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(): number { | ||
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(): string { | ||
let value1 = this.read32(); | ||
let value2 = this.read32(); | ||
/** | ||
* @param {Uint8Array} data Data to use as a buffer. | ||
*/ | ||
constructor(data: Uint8Array) { | ||
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: number) { | ||
this.bytePosition += bytes; | ||
return this; | ||
} | ||
/** | ||
* Read U8 value from the buffer and shift cursor by 1. | ||
* @returns | ||
*/ | ||
read8(): number { | ||
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(): number { | ||
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(): number { | ||
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(): string { | ||
let value1 = this.read32(); | ||
let value2 = this.read32(); | ||
let result = value2.toString(16) + value1.toString(16).padStart(8, "0"); | ||
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(): string { | ||
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 16. | ||
*/ | ||
read128(): string { | ||
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(): string { | ||
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 U128 value from the buffer and shift cursor by 32. | ||
* @returns | ||
*/ | ||
read256(): string { | ||
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: number): Uint8Array { | ||
let start = this.bytePosition + this.dataView.byteOffset; | ||
let value = new Uint8Array(this.dataView.buffer, start, num); | ||
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: number): Uint8Array { | ||
let start = this.bytePosition + this.dataView.byteOffset; | ||
let value = new Uint8Array(this.dataView.buffer, start, num); | ||
this.shift(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(): number { | ||
let start = this.bytePosition + this.dataView.byteOffset; | ||
let buffer = new Uint8Array(this.dataView.buffer, start); | ||
let { value, length } = ulebDecode(buffer); | ||
return value; | ||
} | ||
/** | ||
* Read ULEB value - an integer of varying size. Used for enum indexes and | ||
* vector lengths. | ||
* @returns {Number} The ULEB value. | ||
*/ | ||
readULEB(): number { | ||
let start = this.bytePosition + this.dataView.byteOffset; | ||
let buffer = new Uint8Array(this.dataView.buffer, start); | ||
let { value, length } = ulebDecode(buffer); | ||
this.shift(length); | ||
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: (reader: BcsReader, i: number, length: number) => any): any[] { | ||
let length = this.readULEB(); | ||
let result = []; | ||
for (let i = 0; i < length; i++) { | ||
result.push(cb(this, i, length)); | ||
} | ||
return result; | ||
} | ||
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: (reader: BcsReader, i: number, length: number) => any): any[] { | ||
let length = this.readULEB(); | ||
let result = []; | ||
for (let i = 0; i < length; i++) { | ||
result.push(cb(this, i, length)); | ||
} | ||
return result; | ||
} | ||
} | ||
@@ -225,170 +225,163 @@ | ||
interface BcsWriterOptions { | ||
/** The initial size (in bytes) of the buffer tht will be allocated */ | ||
size?: number; | ||
/** The maximum size (in bytes) that the buffer is allowed to grow to */ | ||
maxSize?: number; | ||
/** The amount of bytes that will be allocated whenever additional memory is required */ | ||
allocateSize?: number; | ||
/** The initial size (in bytes) of the buffer tht will be allocated */ | ||
size?: number; | ||
/** The maximum size (in bytes) that the buffer is allowed to grow to */ | ||
maxSize?: number; | ||
/** The amount of bytes that will be allocated whenever additional memory is required */ | ||
allocateSize?: number; | ||
} | ||
export class BcsWriter { | ||
private dataView: DataView; | ||
private bytePosition: number = 0; | ||
private size: number; | ||
private maxSize: number; | ||
private allocateSize: number; | ||
private dataView: DataView; | ||
private bytePosition: number = 0; | ||
private size: number; | ||
private maxSize: number; | ||
private allocateSize: number; | ||
constructor({ | ||
size = 1024, | ||
maxSize, | ||
allocateSize = 1024, | ||
}: BcsWriterOptions = {}) { | ||
this.size = size; | ||
this.maxSize = maxSize || size; | ||
this.allocateSize = allocateSize; | ||
this.dataView = new DataView(new ArrayBuffer(size)); | ||
} | ||
constructor({ size = 1024, maxSize, allocateSize = 1024 }: BcsWriterOptions = {}) { | ||
this.size = size; | ||
this.maxSize = maxSize || size; | ||
this.allocateSize = allocateSize; | ||
this.dataView = new DataView(new ArrayBuffer(size)); | ||
} | ||
private ensureSizeOrGrow(bytes: number) { | ||
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}` | ||
); | ||
} | ||
private ensureSizeOrGrow(bytes: number) { | ||
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); | ||
} | ||
} | ||
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: number): this { | ||
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: number | bigint): this { | ||
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: number | bigint): this { | ||
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: number | bigint): this { | ||
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: number | bigint): this { | ||
toLittleEndian(BigInt(value), 8).forEach((el) => this.write8(el)); | ||
/** | ||
* Shift current cursor position by `bytes`. | ||
* | ||
* @param {Number} bytes Number of bytes to | ||
* @returns {this} Self for possible chaining. | ||
*/ | ||
shift(bytes: number): this { | ||
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: number | bigint): this { | ||
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: number | bigint): this { | ||
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: number | bigint): this { | ||
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: number | bigint): this { | ||
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: number | bigint): this { | ||
toLittleEndian(BigInt(value), 16).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: number | bigint): this { | ||
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: number | bigint): this { | ||
toLittleEndian(BigInt(value), 32).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: number | bigint): this { | ||
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: number): this { | ||
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: any[], | ||
cb: (writer: BcsWriter, el: any, i: number, len: number) => {} | ||
): this { | ||
this.writeULEB(vector.length); | ||
Array.from(vector).forEach((el, i) => cb(this, el, i, vector.length)); | ||
return this; | ||
} | ||
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: number): this { | ||
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: any[], cb: (writer: BcsWriter, el: any, i: number, len: number) => void): this { | ||
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](): Iterator<number, Iterable<number>> { | ||
for (let i = 0; i < this.bytePosition; i++) { | ||
yield this.dataView.getUint8(i); | ||
} | ||
return this.toBytes(); | ||
} | ||
/** | ||
* Adds support for iterations over the object. | ||
* @returns {Uint8Array} | ||
*/ | ||
*[Symbol.iterator](): Iterator<number, Iterable<number>> { | ||
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(): Uint8Array { | ||
return new Uint8Array(this.dataView.buffer.slice(0, this.bytePosition)); | ||
} | ||
/** | ||
* Get underlying buffer taking only value bytes (in case initial buffer size was bigger). | ||
* @returns {Uint8Array} Resulting bcs. | ||
*/ | ||
toBytes(): Uint8Array { | ||
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: Encoding): string { | ||
return encodeStr(this.toBytes(), encoding); | ||
} | ||
/** | ||
* Represent data as 'hex' or 'base64' | ||
* @param encoding Encoding to use: 'base64' or 'hex' | ||
*/ | ||
toString(encoding: Encoding): string { | ||
return encodeStr(this.toBytes(), encoding); | ||
} | ||
} | ||
@@ -399,18 +392,18 @@ | ||
function ulebEncode(num: number): number[] { | ||
let arr = []; | ||
let len = 0; | ||
let arr = []; | ||
let len = 0; | ||
if (num === 0) { | ||
return [0]; | ||
} | ||
if (num === 0) { | ||
return [0]; | ||
} | ||
while (num > 0) { | ||
arr[len] = num & 0x7f; | ||
if ((num >>= 7)) { | ||
arr[len] |= 0x80; | ||
} | ||
len += 1; | ||
} | ||
while (num > 0) { | ||
arr[len] = num & 0x7f; | ||
if ((num >>= 7)) { | ||
arr[len] |= 0x80; | ||
} | ||
len += 1; | ||
} | ||
return arr; | ||
return arr; | ||
} | ||
@@ -421,23 +414,24 @@ | ||
function ulebDecode(arr: number[] | Uint8Array): { | ||
value: number; | ||
length: number; | ||
value: number; | ||
length: number; | ||
} { | ||
let total = 0; | ||
let shift = 0; | ||
let len = 0; | ||
let total = 0; | ||
let shift = 0; | ||
let len = 0; | ||
while (true) { | ||
let byte = arr[len]; | ||
len += 1; | ||
total |= (byte & 0x7f) << shift; | ||
if ((byte & 0x80) === 0) { | ||
break; | ||
} | ||
shift += 7; | ||
} | ||
// eslint-disable-next-line no-constant-condition | ||
while (true) { | ||
let byte = arr[len]; | ||
len += 1; | ||
total |= (byte & 0x7f) << shift; | ||
if ((byte & 0x80) === 0) { | ||
break; | ||
} | ||
shift += 7; | ||
} | ||
return { | ||
value: total, | ||
length: len, | ||
}; | ||
return { | ||
value: total, | ||
length: len, | ||
}; | ||
} | ||
@@ -450,21 +444,21 @@ | ||
export interface TypeInterface { | ||
encode: ( | ||
self: BCS, | ||
data: any, | ||
options: BcsWriterOptions | undefined, | ||
typeParams: TypeName[] | ||
) => BcsWriter; | ||
decode: (self: BCS, data: Uint8Array, typeParams: TypeName[]) => any; | ||
encode: ( | ||
self: BCS, | ||
data: any, | ||
options: BcsWriterOptions | undefined, | ||
typeParams: TypeName[], | ||
) => BcsWriter; | ||
decode: (self: BCS, data: Uint8Array, typeParams: TypeName[]) => any; | ||
_encodeRaw: ( | ||
writer: BcsWriter, | ||
data: any, | ||
typeParams: TypeName[], | ||
typeMap: { [key: string]: TypeName } | ||
) => BcsWriter; | ||
_decodeRaw: ( | ||
reader: BcsReader, | ||
typeParams: TypeName[], | ||
typeMap: { [key: string]: TypeName } | ||
) => any; | ||
_encodeRaw: ( | ||
writer: BcsWriter, | ||
data: any, | ||
typeParams: TypeName[], | ||
typeMap: { [key: string]: TypeName }, | ||
) => BcsWriter; | ||
_decodeRaw: ( | ||
reader: BcsReader, | ||
typeParams: TypeName[], | ||
typeMap: { [key: string]: TypeName }, | ||
) => any; | ||
} | ||
@@ -477,3 +471,3 @@ | ||
export type StructTypeDefinition = { | ||
[key: string]: TypeName | StructTypeDefinition; | ||
[key: string]: TypeName | StructTypeDefinition; | ||
}; | ||
@@ -495,3 +489,3 @@ | ||
export type EnumTypeDefinition = { | ||
[key: string]: TypeName | StructTypeDefinition | null; | ||
[key: string]: TypeName | StructTypeDefinition | null; | ||
}; | ||
@@ -503,38 +497,38 @@ | ||
export type BcsConfig = { | ||
/** | ||
* Defines type name for the vector / array type. | ||
* In Move: `vector<T>` or `vector`. | ||
*/ | ||
vectorType: string; | ||
/** | ||
* Address length. Varies depending on a platform and | ||
* has to be specified for the `address` type. | ||
*/ | ||
addressLength: number; | ||
/** | ||
* Defines type name for the vector / array type. | ||
* In Move: `vector<T>` or `vector`. | ||
*/ | ||
vectorType: string; | ||
/** | ||
* Address length. Varies depending on a platform and | ||
* has to be specified for the `address` type. | ||
*/ | ||
addressLength: number; | ||
/** | ||
* Custom encoding for address. Supported values are | ||
* either 'hex' or 'base64'. | ||
*/ | ||
addressEncoding?: "hex" | "base64"; | ||
/** | ||
* Opening and closing symbol for type parameters. Can be | ||
* any pair of symbols (eg `['(', ')']`); default value follows | ||
* Rust and Move: `<` and `>`. | ||
*/ | ||
genericSeparators?: [string, string]; | ||
/** | ||
* Type definitions for the BCS. This field allows spawning | ||
* BCS instance from JSON or another prepared configuration. | ||
* Optional. | ||
*/ | ||
types?: { | ||
structs?: { [key: string]: StructTypeDefinition }; | ||
enums?: { [key: string]: EnumTypeDefinition }; | ||
aliases?: { [key: string]: string }; | ||
}; | ||
/** | ||
* Whether to auto-register primitive types on launch. | ||
*/ | ||
withPrimitives?: boolean; | ||
/** | ||
* Custom encoding for address. Supported values are | ||
* either 'hex' or 'base64'. | ||
*/ | ||
addressEncoding?: 'hex' | 'base64'; | ||
/** | ||
* Opening and closing symbol for type parameters. Can be | ||
* any pair of symbols (eg `['(', ')']`); default value follows | ||
* Rust and Move: `<` and `>`. | ||
*/ | ||
genericSeparators?: [string, string]; | ||
/** | ||
* Type definitions for the BCS. This field allows spawning | ||
* BCS instance from JSON or another prepared configuration. | ||
* Optional. | ||
*/ | ||
types?: { | ||
structs?: { [key: string]: StructTypeDefinition }; | ||
enums?: { [key: string]: EnumTypeDefinition }; | ||
aliases?: { [key: string]: string }; | ||
}; | ||
/** | ||
* Whether to auto-register primitive types on launch. | ||
*/ | ||
withPrimitives?: boolean; | ||
}; | ||
@@ -546,899 +540,803 @@ | ||
export class BCS { | ||
// Prefefined types constants | ||
static readonly U8: string = "u8"; | ||
static readonly U16: string = "u16"; | ||
static readonly U32: string = "u32"; | ||
static readonly U64: string = "u64"; | ||
static readonly U128: string = "u128"; | ||
static readonly U256: string = "u256"; | ||
static readonly BOOL: string = "bool"; | ||
static readonly VECTOR: string = "vector"; | ||
static readonly ADDRESS: string = "address"; | ||
static readonly STRING: string = "string"; | ||
static readonly HEX: string = "hex-string"; | ||
static readonly BASE58: string = "base58-string"; | ||
static readonly BASE64: string = "base64-string"; | ||
// Prefefined types constants | ||
static readonly U8: string = 'u8'; | ||
static readonly U16: string = 'u16'; | ||
static readonly U32: string = 'u32'; | ||
static readonly U64: string = 'u64'; | ||
static readonly U128: string = 'u128'; | ||
static readonly U256: string = 'u256'; | ||
static readonly BOOL: string = 'bool'; | ||
static readonly VECTOR: string = 'vector'; | ||
static readonly ADDRESS: string = 'address'; | ||
static readonly STRING: string = 'string'; | ||
static readonly HEX: string = 'hex-string'; | ||
static readonly BASE58: string = 'base58-string'; | ||
static readonly BASE64: string = 'base64-string'; | ||
/** | ||
* 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. | ||
*/ | ||
public types: Map<string, TypeInterface | string> = new Map(); | ||
/** | ||
* 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. | ||
*/ | ||
public types: Map<string, TypeInterface | string> = new Map(); | ||
/** | ||
* Stored BcsConfig for the current instance of BCS. | ||
*/ | ||
protected schema: BcsConfig; | ||
/** | ||
* Stored BcsConfig for the current instance of BCS. | ||
*/ | ||
protected schema: BcsConfig; | ||
/** | ||
* Count temp keys to generate a new one when requested. | ||
*/ | ||
protected counter: number = 0; | ||
/** | ||
* Count temp keys to generate a new one when requested. | ||
*/ | ||
protected counter: number = 0; | ||
/** | ||
* Name of the key to use for temporary struct definitions. | ||
* Returns a temp key + index (for a case when multiple temp | ||
* structs are processed). | ||
*/ | ||
private tempKey() { | ||
return `bcs-struct-${++this.counter}`; | ||
} | ||
/** | ||
* Name of the key to use for temporary struct definitions. | ||
* Returns a temp key + index (for a case when multiple temp | ||
* structs are processed). | ||
*/ | ||
private tempKey() { | ||
return `bcs-struct-${++this.counter}`; | ||
} | ||
/** | ||
* 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: BcsConfig | BCS) { | ||
// if BCS instance is passed -> clone its schema | ||
if (schema instanceof BCS) { | ||
this.schema = schema.schema; | ||
this.types = new Map(schema.types); | ||
return; | ||
} | ||
/** | ||
* 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: BcsConfig | BCS) { | ||
// if BCS instance is passed -> clone its schema | ||
if (schema instanceof BCS) { | ||
this.schema = schema.schema; | ||
this.types = new Map(schema.types); | ||
return; | ||
} | ||
this.schema = schema; | ||
this.schema = schema; | ||
// Register address type under key 'address'. | ||
this.registerAddressType( | ||
BCS.ADDRESS, | ||
schema.addressLength, | ||
schema.addressEncoding | ||
); | ||
this.registerVectorType(schema.vectorType); | ||
// Register address type under key 'address'. | ||
this.registerAddressType(BCS.ADDRESS, schema.addressLength, schema.addressEncoding); | ||
this.registerVectorType(schema.vectorType); | ||
// Register struct types if they were passed. | ||
if (schema.types && schema.types.structs) { | ||
for (let name of Object.keys(schema.types.structs)) { | ||
this.registerStructType(name, schema.types.structs[name]); | ||
} | ||
} | ||
// Register struct types if they were passed. | ||
if (schema.types && schema.types.structs) { | ||
for (let name of Object.keys(schema.types.structs)) { | ||
this.registerStructType(name, schema.types.structs[name]); | ||
} | ||
} | ||
// Register enum types if they were passed. | ||
if (schema.types && schema.types.enums) { | ||
for (let name of Object.keys(schema.types.enums)) { | ||
this.registerEnumType(name, schema.types.enums[name]); | ||
} | ||
} | ||
// Register enum types if they were passed. | ||
if (schema.types && schema.types.enums) { | ||
for (let name of Object.keys(schema.types.enums)) { | ||
this.registerEnumType(name, schema.types.enums[name]); | ||
} | ||
} | ||
// Register aliases if they were passed. | ||
if (schema.types && schema.types.aliases) { | ||
for (let name of Object.keys(schema.types.aliases)) { | ||
this.registerAlias(name, schema.types.aliases[name]); | ||
} | ||
} | ||
// Register aliases if they were passed. | ||
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); | ||
} | ||
} | ||
if (schema.withPrimitives !== false) { | ||
registerPrimitives(this); | ||
} | ||
} | ||
/** | ||
* 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()` | ||
*/ | ||
public ser( | ||
type: TypeName | StructTypeDefinition, | ||
data: any, | ||
options?: BcsWriterOptions | ||
): BcsWriter { | ||
if (typeof type === "string" || Array.isArray(type)) { | ||
const { name, params } = this.parseTypeName(type); | ||
return this.getTypeInterface(name).encode( | ||
this, | ||
data, | ||
options, | ||
params as string[] | ||
); | ||
} | ||
/** | ||
* 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()` | ||
*/ | ||
public ser( | ||
type: TypeName | StructTypeDefinition, | ||
data: any, | ||
options?: BcsWriterOptions, | ||
): BcsWriter { | ||
if (typeof type === 'string' || Array.isArray(type)) { | ||
const { name, params } = this.parseTypeName(type); | ||
return this.getTypeInterface(name).encode(this, data, options, params as string[]); | ||
} | ||
// Quick serialization without registering the type in the main struct. | ||
if (typeof type === "object") { | ||
const key = this.tempKey(); | ||
const temp = new BCS(this); | ||
return temp.registerStructType(key, type).ser(key, data, options); | ||
} | ||
// Quick serialization without registering the type in the main struct. | ||
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. \n${JSON.stringify( | ||
type | ||
)}` | ||
); | ||
} | ||
throw new Error(`Incorrect type passed into the '.ser()' function. \n${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. | ||
*/ | ||
public de( | ||
type: TypeName | StructTypeDefinition, | ||
data: Uint8Array | string, | ||
encoding?: Encoding | ||
): any { | ||
if (typeof data === "string") { | ||
if (encoding) { | ||
data = decodeStr(data, encoding); | ||
} else { | ||
throw new Error("To pass a string to `bcs.de`, specify encoding"); | ||
} | ||
} | ||
/** | ||
* 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. | ||
*/ | ||
public de( | ||
type: TypeName | StructTypeDefinition, | ||
data: Uint8Array | string, | ||
encoding?: Encoding, | ||
): any { | ||
if (typeof data === 'string') { | ||
if (encoding) { | ||
data = decodeStr(data, encoding); | ||
} else { | ||
throw new Error('To pass a string to `bcs.de`, specify encoding'); | ||
} | ||
} | ||
// In case the type specified is already registered. | ||
if (typeof type === "string" || Array.isArray(type)) { | ||
const { name, params } = this.parseTypeName(type); | ||
return this.getTypeInterface(name).decode(this, data, params as string[]); | ||
} | ||
// In case the type specified is already registered. | ||
if (typeof type === 'string' || Array.isArray(type)) { | ||
const { name, params } = this.parseTypeName(type); | ||
return this.getTypeInterface(name).decode(this, data, params as string[]); | ||
} | ||
// Deserialize without registering a type using a temporary clone. | ||
if (typeof type === "object") { | ||
const temp = new BCS(this); | ||
const key = this.tempKey(); | ||
return temp.registerStructType(key, type).de(key, data, encoding); | ||
} | ||
// Deserialize without registering a type using a temporary clone. | ||
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. \n${JSON.stringify( | ||
type | ||
)}` | ||
); | ||
} | ||
throw new Error(`Incorrect type passed into the '.de()' function. \n${JSON.stringify(type)}`); | ||
} | ||
/** | ||
* Check whether a `TypeInterface` has been loaded for a `type`. | ||
* @param type Name of the type to check. | ||
* @returns | ||
*/ | ||
public hasType(type: string): boolean { | ||
return this.types.has(type); | ||
} | ||
/** | ||
* Check whether a `TypeInterface` has been loaded for a `type`. | ||
* @param type Name of the type to check. | ||
* @returns | ||
*/ | ||
public hasType(type: string): boolean { | ||
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'); | ||
* ``` | ||
*/ | ||
public registerAlias(name: string, forType: string): BCS { | ||
this.types.set(name, forType); | ||
return this; | ||
} | ||
/** | ||
* 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'); | ||
* ``` | ||
*/ | ||
public registerAlias(name: string, forType: string): BCS { | ||
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. | ||
*/ | ||
public registerType( | ||
typeName: TypeName, | ||
encodeCb: ( | ||
writer: BcsWriter, | ||
data: any, | ||
typeParams: TypeName[], | ||
typeMap: { [key: string]: TypeName } | ||
) => BcsWriter, | ||
decodeCb: ( | ||
reader: BcsReader, | ||
typeParams: TypeName[], | ||
typeMap: { [key: string]: TypeName } | ||
) => any, | ||
validateCb: (data: any) => boolean = () => true | ||
): BCS { | ||
const { name, params: generics } = this.parseTypeName(typeName); | ||
/** | ||
* 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. | ||
*/ | ||
public registerType( | ||
typeName: TypeName, | ||
encodeCb: ( | ||
writer: BcsWriter, | ||
data: any, | ||
typeParams: TypeName[], | ||
typeMap: { [key: string]: TypeName }, | ||
) => BcsWriter, | ||
decodeCb: ( | ||
reader: BcsReader, | ||
typeParams: TypeName[], | ||
typeMap: { [key: string]: TypeName }, | ||
) => any, | ||
validateCb: (data: any) => boolean = () => true, | ||
): BCS { | ||
const { name, params: generics } = this.parseTypeName(typeName); | ||
this.types.set(name, { | ||
encode(self: BCS, data, options: BcsWriterOptions, typeParams) { | ||
const typeMap = (generics as string[]).reduce( | ||
(acc: any, value: string, index) => { | ||
return Object.assign(acc, { [value]: typeParams[index] }); | ||
}, | ||
{} | ||
); | ||
this.types.set(name, { | ||
encode(self: BCS, data, options: BcsWriterOptions, typeParams) { | ||
const typeMap = (generics as string[]).reduce((acc: any, value: string, index) => { | ||
return Object.assign(acc, { [value]: typeParams[index] }); | ||
}, {}); | ||
return this._encodeRaw.call( | ||
self, | ||
new BcsWriter(options), | ||
data, | ||
typeParams, | ||
typeMap | ||
); | ||
}, | ||
decode(self: BCS, data, typeParams) { | ||
const typeMap = (generics as string[]).reduce( | ||
(acc: any, value: string, index) => { | ||
return Object.assign(acc, { [value]: typeParams[index] }); | ||
}, | ||
{} | ||
); | ||
return this._encodeRaw.call(self, new BcsWriter(options), data, typeParams, typeMap); | ||
}, | ||
decode(self: BCS, data, typeParams) { | ||
const typeMap = (generics as string[]).reduce((acc: any, value: string, index) => { | ||
return Object.assign(acc, { [value]: typeParams[index] }); | ||
}, {}); | ||
return this._decodeRaw.call( | ||
self, | ||
new BcsReader(data), | ||
typeParams, | ||
typeMap | ||
); | ||
}, | ||
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); | ||
}, | ||
} as TypeInterface); | ||
// 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); | ||
}, | ||
} as TypeInterface); | ||
return this; | ||
} | ||
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 | ||
*/ | ||
public registerAddressType( | ||
name: string, | ||
length: number, | ||
encoding: Encoding | void = "hex" | ||
): BCS { | ||
switch (encoding) { | ||
case "base64": | ||
return this.registerType( | ||
name, | ||
function encodeAddress(writer, data: string) { | ||
return fromB64(data).reduce( | ||
(writer, el) => writer.write8(el), | ||
writer | ||
); | ||
}, | ||
function decodeAddress(reader) { | ||
return toB64(reader.readBytes(length)); | ||
} | ||
); | ||
case "hex": | ||
return this.registerType( | ||
name, | ||
function encodeAddress(writer, data: string) { | ||
return fromHEX(data).reduce( | ||
(writer, el) => writer.write8(el), | ||
writer | ||
); | ||
}, | ||
function decodeAddress(reader) { | ||
return toHEX(reader.readBytes(length)); | ||
} | ||
); | ||
default: | ||
throw new Error("Unsupported encoding! Use either hex or base64"); | ||
} | ||
} | ||
/** | ||
* 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 | ||
*/ | ||
public registerAddressType(name: string, length: number, encoding: Encoding | void = 'hex'): BCS { | ||
switch (encoding) { | ||
case 'base64': | ||
return this.registerType( | ||
name, | ||
function encodeAddress(writer, data: string) { | ||
return fromB64(data).reduce((writer, el) => writer.write8(el), writer); | ||
}, | ||
function decodeAddress(reader) { | ||
return toB64(reader.readBytes(length)); | ||
}, | ||
); | ||
case 'hex': | ||
return this.registerType( | ||
name, | ||
function encodeAddress(writer, data: string) { | ||
return fromHEX(data).reduce((writer, el) => writer.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. | ||
*/ | ||
private registerVectorType(typeName: string): BCS { | ||
let { name, params } = this.parseTypeName(typeName); | ||
if (params.length > 1) { | ||
throw new Error("Vector can have only one type parameter; got " + name); | ||
} | ||
/** | ||
* 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. | ||
*/ | ||
private registerVectorType(typeName: string): BCS { | ||
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( | ||
this: BCS, | ||
writer: BcsWriter, | ||
data: any[], | ||
typeParams: TypeName[], | ||
typeMap | ||
) { | ||
return writer.writeVec(data, (writer, el) => { | ||
let elementType: TypeName = typeParams[0]; | ||
if (!elementType) { | ||
throw new Error( | ||
`Incorrect number of type parameters passed a to vector '${typeName}'` | ||
); | ||
} | ||
return this.registerType( | ||
typeName, | ||
function encodeVector( | ||
this: BCS, | ||
writer: BcsWriter, | ||
data: any[], | ||
typeParams: TypeName[], | ||
typeMap, | ||
) { | ||
return writer.writeVec(data, (writer, el) => { | ||
let elementType: TypeName = typeParams[0]; | ||
if (!elementType) { | ||
throw new Error(`Incorrect number of type parameters passed a to vector '${typeName}'`); | ||
} | ||
let { name, params } = this.parseTypeName(elementType); | ||
if (this.hasType(name)) { | ||
return this.getTypeInterface(name)._encodeRaw.call( | ||
this, | ||
writer, | ||
el, | ||
params, | ||
typeMap | ||
); | ||
} | ||
let { name, params } = this.parseTypeName(elementType); | ||
if (this.hasType(name)) { | ||
return this.getTypeInterface(name)._encodeRaw.call(this, writer, el, params, typeMap); | ||
} | ||
if (!(name in typeMap)) { | ||
throw new Error( | ||
`Unable to find a matching type definition for ${name} in vector; make sure you passed a generic` | ||
); | ||
} | ||
if (!(name in typeMap)) { | ||
throw new Error( | ||
`Unable to find a matching type definition for ${name} in vector; make sure you passed a generic`, | ||
); | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName( | ||
typeMap[name] | ||
); | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name]); | ||
return this.getTypeInterface(innerName)._encodeRaw.call( | ||
this, | ||
writer, | ||
el, | ||
innerParams, | ||
typeMap | ||
); | ||
}); | ||
}, | ||
function decodeVector(this: BCS, reader: BcsReader, typeParams, typeMap) { | ||
return reader.readVec((reader) => { | ||
let elementType: TypeName = typeParams[0]; | ||
if (!elementType) { | ||
throw new Error( | ||
`Incorrect number of type parameters passed to a vector '${typeName}'` | ||
); | ||
} | ||
return this.getTypeInterface(innerName)._encodeRaw.call( | ||
this, | ||
writer, | ||
el, | ||
innerParams, | ||
typeMap, | ||
); | ||
}); | ||
}, | ||
function decodeVector(this: BCS, reader: BcsReader, typeParams, typeMap) { | ||
return reader.readVec((reader) => { | ||
let elementType: TypeName = typeParams[0]; | ||
if (!elementType) { | ||
throw new Error(`Incorrect number of type parameters passed to a vector '${typeName}'`); | ||
} | ||
let { name, params } = this.parseTypeName(elementType); | ||
if (this.hasType(name)) { | ||
return this.getTypeInterface(name)._decodeRaw.call( | ||
this, | ||
reader, | ||
params, | ||
typeMap | ||
); | ||
} | ||
let { name, params } = this.parseTypeName(elementType); | ||
if (this.hasType(name)) { | ||
return this.getTypeInterface(name)._decodeRaw.call(this, reader, params, typeMap); | ||
} | ||
if (!(name in typeMap)) { | ||
throw new Error( | ||
`Unable to find a matching type definition for ${name} in vector; make sure you passed a generic` | ||
); | ||
} | ||
if (!(name in typeMap)) { | ||
throw new Error( | ||
`Unable to find a matching type definition for ${name} in vector; make sure you passed a generic`, | ||
); | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName( | ||
typeMap[name] | ||
); | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name]); | ||
return this.getTypeInterface(innerName)._decodeRaw.call( | ||
this, | ||
reader, | ||
innerParams, | ||
typeMap | ||
); | ||
}); | ||
} | ||
); | ||
} | ||
return this.getTypeInterface(innerName)._decodeRaw.call( | ||
this, | ||
reader, | ||
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. | ||
*/ | ||
public registerStructType( | ||
typeName: TypeName, | ||
fields: StructTypeDefinition | ||
): BCS { | ||
// When an Object is passed, we register it under a new key and store it | ||
// in the registered type system. This way we allow nested inline definitions. | ||
for (let key in fields) { | ||
let internalName = this.tempKey(); | ||
let value = fields[key]; | ||
/** | ||
* 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. | ||
*/ | ||
public registerStructType(typeName: TypeName, fields: StructTypeDefinition): BCS { | ||
// When an Object is passed, we register it under a new key and store it | ||
// in the registered type system. This way we allow nested inline definitions. | ||
for (let key in fields) { | ||
let internalName = this.tempKey(); | ||
let value = fields[key]; | ||
// TODO: add a type guard here? | ||
if (!Array.isArray(value) && typeof value !== "string") { | ||
fields[key] = internalName; | ||
this.registerStructType(internalName, value as StructTypeDefinition); | ||
} | ||
} | ||
// TODO: add a type guard here? | ||
if (!Array.isArray(value) && typeof value !== 'string') { | ||
fields[key] = internalName; | ||
this.registerStructType(internalName, value as StructTypeDefinition); | ||
} | ||
} | ||
let struct = Object.freeze(fields); // Make sure the order doesn't get changed | ||
let struct = Object.freeze(fields); // Make sure the order doesn't get changed | ||
// IMPORTANT: we need to store canonical order of fields for each registered | ||
// struct so we maintain it and allow developers to use any field ordering in | ||
// their code (and not cause mismatches based on field order). | ||
let canonicalOrder = Object.keys(struct); | ||
// IMPORTANT: we need to store canonical order of fields for each registered | ||
// struct so we maintain it and allow developers to use any field ordering in | ||
// their code (and not cause mismatches based on field order). | ||
let canonicalOrder = Object.keys(struct); | ||
// Holds generics for the struct definition. At this stage we can check that | ||
// generic parameter matches the one defined in the struct. | ||
let { name: structName, params: generics } = this.parseTypeName(typeName); | ||
// Holds generics for the struct definition. At this stage we can check that | ||
// generic parameter matches the one defined in the struct. | ||
let { name: structName, params: generics } = this.parseTypeName(typeName); | ||
// Make sure all the types in the fields description are already known | ||
// and that all the field types are strings. | ||
return this.registerType( | ||
typeName, | ||
function encodeStruct( | ||
this: BCS, | ||
writer: BcsWriter, | ||
data: { [key: string]: any }, | ||
typeParams, | ||
typeMap | ||
) { | ||
if (!data || data.constructor !== Object) { | ||
throw new Error( | ||
`Expected ${structName} to be an Object, got: ${data}` | ||
); | ||
} | ||
// Make sure all the types in the fields description are already known | ||
// and that all the field types are strings. | ||
return this.registerType( | ||
typeName, | ||
function encodeStruct( | ||
this: BCS, | ||
writer: BcsWriter, | ||
data: { [key: string]: any }, | ||
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}` | ||
); | ||
} | ||
if (typeParams.length !== generics.length) { | ||
throw new Error( | ||
`Incorrect number of generic parameters passed; expected: ${generics.length}, got: ${typeParams.length}`, | ||
); | ||
} | ||
// follow the canonical order when serializing | ||
for (let key of canonicalOrder) { | ||
if (!(key in data)) { | ||
throw new Error( | ||
`Struct ${structName} requires field ${key}:${struct[key]}` | ||
); | ||
} | ||
// follow the canonical order when serializing | ||
for (let key of canonicalOrder) { | ||
if (!(key in data)) { | ||
throw new Error(`Struct ${structName} requires field ${key}:${struct[key]}`); | ||
} | ||
// Before deserializing, read the canonical field type. | ||
const { name: fieldType, params: fieldParams } = this.parseTypeName( | ||
struct[key] as TypeName | ||
); | ||
// Before deserializing, read the canonical field type. | ||
const { name: fieldType, params: fieldParams } = this.parseTypeName( | ||
struct[key] as TypeName, | ||
); | ||
// Check whether this type is a generic defined in this struct. | ||
// If it is -> read the type parameter matching its index. | ||
// If not - tread as a regular field. | ||
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]); | ||
// Check whether this type is a generic defined in this struct. | ||
// If it is -> read the type parameter matching its index. | ||
// If not - tread as a regular field. | ||
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 the type from the type parameters already exists | ||
// and known -> proceed with type decoding. | ||
if (this.hasType(name)) { | ||
this.getTypeInterface(name)._encodeRaw.call( | ||
this, | ||
writer, | ||
data[key], | ||
params as string[], | ||
typeMap | ||
); | ||
continue; | ||
} | ||
// If the type from the type parameters already exists | ||
// and known -> proceed with type decoding. | ||
if (this.hasType(name)) { | ||
this.getTypeInterface(name)._encodeRaw.call( | ||
this, | ||
writer, | ||
data[key], | ||
params as string[], | ||
typeMap, | ||
); | ||
continue; | ||
} | ||
// Alternatively, if it's a global generic parameter... | ||
if (!(name in typeMap)) { | ||
throw new Error( | ||
`Unable to find a matching type definition for ${name} in ${structName}; make sure you passed a generic` | ||
); | ||
} | ||
// Alternatively, if it's a global generic parameter... | ||
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(this: BCS, reader: BcsReader, typeParams, typeMap) { | ||
if (typeParams.length !== generics.length) { | ||
throw new Error( | ||
`Incorrect number of generic parameters passed; expected: ${generics.length}, got: ${typeParams.length}` | ||
); | ||
} | ||
let { name: innerName, params: innerParams } = this.parseTypeName(typeMap[name]); | ||
this.getTypeInterface(innerName)._encodeRaw.call( | ||
this, | ||
writer, | ||
data[key], | ||
innerParams, | ||
typeMap, | ||
); | ||
} | ||
} | ||
return writer; | ||
}, | ||
function decodeStruct(this: BCS, reader: BcsReader, typeParams, typeMap) { | ||
if (typeParams.length !== generics.length) { | ||
throw new Error( | ||
`Incorrect number of generic parameters passed; expected: ${generics.length}, got: ${typeParams.length}`, | ||
); | ||
} | ||
let result: { [key: string]: any } = {}; | ||
for (let key of canonicalOrder) { | ||
const { name: fieldName, params: fieldParams } = this.parseTypeName( | ||
struct[key] as TypeName | ||
); | ||
let result: { [key: string]: any } = {}; | ||
for (let key of canonicalOrder) { | ||
const { name: fieldName, params: fieldParams } = this.parseTypeName( | ||
struct[key] as TypeName, | ||
); | ||
// if it's not a generic | ||
if (!generics.includes(fieldName)) { | ||
result[key] = this.getTypeInterface(fieldName)._decodeRaw.call( | ||
this, | ||
reader, | ||
fieldParams as string[], | ||
typeMap | ||
); | ||
} else { | ||
const paramIdx = generics.indexOf(fieldName); | ||
let { name, params } = this.parseTypeName(typeParams[paramIdx]); | ||
// if it's not a generic | ||
if (!generics.includes(fieldName)) { | ||
result[key] = this.getTypeInterface(fieldName)._decodeRaw.call( | ||
this, | ||
reader, | ||
fieldParams as string[], | ||
typeMap, | ||
); | ||
} else { | ||
const paramIdx = generics.indexOf(fieldName); | ||
let { name, params } = this.parseTypeName(typeParams[paramIdx]); | ||
// If the type from the type parameters already exists | ||
// and known -> proceed with type decoding. | ||
if (this.hasType(name)) { | ||
result[key] = this.getTypeInterface(name)._decodeRaw.call( | ||
this, | ||
reader, | ||
params, | ||
typeMap | ||
); | ||
continue; | ||
} | ||
// If the type from the type parameters already exists | ||
// and known -> proceed with type decoding. | ||
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` | ||
); | ||
} | ||
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; | ||
} | ||
); | ||
} | ||
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 | ||
*/ | ||
public registerEnumType( | ||
typeName: TypeName, | ||
variants: EnumTypeDefinition | ||
): BCS { | ||
// When an Object is passed, we register it under a new key and store it | ||
// in the registered type system. This way we allow nested inline definitions. | ||
for (let key in variants) { | ||
let internalName = this.tempKey(); | ||
let value = variants[key]; | ||
/** | ||
* 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 | ||
*/ | ||
public registerEnumType(typeName: TypeName, variants: EnumTypeDefinition): BCS { | ||
// When an Object is passed, we register it under a new key and store it | ||
// in the registered type system. This way we allow nested inline definitions. | ||
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 as StructTypeDefinition); | ||
} | ||
} | ||
if (value !== null && !Array.isArray(value) && typeof value !== 'string') { | ||
variants[key] = internalName; | ||
this.registerStructType(internalName, value as StructTypeDefinition); | ||
} | ||
} | ||
let struct = Object.freeze(variants); // Make sure the order doesn't get changed | ||
let struct = Object.freeze(variants); // Make sure the order doesn't get changed | ||
// IMPORTANT: enum is an ordered type and we have to preserve ordering in BCS | ||
let canonicalOrder = Object.keys(struct); | ||
// IMPORTANT: enum is an ordered type and we have to preserve ordering in BCS | ||
let canonicalOrder = Object.keys(struct); | ||
// Parse type parameters in advance to know the index of each generic parameter. | ||
let { name, params: canonicalTypeParams } = this.parseTypeName(typeName); | ||
// Parse type parameters in advance to know the index of each generic parameter. | ||
let { name, params: canonicalTypeParams } = this.parseTypeName(typeName); | ||
return this.registerType( | ||
typeName, | ||
function encodeEnum( | ||
this: BCS, | ||
writer: BcsWriter, | ||
data: { [key: string]: any | null }, | ||
typeParams, | ||
typeMap | ||
) { | ||
if (!data) { | ||
throw new Error( | ||
`Unable to write enum "${name}", missing data.\nReceived: "${data}"` | ||
); | ||
} | ||
if (typeof data !== "object") { | ||
throw new Error( | ||
`Incorrect data passed into enum "${name}", expected object with properties: "${canonicalOrder.join( | ||
" | " | ||
)}".\nReceived: "${JSON.stringify(data)}"` | ||
); | ||
} | ||
return this.registerType( | ||
typeName, | ||
function encodeEnum( | ||
this: BCS, | ||
writer: BcsWriter, | ||
data: { [key: string]: any | null }, | ||
typeParams, | ||
typeMap, | ||
) { | ||
if (!data) { | ||
throw new Error(`Unable to write enum "${name}", missing data.\nReceived: "${data}"`); | ||
} | ||
if (typeof data !== 'object') { | ||
throw new Error( | ||
`Incorrect data passed into enum "${name}", expected object with properties: "${canonicalOrder.join( | ||
' | ', | ||
)}".\nReceived: "${JSON.stringify(data)}"`, | ||
); | ||
} | ||
let key = Object.keys(data)[0]; | ||
if (key === undefined) { | ||
throw new Error( | ||
`Empty object passed as invariant of the enum "${name}"` | ||
); | ||
} | ||
let key = Object.keys(data)[0]; | ||
if (key === undefined) { | ||
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] as TypeName | null; | ||
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] as TypeName | null; | ||
// write order byte | ||
writer.write8(orderByte); | ||
// write order byte | ||
writer.write8(orderByte); | ||
// When { "key": null } - empty value for the invariant. | ||
if (invariantType === null) { | ||
return writer; | ||
} | ||
// When { "key": null } - empty value for the invariant. | ||
if (invariantType === null) { | ||
return writer; | ||
} | ||
let paramIndex = canonicalTypeParams.indexOf(invariantType); | ||
let typeOrParam = | ||
paramIndex === -1 ? invariantType : typeParams[paramIndex]; | ||
let paramIndex = canonicalTypeParams.indexOf(invariantType); | ||
let typeOrParam = paramIndex === -1 ? invariantType : typeParams[paramIndex]; | ||
{ | ||
let { name, params } = this.parseTypeName(typeOrParam); | ||
return this.getTypeInterface(name)._encodeRaw.call( | ||
this, | ||
writer, | ||
data[key], | ||
params, | ||
typeMap | ||
); | ||
} | ||
}, | ||
function decodeEnum(this: BCS, reader: BcsReader, typeParams, typeMap) { | ||
let orderByte = reader.readULEB(); | ||
let invariant = canonicalOrder[orderByte]; | ||
let invariantType = struct[invariant] as TypeName | null; | ||
{ | ||
let { name, params } = this.parseTypeName(typeOrParam); | ||
return this.getTypeInterface(name)._encodeRaw.call( | ||
this, | ||
writer, | ||
data[key], | ||
params, | ||
typeMap, | ||
); | ||
} | ||
}, | ||
function decodeEnum(this: BCS, reader: BcsReader, typeParams, typeMap) { | ||
let orderByte = reader.readULEB(); | ||
let invariant = canonicalOrder[orderByte]; | ||
let invariantType = struct[invariant] as TypeName | null; | ||
if (orderByte === -1) { | ||
throw new Error( | ||
`Decoding type mismatch, expected enum "${name}" invariant index, received "${orderByte}"` | ||
); | ||
} | ||
if (orderByte === -1) { | ||
throw new Error( | ||
`Decoding type mismatch, expected enum "${name}" invariant index, received "${orderByte}"`, | ||
); | ||
} | ||
// Encode an empty value for the enum. | ||
if (invariantType === null) { | ||
return { [invariant]: true }; | ||
} | ||
// Encode an empty value for the enum. | ||
if (invariantType === null) { | ||
return { [invariant]: true }; | ||
} | ||
let paramIndex = canonicalTypeParams.indexOf(invariantType); | ||
let typeOrParam = | ||
paramIndex === -1 ? invariantType : typeParams[paramIndex]; | ||
let paramIndex = canonicalTypeParams.indexOf(invariantType); | ||
let typeOrParam = paramIndex === -1 ? invariantType : typeParams[paramIndex]; | ||
{ | ||
let { name, params } = this.parseTypeName(typeOrParam); | ||
return { | ||
[invariant]: this.getTypeInterface(name)._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} | ||
*/ | ||
public getTypeInterface(type: string): TypeInterface { | ||
let typeInterface = this.types.get(type); | ||
{ | ||
let { name, params } = this.parseTypeName(typeOrParam); | ||
return { | ||
[invariant]: this.getTypeInterface(name)._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} | ||
*/ | ||
public getTypeInterface(type: string): TypeInterface { | ||
let typeInterface = this.types.get(type); | ||
// Special case - string means an alias. | ||
// Goes through the alias chain and tracks recursion. | ||
if (typeof typeInterface === "string") { | ||
let chain: string[] = []; | ||
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); | ||
} | ||
} | ||
// Special case - string means an alias. | ||
// Goes through the alias chain and tracks recursion. | ||
if (typeof typeInterface === 'string') { | ||
let chain: string[] = []; | ||
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 === undefined) { | ||
throw new Error(`Type ${type} is not registered`); | ||
} | ||
if (typeInterface === undefined) { | ||
throw new Error(`Type ${type} is not registered`); | ||
} | ||
return typeInterface; | ||
} | ||
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 | ||
*/ | ||
public parseTypeName(name: TypeName): { | ||
name: string; | ||
params: TypeName[]; | ||
} { | ||
if (Array.isArray(name)) { | ||
let [typeName, ...params] = name; | ||
return { name: typeName, params }; | ||
} | ||
/** | ||
* 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 | ||
*/ | ||
public parseTypeName(name: TypeName): { | ||
name: string; | ||
params: TypeName[]; | ||
} { | ||
if (Array.isArray(name)) { | ||
let [typeName, ...params] = name; | ||
return { name: typeName, params }; | ||
} | ||
if (typeof name !== "string") { | ||
throw new Error(`Illegal type passed as a name of the type: ${name}`); | ||
} | ||
if (typeof name !== 'string') { | ||
throw new Error(`Illegal type passed as a name of the type: ${name}`); | ||
} | ||
let [left, right] = this.schema.genericSeparators || ["<", ">"]; | ||
let [left, right] = this.schema.genericSeparators || ['<', '>']; | ||
let l_bound = name.indexOf(left); | ||
let r_bound = Array.from(name).reverse().indexOf(right); | ||
let l_bound = name.indexOf(left); | ||
let r_bound = Array.from(name).reverse().indexOf(right); | ||
// if there are no generics - exit gracefully. | ||
if (l_bound === -1 && r_bound === -1) { | ||
return { name: name, params: [] }; | ||
} | ||
// if there are no generics - exit gracefully. | ||
if (l_bound === -1 && r_bound === -1) { | ||
return { name: name, params: [] }; | ||
} | ||
// if one of the bounds is not defined - throw an Error. | ||
if (l_bound === -1 || r_bound === -1) { | ||
throw new Error(`Unclosed generic in name '${name}'`); | ||
} | ||
// if one of the bounds is not defined - throw an Error. | ||
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 | ||
); | ||
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 }; | ||
} | ||
return { name: typeName, params }; | ||
} | ||
} | ||
@@ -1454,14 +1352,12 @@ | ||
export function encodeStr(data: Uint8Array, encoding: Encoding): string { | ||
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" | ||
); | ||
} | ||
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'); | ||
} | ||
} | ||
@@ -1477,14 +1373,12 @@ | ||
export function decodeStr(data: string, encoding: Encoding): Uint8Array { | ||
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" | ||
); | ||
} | ||
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'); | ||
} | ||
} | ||
@@ -1498,174 +1392,169 @@ | ||
export function registerPrimitives(bcs: BCS): void { | ||
bcs.registerType( | ||
BCS.U8, | ||
function (writer: BcsWriter, data) { | ||
return writer.write8(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read8(); | ||
}, | ||
(u8) => u8 < 256 | ||
); | ||
bcs.registerType( | ||
BCS.U8, | ||
function (writer: BcsWriter, data) { | ||
return writer.write8(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read8(); | ||
}, | ||
(u8) => u8 < 256, | ||
); | ||
bcs.registerType( | ||
BCS.U16, | ||
function (writer: BcsWriter, data) { | ||
return writer.write16(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read16(); | ||
}, | ||
(u16) => u16 < 65536 | ||
); | ||
bcs.registerType( | ||
BCS.U16, | ||
function (writer: BcsWriter, data) { | ||
return writer.write16(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read16(); | ||
}, | ||
(u16) => u16 < 65536, | ||
); | ||
bcs.registerType( | ||
BCS.U32, | ||
function (writer: BcsWriter, data) { | ||
return writer.write32(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read32(); | ||
}, | ||
(u32) => u32 <= 4294967296n | ||
); | ||
bcs.registerType( | ||
BCS.U32, | ||
function (writer: BcsWriter, data) { | ||
return writer.write32(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read32(); | ||
}, | ||
(u32) => u32 <= 4294967296n, | ||
); | ||
bcs.registerType( | ||
BCS.U64, | ||
function (writer: BcsWriter, data) { | ||
return writer.write64(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read64(); | ||
} | ||
); | ||
bcs.registerType( | ||
BCS.U64, | ||
function (writer: BcsWriter, data) { | ||
return writer.write64(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read64(); | ||
}, | ||
); | ||
bcs.registerType( | ||
BCS.U128, | ||
function (writer: BcsWriter, data: bigint) { | ||
return writer.write128(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read128(); | ||
} | ||
); | ||
bcs.registerType( | ||
BCS.U128, | ||
function (writer: BcsWriter, data: bigint) { | ||
return writer.write128(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read128(); | ||
}, | ||
); | ||
bcs.registerType( | ||
BCS.U256, | ||
function (writer: BcsWriter, data) { | ||
return writer.write256(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read256(); | ||
} | ||
); | ||
bcs.registerType( | ||
BCS.U256, | ||
function (writer: BcsWriter, data) { | ||
return writer.write256(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read256(); | ||
}, | ||
); | ||
bcs.registerType( | ||
BCS.BOOL, | ||
function (writer: BcsWriter, data) { | ||
return writer.write8(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read8().toString(10) === "1"; | ||
} | ||
); | ||
bcs.registerType( | ||
BCS.BOOL, | ||
function (writer: BcsWriter, data) { | ||
return writer.write8(data); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader.read8().toString(10) === '1'; | ||
}, | ||
); | ||
bcs.registerType( | ||
BCS.STRING, | ||
function (writer: BcsWriter, data: string) { | ||
return writer.writeVec(Array.from(data), (writer, el) => | ||
writer.write8(el.charCodeAt(0)) | ||
); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader | ||
.readVec((reader) => reader.read8()) | ||
.map((el: bigint) => String.fromCharCode(Number(el))) | ||
.join(""); | ||
}, | ||
(_str: string) => true | ||
); | ||
bcs.registerType( | ||
BCS.STRING, | ||
function (writer: BcsWriter, data: string) { | ||
return writer.writeVec(Array.from(data), (writer, el) => writer.write8(el.charCodeAt(0))); | ||
}, | ||
function (reader: BcsReader) { | ||
return reader | ||
.readVec((reader) => reader.read8()) | ||
.map((el: bigint) => String.fromCharCode(Number(el))) | ||
.join(''); | ||
}, | ||
(_str: string) => true, | ||
); | ||
bcs.registerType( | ||
BCS.HEX, | ||
function (writer: BcsWriter, data: string) { | ||
return writer.writeVec(Array.from(fromHEX(data)), (writer, el) => | ||
writer.write8(el) | ||
); | ||
}, | ||
function (reader: BcsReader) { | ||
let bytes = reader.readVec((reader) => reader.read8()); | ||
return toHEX(new Uint8Array(bytes)); | ||
} | ||
); | ||
bcs.registerType( | ||
BCS.HEX, | ||
function (writer: BcsWriter, data: string) { | ||
return writer.writeVec(Array.from(fromHEX(data)), (writer, el) => writer.write8(el)); | ||
}, | ||
function (reader: BcsReader) { | ||
let bytes = reader.readVec((reader) => reader.read8()); | ||
return toHEX(new Uint8Array(bytes)); | ||
}, | ||
); | ||
bcs.registerType( | ||
BCS.BASE58, | ||
function (writer: BcsWriter, data: string) { | ||
return writer.writeVec(Array.from(fromB58(data)), (writer, el) => | ||
writer.write8(el) | ||
); | ||
}, | ||
function (reader: BcsReader) { | ||
let bytes = reader.readVec((reader) => reader.read8()); | ||
return toB58(new Uint8Array(bytes)); | ||
} | ||
); | ||
bcs.registerType( | ||
BCS.BASE58, | ||
function (writer: BcsWriter, data: string) { | ||
return writer.writeVec(Array.from(fromB58(data)), (writer, el) => writer.write8(el)); | ||
}, | ||
function (reader: BcsReader) { | ||
let bytes = reader.readVec((reader) => reader.read8()); | ||
return toB58(new Uint8Array(bytes)); | ||
}, | ||
); | ||
bcs.registerType( | ||
BCS.BASE64, | ||
function (writer: BcsWriter, data: string) { | ||
return writer.writeVec(Array.from(fromB64(data)), (writer, el) => | ||
writer.write8(el) | ||
); | ||
}, | ||
function (reader: BcsReader) { | ||
let bytes = reader.readVec((reader) => reader.read8()); | ||
return toB64(new Uint8Array(bytes)); | ||
} | ||
); | ||
bcs.registerType( | ||
BCS.BASE64, | ||
function (writer: BcsWriter, data: string) { | ||
return writer.writeVec(Array.from(fromB64(data)), (writer, el) => writer.write8(el)); | ||
}, | ||
function (reader: BcsReader) { | ||
let bytes = reader.readVec((reader) => reader.read8()); | ||
return toB64(new Uint8Array(bytes)); | ||
}, | ||
); | ||
} | ||
export function getRustConfig(): BcsConfig { | ||
return { | ||
genericSeparators: ["<", ">"], | ||
vectorType: "Vec", | ||
addressLength: SUI_ADDRESS_LENGTH, | ||
addressEncoding: "hex", | ||
}; | ||
return { | ||
genericSeparators: ['<', '>'], | ||
vectorType: 'Vec', | ||
addressLength: SUI_ADDRESS_LENGTH, | ||
addressEncoding: 'hex', | ||
}; | ||
} | ||
export function getSuiMoveConfig(): BcsConfig { | ||
return { | ||
genericSeparators: ["<", ">"], | ||
vectorType: "vector", | ||
addressLength: SUI_ADDRESS_LENGTH, | ||
addressEncoding: "hex", | ||
}; | ||
return { | ||
genericSeparators: ['<', '>'], | ||
vectorType: 'vector', | ||
addressLength: SUI_ADDRESS_LENGTH, | ||
addressEncoding: 'hex', | ||
}; | ||
} | ||
export function splitGenericParameters(str: string, genericSeparators: [string, string] = ["<", ">"]) { | ||
const [left, right] = genericSeparators; | ||
const tok = []; | ||
let word = ""; | ||
let nestedAngleBrackets = 0; | ||
export function splitGenericParameters( | ||
str: string, | ||
genericSeparators: [string, string] = ['<', '>'], | ||
) { | ||
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; | ||
} | ||
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()); | ||
tok.push(word.trim()); | ||
return tok; | ||
return tok; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
441
359382
5682