@ton/core
Version:
Core TypeScript library that implements low level primitives for TON blockchain.
623 lines (622 loc) • 18 kB
JavaScript
/**
* Copyright (c) Whales Corp.
* All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Dictionary = void 0;
const Address_1 = require("../address/Address");
const Builder_1 = require("../boc/Builder");
const Cell_1 = require("../boc/Cell");
const BitString_1 = require("../boc/BitString");
const generateMerkleProof_1 = require("./generateMerkleProof");
const generateMerkleUpdate_1 = require("./generateMerkleUpdate");
const parseDict_1 = require("./parseDict");
const serializeDict_1 = require("./serializeDict");
const internalKeySerializer_1 = require("./utils/internalKeySerializer");
class Dictionary {
/**
* Create an empty map
* @param key key type
* @param value value type
* @returns Dictionary<K, V>
*/
static empty(key, value) {
if (key && value) {
return new Dictionary(new Map(), key, value);
}
else {
return new Dictionary(new Map(), null, null);
}
}
/**
* Load dictionary from slice
* @param key key description
* @param value value description
* @param src slice
* @returns Dictionary<K, V>
*/
static load(key, value, sc) {
let slice;
if (sc instanceof Cell_1.Cell) {
if (sc.isExotic) {
return Dictionary.empty(key, value);
}
slice = sc.beginParse();
}
else {
slice = sc;
}
let cell = slice.loadMaybeRef();
if (cell && !cell.isExotic) {
return Dictionary.loadDirect(key, value, cell.beginParse());
}
else {
return Dictionary.empty(key, value);
}
}
/**
* Low level method for rare dictionaries from system contracts.
* Loads dictionary from slice directly without going to the ref.
*
* @param key key description
* @param value value description
* @param sc slice
* @returns Dictionary<K, V>
*/
static loadDirect(key, value, sc) {
if (!sc) {
return Dictionary.empty(key, value);
}
let slice;
if (sc instanceof Cell_1.Cell) {
slice = sc.beginParse();
}
else {
slice = sc;
}
let values = (0, parseDict_1.parseDict)(slice, key.bits, value.parse);
let prepare = new Map();
for (let [k, v] of values) {
prepare.set((0, internalKeySerializer_1.serializeInternalKey)(key.parse(k)), v);
}
return new Dictionary(prepare, key, value);
}
constructor(values, key, value) {
this._key = key;
this._value = value;
this._map = values;
}
get size() {
return this._map.size;
}
get(key) {
return this._map.get((0, internalKeySerializer_1.serializeInternalKey)(key));
}
has(key) {
return this._map.has((0, internalKeySerializer_1.serializeInternalKey)(key));
}
set(key, value) {
this._map.set((0, internalKeySerializer_1.serializeInternalKey)(key), value);
return this;
}
delete(key) {
const k = (0, internalKeySerializer_1.serializeInternalKey)(key);
return this._map.delete(k);
}
clear() {
this._map.clear();
}
*[Symbol.iterator]() {
for (const [k, v] of this._map) {
const key = (0, internalKeySerializer_1.deserializeInternalKey)(k);
yield [key, v];
}
}
keys() {
return Array.from(this._map.keys()).map((v) => (0, internalKeySerializer_1.deserializeInternalKey)(v));
}
values() {
return Array.from(this._map.values());
}
store(builder, key, value) {
if (this._map.size === 0) {
builder.storeBit(0);
}
else {
// Resolve serializer
let resolvedKey = this._key;
if (key !== null && key !== undefined) {
resolvedKey = key;
}
let resolvedValue = this._value;
if (value !== null && value !== undefined) {
resolvedValue = value;
}
if (!resolvedKey) {
throw Error('Key serializer is not defined');
}
if (!resolvedValue) {
throw Error('Value serializer is not defined');
}
// Prepare map
let prepared = new Map();
for (const [k, v] of this._map) {
prepared.set(resolvedKey.serialize((0, internalKeySerializer_1.deserializeInternalKey)(k)), v);
}
// Store
builder.storeBit(1);
let dd = (0, Builder_1.beginCell)();
(0, serializeDict_1.serializeDict)(prepared, resolvedKey.bits, resolvedValue.serialize, dd);
builder.storeRef(dd.endCell());
}
}
storeDirect(builder, key, value) {
if (this._map.size === 0) {
throw Error('Cannot store empty dictionary directly');
}
// Resolve serializer
let resolvedKey = this._key;
if (key !== null && key !== undefined) {
resolvedKey = key;
}
let resolvedValue = this._value;
if (value !== null && value !== undefined) {
resolvedValue = value;
}
if (!resolvedKey) {
throw Error('Key serializer is not defined');
}
if (!resolvedValue) {
throw Error('Value serializer is not defined');
}
// Prepare map
let prepared = new Map();
for (const [k, v] of this._map) {
prepared.set(resolvedKey.serialize((0, internalKeySerializer_1.deserializeInternalKey)(k)), v);
}
// Store
(0, serializeDict_1.serializeDict)(prepared, resolvedKey.bits, resolvedValue.serialize, builder);
}
/**
* Generate merkle proof for multiple keys in the dictionary
* @param keys an array of the keys
* @returns generated merkle proof cell
*/
generateMerkleProof(keys) {
return (0, generateMerkleProof_1.generateMerkleProof)(this, keys, this._key);
}
/**
* Low level method for generating pruned dictionary directly.
* The result can be used as a part of a bigger merkle proof
* @param keys an array of the keys
* @returns cell that contains the pruned dictionary
*/
generateMerkleProofDirect(keys) {
return (0, generateMerkleProof_1.generateMerkleProofDirect)(this, keys, this._key);
}
generateMerkleUpdate(key, newValue) {
return (0, generateMerkleUpdate_1.generateMerkleUpdate)(this, key, this._key, newValue);
}
}
exports.Dictionary = Dictionary;
Dictionary.Keys = {
/**
* Standard address key
* @returns DictionaryKey<Address>
*/
Address: () => {
return createAddressKey();
},
/**
* Create standard big integer key
* @param bits number of bits
* @returns DictionaryKey<bigint>
*/
BigInt: (bits) => {
return createBigIntKey(bits);
},
/**
* Create integer key
* @param bits bits of integer
* @returns DictionaryKey<number>
*/
Int: (bits) => {
return createIntKey(bits);
},
/**
* Create standard unsigned big integer key
* @param bits number of bits
* @returns DictionaryKey<bigint>
*/
BigUint: (bits) => {
return createBigUintKey(bits);
},
/**
* Create standard unsigned integer key
* @param bits number of bits
* @returns DictionaryKey<number>
*/
Uint: (bits) => {
return createUintKey(bits);
},
/**
* Create standard buffer key
* @param bytes number of bytes of a buffer
* @returns DictionaryKey<Buffer>
*/
Buffer: (bytes) => {
return createBufferKey(bytes);
},
/**
* Create BitString key
* @param bits key length
* @returns DictionaryKey<BitString>
* Point is that Buffer has to be 8 bit aligned,
* while key is TVM dictionary doesn't have to be
* aligned at all.
*/
BitString: (bits) => {
return createBitStringKey(bits);
}
};
Dictionary.Values = {
/**
* Create standard integer value
* @returns DictionaryValue<bigint>
*/
BigInt: (bits) => {
return createBigIntValue(bits);
},
/**
* Create standard integer value
* @returns DictionaryValue<number>
*/
Int: (bits) => {
return createIntValue(bits);
},
/**
* Create big var int
* @param bits nubmer of header bits
* @returns DictionaryValue<bigint>
*/
BigVarInt: (bits) => {
return createBigVarIntValue(bits);
},
/**
* Create standard unsigned integer value
* @param bits number of bits
* @returns DictionaryValue<bigint>
*/
BigUint: (bits) => {
return createBigUintValue(bits);
},
/**
* Create standard unsigned integer value
* @param bits number of bits
* @returns DictionaryValue<bigint>
*/
Uint: (bits) => {
return createUintValue(bits);
},
/**
* Create big var int
* @param bits nubmer of header bits
* @returns DictionaryValue<bigint>
*/
BigVarUint: (bits) => {
return createBigVarUintValue(bits);
},
/**
* Create standard boolean value
* @returns DictionaryValue<boolean>
*/
Bool: () => {
return createBooleanValue();
},
/**
* Create standard address value
* @returns DictionaryValue<Address>
*/
Address: () => {
return createAddressValue();
},
/**
* Create standard cell value
* @returns DictionaryValue<Cell>
*/
Cell: () => {
return createCellValue();
},
/**
* Create Builder value
* @param bytes number of bytes of a buffer
* @returns DictionaryValue<Builder>
*/
Buffer: (bytes) => {
return createBufferValue(bytes);
},
/**
* Create BitString value
* @param requested bit length
* @returns DictionaryValue<BitString>
* Point is that Buffer is not applicable
* when length is not 8 bit alligned.
*/
BitString: (bits) => {
return createBitStringValue(bits);
},
/**
* Create dictionary value
* @param key
* @param value
*/
Dictionary: (key, value) => {
return createDictionaryValue(key, value);
}
};
//
// Keys and Values
//
function createAddressKey() {
return {
bits: 267,
serialize: (src) => {
if (!Address_1.Address.isAddress(src)) {
throw Error('Key is not an address');
}
return (0, Builder_1.beginCell)().storeAddress(src).endCell().beginParse().preloadUintBig(267);
},
parse: (src) => {
return (0, Builder_1.beginCell)().storeUint(src, 267).endCell().beginParse().loadAddress();
}
};
}
function createBigIntKey(bits) {
return {
bits,
serialize: (src) => {
if (typeof src !== 'bigint') {
throw Error('Key is not a bigint');
}
return (0, Builder_1.beginCell)().storeInt(src, bits).endCell().beginParse().loadUintBig(bits);
},
parse: (src) => {
return (0, Builder_1.beginCell)().storeUint(src, bits).endCell().beginParse().loadIntBig(bits);
}
};
}
function createIntKey(bits) {
return {
bits: bits,
serialize: (src) => {
if (typeof src !== 'number') {
throw Error('Key is not a number');
}
if (!Number.isSafeInteger(src)) {
throw Error('Key is not a safe integer: ' + src);
}
return (0, Builder_1.beginCell)().storeInt(src, bits).endCell().beginParse().loadUintBig(bits);
},
parse: (src) => {
return (0, Builder_1.beginCell)().storeUint(src, bits).endCell().beginParse().loadInt(bits);
}
};
}
function createBigUintKey(bits) {
return {
bits,
serialize: (src) => {
if (typeof src !== 'bigint') {
throw Error('Key is not a bigint');
}
if (src < 0) {
throw Error('Key is negative: ' + src);
}
return (0, Builder_1.beginCell)().storeUint(src, bits).endCell().beginParse().loadUintBig(bits);
},
parse: (src) => {
return (0, Builder_1.beginCell)().storeUint(src, bits).endCell().beginParse().loadUintBig(bits);
}
};
}
function createUintKey(bits) {
return {
bits,
serialize: (src) => {
if (typeof src !== 'number') {
throw Error('Key is not a number');
}
if (!Number.isSafeInteger(src)) {
throw Error('Key is not a safe integer: ' + src);
}
if (src < 0) {
throw Error('Key is negative: ' + src);
}
return (0, Builder_1.beginCell)().storeUint(src, bits).endCell().beginParse().loadUintBig(bits);
},
parse: (src) => {
return Number((0, Builder_1.beginCell)().storeUint(src, bits).endCell().beginParse().loadUint(bits));
}
};
}
function createBufferKey(bytes) {
return {
bits: bytes * 8,
serialize: (src) => {
if (!Buffer.isBuffer(src)) {
throw Error('Key is not a buffer');
}
return (0, Builder_1.beginCell)().storeBuffer(src).endCell().beginParse().loadUintBig(bytes * 8);
},
parse: (src) => {
return (0, Builder_1.beginCell)().storeUint(src, bytes * 8).endCell().beginParse().loadBuffer(bytes);
}
};
}
function createBitStringKey(bits) {
return {
bits,
serialize: (src) => {
if (!BitString_1.BitString.isBitString(src))
throw Error('Key is not a BitString');
return (0, Builder_1.beginCell)().storeBits(src).endCell().beginParse().loadUintBig(bits);
},
parse: (src) => {
return (0, Builder_1.beginCell)().storeUint(src, bits).endCell().beginParse().loadBits(bits);
}
};
}
function createIntValue(bits) {
return {
serialize: (src, buidler) => {
buidler.storeInt(src, bits);
},
parse: (src) => {
let value = src.loadInt(bits);
src.endParse();
return value;
}
};
}
function createBigIntValue(bits) {
return {
serialize: (src, buidler) => {
buidler.storeInt(src, bits);
},
parse: (src) => {
let value = src.loadIntBig(bits);
src.endParse();
return value;
}
};
}
function createBigVarIntValue(bits) {
return {
serialize: (src, buidler) => {
buidler.storeVarInt(src, bits);
},
parse: (src) => {
let value = src.loadVarIntBig(bits);
src.endParse();
return value;
}
};
}
function createBigVarUintValue(bits) {
return {
serialize: (src, buidler) => {
buidler.storeVarUint(src, bits);
},
parse: (src) => {
let value = src.loadVarUintBig(bits);
src.endParse();
return value;
}
};
}
function createUintValue(bits) {
return {
serialize: (src, buidler) => {
buidler.storeUint(src, bits);
},
parse: (src) => {
let value = src.loadUint(bits);
src.endParse();
return value;
}
};
}
function createBigUintValue(bits) {
return {
serialize: (src, buidler) => {
buidler.storeUint(src, bits);
},
parse: (src) => {
let value = src.loadUintBig(bits);
src.endParse();
return value;
}
};
}
function createBooleanValue() {
return {
serialize: (src, buidler) => {
buidler.storeBit(src);
},
parse: (src) => {
let value = src.loadBit();
src.endParse();
return value;
}
};
}
function createAddressValue() {
return {
serialize: (src, buidler) => {
buidler.storeAddress(src);
},
parse: (src) => {
let addr = src.loadAddress();
src.endParse();
return addr;
}
};
}
function createCellValue() {
return {
serialize: (src, buidler) => {
buidler.storeRef(src);
},
parse: (src) => {
let value = src.loadRef();
src.endParse();
return value;
}
};
}
function createDictionaryValue(key, value) {
return {
serialize: (src, buidler) => {
src.store(buidler);
},
parse: (src) => {
let dict = Dictionary.load(key, value, src);
src.endParse();
return dict;
}
};
}
function createBufferValue(size) {
return {
serialize: (src, buidler) => {
if (src.length !== size) {
throw Error('Invalid buffer size');
}
buidler.storeBuffer(src);
},
parse: (src) => {
let value = src.loadBuffer(size);
src.endParse();
return value;
}
};
}
function createBitStringValue(bits) {
return {
serialize: (src, builder) => {
if (src.length !== bits) {
throw Error('Invalid BitString size');
}
builder.storeBits(src);
},
parse: (src) => {
let value = src.loadBits(bits);
src.endParse();
return value;
}
};
}
;