@canvas-js/interfaces
This package exports TypeScript types for Canvas messages and other interfaces, along with some serialization utility methods.
Table of Contents
Messages
Canvas operates on two basic types of messages: actions and sessions. Actions are signed function calls that are evaluated by the contract; sessions are used to authorize delegate keys so that users don't have to directly sign every action they want to take.
export type Message = Action | Session | CustomAction
Sessions
export type Session = {
type: "session"
signature: string
payload: {
app: string
chain: string
from: string
sessionAddress: string
sessionDuration: number
sessionIssued: number
block: string | null
}
}
export type SessionPayload = Session["payload"]
Actions
export type Action = {
type: "action"
signature: string
session: string | null
payload: {
app: string
chain: string
from: string
call: string
callArgs: Record<string, ActionArgument>
block: string | null
timestamp: number
}
}
export type ActionPayload = Action["payload"]
export type ActionArgument = null | boolean | number | string
Custom actions
⚠️ This is an advanced use case that is likely to evolve. Use with caution.
Contracts can also optionally export a handler for "custom actions", which are unsigned payloads validating an application-defined JSON Schema document.
export type CustomAction = {
type: "customAction"
name: string
payload: any
app: string
}
Models
Canvas contracts export a set of model types, which are schemas for the application database that the action handlers in the contract can write to.
The model schemas are very simple, consisting of just
export type ModelType = "boolean" | "string" | "integer" | "float" | "datetime"
export type ModelValue = null | boolean | number | string
export type Index = string | string[]
export type Model = {
id: "string";
updated_at: "datetime";
indexes?: Index[];
} & Record<string, ModelType>
Chain Implementations
Canvas is designed to be chain-agnostic, but it needs to know something about the chains so that it can validate signatures and blockhashes. The methods necessary for this are encapsulated in the ChainImplementation
interface.
ChainImplementation
is generic in two parameters Signer
and DelegatedSigner
, which in each chain implementation class are instantiated with the appropriate chain-specific type. Signer
is for direct interactions with the user, and DelegatedSigner
is whatever the delegated key or private wallet class is appropriate for the chain. For example, EthereumChainImplementation
is declared as a ChainImplementation<ethers.Signer, ethers.Wallet>
.
export interface ChainImplementation<Signer = unknown, DelegatedSigner = unknown> {
chain: string
verifyAction(action: Action): Promise<void>
verifySession(session: Session): Promise<void>
signSession(signer: Signer, payload: SessionPayload): Promise<Session>
signAction(signer: Signer, payload: ActionPayload): Promise<Action>
signDelegatedAction(delegatedSigner: DelegatedSigner, payload: ActionPayload): Promise<Action>
getSignerAddress(signer: Signer): Promise<string>
getDelegatedSignerAddress(delegatedSigner: DelegatedSigner): Promise<string>
generateDelegatedSigner(): Promise<DelegatedSigner>
exportDelegatedSigner(delegatedSigner: DelegatedSigner): string
importDelegatedSigner(privateKey: string): DelegatedSigner
hasProvider(): boolean
getLatestBlock(): Promise<string>
}
Serialization methods
These methods rely on deterministic and canonical JSON serialization, which isn't guaranteed by native JSON.stringify
. They use the safe-stable-stringify
package configured with { bigint: false, strict: true, deterministic: true }
.
declare function serializeSessionPayload(payload: SessionPayload): string
declare function serializeActionPayload(payload: ActionPayload): string
declare function getMessageHash(message: Message): string