Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Quickstart

A minimal, end-to-end path to deploy a contract, register a sale, sell tickets, and withdraw funds.

1) Deploy a ticket contract

You will:

  • Grant MINTER_ROLE to the Gatekeeper contract (required for purchases to mint).
  • Create the first ticket tier.
import {
  Factory,
  MINTER_ROLE,
  gatekeeperAddress,
  gatekeeper1155Address,
  gatekeeper1155FactoryAddress,
} from '@sceneinfrastructure/sdk'
 
const chainId = 8453 // Base
const walletAddress = '0x3456789012345678901234567890123456789012'
 
const setupCalls = [
  // Allow the Gatekeeper contract to mint tickets on purchase
  Factory.encodeGrantRoles({
    user: gatekeeperAddress[chainId],
    roles: MINTER_ROLE,
  }),
  // Create the first tier (tokenId 1 on a new contract)
  Factory.encodeSetupNewTier({
    tokenURI: 'ipfs://QmTicketMetadata...',
    stubURI: 'ipfs://QmStubMetadata...',
    maxSupply: 1000n,
  }),
]
 
const { to, data, predictedAddress } = Factory.prepareDeploy({
  factory: gatekeeper1155FactoryAddress[chainId],
  implementation: gatekeeper1155Address[chainId],
  nonce: BigInt(Date.now()),
  creator: walletAddress,
  owner: walletAddress,
  contractURI: 'ipfs://QmContractMetadata...',
  calls: setupCalls,
})
 
console.log('Predicted contract address:', predictedAddress)
 
// await walletClient.sendTransaction({ to, data })

2) Register a sale

registerSale must be executed via Gatekeeper1155.callGatekeeper on your token contract (requires ADMIN_ROLE or owner).

import {
  Gatekeeper,
  gatekeeper1155Abi,
  gatekeeperAddress,
} from '@sceneinfrastructure/sdk'
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { base } from 'viem/chains'
 
const account = privateKeyToAccount('0x...' as `0x${string}`)
const walletClient = createWalletClient({
  account,
  chain: base,
  transport: http(),
})
 
const chainId = 8453 // Base
const tokenContract = '0x2345678901234567890123456789012345678901'
 
const saleKey = {
  token: tokenContract,
  tokenId: 1n,
  currency: '0x1111111111111111111111111111111111111111',
  fundsRecipient: '0x4567890123456789012345678901234567890123',
  vendor: '0x5678901234567890123456789012345678901234',
  vendorFeeBps: 0,
  referralFeeBps: 0,
  salesTaxBps: 0,
  nonce: 1n,
} as const
 
const saleState = {
  startTime: 1n,
  endTime: 9999999999n,
  quantity: 100,
  price: 1_000000n, // 1.000000 in a 6-decimal currency
  approvers: [],
}
 
const registerData = Gatekeeper.encodeRegisterSale({ saleKey, saleState })
 
// await walletClient.writeContract({
//   address: tokenContract,
//   abi: gatekeeper1155Abi,
//   functionName: 'callGatekeeper',
//   args: [gatekeeperAddress[chainId], registerData],
// })

3) Approve currency + purchase

If totalPrice is greater than zero, the buyer must approve the Gatekeeper contract to spend saleKey.currency before purchasing.

import { gatekeeperAddress } from '@sceneinfrastructure/sdk'
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { base } from 'viem/chains'
 
const account = privateKeyToAccount('0x...' as `0x${string}`)
const walletClient = createWalletClient({
  account,
  chain: base,
  transport: http(),
})
 
const chainId = 8453 // Base
 
const currencyAddress = '0x1111111111111111111111111111111111111111'
const erc20Abi = [
  {
    type: 'function',
    name: 'approve',
    stateMutability: 'nonpayable',
    inputs: [
      { name: 'spender', type: 'address' },
      { name: 'amount', type: 'uint256' },
    ],
    outputs: [{ name: 'success', type: 'bool' }],
  },
] as const
 
// await walletClient.writeContract({
//   address: currencyAddress,
//   abi: erc20Abi,
//   functionName: 'approve',
//   args: [gatekeeperAddress[chainId], 1_000000n],
// })

Then execute the purchase:

import { Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk'
 
const chainId = 8453 // Base
 
const saleKey = {
  token: '0x2345678901234567890123456789012345678901',
  tokenId: 1n,
  currency: '0x1111111111111111111111111111111111111111',
  fundsRecipient: '0x4567890123456789012345678901234567890123',
  vendor: '0x5678901234567890123456789012345678901234',
  vendorFeeBps: 0,
  referralFeeBps: 0,
  salesTaxBps: 0,
  nonce: 1n,
} as const
 
const { to, data } = Gatekeeper.preparePurchase({
  gatekeeper: gatekeeperAddress[chainId],
  saleKey,
  purchaseParams: {
    recipient: '0x6789012345678901234567890123456789012345',
    referrer: '0x0000000000000000000000000000000000000000',
    quantity: 1,
  },
})
 
// await walletClient.sendTransaction({ to, data })

If the sale has approvers, use Approval + preparePurchaseWithApproval instead.

4) (Optional) Read data from the indexer

If you have a Ponder SQL endpoint, you can read back the contract, sale, and ticket status.

import { Client, SaleKey } from '@sceneinfrastructure/sdk'
 
const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' })
 
const chainId = 8453 // Base
const tokenContract = '0x2345678901234567890123456789012345678901'
 
// Contract metadata
const contract = await client.eventContract.get({
  contractAddress: tokenContract,
})
 
// Sale config
const saleKey = {
  token: tokenContract,
  tokenId: 1n,
  currency: '0x1111111111111111111111111111111111111111',
  fundsRecipient: '0x4567890123456789012345678901234567890123',
  vendor: '0x5678901234567890123456789012345678901234',
  vendorFeeBps: 0,
  referralFeeBps: 0,
  salesTaxBps: 0,
  nonce: 1n,
} as const
 
const saleKeyId = SaleKey.generateId(saleKey)
const sale = await client.sale.get({ saleKeyId })
 
// Buyer ticket status
const status = await client.balance.getTokenStatus({
  contractAddress: tokenContract,
  tokenId: 1n,
  address: '0x6789012345678901234567890123456789012345',
})

5) Withdraw funds

Gatekeeper tracks balances internally. Each recipient withdraws their own balance.

import { Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk'
 
const chainId = 8453 // Base
 
const { to, data } = Gatekeeper.prepareWithdraw({
  gatekeeper: gatekeeperAddress[chainId],
  currency: '0x1111111111111111111111111111111111111111',
  amount: 1_000000n,
})
 
// await walletClient.sendTransaction({ to, data })

6) (Optional) Check-in or void tickets

Use Gatekeeper1155.prepareAdminStubTickets to check-in (stub) tickets, or Gatekeeper1155.prepareVoidTickets to void them. See Ticket Management.

Next Steps