Feat/parse invitation
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
/* 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();
|
||||
Reference in New Issue
Block a user