@arkiv-network/sdk
Version:
TypeScript client library for Arkiv's blockchains interactions
136 lines (125 loc) • 4.34 kB
text/typescript
import { type Address, encodeAbiParameters, type Hex, parseAbi, parseAbiParameters } from "viem"
import type { AbiAttribute } from "../schema/codec"
import type { EncodedExpiry } from "./expiry"
import type { predictEntityKey } from "./key"
import { OperationType } from "./params"
/**
* The one external entry point. A transaction is an ordered, **atomic** batch of entity operations:
* all of them apply or none do, operation *n+1* observes operation *n*'s effects, and each applied
* operation emits one event.
*/
export const EXECUTE_ABI = parseAbi([
"function execute((uint8 operation, bytes operationData)[] ops) external returns (bytes32[] keys)",
])
/**
* The engine's one read-only view: an owner's entity-minting nonce, which is the input
* {@link predictEntityKey} needs and the only part of the derivation the caller cannot know.
*
* It is a `view` on the same address `execute` is called on, so it answers over `eth_call` without
* a transaction.
*/
export const ENTITY_NONCE_ABI = parseAbi([
"function entityNonce(address owner) external view returns (uint64)",
])
/**
* The shared attribute shape, used by `create` (attributes) and `patch` (mutations). Written out
* once and reused by every payload that carries attributes.
*/
const ATTRIBUTE_TUPLE = "(bytes32 name, uint8 typeId, bytes value)[]"
/**
* The payload struct for each operation tag. `operationData` is `abi.encode` of the struct the tag
* selects — a tagged union, so a new operation is additive rather than a change to `execute`.
*/
const PAYLOAD_PARAMS = {
[OperationType.Create]: parseAbiParameters(
`(uint128 salt, uint64 expiresAt, uint64 minLifetime, uint8 creationFlags, ${ATTRIBUTE_TUPLE} attributes)`,
),
[OperationType.Patch]: parseAbiParameters(`(bytes32 entityKey, ${ATTRIBUTE_TUPLE} mutations)`),
[OperationType.ExtendExpiry]: parseAbiParameters(
"(bytes32 entityKey, uint64 expiresAt, uint64 minLifetime)",
),
[OperationType.TransferOwnership]: parseAbiParameters("(bytes32 entityKey, address newOwner)"),
[OperationType.Delete]: parseAbiParameters("(bytes32 entityKey)"),
} as const
/** One entry of the `execute` batch: the operation tag and its ABI-encoded payload. */
export type Operation = {
operation: number
operationData: Hex
}
/** Creates an entity. The key is derived by the engine; predict it with `predictEntityKey`. */
export function createOperation({
salt,
expiry,
creationFlags,
attributes,
}: {
salt: bigint
expiry: EncodedExpiry
creationFlags: number
attributes: AbiAttribute[]
}): Operation {
return {
operation: OperationType.Create,
operationData: encodeAbiParameters(PAYLOAD_PARAMS[OperationType.Create], [
{
salt,
expiresAt: expiry.expiresAt,
minLifetime: expiry.minLifetime,
creationFlags,
attributes,
},
]),
}
}
/** Applies a set of mutations to an existing entity. Tombstones unset attributes. */
export function patchOperation({
entityKey,
mutations,
}: {
entityKey: Hex
mutations: AbiAttribute[]
}): Operation {
return {
operation: OperationType.Patch,
operationData: encodeAbiParameters(PAYLOAD_PARAMS[OperationType.Patch], [
{ entityKey, mutations },
]),
}
}
/** Pushes an entity's expiry further out. Extending is allowed; shortening reverts. */
export function extendExpiryOperation({
entityKey,
expiry,
}: {
entityKey: Hex
expiry: EncodedExpiry
}): Operation {
return {
operation: OperationType.ExtendExpiry,
operationData: encodeAbiParameters(PAYLOAD_PARAMS[OperationType.ExtendExpiry], [
{ entityKey, expiresAt: expiry.expiresAt, minLifetime: expiry.minLifetime },
]),
}
}
/** Hands an entity to a new owner. */
export function transferOwnershipOperation({
entityKey,
newOwner,
}: {
entityKey: Hex
newOwner: Address
}): Operation {
return {
operation: OperationType.TransferOwnership,
operationData: encodeAbiParameters(PAYLOAD_PARAMS[OperationType.TransferOwnership], [
{ entityKey, newOwner },
]),
}
}
/** Deletes an entity before its expiry. */
export function deleteOperation({ entityKey }: { entityKey: Hex }): Operation {
return {
operation: OperationType.Delete,
operationData: encodeAbiParameters(PAYLOAD_PARAMS[OperationType.Delete], [{ entityKey }]),
}
}