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_ROLEto 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.