Skip to content

Creating a Sigil

What creating a Sigil will involve, and what you can experiment with today.

Draft

Creating a Sigil means constructing an envelope, serializing it and publishing it to Elysium through the Saelis transport. Two of those three steps depend on decisions that have not been made, so this page separates what is specified from what is experimental.

What creation will involve

  1. Construct an envelope with a protocol identifier, version, optional standard, metadata, references and body.
  2. Serialize it in the canonical encoding.
  3. Publish it in a transaction through the chosen transport.
  4. Confirm inclusion, then wait for indexers to validate it.

Steps 1 and 2 follow the draft in Data Structure. Step 3 is an open design decision.

Construct an envelope

The envelope is plain data. Constructing it requires no network access.

build-envelope.ts

Draft Example — Not Finalized

TypeScript
import type { SigilEnvelope } from './types'

const envelope: SigilEnvelope = {
  p: 'saelis',
  v: 0,
  std: null,
  meta: {
    name: 'Fragment 01',
    description: 'An experimental piece.',
  },
  refs: [],
  body: {},
}

The SigilEnvelope type is defined in Data Structure.

Serialize

The draft encoding is a fixed marker followed by the JSON payload, converted to bytes for the transaction.

serialize.ts

Draft Example — Not Finalized

TypeScript
import { stringToHex, type Hex } from 'viem'
import type { SigilEnvelope } from './types'

export const SIGIL_MARKER = 'data:application/vnd.saelis+json,'

export function serializeSigil(envelope: SigilEnvelope): Hex {
  return stringToHex(SIGIL_MARKER + JSON.stringify(envelope))
}

Experiment with calldata on testnet

Calldata is one of the transport candidates. The code below is valid viem and sends a zero-value transaction to your own address with the serialized envelope as input data. It is a way to experiment with cost, size and retrieval. It does not create a recognized Sigil.

TypeScript
import 'viem/window'
import { createWalletClient, custom } from 'viem'
import { elysiumTestnet } from './lib/elysium'
import { serializeSigil } from './serialize'

const walletClient = createWalletClient({
  chain: elysiumTestnet,
  transport: custom(window.ethereum!),
})

const [account] = await walletClient.requestAddresses()
await walletClient.switchChain({ id: elysiumTestnet.id })

const hash = await walletClient.sendTransaction({
  account,
  to: account,
  value: 0n,
  data: serializeSigil({ p: 'saelis', v: 0, body: {} }),
})

Estimating cost

On Arbitrum Orbit chains, a transaction's fee includes execution gas and a component for the data it posts to the parent chain. Larger envelopes cost more. Estimate before sending:

TypeScript
import { formatEther, type Address } from 'viem'
import { publicClient } from './lib/client'
import { serializeSigil } from './serialize'

const sender: Address = '0x000000000000000000000000000000000000dEaD'

const gas = await publicClient.estimateGas({
  account: sender,
  to: sender,
  data: serializeSigil({ p: 'saelis', v: 0, body: {} }),
})
const gasPrice = await publicClient.getGasPrice()

console.log(`~${formatEther(gas * gasPrice)} HYPE`)

When the transport is finalized

This page will be updated with the canonical publishing method. Until then, treat every example here as a prototype for understanding the model, not as a way to create Sigils.