242 lines
11 KiB
TypeScript
242 lines
11 KiB
TypeScript
/* eslint-disable @stylistic/newline-per-chained-call */
|
|
import type { XOInvitation } from '@xo-cash/types';
|
|
import { z } from 'zod';
|
|
import { satoshisSchema, uint8ArraySchema, xoTemplateNftCapabilitySchema } from '../template/schemas.ts';
|
|
|
|
/**
|
|
* Validation schema for a reference to an input or output in an earlier commit that a later item extends.
|
|
*
|
|
* ```
|
|
* {
|
|
* "inputs|outputs": [
|
|
* {
|
|
* "mergesWith": { "commitIdentifier": "...", "itemIndex": 0 } ← this schema
|
|
* }
|
|
* ]
|
|
* }
|
|
* ```
|
|
*/
|
|
export const xoInvitationMergesWithReferenceSchema = z
|
|
.object({
|
|
commitIdentifier: z.string().describe('The commit that holds the item being extended.'),
|
|
itemIndex: z.number().int().nonnegative().describe('The position of the extended item inside that commit.'),
|
|
})
|
|
.strict();
|
|
|
|
/**
|
|
* Validation schema for the template action a commit is resolved against when it differs from the invitation root.
|
|
*/
|
|
export const xoInvitationActionReferenceSchema = z
|
|
.object({
|
|
templateIdentifier: z.string().describe('The template that defines the action.'),
|
|
actionIdentifier: z.string().describe('The action inside that template.'),
|
|
})
|
|
.strict();
|
|
|
|
// ============================================================
|
|
// Commit data
|
|
// ============================================================
|
|
|
|
/**
|
|
* Validation schema for a variable value. Matches every value type an invitation variable can hold.
|
|
*/
|
|
export const xoInvitationVariableValueSchema = z
|
|
.union([ z.bigint(), z.boolean(), z.number(), z.string() ])
|
|
.describe('A variable value expressed as a bigint, boolean, number, or string.');
|
|
|
|
/**
|
|
* Validation schema for a variable supplied by a commit.
|
|
*/
|
|
export const xoInvitationVariableSchema = z
|
|
.object({
|
|
variableIdentifier: z.string().describe('The template variable this value is for.'),
|
|
value: xoInvitationVariableValueSchema,
|
|
roleIdentifier: z.string().exactOptional().describe('The role this variable belongs to.'),
|
|
mergesWith: xoInvitationMergesWithReferenceSchema.exactOptional(),
|
|
})
|
|
.strict();
|
|
|
|
/**
|
|
* Validation schema for a cashtoken.
|
|
*/
|
|
export const outputTokenSchema = z
|
|
.object({
|
|
category: uint8ArraySchema.describe('The token category.'),
|
|
amount: z.bigint().describe('The fungible token amount.'),
|
|
nft: z
|
|
.object({
|
|
capability: xoTemplateNftCapabilitySchema,
|
|
commitment: uint8ArraySchema.describe('The NFT commitment.'),
|
|
})
|
|
.strict()
|
|
.exactOptional(),
|
|
})
|
|
.strict();
|
|
|
|
/**
|
|
* Validation schema for a cashtoken supplied by a commit output. Every field is optional because a later commit may
|
|
* supply the fields an earlier commit left unset.
|
|
*/
|
|
export const xoInvitationTokenSchema = z
|
|
.object({
|
|
amount: z.bigint().exactOptional().describe('The fungible token amount.'),
|
|
category: uint8ArraySchema.exactOptional().describe('The token category.'),
|
|
nft: z
|
|
.object({
|
|
capability: xoTemplateNftCapabilitySchema.exactOptional(),
|
|
commitment: uint8ArraySchema.exactOptional().describe('The NFT commitment.'),
|
|
})
|
|
.strict()
|
|
.exactOptional(),
|
|
})
|
|
.strict();
|
|
|
|
/**
|
|
* Validation schema for a value one participant resolved from its own template key, such as a public key or a signature.
|
|
*/
|
|
export const xoInvitationResolutionSchema = z
|
|
.object({
|
|
resolutionIdentifier: z.string().describe('The key operation this value resolves, such as someKey.public_key.'),
|
|
value: uint8ArraySchema.describe('The resolved bytes.'),
|
|
roleIdentifier: z.string().exactOptional().describe('The role whose key this value was resolved from.'),
|
|
})
|
|
.strict();
|
|
|
|
/**
|
|
* Validation schema for a transaction input supplied by a commit.
|
|
*/
|
|
export const xoInvitationInputSchema = z
|
|
.object({
|
|
outpointIndex: z.number().int().nonnegative().exactOptional().describe('The index of the spent output in its transaction.'),
|
|
outpointTransactionHash: uint8ArraySchema.exactOptional().describe('The hash of the transaction that holds the spent output.'),
|
|
sequenceNumber: z.number().int().nonnegative().exactOptional().describe('The sequence number of this input.'),
|
|
unlockingBytecode: uint8ArraySchema.exactOptional().describe('The script that unlocks the spent output.'),
|
|
inputIdentifier: z.string().exactOptional().describe('The template input this input fills.'),
|
|
transactionIndex: z.number().int().nonnegative().exactOptional().describe('The position this input must take in the final transaction.'),
|
|
roleIdentifier: z.string().exactOptional().describe('The role this input belongs to.'),
|
|
resolutions: z.array(xoInvitationResolutionSchema).exactOptional(),
|
|
mergesWith: xoInvitationMergesWithReferenceSchema.exactOptional(),
|
|
valueSatoshis: satoshisSchema.exactOptional(),
|
|
token: outputTokenSchema.exactOptional(),
|
|
})
|
|
.strict();
|
|
|
|
/**
|
|
* Validation schema for a transaction output supplied by a commit.
|
|
*/
|
|
export const xoInvitationOutputSchema = z
|
|
.object({
|
|
lockingBytecode: uint8ArraySchema.exactOptional().describe('The locking bytecode of this output.'),
|
|
valueSatoshis: satoshisSchema.exactOptional(),
|
|
token: xoInvitationTokenSchema.exactOptional(),
|
|
outputIdentifier: z.string().exactOptional().describe('The template output this output fills.'),
|
|
transactionIndex: z.number().int().nonnegative().exactOptional().describe('The position this output must take in the final transaction.'),
|
|
secretIdentifier: z.string().exactOptional().describe('The secret used to derive this output.'),
|
|
roleIdentifier: z.string().exactOptional().describe('The role this output belongs to.'),
|
|
mergesWith: xoInvitationMergesWithReferenceSchema.exactOptional(),
|
|
})
|
|
.strict();
|
|
|
|
/**
|
|
* Validation schema for the data a commit supplies.
|
|
*/
|
|
export const xoInvitationCommitDataSchema = z
|
|
.object({
|
|
transactionVersion: z.number().int().nonnegative().exactOptional().describe('The transaction version.'),
|
|
transactionLocktime: z.number().int().nonnegative().exactOptional().describe('The transaction locktime.'),
|
|
variables: z.array(xoInvitationVariableSchema).exactOptional(),
|
|
inputs: z.array(xoInvitationInputSchema).exactOptional(),
|
|
outputs: z.array(xoInvitationOutputSchema).exactOptional(),
|
|
resolutions: z.array(xoInvitationResolutionSchema).exactOptional(),
|
|
roleIdentifier: z.string().exactOptional().describe('The role the committing entity takes in the action with this commit.'),
|
|
})
|
|
.strict();
|
|
|
|
/**
|
|
* Validation schema for one signed commit in an invitation.
|
|
*/
|
|
export const xoInvitationCommitSchema = z
|
|
.object({
|
|
commitIdentifier: z.string().describe('The identifier of this commit.'),
|
|
// The type declares this key on every commit, but serialization drops it on the first commit where it is undefined.
|
|
// Prefault restores the missing key, in this case it's undefined.
|
|
previousCommitIdentifier: z.string().optional().prefault(undefined).describe('The commit this one follows. Undefined on the first commit.'),
|
|
entityIdentifier: z.string().describe('The entity that made this commit.'),
|
|
createdAtTimestamp: z.number().int().nonnegative().describe('When this commit was created, as a Unix timestamp in milliseconds.'),
|
|
actionReference: xoInvitationActionReferenceSchema.exactOptional(),
|
|
data: xoInvitationCommitDataSchema,
|
|
signature: z.string().describe('The signature over this commit.'),
|
|
expiresAtTimestamp: z.number().int().nonnegative().describe('When this commit expires, as a Unix timestamp in milliseconds.'),
|
|
memo: z.string().exactOptional().describe('A free text note attached to this commit.'),
|
|
})
|
|
.strict();
|
|
|
|
// ============================================================
|
|
// Results
|
|
// ============================================================
|
|
|
|
/**
|
|
* Validation schema for a transaction result that has been created but not broadcast.
|
|
*/
|
|
export const xoInvitationTransactionCreatedResultSchema = z
|
|
.object({
|
|
transactionHash: z.string().describe('The hash of the created transaction.'),
|
|
createdAtTimestamp: z.number().int().nonnegative().describe('When the transaction was created, as a Unix timestamp in milliseconds.'),
|
|
createdByEntityIdentifier: z.string().describe('The entity that created the transaction.'),
|
|
createdBySignature: z.string().describe('The signature over the created result.'),
|
|
})
|
|
.strict();
|
|
|
|
/**
|
|
* Validation schema for a transaction result that has been created and broadcast.
|
|
*/
|
|
export const xoInvitationTransactionBroadcastedResultSchema = xoInvitationTransactionCreatedResultSchema
|
|
.extend({
|
|
broadcastedAtTimestamp: z.number().int().nonnegative().describe('When the transaction was broadcasted, as a Unix timestamp in milliseconds.'),
|
|
broadcastedByEntityIdentifier: z.string().describe('The entity that broadcasted the transaction.'),
|
|
broadcastedBySignature: z.string().describe('The signature over the broadcasted result.'),
|
|
})
|
|
.strict();
|
|
|
|
/**
|
|
* Validation schema for a data result created on an invitation.
|
|
*/
|
|
export const xoInvitationCreatedResultSchema = z
|
|
.object({
|
|
value: xoInvitationVariableValueSchema,
|
|
createdAtTimestamp: z.number().int().nonnegative().describe('When the value was created, as a Unix timestamp in milliseconds.'),
|
|
createdByEntityIdentifier: z.string().describe('The entity that created the value.'),
|
|
createdBySignature: z.string().describe('The signature over the created value.'),
|
|
})
|
|
.strict();
|
|
|
|
/**
|
|
* Validation schema for the results created on an invitation.
|
|
*/
|
|
export const xoInvitationResultSchema = z
|
|
.object({
|
|
transactions: z
|
|
.record(z.string(), z.union([ xoInvitationTransactionBroadcastedResultSchema, xoInvitationTransactionCreatedResultSchema ]))
|
|
.exactOptional(),
|
|
data: z.record(z.string(), xoInvitationCreatedResultSchema).exactOptional(),
|
|
})
|
|
.strict();
|
|
|
|
// ============================================================
|
|
// Invitation
|
|
// ============================================================
|
|
|
|
/**
|
|
* Validation schema for an invitation.
|
|
*/
|
|
export const xoInvitationSchema: z.ZodType<XOInvitation> = z
|
|
.object({
|
|
invitationIdentifier: z.string().describe('The identifier of this invitation.'),
|
|
createdAtTimestamp: z.number().int().nonnegative().describe('When this invitation was created, as a Unix timestamp in milliseconds.'),
|
|
templateIdentifier: z.string().describe('The template this invitation uses.'),
|
|
actionIdentifier: z.string().describe('The template action this invitation performs.'),
|
|
commits: z.array(xoInvitationCommitSchema),
|
|
result: xoInvitationResultSchema.exactOptional(),
|
|
})
|
|
.strict();
|