UNPKG

@arkiv-network/sdk

Version:

TypeScript client library for Arkiv's blockchains interactions

156 lines (141 loc) • 6.04 kB
import type { Address, Hex } from "viem" import type { decUnits } from "./values" /** * The protocol's attribute type vocabulary, mapping each short **tag** (the name used by the * client, the query language and this SDK) to its **typeId** (the byte carried on the wire and in * the entity record). * * The typeIds are consensus-critical: they are written into entity records and mixed into index * keys, so this table must never drift from the node's `AttributeType`. * * | tag | typeId | ABI wire type | when indexed | * | ------- | ------ | -------------------- | ------------------------- | * | `bool` | 1 | `bool` | equality + range | * | `i32` | 2 | `int32` | equality + range | * | `u64` | 3 | `uint64` | equality + range | * | `u256` | 4 | `uint256` | equality + range | * | `dec` | 5 | `int256` (18 dp) | equality + range | * | — | 6 | `bytes32` | reserved | * | `bytes` | 7 | `bytes` (<= 128 KiB) | never indexed | * | `str` | 8 | `string` (<= 128 B) | equality + range + prefix | * | `addr` | 9 | `address` | equality + range | * | `key` | 10 | `bytes32` | equality + range | * * typeId 6 is the node's `bytes32` type, which this SDK does not expose: it is reserved here, so * `TYPE_IDS` leaves it out, and a value of that type is read as a `key`. * * On the wire the typeId shares its byte with the indexed flag — see {@link INDEXED_FLAG}. */ export const TYPE_IDS = { bool: 1, i32: 2, u64: 3, u256: 4, dec: 5, bytes: 7, str: 8, addr: 9, key: 10, } as const /** * Bit 7 of the `typeId` byte: set when the node should index the attribute. The low seven bits are * the type itself, so `str` travels as `8` unindexed and `0x88` indexed. */ export const INDEXED_FLAG = 0x80 /** Every type tag. */ export type TypeTag = keyof typeof TYPE_IDS /** * The tags an attribute may be indexed with — every type except `bytes`, which is stored but never * indexed, and so can never be filtered on. */ export type IndexableTypeTag = Exclude<TypeTag, "bytes"> /** Whether attributes of this type may be indexed — everything except `bytes`. */ export function isIndexable(tag: TypeTag): tag is IndexableTypeTag { return tag !== "bytes" } /** * Marks a value as having passed its constructor's validation. The symbol is module-private, so a * hand-written object literal is not assignable to {@link ArkivValue} — every value crossing the * wire has been range-, length- and format-checked by `i32`, `u256`, `dec`, and friends. */ declare const validated: unique symbol /** * A typed attribute value: the tag it was created with, plus the JavaScript representation of that * type. Constructed only by the tagged constructors in `@arkiv-network/sdk/schema`. * * @typeParam tag - The type tag, which also discriminates the {@link ArkivValue} union. * @typeParam value - The decoded JavaScript representation. */ export type Value<tag extends TypeTag, value> = { readonly type: tag readonly value: value readonly [validated]: tag } /** A boolean. */ export type BoolValue = Value<"bool", boolean> /** A 32-bit signed integer — the default for an untagged number. */ export type I32Value = Value<"i32", number> /** * A 64-bit unsigned integer — block heights, the * entity-nonce and `$expiresAt` are all `u64` */ export type U64Value = Value<"u64", bigint> /** A 256-bit unsigned integer. */ export type U256Value = Value<"u256", bigint> /** * A fixed-point decimal with 18 decimal places, held as its canonical decimal string (`"3.5"`). * Use {@link decUnits} for the exact scaled integer when you need to do arithmetic. */ export type DecValue = Value<"dec", string> /** A UTF-8 string of at most 128 bytes. */ export type StrValue = Value<"str", string> /** A 20-byte Ethereum address, as EIP-55 checksummed hex. */ export type AddrValue = Value<"addr", Address> /** A reference to another entity. Weak by spec: dangling references are permitted. */ export type KeyValue = Value<"key", Hex> /** * Opaque variable-length bytes, as lowercase `0x` hex — a blob. Stored but never indexed, so it can * be selected but not filtered on. */ export type BytesValue = Value<"bytes", Hex> /** Any attribute value. */ export type ArkivValue = | BoolValue | I32Value | U64Value | U256Value | DecValue | BytesValue | StrValue | AddrValue | KeyValue /** Any value of a type that can be indexed, and so can appear in a query — everything but `bytes`. */ export type IndexableValue = Exclude<ArkivValue, BytesValue> /** The value type for a given tag — e.g. `ValueOf<"u256">` is {@link U256Value}. */ export type ValueOf<tag extends TypeTag> = Extract<ArkivValue, { type: tag }> /** The JavaScript representation carried by a value — e.g. `Decoded<U256Value>` is `bigint`. */ export type Decoded<value extends ArkivValue> = value["value"] /** * Builds a validated value. Module-internal: the constructors in `values.ts` call this **after** * validating, which is what makes the {@link validated} brand meaningful. */ export function makeValue<tag extends TypeTag>( type: tag, value: Decoded<ValueOf<tag>>, ): ValueOf<tag> { return { type, value } as unknown as ValueOf<tag> } /** * Whether `input` has the *shape* of an {@link ArkivValue} — a known type tag and a value. */ export function isArkivValue(input: unknown): input is ArkivValue { return ( typeof input === "object" && input !== null && "type" in input && "value" in input && typeof (input as { type: unknown }).type === "string" && // `Object.hasOwn`, not `in`: `in` walks the prototype chain, so `{ type: "constructor" }` would // be accepted as a typed value and then decode to nothing. Object.hasOwn(TYPE_IDS, (input as { type: string }).type) ) }