js-moi-logic
Version:
Module to interact with MOI Logic Objects.
223 lines (194 loc) • 6.62 kB
text/typescript
import { blake2b } from "@noble/hashes/blake2b";
import BN from "bn.js";
import { encodeToString } from "js-moi-utils";
import { Polorizer } from "js-polo";
export class StorageKey {
private value: BN;
constructor(value: number | string | Buffer | Uint8Array | BN) {
this.value = new BN(value);
}
hex(): string {
return encodeToString(this.toBuffer());
}
toBuffer(): Buffer {
return this.value.toBuffer("be", 32);
}
}
/**
* Represents an accessor that provides access to a slot hash.
*/
export interface Accessor {
/**
* Retrieves the slot hash associated with the given hash.
*
* @param hash - The hash of the slot.
* @returns The slot hash associated with the given hash.
*/
access(hash: StorageKey): StorageKey;
}
/**
* Represents an accessor provider that provides accessors.
*/
export interface AccessorProvider {
/**
* Retrieves the accessors.
* @returns An array of accessors.
*/
getAccessors(): Accessor[];
}
/**
* Represents a interface that provides the type of the value present at slot hash.
*/
export interface StorageTypeProvider {
/**
* Gets the type of the slot hash.
* @returns The type of the slot hash as a string.
*/
getStorageType(): string;
}
export type AccessorAndStorageProvider = AccessorProvider & StorageTypeProvider;
/**
* AbstractAccessor class provides a base implementation of the Accessor interface.
*/
export abstract class AbstractAccessor implements Accessor {
/**
* Calculates the sum256 hash of a Uint8Array using the blake2b algorithm.
* @param hash - The input Uint8Array to be hashed.
* @returns The calculated sum256 hash as a Uint8Array.
*/
protected sum256(hash: Uint8Array): Uint8Array {
return blake2b(hash, { dkLen: 32 });
}
/**
* Abstract method that needs to be implemented by subclasses.
* It defines the logic for accessing a SlotHash.
* @param hash - The SlotHash to be accessed.
* @returns The accessed SlotHash.
*/
public abstract access(hash: StorageKey): StorageKey;
}
/**
* Represents a LengthAccessor class that extends the AbstractAccessor class.
*
* It generates slot hash for accessing the length of an Array/VArray or a Map.
*/
export class LengthAccessor extends AbstractAccessor {
public access(hash: StorageKey): StorageKey {
return hash;
}
}
/**
* Generates a slot hash for accessing the key of Map.
*/
export class PropertyAccessor extends AbstractAccessor {
key: Uint8Array;
/**
* Creates a new instance of PropertyAccessor.
* @param key The label of the property.
*/
public constructor(key: string | number) {
super();
this.key = this.sum256(this.polorize(key));
}
/**
* Polorizes the given key.
* @param key The key to polorize.
* @returns The polorized key as a bytes.
*/
private polorize(key: string | number | boolean | Uint8Array | Buffer): Uint8Array {
const polorizer = new Polorizer();
switch (true) {
case typeof key === "string":
polorizer.polorizeString(key);
break;
case typeof key === "number":
if (Number.isInteger(key)) {
polorizer.polorizeInteger(key);
}
if (!Number.isInteger(key)) {
polorizer.polorizeFloat(key);
}
break;
case typeof key === "boolean":
polorizer.polorizeBool(key);
break;
case key instanceof Uint8Array:
case key instanceof Buffer:
polorizer.polorizeBytes(key);
break;
default:
throw new Error(`Unsupported type: ${typeof key}`);
}
return polorizer.bytes();
}
/**
* Accesses the property with the given hash and returns the resulting hash.
* @param hash The hash of the property.
* @returns The resulting hash after accessing the property.
*/
public access(hash: StorageKey): StorageKey {
const separator = Buffer.from(".");
const buffer = Buffer.concat([hash.toBuffer(), separator, this.key]);
return new StorageKey(this.sum256(buffer));
}
}
/**
* Represents an accessor for accessing elements in an array by index.
*/
export class ArrayIndexAccessor extends AbstractAccessor {
/**
* Creates a new instance of ArrayIndexAccessor.
* @param index The index of the element to access.
*/
public constructor(private index: number) {
super();
}
/**
* Generates a slot hash for accessing the element at the given index.
* @param hash The input hash.
* @returns The updated hash after accessing the element.
*/
public access(hash: StorageKey): StorageKey {
const bytes = this.sum256(hash.toBuffer());
const slot = new BN(bytes).add(new BN(this.index));
return new StorageKey(slot);
}
}
/**
* Represents an accessor for accessing fields of a class.
*/
export class ClassFieldAccessor extends AbstractAccessor {
constructor(private index: number) {
super();
}
public access(hash: StorageKey): StorageKey {
let blob: Uint8Array = hash.toBuffer();
blob = this.sum256(blob);
const bn = new BN(blob).add(new BN(this.index));
return new StorageKey(bn);
}
}
/**
* Generates a slot hash based on the provided base and accessors.
*
* @param base - The base value for the slot hash.
* @param accessors - The accessors used to generate the slot hash.
* @returns The generated slot hash as a Uint8Array.
*/
export function generateStorageKey(base: number | StorageKey, ...accessors: Accessor[]): StorageKey;
/**
* Generates a slot hash based on the provided base value and array of accessors.
*
* @param base - The base value for generating the slot hash.
* @param accessorsArray - An array of accessors used in generating the slot hash.
* @returns The generated slot hash as a Uint8Array.
*/
export function generateStorageKey(base: number | StorageKey, accessorsArray: Accessor[]): StorageKey;
export function generateStorageKey(base: number | StorageKey, ...args: any[]): StorageKey {
let slot = typeof base === "number" ? new StorageKey(base) : base;
const accessors: Accessor[] = Array.isArray(args[0]) ? args[0] : args;
for (const accessor of accessors) {
slot = accessor.access(slot);
}
return slot;
}