# Gatekeeper SDK > Type-safe utilities for the Gatekeeper ticketing protocol ## Getting Started ### Installation :::code-group ```bash [pnpm] pnpm add @sceneinfrastructure/sdk ``` ```bash [npm] npm install @sceneinfrastructure/sdk ``` ```bash [yarn] yarn add @sceneinfrastructure/sdk ``` ::: #### Peer Dependencies ```bash pnpm add ox zod viem @ponder/client ``` :::info Only install `@ponder/client` if you plan to use the indexed data client. `viem` is only required if you intend to send transactions or read onchain state; the SDK itself just prepares calldata. ::: ### SDK Surface Area * **Onchain helpers**: `Factory`, `Gatekeeper`, `Gatekeeper1155` for deployment, sales, purchases, and ticket ops. * **Indexer client**: `Client` for Ponder SQL queries and live subscriptions. ### Basic Usage #### Import the SDK ```ts twoslash /// // ---cut--- import { Factory, Gatekeeper, Approval, Sale, SaleKey, MINTER_ROLE, gatekeeperAddress, gatekeeper1155FactoryAddress, } from '@sceneinfrastructure/sdk' ``` #### Contract Addresses The SDK exports deployed contract addresses for supported chains: ```ts twoslash /// // ---cut--- import { gatekeeperAddress, gatekeeper1155FactoryAddress, gatekeeper1155Address, } from '@sceneinfrastructure/sdk' // Access addresses by chain ID const gatekeeperOnBase = gatekeeperAddress[8453] const factoryOnBase = gatekeeper1155FactoryAddress[8453] ``` #### Role Constants Common role constants for access control: ```ts twoslash /// // ---cut--- import { ADMIN_ROLE, HOST_ROLE, DOOR_ROLE, MINTER_ROLE } from '@sceneinfrastructure/sdk' // Combine roles with bitwise OR const combinedRoles = ADMIN_ROLE | MINTER_ROLE ``` ### Output Formats SDK functions support two output formats via the `as` parameter: | Format | Returns | Use With | | ------------------ | ---------------------------- | ----------------- | | `'data'` (default) | `{ to, data }` | `sendTransaction` | | `'args'` | `{ to, functionName, args }` | `writeContract` | ```ts twoslash /// // ---cut--- import { Gatekeeper } from '@sceneinfrastructure/sdk' const saleKey = { token: '0x1234567890123456789012345678901234567890', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, vendor: '0x2345678901234567890123456789012345678901', nonce: 1n, currency: '0x3456789012345678901234567890123456789012', fundsRecipient: '0x4567890123456789012345678901234567890123', tokenId: 1n, } as const // Default: raw calldata const { to, data } = Gatekeeper.preparePurchase({ gatekeeper: '0x5678901234567890123456789012345678901234', saleKey, purchaseParams: { recipient: '0x6789012345678901234567890123456789012345', referrer: '0x0000000000000000000000000000000000000000', quantity: 1, }, }) // With as: 'args' for viem's writeContract const { to: addr, functionName, args } = Gatekeeper.preparePurchase({ gatekeeper: '0x5678901234567890123456789012345678901234', saleKey, purchaseParams: { recipient: '0x6789012345678901234567890123456789012345', referrer: '0x0000000000000000000000000000000000000000', quantity: 1, }, as: 'args', }) ``` ### Querying Indexed Data ```ts twoslash /// // ---cut--- import { Client, ADMIN_ROLE } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) // Query contracts const contract = await client.eventContract.get({ contractAddress: '0x1234567890123456789012345678901234567890' }) // Check permissions const isAdmin = await client.permission.hasRole({ contractAddress: '0x1234567890123456789012345678901234567890', user: '0x2345678901234567890123456789012345678901', role: ADMIN_ROLE, }) ``` See [Querying Guide](/guides/querying) for live subscriptions and raw queries. ### Next Steps Now that you have the SDK installed, learn how to: * [Quickstart](/guides/quickstart) * [Deploy a Gatekeeper1155 contract](/guides/deploying) * [Register and manage sales](/guides/sales) * [Execute purchases](/guides/purchasing) * [Query indexed data](/guides/querying) ## ABIs Contract ABIs and config objects exported from the SDK. ```ts twoslash /// // ---cut--- import { gatekeeperAbi, gatekeeper1155Abi, gatekeeper1155FactoryAbi, } from '@sceneinfrastructure/sdk' ``` ### Available ABIs #### gatekeeperAbi The Gatekeeper contract ABI for sales and purchases. ```ts twoslash /// // ---cut--- import { gatekeeperAbi } from '@sceneinfrastructure/sdk' // Use with viem // const hash = await walletClient.writeContract({ // address: gatekeeperAddress[chainId], // abi: gatekeeperAbi, // functionName: 'purchase', // args: [saleKey, purchaseParams], // }) ``` Key functions: * `registerSale` - Register a new sale configuration * `updateSale` - Update an existing sale * `purchase` - Execute a purchase * `purchaseWithApproval` - Execute a purchase with EIP-712 signature * `getPrice` - Calculate purchase price with fees * `withdraw` - Withdraw funds #### gatekeeper1155Abi The Gatekeeper1155 ERC1155 contract ABI for ticket operations. ```ts twoslash /// // ---cut--- import { gatekeeper1155Abi } from '@sceneinfrastructure/sdk' // Use with viem // const balance = await publicClient.readContract({ // address: contractAddress, // abi: gatekeeper1155Abi, // functionName: 'balanceOf', // args: [userAddress, tokenId], // }) ``` Key functions: * `adminMint` - Mint tickets (requires MINTER\_ROLE, HOST\_ROLE, or ADMIN\_ROLE) * `adminStubTickets` - Check-in attendees (requires DOOR\_ROLE, HOST\_ROLE, or ADMIN\_ROLE) * `voidTickets` - Burn tickets (requires ADMIN\_ROLE, HOST\_ROLE, or MINTER\_ROLE) * `setupNewTier` - Create a new token tier * `updateToken` - Update tier metadata * `grantRoles` / `revokeRoles` - Role management * `balanceOf` - Check ticket balance * `getTierInfo` - Get tier metadata #### gatekeeper1155FactoryAbi The factory contract ABI for deploying ticket contracts. ```ts twoslash /// // ---cut--- import { gatekeeper1155FactoryAbi } from '@sceneinfrastructure/sdk' // Use with viem // const { request } = await publicClient.simulateContract({ // address: gatekeeper1155FactoryAddress[chainId], // abi: gatekeeper1155FactoryAbi, // functionName: 'deploy', // args: [implementation, salt, owner, contractURI, calls], // }) ``` Key functions: * `deploy` - Deploy a new Gatekeeper1155 proxy * `predictDeterministicAddress` - Calculate CREATE2 address ### Config Objects For convenience, the SDK also exports config objects that combine addresses and ABIs: ```ts twoslash /// // ---cut--- import { gatekeeperConfig, gatekeeper1155Config, gatekeeper1155FactoryConfig, } from '@sceneinfrastructure/sdk' // Each config contains address and abi console.log(gatekeeperConfig.address[8453]) console.log(gatekeeperConfig.abi) ``` ### Usage with SDK Modules The SDK modules handle ABI encoding internally, so you typically don't need to use ABIs directly: ```ts twoslash /// // ---cut--- import { Gatekeeper1155 } from '@sceneinfrastructure/sdk' // SDK encodes the ABI call for you const { to, data } = Gatekeeper1155.prepareAdminMint({ contract: '0x...', recipient: '0x...', tokenId: 1n, quantity: 1n, }) // Just send the transaction // await walletClient.sendTransaction({ to, data }) ``` However, ABIs are useful for: * Reading contract state with `publicClient.readContract` * Using viem's `simulateContract` for gas estimation * Building custom integrations ### Usage Example ```ts twoslash /// // ---cut--- import { gatekeeperAbi, gatekeeperAddress, gatekeeper1155Abi, } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base // Read sale state // const sale = await publicClient.readContract({ // address: gatekeeperAddress[chainId], // abi: gatekeeperAbi, // functionName: 'getSale', // args: [saleKeyId], // }) // Read ticket balance // const balance = await publicClient.readContract({ // address: contractAddress, // abi: gatekeeper1155Abi, // functionName: 'balanceOf', // args: [userAddress, 1n], // }) ``` ## Contract Addresses Deployed contract addresses for the Gatekeeper protocol. ```ts twoslash /// // ---cut--- import { gatekeeperAddress, gatekeeper1155Address, gatekeeper1155FactoryAddress, } from '@sceneinfrastructure/sdk' ``` ### Supported Networks | Network | Chain ID | | ------------ | -------- | | Base Mainnet | `8453` | | Base Sepolia | `84532` | ### Gatekeeper The main Gatekeeper contract handles sales, purchases, and fund distribution. ```ts twoslash /// // ---cut--- import { gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const address = gatekeeperAddress[chainId] // '0xC937fb52d0dF60ecba04eb5b1186aA3A9d267910' ``` | Network | Address | | ------------ | ------------------------------------------------------------------------------------------------------------------------------- | | Base Mainnet | [`0xC937fb52d0dF60ecba04eb5b1186aA3A9d267910`](https://basescan.org/address/0xC937fb52d0dF60ecba04eb5b1186aA3A9d267910) | | Base Sepolia | [`0x34b3E31A9a48b1254f221F065c6840C4c869ED6A`](https://sepolia.basescan.org/address/0x34b3E31A9a48b1254f221F065c6840C4c869ED6A) | ### Gatekeeper1155 The ERC1155 implementation contract used as the template for ticket contracts. ```ts twoslash /// // ---cut--- import { gatekeeper1155Address } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const implementation = gatekeeper1155Address[chainId] // '0x3740Eb562aff5eda722C5530555f98669F9F8489' ``` | Network | Address | | ------------ | ------------------------------------------------------------------------------------------------------------------------------- | | Base Mainnet | [`0x3740Eb562aff5eda722C5530555f98669F9F8489`](https://basescan.org/address/0x3740Eb562aff5eda722C5530555f98669F9F8489) | | Base Sepolia | [`0x9aF5185589bDA188de8FA5Fc3319540042a881e8`](https://sepolia.basescan.org/address/0x9aF5185589bDA188de8FA5Fc3319540042a881e8) | ### Gatekeeper1155Factory The factory contract for deploying new ticket contracts. ```ts twoslash /// // ---cut--- import { gatekeeper1155FactoryAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const factory = gatekeeper1155FactoryAddress[chainId] // '0x36d9641Aa964061CA21253e23d7BbfEBc33d1Bae' ``` | Network | Address | | ------------ | ------------------------------------------------------------------------------------------------------------------------------- | | Base Mainnet | [`0x36d9641Aa964061CA21253e23d7BbfEBc33d1Bae`](https://basescan.org/address/0x36d9641Aa964061CA21253e23d7BbfEBc33d1Bae) | | Base Sepolia | [`0xe6f9A5E7868C5daBCa486f8cd75a47b50333D8bb`](https://sepolia.basescan.org/address/0xe6f9A5E7868C5daBCa486f8cd75a47b50333D8bb) | ### Type-Safe Access All address objects are typed with chain IDs: ```ts twoslash /// // ---cut--- import { gatekeeperAddress } from '@sceneinfrastructure/sdk' // TypeScript knows valid chain IDs gatekeeperAddress[8453] // OK gatekeeperAddress[84532] // OK // gatekeeperAddress[1] // Type error - chain not supported ``` ### Usage Example ```ts twoslash /// // ---cut--- import { Factory, Gatekeeper, gatekeeperAddress, gatekeeper1155Address, gatekeeper1155FactoryAddress, } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const walletAddress = '0x3456789012345678901234567890123456789012' // Deploy a new ticket contract const { to, data, predictedAddress } = Factory.prepareDeploy({ factory: gatekeeper1155FactoryAddress[chainId], implementation: gatekeeper1155Address[chainId], nonce: BigInt(Date.now()), creator: walletAddress, owner: walletAddress, contractURI: 'ipfs://...', }) // Create a sale key with a currency address const saleKey = { token: predictedAddress, currency: '0x1111111111111111111111111111111111111111', // ... other fields } as const ``` ## Constants Protocol constants exported from the SDK. ```ts twoslash /// // ---cut--- import { ADMIN_ROLE, HOST_ROLE, DOOR_ROLE, MINTER_ROLE, PROTOCOL_FEE_BPS, } from '@sceneinfrastructure/sdk' ``` ### Role Constants Bitmap-based role values for access control on Gatekeeper1155 contracts. #### ADMIN\_ROLE ```ts twoslash /// // ---cut--- const ADMIN_ROLE = 1n // 1 << 0 ``` Full contract control: * Setup new tiers * Mint tickets * Stub tickets (check-in) * Void tickets * Update token metadata * Grant and revoke roles * Call gatekeeper contract #### HOST\_ROLE ```ts twoslash /// // ---cut--- const HOST_ROLE = 2n // 1 << 1 ``` Event management capabilities: * Mint tickets * Stub tickets (check-in) * Void tickets #### DOOR\_ROLE ```ts twoslash /// // ---cut--- const DOOR_ROLE = 4n // 1 << 2 ``` Check-in only: * Stub tickets (convert to used state) #### MINTER\_ROLE ```ts twoslash /// // ---cut--- const MINTER_ROLE = 8n // 1 << 3 ``` Issuance permissions: * Mint tickets * Void tickets * Required by Gatekeeper contract for purchases ### Combining Roles Roles can be combined with bitwise OR when granting multiple roles to staff: ```ts twoslash /// // ---cut--- import { HOST_ROLE, DOOR_ROLE } from '@sceneinfrastructure/sdk' // Grant HOST + DOOR to event staff const staffRoles = HOST_ROLE | DOOR_ROLE // 6n ``` ### Protocol Constants #### PROTOCOL\_FEE\_BPS ```ts twoslash /// // ---cut--- const PROTOCOL_FEE_BPS = 100 // 1% ``` The protocol fee in basis points (1/100th of a percent). * 100 BPS = 1% fee on all sales * Fee is collected by the protocol on each purchase ### Indexer Endpoints Docs-only placeholders for Ponder SQL endpoints: ```ts const PONDER_ENDPOINTS = { dev: 'https://ponder-dev.example.com/sql', prod: 'https://ponder.example.com/sql', } as const ``` ### Role Values Reference | Constant | Value | Binary | Hex | | ------------- | ----- | ------ | ----- | | `ADMIN_ROLE` | `1n` | `0001` | `0x1` | | `HOST_ROLE` | `2n` | `0010` | `0x2` | | `DOOR_ROLE` | `4n` | `0100` | `0x4` | | `MINTER_ROLE` | `8n` | `1000` | `0x8` | ### Usage Example ```ts twoslash /// // ---cut--- import { Gatekeeper1155, Factory, MINTER_ROLE, HOST_ROLE, DOOR_ROLE, gatekeeperAddress, } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base // Grant MINTER to Gatekeeper for sales const grantMinter = Factory.encodeGrantRoles({ user: gatekeeperAddress[chainId], roles: MINTER_ROLE, }) // Grant HOST + DOOR to staff const grantStaff = Factory.encodeGrantRoles({ user: '0x2345678901234567890123456789012345678901', roles: HOST_ROLE | DOOR_ROLE, }) ``` ## Deploying Contracts Deploy Gatekeeper1155 contracts via the factory using CREATE2 for deterministic addresses. ### Deploy with Setup Calls ```ts twoslash /// // ---cut--- import { Factory, MINTER_ROLE, gatekeeper1155FactoryAddress, gatekeeper1155Address, gatekeeperAddress, } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const walletAddress = '0x3456789012345678901234567890123456789012' // Prepare setup calls const setupCalls = [ // Grant MINTER role to the Gatekeeper contract Factory.encodeGrantRoles({ user: gatekeeperAddress[chainId], roles: MINTER_ROLE, }), // Create a token tier Factory.encodeSetupNewTier({ tokenURI: 'ipfs://QmTokenMetadata...', 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, }) ``` ### Setup Call Helpers #### Grant Roles Grant access control roles to addresses: ```ts twoslash /// // ---cut--- import { Factory, ADMIN_ROLE, HOST_ROLE, DOOR_ROLE, MINTER_ROLE, gatekeeperAddress, } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base // Grant single role (e.g., to the Gatekeeper contract) const grantMinter = Factory.encodeGrantRoles({ user: gatekeeperAddress[chainId], roles: MINTER_ROLE, }) // Grant multiple roles to an admin address const grantMultiple = Factory.encodeGrantRoles({ user: '0x2345678901234567890123456789012345678901', roles: ADMIN_ROLE | HOST_ROLE, }) ``` #### Setup Token Tiers Create token tiers with metadata and supply limits: ```ts twoslash /// // ---cut--- import { Factory } from '@sceneinfrastructure/sdk' // Limited supply tier const limitedTier = Factory.encodeSetupNewTier({ tokenURI: 'ipfs://QmTokenMetadata...', stubURI: 'ipfs://QmStubMetadata...', maxSupply: 500n, }) // Unlimited supply tier (maxSupply = 0) const unlimitedTier = Factory.encodeSetupNewTier({ tokenURI: 'ipfs://QmTokenMetadata...', stubURI: 'ipfs://QmStubMetadata...', maxSupply: 0n, }) ``` #### Call Gatekeeper Execute calls to the Gatekeeper contract during deployment: ```ts twoslash /// // ---cut--- import { Factory, Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base // Token contract will be the predictedAddress from prepareDeploy const tokenContractAddress = '0x2345678901234567890123456789012345678901' const saleKey = { token: tokenContractAddress, vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, vendor: '0x3456789012345678901234567890123456789012', nonce: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x5678901234567890123456789012345678901234', tokenId: 1n, } as const // Encode a registerSale call const registerData = Gatekeeper.encodeRegisterSale({ saleKey, saleState: { startTime: 1n, endTime: 9999999999n, quantity: 100, price: 1000000n, approvers: [], }, }) // Wrap in callGatekeeper const gatekeeperCall = Factory.encodeCallGatekeeper({ gatekeeper: gatekeeperAddress[chainId], data: registerData, }) ``` ### Next Steps * [Register a sale](/guides/sales) on your deployed contract * [API Reference: Factory](/api/factory) for complete function documentation ## Executing Purchases ### Approve Currency (ERC20) Gatekeeper pulls payment tokens from the buyer. If `totalPrice` is greater than zero, the buyer must approve the Gatekeeper contract to spend the sale currency (the `SaleKey.currency` address) before calling `purchase` or `purchaseWithApproval`. ```ts twoslash /// // ---cut--- 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 // Approve at least the total price for this purchase const amount = 10_000000n // await walletClient.writeContract({ // address: currencyAddress, // abi: erc20Abi, // functionName: 'approve', // args: [gatekeeperAddress[chainId], amount], // }) ``` Use `Gatekeeper.prepareGetPrice` to calculate the `totalPrice` you want to approve. ### Open Purchases `preparePurchase` is only for sales with no approvers. If the sale has approvers, use `preparePurchaseWithApproval` instead. ```ts twoslash /// // ---cut--- import { Gatekeeper, 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 saleKey = { token: '0x2345678901234567890123456789012345678901', // Your deployed contract 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: 2, }, }) // Execute the purchase // await walletClient.sendTransaction({ to, data }) ``` ### Approval-Gated Purchases For sales with approvers, you need: 1. Generate EIP-712 typed data 2. Get a signature from an authorized approver 3. Execute the purchase with the signature The signature must be from one of the addresses in `saleState.approvers`. Signatures are single-use and bound to `saleKeyId`, `recipient`, `referrer`, `quantity`, `deadline`, and `salt`. #### Step 1: Generate Typed Data ```ts twoslash /// // ---cut--- import { Approval, SaleKey, gatekeeperAddress } from '@sceneinfrastructure/sdk' import { Hex } from 'ox' const chainId = 8453 // Base const buyerAddress = '0x2345678901234567890123456789012345678901' const saleKey = { token: '0x3456789012345678901234567890123456789012', // Your deployed contract tokenId: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x5678901234567890123456789012345678901234', vendor: '0x6789012345678901234567890123456789012345', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, nonce: 1n, } as const // Generate the sale key ID const saleKeyId = SaleKey.generateId(saleKey) // Build the typed data const typedData = Approval.getTypedData({ chainId: BigInt(chainId), gatekeeper: gatekeeperAddress[chainId], message: { recipient: buyerAddress, referrer: '0x0000000000000000000000000000000000000000', quantity: 1, deadline: Approval.getDeadline(), // 15 minutes from now salt: Hex.random(32), saleKeyId, }, }) ``` #### Step 2: Get Approver Signature Have an authorized approver sign the typed data: ```ts twoslash /// // ---cut--- // This happens on the approver's side // const signature = await approverWallet.signTypedData(typedData) ``` #### Step 3: Execute with Approval ```ts twoslash /// // ---cut--- import { Gatekeeper, 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 saleKey = { token: '0x2345678901234567890123456789012345678901', // Your deployed contract tokenId: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x4567890123456789012345678901234567890123', vendor: '0x5678901234567890123456789012345678901234', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, nonce: 1n, } as const // Assume we have the signature params from the approval flow const deadline = BigInt(Math.floor(Date.now() / 1000) + 900) // 15 min const salt = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' const signature = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12' const { to, data } = Gatekeeper.preparePurchaseWithApproval({ gatekeeper: gatekeeperAddress[chainId], saleKey, purchaseParams: { recipient: '0x6789012345678901234567890123456789012345', referrer: '0x0000000000000000000000000000000000000000', quantity: 1, }, signatureParams: { deadline, salt, signature, }, }) // Execute the purchase // await walletClient.sendTransaction({ to, data }) ``` ### Custom Deadline Configure how long an approval is valid: ```ts twoslash /// // ---cut--- import { Approval } from '@sceneinfrastructure/sdk' // Default: 15 minutes from now const defaultDeadline = Approval.getDeadline() // Custom: 30 minutes from now const thirtyMinutes = Approval.getDeadline(new Date(), 30) // Custom: 1 hour from a specific time const futureTime = new Date('2024-12-31T00:00:00Z') const oneHour = Approval.getDeadline(futureTime, 60) ``` ### Getting Price Information Before purchasing, get the price breakdown: ```ts twoslash /// // ---cut--- import { Gatekeeper, gatekeeperAddress, gatekeeperAbi } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x2345678901234567890123456789012345678901', // Your deployed contract tokenId: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x4567890123456789012345678901234567890123', vendor: '0x5678901234567890123456789012345678901234', vendorFeeBps: 250, referralFeeBps: 100, salesTaxBps: 0, nonce: 1n, } as const const { to, functionName, args } = Gatekeeper.prepareGetPrice({ gatekeeper: gatekeeperAddress[chainId], saleKey, purchaseParams: { recipient: '0x6789012345678901234567890123456789012345', referrer: '0x7890123456789012345678901234567890123456', quantity: 1, }, as: 'args', }) // Read with viem // const result = await publicClient.readContract({ // address: to, // abi: gatekeeperAbi, // functionName, // args, // }) // Result: { totalPrice, subtotal, protocolFee, vendorFee, referrerFee, salesTax } ``` ### Checking Sale Status Verify a sale exists and is active: ```ts twoslash /// // ---cut--- import { Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x2345678901234567890123456789012345678901', // Your deployed contract tokenId: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x4567890123456789012345678901234567890123', vendor: '0x5678901234567890123456789012345678901234', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, nonce: 1n, } as const // Check if sale is registered const { to, functionName, args } = Gatekeeper.prepareIsSaleRegistered({ gatekeeper: gatekeeperAddress[chainId], saleKey, as: 'args', }) // Get full sale state const getSale = Gatekeeper.prepareGetSale({ gatekeeper: gatekeeperAddress[chainId], saleKey, as: 'args', }) ``` ### Withdrawing Funds After sales, withdraw accumulated funds: ```ts twoslash /// // ---cut--- import { Gatekeeper, 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 { to, data } = Gatekeeper.prepareWithdraw({ gatekeeper: gatekeeperAddress[chainId], currency: '0x1111111111111111111111111111111111111111', amount: 1000000n, // 1.000000 in a 6-decimal currency }) // Execute withdrawal // await walletClient.sendTransaction({ to, data }) ``` Gatekeeper tracks balances internally and does not transfer funds directly to vendors or recipients at purchase time. Each recipient calls `withdraw` for their own balance. Protocol fees are withdrawn by the contract owner via `withdrawProtocol`. ### Next Steps * [API Reference: Gatekeeper](/api/gatekeeper) for complete function documentation * [API Reference: Approval](/api/approval) for EIP-712 signature details ### Common Errors * **Approval required**: the sale has approvers set, so `preparePurchase` will revert. Use `preparePurchaseWithApproval` instead. * **Signature expired / already used**: approval signatures are time‑bounded and single‑use. Regenerate with a fresh `salt` and a new `deadline`. * **Approver not authorized**: the signer isn’t in `saleState.approvers`. * **Sale inactive**: current time is outside `startTime`/`endTime`. * **Insufficient quantity**: requested `quantity` exceeds remaining sale `quantity`. * **ERC20 allowance or balance**: buyer hasn’t approved enough or doesn’t have enough balance in `SaleKey.currency`. import { PonderReadDemo } from '../../components/PonderReadDemo' ## Querying Indexed Data ### Creating a Client ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) ``` The `url` should point to your Ponder SQL endpoint. ### Try It Point this at your Ponder SQL endpoint to run a live read query in the browser. ### Domain Namespaces The client provides domain-specific namespaces for common query patterns: ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) const walletAddress = '0x3456789012345678901234567890123456789012' // Query deployed contracts const contract = await client.eventContract.get({ contractAddress: '0x1234567890123456789012345678901234567890' }) // List contracts by creator const myContracts = await client.eventContract.listByCreator({ creator: walletAddress }) // Check token balances const balance = await client.balance.get({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n, address: walletAddress }) // Query sale configurations const sales = await client.sale.listByContract({ contractAddress: '0x1234567890123456789012345678901234567890' }) ``` #### Available Namespaces | Namespace | Description | | --------------- | ------------------------------------------ | | `eventContract` | Deployed Gatekeeper1155 contracts | | `sale` | Sale configurations | | `token` | ERC1155 token data | | `tier` | Ticket tier configurations | | `balance` | Token holder balances | | `transfer` | Transfer history | | `permission` | Role grants and permissions | | `stub` | Check-in events (ticket to stub exchanges) | | `void` | Voiding events (burns without stub) | ### Pagination ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) const page1 = await client.eventContract.list({ limit: 10, offset: 0 }) const page2 = await client.eventContract.list({ limit: 10, offset: 10 }) const sales = await client.sale.listByContract({ contractAddress: '0x1234567890123456789012345678901234567890', orderBy: 'createdAt', order: 'desc' }) ``` ### Raw Drizzle Queries For complex queries not covered by the namespaces, use the raw Drizzle query builder: ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' import { eq, and, gt } from '@ponder/client' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) // Access the query builder const results = await client.db .select() .from(client.schema.onchainTicketBalance) .where( and( eq(client.schema.onchainTicketBalance.contractAddress, '0x1234567890123456789012345678901234567890'), gt(client.schema.onchainTicketBalance.balance, 0n) ) ) .limit(100) ``` #### Available Schema Tables Access tables via `client.schema`: * `onchainEventContract` - Deployed contracts * `onchainToken` - Token configurations * `onchainTicketTier` - Tier configurations * `onchainSaleConfig` - Sale configurations * `onchainOrder` - Purchase orders * `onchainOrderItem` - Order line items * `onchainTicketTransfer` - Transfer events * `onchainTicketBalance` - Current balances * `onchainRoleGrant` - Role assignments * `onchainSwap` - Check-in swaps * `onchainVoid` - Void events ### Live Subscriptions Subscribe to real-time updates using Server-Sent Events: ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) const { unsubscribe } = client.live( // Query function (db) => db.select().from(client.schema.onchainOrder).limit(10), // On result callback (result) => { console.log('Orders updated:', result) }, // On error callback (error) => { console.error('Subscription error:', error) } ) // Later: clean up the subscription unsubscribe() ``` #### Cleanup Pattern Always unsubscribe when the component unmounts or the subscription is no longer needed: ```ts import { useEffect, useState } from 'react' import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) // React example const [balances, setBalances] = useState([]) useEffect(() => { const { unsubscribe } = client.live( (db) => db.select().from(client.schema.onchainTicketBalance), setBalances, console.error ) return () => unsubscribe() }, []) ``` ### Indexer Status Check the indexer sync status for all chains: ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) const status = await client.getStatus() console.log('Indexer status:', status) ``` ### Next Steps * [Client API Reference](/api/client) - Full client API documentation * [EventContract](/api/event-contract) - Query deployed contracts * [Balance](/api/balance) - Query token balances * [Permission](/api/permission) - Query role assignments ## Quickstart A minimal, end-to-end path to deploy a contract, register a sale, sell tickets, and withdraw funds. :::info This guide assumes you have a funded wallet on your target chain and a Gatekeeper indexer running (optional for reads). ::: ### 1) Deploy a ticket contract You will: * Grant `MINTER_ROLE` to the Gatekeeper contract (required for purchases to mint). * Create the first ticket tier. ```ts twoslash /// // ---cut--- 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). ```ts twoslash /// // ---cut--- 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. ```ts twoslash /// // ---cut--- 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: ```ts twoslash /// // ---cut--- 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](/api/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. ```ts twoslash /// // ---cut--- 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. ```ts twoslash /// // ---cut--- 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](/guides/tickets). ### Next Steps * [Role Management](/guides/roles) * [Managing Sales](/guides/sales) * [Executing Purchases](/guides/purchasing) * [Ticket Management](/guides/tickets) ## Role Management ### Available Roles ```ts twoslash /// // ---cut--- import { ADMIN_ROLE, HOST_ROLE, DOOR_ROLE, MINTER_ROLE, } from '@sceneinfrastructure/sdk' // Role values (bitmask) console.log(ADMIN_ROLE) // 1n (1 << 0) console.log(HOST_ROLE) // 2n (1 << 1) console.log(DOOR_ROLE) // 4n (1 << 2) console.log(MINTER_ROLE) // 8n (1 << 3) ``` #### Role Capabilities | Role | Value | Capabilities | | ------------- | ----- | ------------------------------------------------------------------------------------------- | | `ADMIN_ROLE` | `1n` | Full control: setup tiers, mint, stub, void, update metadata, manage roles, call gatekeeper | | `HOST_ROLE` | `2n` | Event management: mint tickets, stub tickets (check-in), void tickets | | `DOOR_ROLE` | `4n` | Check-in only: stub tickets | | `MINTER_ROLE` | `8n` | Issuance: mint tickets, void tickets | #### Permission Matrix | Function | ADMIN | HOST | DOOR | MINTER | Owner | | ------------------ | :---: | :--: | :--: | :----: | :---: | | `setupNewTier` | ✓ | | | | ✓ | | `adminMint` | ✓ | ✓ | | ✓ | ✓ | | `adminStubTickets` | ✓ | ✓ | ✓ | | ✓ | | `voidTickets` | ✓ | ✓ | | ✓ | ✓ | | `updateToken` | ✓ | | | | ✓ | | `callGatekeeper` | ✓ | | | | ✓ | | `grantRoles` | ✓ | | | | ✓ | | `revokeRoles` | ✓ | | | | ✓ | ### Granting Roles Grant roles to addresses. Requires `ADMIN_ROLE` (the owner is also permitted). #### Single Role ```ts twoslash /// // ---cut--- import { createWalletClient, http } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { base } from 'viem/chains' import { Gatekeeper1155, MINTER_ROLE, gatekeeperAddress } from '@sceneinfrastructure/sdk' const account = privateKeyToAccount('0x...' as `0x${string}`) const walletClient = createWalletClient({ account, chain: base, transport: http(), }) const chainId = 8453 // Base // Grant MINTER to Gatekeeper contract (required for sales) const { to, data } = Gatekeeper1155.prepareGrantRoles({ contract: '0x1234567890123456789012345678901234567890', user: gatekeeperAddress[chainId], roles: MINTER_ROLE, }) // await walletClient.sendTransaction({ to, data }) ``` #### Multiple Roles Combine roles with bitwise OR: ```ts twoslash /// // ---cut--- import { Gatekeeper1155, ADMIN_ROLE, HOST_ROLE, DOOR_ROLE } from '@sceneinfrastructure/sdk' // Grant multiple roles to an operator const { to, data } = Gatekeeper1155.prepareGrantRoles({ contract: '0x1234567890123456789012345678901234567890', user: '0x2345678901234567890123456789012345678901', roles: HOST_ROLE | DOOR_ROLE, // Combine with | }) ``` ### Revoking Roles Remove roles from addresses. Requires `ADMIN_ROLE` (the owner is also permitted). ```ts twoslash /// // ---cut--- import { createWalletClient, http } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { base } from 'viem/chains' import { Gatekeeper1155, MINTER_ROLE } from '@sceneinfrastructure/sdk' const account = privateKeyToAccount('0x...' as `0x${string}`) const walletClient = createWalletClient({ account, chain: base, transport: http(), }) const { to, data } = Gatekeeper1155.prepareRevokeRoles({ contract: '0x1234567890123456789012345678901234567890', user: '0x2345678901234567890123456789012345678901', roles: MINTER_ROLE, }) // await walletClient.sendTransaction({ to, data }) ``` ### Roles During Deployment Grant roles as part of contract deployment: ```ts twoslash /// // ---cut--- import { Factory, MINTER_ROLE, HOST_ROLE, DOOR_ROLE, gatekeeperAddress, gatekeeper1155FactoryAddress, gatekeeper1155Address, } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const walletAddress = '0x3456789012345678901234567890123456789012' const operatorAddress = '0x4567890123456789012345678901234567890123' const setupCalls = [ // Grant MINTER to Gatekeeper (required for sales) Factory.encodeGrantRoles({ user: gatekeeperAddress[chainId], roles: MINTER_ROLE, }), // Grant HOST + DOOR to operator Factory.encodeGrantRoles({ user: operatorAddress, roles: HOST_ROLE | DOOR_ROLE, }), ] const { to, data, predictedAddress } = Factory.prepareDeploy({ factory: gatekeeper1155FactoryAddress[chainId], implementation: gatekeeper1155Address[chainId], nonce: BigInt(Date.now()), creator: walletAddress, owner: walletAddress, contractURI: 'ipfs://...', calls: setupCalls, }) ``` ### Typical Role Assignments #### Event Setup For a typical event, assign roles as follows: | Address | Roles | Purpose | | ------------------- | ------------------------ | ------------------------------------- | | Gatekeeper Contract | `MINTER_ROLE` | Enable ticket purchases through sales | | Event Owner | `ADMIN_ROLE` | Full contract control | | Event Staff | `HOST_ROLE \| DOOR_ROLE` | Manage event and check-in attendees | #### Checking Roles Read role assignments using viem directly: ```ts twoslash /// // ---cut--- import { gatekeeper1155Abi } from '@sceneinfrastructure/sdk' // Check if address has role // const hasRole = await publicClient.readContract({ // address: contractAddress, // abi: gatekeeper1155Abi, // functionName: 'hasAllRoles', // args: [userAddress, DOOR_ROLE], // }) ``` ### Next Steps * [Deploying Contracts](/guides/deploying) with role setup * [Ticket Management](/guides/tickets) for operations requiring roles * [Reference: Constants](/reference/constants) for role values ## Managing Sales Sales are identified by a `SaleKey` and configured with a `SaleState`. Key details: * The `SaleKey` is the immutable identity of a sale. Changing any field creates a new `saleKeyId`. Use `updateSale` only when the `SaleKey` stays the same, and use `nonce` to create multiple sales for the same token. * `startTime` and `endTime` are Unix timestamps (seconds). `startTime` must be non-zero; `0n` is treated as "uninitialized" onchain. * `price` is expressed in the smallest unit of the sale currency (use the ERC20's decimals). ### Registering a Sale ```ts twoslash /// // ---cut--- import { Gatekeeper, Factory, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x2345678901234567890123456789012345678901', // Your deployed contract 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: 1000000n, approvers: [], } // Encode the registerSale call const registerData = Gatekeeper.encodeRegisterSale({ saleKey, saleState, }) // Wrap in callGatekeeper (for use during deployment) const callData = Factory.encodeCallGatekeeper({ gatekeeper: gatekeeperAddress[chainId], data: registerData, }) ``` If you're registering or updating sales after deployment, call `Gatekeeper1155.callGatekeeper` directly (requires `ADMIN_ROLE` or owner on the token contract): ```ts twoslash /// // ---cut--- import { gatekeeper1155Abi, Gatekeeper, 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: 1000000n, approvers: [], } const registerData = Gatekeeper.encodeRegisterSale({ saleKey, saleState }) // await walletClient.writeContract({ // address: tokenContract, // abi: gatekeeper1155Abi, // functionName: 'callGatekeeper', // args: [gatekeeperAddress[chainId], registerData], // }) ``` ### Approval-Gated Sales For sales requiring approval signatures, add approver addresses: ```ts twoslash /// // ---cut--- import { Gatekeeper } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', // Your deployed contract tokenId: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x3456789012345678901234567890123456789012', vendor: '0x4567890123456789012345678901234567890123', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, nonce: 1n, } as const const registerData = Gatekeeper.encodeRegisterSale({ saleKey, saleState: { startTime: 1n, endTime: 9999999999n, quantity: 100, price: 1000000n, approvers: [ '0x5678901234567890123456789012345678901234', '0x6789012345678901234567890123456789012345', ], }, }) ``` When approvers are set, purchases require a valid EIP-712 signature from one of the approvers. See [Executing Purchases](/guides/purchasing) for details. ### Updating a Sale Update an existing sale's configuration: ```ts twoslash /// // ---cut--- import { Gatekeeper, Factory, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x2345678901234567890123456789012345678901', // Your deployed contract tokenId: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x4567890123456789012345678901234567890123', vendor: '0x5678901234567890123456789012345678901234', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, nonce: 1n, } as const // End the sale by setting endTime to now const updateData = Gatekeeper.encodeUpdateSale({ saleKey, saleState: { startTime: 1n, endTime: BigInt(Math.floor(Date.now() / 1000)), quantity: 0, price: 0n, approvers: [], }, }) // Wrap in callGatekeeper const callData = Factory.encodeCallGatekeeper({ gatekeeper: gatekeeperAddress[chainId], data: updateData, }) ``` ### Generating Sale Key IDs The `SaleKey` is hashed to create a unique identifier: ```ts twoslash /// // ---cut--- import { SaleKey } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', // Your deployed contract tokenId: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x3456789012345678901234567890123456789012', vendor: '0x4567890123456789012345678901234567890123', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, nonce: 1n, } as const const saleKeyId = SaleKey.generateId(saleKey) console.log('Sale Key ID:', saleKeyId) ``` ### Converting Sale to SaleKey If you have a full `Sale` object from the indexer, convert it to a `SaleKey`: ```ts twoslash /// // ---cut--- import { Client, Sale } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) // Get a sale from the indexer const saleKeyId = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as const const sale = await client.sale.get({ saleKeyId }) if (sale) { // Convert to SaleKey for contract calls const saleKey = Sale.toSaleKey(sale) console.log('SaleKey:', saleKey) } ``` ### Next Steps * [Execute purchases](/guides/purchasing) from your registered sales * [API Reference: Gatekeeper](/api/gatekeeper) for complete function documentation * [API Reference: Sale](/api/sale) for type definitions ## Ticket Management ### Minting Tickets Admin mint tickets to recipients. Requires `MINTER_ROLE`, `HOST_ROLE`, or `ADMIN_ROLE` (the owner is also permitted). ```ts twoslash /// // ---cut--- import { Gatekeeper1155 } 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 contractAddress = '0x1234567890123456789012345678901234567890' const recipientAddress = '0x2345678901234567890123456789012345678901' const { to, data } = Gatekeeper1155.prepareAdminMint({ contract: contractAddress, recipient: recipientAddress, tokenId: 1n, // Tier ID quantity: 2n, }) // Execute with your wallet client // await walletClient.sendTransaction({ to, data }) ``` #### Mint with Custom Data Pass custom data to the recipient's `onERC1155Received` hook: ```ts twoslash /// // ---cut--- import { Gatekeeper1155 } from '@sceneinfrastructure/sdk' const { to, data } = Gatekeeper1155.prepareAdminMint({ contract: '0x1234567890123456789012345678901234567890', recipient: '0x2345678901234567890123456789012345678901', tokenId: 1n, quantity: 1n, data: '0xdeadbeef', // Custom data }) ``` ### Check-in (Stubbing Tickets) Convert tickets to stubs when attendees check in. Requires `DOOR_ROLE`, `HOST_ROLE`, or `ADMIN_ROLE` (the owner is also permitted). ```ts twoslash /// // ---cut--- import { Gatekeeper1155 } 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 { to, data } = Gatekeeper1155.prepareAdminStubTickets({ contract: '0x1234567890123456789012345678901234567890', attendee: '0x2345678901234567890123456789012345678901', tierId: 1n, amount: 1n, }) // await walletClient.sendTransaction({ to, data }) ``` The stub process: 1. Burns the ticket token (ID = tierId) 2. Mints a stub token (ID = tierId + 1) 3. Updates the stub URI metadata ### Voiding Tickets Burn tickets without creating stubs. Requires `ADMIN_ROLE`, `HOST_ROLE`, or `MINTER_ROLE` (the owner is also permitted). ```ts twoslash /// // ---cut--- import { Gatekeeper1155 } 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 { to, data } = Gatekeeper1155.prepareVoidTickets({ contract: '0x1234567890123456789012345678901234567890', attendee: '0x2345678901234567890123456789012345678901', tierId: 1n, amount: 1n, }) // await walletClient.sendTransaction({ to, data }) ``` ### Creating Token Tiers Set up new ticket tiers with metadata and supply limits: ```ts twoslash /// // ---cut--- import { Gatekeeper1155 } from '@sceneinfrastructure/sdk' // Limited supply tier const { to, data } = Gatekeeper1155.prepareSetupNewTier({ contract: '0x1234567890123456789012345678901234567890', tokenURI: 'ipfs://QmTokenMetadata...', stubURI: 'ipfs://QmStubMetadata...', maxSupply: 500n, }) // Unlimited supply tier (maxSupply = 0) const unlimitedTier = Gatekeeper1155.prepareSetupNewTier({ contract: '0x1234567890123456789012345678901234567890', tokenURI: 'ipfs://QmTokenMetadata...', stubURI: 'ipfs://QmStubMetadata...', maxSupply: 0n, }) ``` ### Updating Token Metadata Update tier metadata after creation. Requires `ADMIN_ROLE` (the owner is also permitted). ```ts twoslash /// // ---cut--- import { Gatekeeper1155 } from '@sceneinfrastructure/sdk' const { to, data } = Gatekeeper1155.prepareUpdateToken({ contract: '0x1234567890123456789012345678901234567890', tokenId: 1n, uri: 'ipfs://QmNewMetadata...', maxSupply: 1000n, // Can increase, but not decrease below minted }) ``` ### Reading Tier Information Query tier metadata and supply info: ```ts twoslash /// // ---cut--- import { Gatekeeper1155, gatekeeper1155Abi } from '@sceneinfrastructure/sdk' const { to, functionName, args } = Gatekeeper1155.prepareGetTierInfo({ contract: '0x1234567890123456789012345678901234567890', tokenId: 1n, as: 'args', }) // const [uri, maxSupply, totalMinted] = await publicClient.readContract({ // address: to, // abi: gatekeeper1155Abi, // functionName, // args, // }) ``` ### Token ID Structure Gatekeeper1155 uses a specific token ID scheme: | Token ID | Type | Description | | ------------ | ------ | ------------------------------- | | 1, 3, 5, ... | Ticket | Odd IDs are active tickets | | 2, 4, 6, ... | Stub | Even IDs are stubs (tierId + 1) | When you call `setupNewTier`, it creates a tier with the next odd ID. Stubbing burns the ticket and mints the next ID (stub). ### Next Steps * [Role Management](/guides/roles) for access control * [API Reference: Gatekeeper1155](/api/gatekeeper-1155) for complete function documentation ## Approval EIP-712 typed data utilities for purchase approval signatures. ```ts twoslash /// // ---cut--- import { Approval } from '@sceneinfrastructure/sdk' ``` ### Functions #### getTypedData Returns complete EIP-712 typed data for signing a purchase approval. ```ts twoslash /// // ---cut--- import { Approval, SaleKey, gatekeeperAddress } from '@sceneinfrastructure/sdk' import { Hex } from 'ox' const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', // Your deployed contract vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, vendor: '0x2345678901234567890123456789012345678901', nonce: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x3456789012345678901234567890123456789012', tokenId: 1n, } as const const typedData = Approval.getTypedData({ chainId: BigInt(chainId), gatekeeper: gatekeeperAddress[chainId], message: { recipient: '0x4567890123456789012345678901234567890123', referrer: '0x0000000000000000000000000000000000000000', quantity: 1, deadline: Approval.getDeadline(), salt: Hex.random(32), saleKeyId: SaleKey.generateId(saleKey), }, }) // Sign with viem // const signature = await walletClient.signTypedData(typedData) ``` Each approval signature is single-use. Gatekeeper rejects replays using the `(recipient, referrer, quantity, deadline, salt, saleKeyId)` hash, so generate a fresh `salt` for every approval and keep deadlines short. ##### Parameters | Parameter | Type | Description | | ------------ | ----------------- | ----------------------------- | | `chainId` | `bigint` | Chain ID for domain separator | | `gatekeeper` | `Address` | Gatekeeper contract address | | `message` | `ApprovalMessage` | Message to sign | ##### Returns ```ts { domain: { name: 'Gatekeeper' version: '1' chainId: bigint verifyingContract: Address } types: typeof purchaseApprovalTypes primaryType: 'PurchaseApproval' message: ApprovalMessage } ``` *** #### getDeadline Calculates a Unix timestamp deadline for approval signatures. ```ts twoslash /// // ---cut--- import { Approval } from '@sceneinfrastructure/sdk' // Default: 15 minutes from now const deadline = Approval.getDeadline() // Custom: 30 minutes from now const thirtyMin = Approval.getDeadline(new Date(), 30) // From specific date const future = Approval.getDeadline(new Date('2024-12-31'), 60) ``` ##### Parameters | Parameter | Type | Default | Description | | --------- | -------- | ------------ | ------------------------ | | `date` | `Date` | `new Date()` | Base date | | `minutes` | `number` | `15` | Minutes until expiration | ##### Returns `bigint` - Unix timestamp *** ### Constants #### types EIP-712 type definitions for PurchaseApproval signatures. ```ts twoslash /// // ---cut--- import { Approval } from '@sceneinfrastructure/sdk' console.log(Approval.types) // { // EIP712Domain: [...], // PurchaseApproval: [ // { name: 'recipient', type: 'address' }, // { name: 'referrer', type: 'address' }, // { name: 'quantity', type: 'uint16' }, // { name: 'deadline', type: 'uint256' }, // { name: 'salt', type: 'bytes32' }, // { name: 'saleKeyId', type: 'bytes32' }, // ] // } ``` *** #### domain Base EIP-712 domain (name and version only). ```ts twoslash /// // ---cut--- import { Approval, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base console.log(Approval.domain) // { name: 'Gatekeeper', version: '1' } // Complete domain for signing: const fullDomain = { ...Approval.domain, chainId: BigInt(chainId), verifyingContract: gatekeeperAddress[chainId], } ``` *** #### primaryType The primary type identifier. ```ts twoslash /// // ---cut--- import { Approval } from '@sceneinfrastructure/sdk' console.log(Approval.primaryType) // 'PurchaseApproval' ``` *** ### Types #### ApprovalMessage The message payload for purchase approval signatures. ```ts twoslash /// // ---cut--- import type { Approval } from '@sceneinfrastructure/sdk' const message: Approval.ApprovalMessage = { recipient: '0x1234567890123456789012345678901234567890', // Ticket recipient referrer: '0x0000000000000000000000000000000000000000', // Referrer address (or zero) quantity: 1, // Number of tickets deadline: 1700000000n, // Expiration timestamp salt: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', // Random bytes32 saleKeyId: '0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab', // Sale key hash } ``` | Field | Type | Description | | ----------- | --------- | -------------------------- | | `recipient` | `Address` | Address receiving tickets | | `referrer` | `Address` | Referrer address (or zero) | | `quantity` | `number` | Number of tickets | | `deadline` | `bigint` | Unix timestamp expiration | | `salt` | `Hex` | Random bytes32 nonce | | `saleKeyId` | `Hex` | Hash of SaleKey struct | *** ### Schema #### messageSchema Zod schema for validating approval messages. ```ts twoslash /// // ---cut--- import { Approval } from '@sceneinfrastructure/sdk' const result = Approval.messageSchema.safeParse({ recipient: '0x...', referrer: '0x0000000000000000000000000000000000000000', quantity: 1, deadline: 1700000000n, salt: '0x...', saleKeyId: '0x...', }) if (result.success) { console.log('Valid:', result.data) } ``` import { BalanceDemo } from '../../components/BalanceDemo' ## Balance Query token holder balances. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) ``` ### Try It ### Methods #### get Get a specific balance for a token and holder. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const balance = await client.balance.get({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n, address: '0x2345678901234567890123456789012345678901' }) if (balance) { console.log('Balance:', balance.balance) } ``` ##### Parameters | Parameter | Type | Description | | ----------------- | --------- | -------------------- | | `contractAddress` | `Address` | The contract address | | `tokenId` | `bigint` | The token ID | | `address` | `Address` | The holder address | ##### Returns `Balance | null` - The balance record or null if not found. *** #### getTokenStatus Get detailed status for a holder's tokens and stubs. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const status = await client.balance.getTokenStatus({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n, // Ticket token ID (odd) address: '0x2345678901234567890123456789012345678901' }) console.log('Ticket count:', status.tokenCount) console.log('Stub count:', status.stubCount) console.log('Has tickets:', status.hasTokens) console.log('Has stubs:', status.hasStubs) console.log('Fully stubbed:', status.isFullyStubbed) ``` ##### Parameters | Parameter | Type | Description | | ----------------- | --------- | -------------------------------- | | `contractAddress` | `Address` | The contract address | | `tokenId` | `bigint` | The ticket token ID (odd number) | | `address` | `Address` | The holder address | ##### Returns `TokenStatus` - Status object with token and stub counts. *** #### getHoldings Get all token holdings for an address. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const holdings = await client.balance.getHoldings({ address: '0x2345678901234567890123456789012345678901' }) console.log('Tickets:', holdings.tokens.length) console.log('Stubs:', holdings.stubs.length) // Filter by contract const contractHoldings = await client.balance.getHoldings({ address: '0x2345678901234567890123456789012345678901', contractAddress: '0x1234567890123456789012345678901234567890' }) // Filter by minimum balance const significantHoldings = await client.balance.getHoldings({ address: '0x2345678901234567890123456789012345678901', minBalance: 5n }) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | --------------------------- | | `address` | `Address` | **required** | The holder address | | `contractAddress` | `Address` | - | Filter to specific contract | | `minBalance` | `bigint` | `1n` | Minimum balance to include | ##### Returns `TokenHoldings` - Object with `tokens` and `stubs` arrays. *** #### isStubbed Check if a holder has converted their tickets to stubs. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const stubbed = await client.balance.isStubbed({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n, address: '0x2345678901234567890123456789012345678901' }) if (stubbed) { console.log('User has checked in') } ``` ##### Parameters | Parameter | Type | Description | | ----------------- | --------- | -------------------- | | `contractAddress` | `Address` | The contract address | | `tokenId` | `bigint` | The ticket token ID | | `address` | `Address` | The holder address | ##### Returns `boolean` - True if the holder has any stubs for this tier. *** #### listByToken List all holders of a specific token. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const holders = await client.balance.listByToken({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n, limit: 100 }) for (const balance of holders) { console.log(`${balance.address}: ${balance.balance}`) } // Filter by minimum balance const whales = await client.balance.listByToken({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n, minBalance: 10n }) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | -------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `tokenId` | `bigint` | **required** | The token ID | | `minBalance` | `bigint` | - | Minimum balance to include | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Balance[]` - Array of balance records. *** #### listByHolder List all balances for a specific holder. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const balances = await client.balance.listByHolder({ address: '0x2345678901234567890123456789012345678901', limit: 100 }) for (const balance of balances) { console.log(`Token ${balance.tokenId}: ${balance.balance}`) } // Filter by contract const contractBalances = await client.balance.listByHolder({ address: '0x2345678901234567890123456789012345678901', contractAddress: '0x1234567890123456789012345678901234567890' }) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | --------------------------- | | `address` | `Address` | **required** | The holder address | | `contractAddress` | `Address` | - | Filter to specific contract | | `minBalance` | `bigint` | - | Minimum balance to include | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Balance[]` - Array of balance records. *** #### hasBalance Check if a holder has any balance of a specific token. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const hasTicket = await client.balance.hasBalance({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n, address: '0x2345678901234567890123456789012345678901' }) if (hasTicket) { console.log('User has tickets') } ``` ##### Parameters | Parameter | Type | Description | | ----------------- | --------- | -------------------- | | `contractAddress` | `Address` | The contract address | | `tokenId` | `bigint` | The token ID | | `address` | `Address` | The holder address | ##### Returns `boolean` - True if balance > 0. *** ### Types #### Balance ```ts twoslash /// // ---cut--- import type { Address, Hex } from 'viem' type Balance = { contractAddress: Address tokenId: bigint address: Address balance: bigint createdAt: bigint createdAtBlock: bigint createdAtLogIndex: number creationTxHash: Hex updatedAt: bigint updatedAtBlock: bigint updatedAtLogIndex: number updatedTxHash: Hex | null } ``` | Field | Type | Description | | ----------------- | --------- | -------------------- | | `contractAddress` | `Address` | The contract address | | `tokenId` | `bigint` | The token ID | | `address` | `Address` | The holder address | | `balance` | `bigint` | Current balance | *** #### TokenStatus ```ts twoslash /// // ---cut--- type TokenStatus = { tokenCount: bigint // Balance of ticket token (odd ID) stubCount: bigint // Balance of stub token (even ID) hasTokens: boolean // tokenCount > 0 hasStubs: boolean // stubCount > 0 isFullyStubbed: boolean // hasStubs && !hasTokens } ``` *** #### TokenHoldings ```ts twoslash /// // ---cut--- import type { Balance } from '@sceneinfrastructure/sdk' type TokenHoldings = { tokens: Balance.Balance[] // Balances with odd token IDs (tickets) stubs: Balance.Balance[] // Balances with even token IDs (stubs) } ``` *** ### Schema #### Zod Validation ```ts twoslash /// // ---cut--- import { Balance } from '@sceneinfrastructure/sdk' const data = {} // Data from API or external source const result = Balance.schema.safeParse(data) if (result.success) { console.log('Valid balance:', result.data) } ``` ## Client Query indexed blockchain data via Ponder. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' ``` :::info The Client requires the `@ponder/client` peer dependency. Install it with: ```bash pnpm add @ponder/client ``` ::: ### Functions #### create Creates a configured Gatekeeper client instance. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) ``` ##### Parameters | Parameter | Type | Description | | ------------- | -------- | --------------------------- | | `options.url` | `string` | The Ponder SQL endpoint URL | ##### Returns A `Client` instance with: * Domain namespaces for type-safe queries * `db` - Drizzle query builder for custom queries * `live` - SSE subscription method * `getStatus` - Indexer status query * `schema` - Ponder schema tables *** ### Client Interface #### db Drizzle query builder for custom SQL queries. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' import { eq } from '@ponder/client' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) const results = await client.db .select() .from(client.schema.onchainSaleConfig) .where(eq(client.schema.onchainSaleConfig.contractAddress, '0x1234567890123456789012345678901234567890')) ``` *** #### live Subscribe to live query updates via Server-Sent Events. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) const { unsubscribe } = client.live( // Query function (db) => db.select().from(client.schema.onchainOrder), // On result callback (result) => console.log('Updated:', result), // On error callback (error) => console.error('Error:', error) ) // Later: clean up subscription unsubscribe() ``` ##### Parameters | Parameter | Type | Description | | ---------- | ------------------------ | ------------------------------ | | `queryFn` | `(db: DB) => Query` | Function that builds the query | | `onResult` | `(result: T[]) => void` | Callback for query results | | `onError` | `(error: Error) => void` | Callback for errors | ##### Returns ```ts twoslash /// // ---cut--- type LiveResult = { unsubscribe: () => void // Function to stop the subscription } ``` *** #### getStatus Get indexer status for all indexed chains. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) const status = await client.getStatus() console.log('Indexer status:', status) ``` Returns an array of chain status objects with sync progress information. *** #### schema Ponder schema tables for building custom queries. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) // Access schema tables client.schema.onchainEventContract client.schema.onchainToken client.schema.onchainTicketTier client.schema.onchainSaleConfig client.schema.onchainOrder client.schema.onchainOrderItem client.schema.onchainTicketTransfer client.schema.onchainTicketBalance client.schema.onchainRoleGrant client.schema.onchainSwap client.schema.onchainVoid ``` *** ### Domain Namespaces The client provides domain-specific namespaces for common queries: | Namespace | Description | Documentation | | --------------- | --------------------------------- | ------------------------------------ | | `eventContract` | Deployed Gatekeeper1155 contracts | [EventContract](/api/event-contract) | | `sale` | Sale configurations | [Sale](/api/sale) | | `token` | ERC1155 token data | [Token](/api/token) | | `tier` | Ticket tier configurations | [Tier](/api/tier) | | `balance` | Token holder balances | [Balance](/api/balance) | | `transfer` | Transfer history | [Transfer](/api/transfer) | | `permission` | Role grants and permissions | [Permission](/api/permission) | | `stub` | Check-in events | [Stub](/api/stub) | | `void` | Voiding events | [Void](/api/void) | #### Example Usage ```ts twoslash /// // ---cut--- import { Client, ADMIN_ROLE } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) // Get a deployed contract const contract = await client.eventContract.get({ contractAddress: '0x1234567890123456789012345678901234567890' }) // List sales for a contract const sales = await client.sale.listByContract({ contractAddress: '0x1234567890123456789012345678901234567890' }) // Check a user's balance const balance = await client.balance.get({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n, address: '0x2345678901234567890123456789012345678901' }) // Check if user has admin role const isAdmin = await client.permission.hasRole({ contractAddress: '0x1234567890123456789012345678901234567890', user: '0x2345678901234567890123456789012345678901', role: ADMIN_ROLE }) ``` *** ### Static Exports #### schema The Ponder schema is also available as a static export: ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' // Access schema without creating a client Client.schema.onchainEventContract Client.schema.onchainSaleConfig // ... etc ``` This is useful for type definitions and building queries before client initialization. ## Errors Error handling utilities with structured error hierarchy and error chaining. ```ts twoslash /// // ---cut--- import { Errors } from '@sceneinfrastructure/sdk' ``` Based on [ox](https://github.com/wevm/ox) error patterns. ### Types #### GlobalErrorType Catch-all error type for function error type unions. ```ts twoslash /// // ---cut--- import { Errors } from '@sceneinfrastructure/sdk' // Define a custom error type for your function type SpecificError = Error & { code: 'SPECIFIC_ERROR' } export declare namespace myFunction { type ErrorType = SpecificError | Errors.GlobalErrorType } ``` *** ### Classes #### BaseError Base error class for all Gatekeeper SDK errors. Provides structured error handling with: * Short message for quick identification * Detailed information for debugging * Error chaining via `cause` property ```ts twoslash /// // ---cut--- import { Errors } from '@sceneinfrastructure/sdk' throw new Errors.BaseError('Something went wrong.', { details: 'The operation failed due to invalid input.', }) ``` ##### Constructor ```ts new Errors.BaseError(shortMessage: string, options?: BaseError.Options) ``` | Parameter | Type | Description | | ----------------- | -------- | --------------------------------------- | | `shortMessage` | `string` | Primary error message | | `options.cause` | `Error` | Underlying error that caused this error | | `options.details` | `string` | Extended debugging information | ##### Properties | Property | Type | Description | | -------------- | -------------------- | ------------------------------------- | | `name` | `string` | Error name (`'BaseError'` by default) | | `shortMessage` | `string` | The primary error message | | `details` | `string` | Extended error information | | `cause` | `Error \| undefined` | The underlying error | ##### Methods ##### walk Traverses the error chain, calling the provided function for each error. ```ts twoslash /// // ---cut--- import { Errors } from '@sceneinfrastructure/sdk' const error = new Errors.BaseError('Outer error', { cause: new Errors.BaseError('Inner error', { cause: new TypeError('Root cause'), }), }) // Find a specific error type in the chain const typeError = error.walk((err) => err instanceof TypeError) ``` *** ### Usage Patterns #### Creating Custom Errors ```ts import { Errors } from '@sceneinfrastructure/sdk' class InvalidSaleKeyError extends Errors.BaseError { override name = 'InvalidSaleKeyError' constructor(options: Errors.BaseError.Options = {}) { super('Invalid sale key configuration.', options) } } ``` #### Error Chaining Wrap lower-level errors with context: ```ts twoslash /// // ---cut--- import { Errors } from '@sceneinfrastructure/sdk' async function sendTransaction(): Promise { // Example function that might fail throw new Error('Network error') } async function processTransaction() { try { await sendTransaction() } catch (error) { throw new Errors.BaseError('Failed to process transaction.', { cause: error as Error, details: 'Transaction was rejected by the network.', }) } } ``` #### Error Inspection Walk the error chain to find root causes: ```ts twoslash /// // ---cut--- import { Errors } from '@sceneinfrastructure/sdk' function getRootCause(error: Error): Error { if (error instanceof Errors.BaseError) { return error.walk() ?? error } return error } function hasSpecificCause(error: Error, predicate: (e: Error) => boolean): boolean { if (error instanceof Errors.BaseError) { return error.walk(predicate) !== undefined } return predicate(error) } ``` import { EventContractDemo } from '../../components/EventContractDemo' ## EventContract Query deployed Gatekeeper1155 contracts. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) ``` ### Try It ### Methods #### get Get a single contract by address. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const contract = await client.eventContract.get({ contractAddress: '0x1234567890123456789012345678901234567890' }) if (contract) { console.log('Owner:', contract.ownerAddress) console.log('Creator:', contract.creatorAddress) console.log('Contract URI:', contract.contractURI) } ``` ##### Parameters | Parameter | Type | Description | | ----------------- | --------- | ------------------------------- | | `contractAddress` | `Address` | The contract address to look up | ##### Returns `EventContract | null` - The contract data or null if not found. *** #### list List contracts with optional filters. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) // List all contracts const contracts = await client.eventContract.list({ limit: 20, offset: 0 }) // List contracts by creator const myContracts = await client.eventContract.list({ creator: '0x2345678901234567890123456789012345678901', limit: 10 }) // List contracts by owner const ownedContracts = await client.eventContract.list({ owner: '0x2345678901234567890123456789012345678901', orderBy: 'createdAt', order: 'desc' }) ``` ##### Parameters | Parameter | Type | Default | Description | | --------- | ---------------------------- | ------------- | ------------------------- | | `creator` | `Address` | - | Filter by creator address | | `owner` | `Address` | - | Filter by owner address | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | | `orderBy` | `'createdAt' \| 'updatedAt'` | `'createdAt'` | Sort field | | `order` | `'asc' \| 'desc'` | `'desc'` | Sort direction | ##### Returns `EventContract[]` - Array of matching contracts. *** #### listByCreator List contracts created by a specific address. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const myContracts = await client.eventContract.listByCreator({ creator: '0x2345678901234567890123456789012345678901', limit: 10 }) for (const contract of myContracts) { console.log(contract.contractAddress) } ``` ##### Parameters | Parameter | Type | Default | Description | | --------- | --------- | ------------ | ------------------------- | | `creator` | `Address` | **required** | The creator address | | `limit` | `number` | `50` | Maximum results to return | ##### Returns `EventContract[]` - Array of contracts created by the address. *** #### listByOwner List contracts owned by a specific address. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const ownedContracts = await client.eventContract.listByOwner({ owner: '0x2345678901234567890123456789012345678901', limit: 10 }) ``` ##### Parameters | Parameter | Type | Default | Description | | --------- | --------- | ------------ | ------------------------- | | `owner` | `Address` | **required** | The owner address | | `limit` | `number` | `50` | Maximum results to return | ##### Returns `EventContract[]` - Array of contracts owned by the address. *** ### Types #### EventContract ```ts twoslash /// // ---cut--- import type { Address, Hex } from 'viem' type EventContract = { contractAddress: Address contractURI: string creatorAddress: Address implementationAddress: Address ownerAddress: Address createdAt: bigint createdAtBlock: bigint createdAtLogIndex: number creationTxHash: Hex updatedAt: bigint updatedAtBlock: bigint updatedAtLogIndex: number updatedTxHash: Hex | null } ``` | Field | Type | Description | | ----------------------- | --------- | ---------------------------------- | | `contractAddress` | `Address` | The deployed contract address | | `contractURI` | `string` | Contract metadata URI | | `creatorAddress` | `Address` | Address that deployed the contract | | `implementationAddress` | `Address` | Implementation contract address | | `ownerAddress` | `Address` | Current owner address | | `createdAt` | `bigint` | Creation timestamp | | `createdAtBlock` | `bigint` | Block number of creation | | `creationTxHash` | `Hex` | Transaction hash of creation | | `updatedAt` | `bigint` | Last update timestamp | *** ### Schema #### Zod Validation ```ts twoslash /// // ---cut--- import { EventContract } from '@sceneinfrastructure/sdk' const data = { contractAddress: '0x1234567890123456789012345678901234567890', contractURI: 'https://example.com/metadata.json', creatorAddress: '0x2345678901234567890123456789012345678901', implementationAddress: '0x3456789012345678901234567890123456789012', ownerAddress: '0x2345678901234567890123456789012345678901', createdAt: 1700000000n, createdAtBlock: 100000n, createdAtLogIndex: 0, creationTxHash: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', updatedAt: 1700000000n, updatedAtBlock: 100000n, updatedAtLogIndex: 0, updatedTxHash: null, } const result = EventContract.schema.safeParse(data) if (result.success) { console.log('Valid contract:', result.data) } ``` ## Factory Utilities for deploying Gatekeeper1155 contracts via the factory pattern. ```ts twoslash /// // ---cut--- import { Factory } from '@sceneinfrastructure/sdk' ``` ### Functions #### prepareDeploy Prepares a factory deploy transaction with optional setup calls. ```ts twoslash /// // ---cut--- import { Factory, gatekeeper1155FactoryAddress, gatekeeper1155Address, } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const walletAddress = '0x3456789012345678901234567890123456789012' // With raw calldata (default) const { to, data, salt, predictedAddress } = Factory.prepareDeploy({ factory: gatekeeper1155FactoryAddress[chainId], implementation: gatekeeper1155Address[chainId], nonce: BigInt(Date.now()), creator: walletAddress, owner: walletAddress, contractURI: 'ipfs://...', }) // With viem args const result = Factory.prepareDeploy({ factory: gatekeeper1155FactoryAddress[chainId], implementation: gatekeeper1155Address[chainId], nonce: BigInt(Date.now()), creator: walletAddress, owner: walletAddress, contractURI: 'ipfs://...', as: 'args', }) ``` ##### Parameters | Parameter | Type | Description | | ---------------- | ------------------ | -------------------------------------- | | `factory` | `Address` | Factory contract address | | `implementation` | `Address` | Implementation contract address | | `nonce` | `bigint` | Deployment nonce (typically timestamp) | | `creator` | `Address` | Address calling deploy | | `owner` | `Address` | Owner of deployed contract | | `contractURI` | `string` | Contract metadata URI | | `calls` | `Hex[]` | Optional setup calls | | `as` | `'data' \| 'args'` | Output format (default: `'data'`) | ##### Returns | Field | Type | Description | | ------------------ | --------- | ------------------------------------ | | `to` | `Address` | Factory address | | `data` | `Hex` | Encoded calldata (when `as: 'data'`) | | `args` | `tuple` | Function args (when `as: 'args'`) | | `salt` | `Hex` | CREATE2 salt | | `predictedAddress` | `Address` | Predicted deployment address | *** #### generateSalt Generates the CREATE2 salt from deployment parameters. ```ts twoslash /// // ---cut--- import { Factory } from '@sceneinfrastructure/sdk' const creatorAddress = '0x3456789012345678901234567890123456789012' const salt = Factory.generateSalt({ nonce: BigInt(Date.now()), creator: creatorAddress, contractURI: 'ipfs://...', }) ``` ##### Parameters | Parameter | Type | Description | | ------------- | --------- | --------------------- | | `nonce` | `bigint` | Deployment nonce | | `creator` | `Address` | Creator address | | `contractURI` | `string` | Contract metadata URI | ##### Returns `Hex` - The keccak256 hash (bytes32) *** #### predictAddress Predicts the deployment address using CREATE2. ```ts twoslash /// // ---cut--- import { Factory, gatekeeper1155FactoryAddress, gatekeeper1155Address, } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const creatorAddress = '0x3456789012345678901234567890123456789012' const salt = Factory.generateSalt({ nonce: 1n, creator: creatorAddress, contractURI: 'ipfs://...', }) const address = Factory.predictAddress({ factory: gatekeeper1155FactoryAddress[chainId], implementation: gatekeeper1155Address[chainId], salt, }) ``` ##### Parameters | Parameter | Type | Description | | ---------------- | --------- | ------------------------ | | `factory` | `Address` | Factory contract address | | `implementation` | `Address` | Implementation address | | `salt` | `Hex` | CREATE2 salt | ##### Returns `Address` - The predicted contract address *** #### encodeGrantRoles Encodes a `grantRoles` call for contract initialization. ```ts twoslash /// // ---cut--- import { Factory, MINTER_ROLE, ADMIN_ROLE, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base // Single role (e.g., grant minter to Gatekeeper) const data = Factory.encodeGrantRoles({ user: gatekeeperAddress[chainId], roles: MINTER_ROLE, }) // Multiple roles (bitwise OR) const multiData = Factory.encodeGrantRoles({ user: '0x2345678901234567890123456789012345678901', roles: ADMIN_ROLE | MINTER_ROLE, }) ``` ##### Parameters | Parameter | Type | Description | | --------- | --------- | ------------------------- | | `user` | `Address` | Address to grant roles to | | `roles` | `bigint` | Role bitmap | ##### Returns `Hex` - Encoded function calldata *** #### encodeSetupNewTier Encodes a `setupNewTier` call for creating token tiers. ```ts twoslash /// // ---cut--- import { Factory } from '@sceneinfrastructure/sdk' const data = Factory.encodeSetupNewTier({ tokenURI: 'ipfs://QmToken...', stubURI: 'ipfs://QmStub...', maxSupply: 1000n, // 0n for unlimited }) ``` ##### Parameters | Parameter | Type | Description | | ----------- | -------- | -------------------------- | | `tokenURI` | `string` | Token metadata URI | | `stubURI` | `string` | Stub metadata URI | | `maxSupply` | `bigint` | Max supply (0 = unlimited) | ##### Returns `Hex` - Encoded function calldata *** #### encodeCallGatekeeper Encodes a `callGatekeeper` call for executing Gatekeeper operations. ```ts twoslash /// // ---cut--- import { Factory, Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x2345678901234567890123456789012345678901', // Your deployed contract vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, vendor: '0x3456789012345678901234567890123456789012', nonce: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x4567890123456789012345678901234567890123', tokenId: 1n, } as const const innerCall = Gatekeeper.encodeRegisterSale({ saleKey, saleState: { startTime: 1n, endTime: 9999999999n, quantity: 100, price: 1000000n, approvers: [], }, }) const data = Factory.encodeCallGatekeeper({ gatekeeper: gatekeeperAddress[chainId], data: innerCall, }) ``` ##### Parameters | Parameter | Type | Description | | ------------ | --------- | --------------------------- | | `gatekeeper` | `Address` | Gatekeeper contract address | | `data` | `Hex` | Encoded Gatekeeper call | ##### Returns `Hex` - Encoded function calldata ### Errors #### InvalidDeployOptionsError Thrown when deploy options are invalid. ```ts twoslash /// // ---cut--- import { Factory } from '@sceneinfrastructure/sdk' try { // ... } catch (error) { if (error instanceof Factory.InvalidDeployOptionsError) { console.error('Invalid options:', error.message) } } ``` ## Gatekeeper1155 Utilities for ERC1155 token operations on Gatekeeper1155 contracts. ```ts twoslash /// // ---cut--- import { Gatekeeper1155 } from '@sceneinfrastructure/sdk' ``` ### Token Operations #### prepareSetupNewTier Creates a new ticket tier with metadata and supply configuration. Requires admin permissions (see [Role Management](/guides/roles)). ```ts twoslash /// // ---cut--- import { createWalletClient, http } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { base } from 'viem/chains' import { Gatekeeper1155 } from '@sceneinfrastructure/sdk' const account = privateKeyToAccount('0x...' as `0x${string}`) const walletClient = createWalletClient({ account, chain: base, transport: http(), }) const { to, data } = Gatekeeper1155.prepareSetupNewTier({ contract: '0x1234567890123456789012345678901234567890', tokenURI: 'ipfs://QmTokenMetadata...', stubURI: 'ipfs://QmStubMetadata...', maxSupply: 1000n, // 0n for unlimited }) await walletClient.sendTransaction({ to, data }) ``` ##### Parameters | Parameter | Type | Description | | ----------- | ------------------ | --------------------------------------- | | `contract` | `Address` | Gatekeeper1155 contract address | | `tokenURI` | `string` | Metadata URI for the ticket tier | | `stubURI` | `string` | Metadata URI for stubbed (used) tickets | | `maxSupply` | `bigint` | Maximum supply (0 for unlimited) | | `as` | `'data' \| 'args'` | Output format | *** #### prepareAdminMint Mints tickets to a recipient. Requires minting permissions (see [Role Management](/guides/roles)). ```ts twoslash /// // ---cut--- import { createWalletClient, http } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { base } from 'viem/chains' import { Gatekeeper1155 } from '@sceneinfrastructure/sdk' const account = privateKeyToAccount('0x...' as `0x${string}`) const walletClient = createWalletClient({ account, chain: base, transport: http(), }) const { to, data } = Gatekeeper1155.prepareAdminMint({ contract: '0x1234567890123456789012345678901234567890', recipient: '0x2345678901234567890123456789012345678901', tokenId: 1n, quantity: 2n, }) await walletClient.sendTransaction({ to, data }) ``` ##### Parameters | Parameter | Type | Description | | ----------- | ------------------ | --------------------------------- | | `contract` | `Address` | Gatekeeper1155 contract address | | `recipient` | `Address` | Address to receive the tickets | | `tokenId` | `bigint` | Token tier ID | | `quantity` | `bigint` | Number of tickets to mint | | `data` | `Hex` | Optional data to pass to receiver | | `as` | `'data' \| 'args'` | Output format | *** #### prepareAdminStubTickets Checks in an attendee by converting their tickets to stubs. Requires check-in permissions (see [Role Management](/guides/roles)). ```ts twoslash /// // ---cut--- import { createWalletClient, http } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { base } from 'viem/chains' import { Gatekeeper1155 } from '@sceneinfrastructure/sdk' const account = privateKeyToAccount('0x...' as `0x${string}`) const walletClient = createWalletClient({ account, chain: base, transport: http(), }) const { to, data } = Gatekeeper1155.prepareAdminStubTickets({ contract: '0x1234567890123456789012345678901234567890', attendee: '0x2345678901234567890123456789012345678901', tierId: 1n, amount: 1n, }) await walletClient.sendTransaction({ to, data }) ``` ##### Parameters | Parameter | Type | Description | | ---------- | ------------------ | ------------------------------- | | `contract` | `Address` | Gatekeeper1155 contract address | | `attendee` | `Address` | Attendee address to check-in | | `tierId` | `bigint` | Token tier ID | | `amount` | `bigint` | Number of tickets to stub | | `as` | `'data' \| 'args'` | Output format | *** #### prepareVoidTickets Burns tickets from an attendee. Requires void permissions (see [Role Management](/guides/roles)). ```ts twoslash /// // ---cut--- import { createWalletClient, http } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { base } from 'viem/chains' import { Gatekeeper1155 } from '@sceneinfrastructure/sdk' const account = privateKeyToAccount('0x...' as `0x${string}`) const walletClient = createWalletClient({ account, chain: base, transport: http(), }) const { to, data } = Gatekeeper1155.prepareVoidTickets({ contract: '0x1234567890123456789012345678901234567890', attendee: '0x2345678901234567890123456789012345678901', tierId: 1n, amount: 1n, }) await walletClient.sendTransaction({ to, data }) ``` ##### Parameters | Parameter | Type | Description | | ---------- | ------------------ | ------------------------------- | | `contract` | `Address` | Gatekeeper1155 contract address | | `attendee` | `Address` | Attendee address | | `tierId` | `bigint` | Token tier ID | | `amount` | `bigint` | Number of tickets to void | | `as` | `'data' \| 'args'` | Output format | *** #### prepareUpdateToken Updates tier metadata. Requires admin permissions (see [Role Management](/guides/roles)). ```ts twoslash /// // ---cut--- import { createWalletClient, http } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { base } from 'viem/chains' import { Gatekeeper1155 } from '@sceneinfrastructure/sdk' const account = privateKeyToAccount('0x...' as `0x${string}`) const walletClient = createWalletClient({ account, chain: base, transport: http(), }) const { to, data } = Gatekeeper1155.prepareUpdateToken({ contract: '0x1234567890123456789012345678901234567890', tokenId: 1n, uri: 'ipfs://new-metadata-uri', maxSupply: 500n, }) await walletClient.sendTransaction({ to, data }) ``` ##### Parameters | Parameter | Type | Description | | ----------- | ------------------ | -------------------------------- | | `contract` | `Address` | Gatekeeper1155 contract address | | `tokenId` | `bigint` | Token tier ID to update | | `uri` | `string` | New metadata URI | | `maxSupply` | `bigint` | New max supply (0 for unlimited) | | `as` | `'data' \| 'args'` | Output format | *** ### Role Management #### prepareGrantRoles Grants roles to an address. Requires admin permissions (see [Role Management](/guides/roles)). ```ts twoslash /// // ---cut--- import { Gatekeeper1155, MINTER_ROLE, DOOR_ROLE } from '@sceneinfrastructure/sdk' // Grant single role const { to, data } = Gatekeeper1155.prepareGrantRoles({ contract: '0x1234567890123456789012345678901234567890', user: '0x2345678901234567890123456789012345678901', roles: MINTER_ROLE, }) // Grant multiple roles with bitwise OR const { to: to2, data: data2 } = Gatekeeper1155.prepareGrantRoles({ contract: '0x1234567890123456789012345678901234567890', user: '0x2345678901234567890123456789012345678901', roles: MINTER_ROLE | DOOR_ROLE, }) ``` ##### Parameters | Parameter | Type | Description | | ---------- | ------------------ | -------------------------------- | | `contract` | `Address` | Gatekeeper1155 contract address | | `user` | `Address` | Address to grant roles to | | `roles` | `bigint` | Role bitmask (combine with `\|`) | | `as` | `'data' \| 'args'` | Output format | *** #### prepareRevokeRoles Revokes roles from an address. Requires admin permissions (see [Role Management](/guides/roles)). ```ts twoslash /// // ---cut--- import { Gatekeeper1155, MINTER_ROLE } from '@sceneinfrastructure/sdk' const { to, data } = Gatekeeper1155.prepareRevokeRoles({ contract: '0x1234567890123456789012345678901234567890', user: '0x2345678901234567890123456789012345678901', roles: MINTER_ROLE, }) ``` ##### Parameters | Parameter | Type | Description | | ---------- | ------------------ | ------------------------------- | | `contract` | `Address` | Gatekeeper1155 contract address | | `user` | `Address` | Address to revoke roles from | | `roles` | `bigint` | Role bitmask to revoke | | `as` | `'data' \| 'args'` | Output format | *** ### View Functions #### prepareContractURI Gets the contract-level metadata URI. ```ts twoslash /// // ---cut--- import { Gatekeeper1155, gatekeeper1155Abi } from '@sceneinfrastructure/sdk' import { createPublicClient, http } from 'viem' import { base } from 'viem/chains' const publicClient = createPublicClient({ chain: base, transport: http(), }) const { to, functionName, args } = Gatekeeper1155.prepareContractURI({ contract: '0x1234567890123456789012345678901234567890', as: 'args', }) const uri = await publicClient.readContract({ address: to, abi: gatekeeper1155Abi, functionName, args, }) ``` *** #### prepareGetTierInfo Gets tier metadata including URI, max supply, and total minted. ```ts twoslash /// // ---cut--- import { Gatekeeper1155, gatekeeper1155Abi } from '@sceneinfrastructure/sdk' import { createPublicClient, http } from 'viem' import { base } from 'viem/chains' const publicClient = createPublicClient({ chain: base, transport: http(), }) const { to, functionName, args } = Gatekeeper1155.prepareGetTierInfo({ contract: '0x1234567890123456789012345678901234567890', tokenId: 1n, as: 'args', }) const result = await publicClient.readContract({ address: to, abi: gatekeeper1155Abi, functionName, args, }) console.log('Tier info:', result) ``` ##### Parameters | Parameter | Type | Description | | ---------- | ------------------ | ------------------------------- | | `contract` | `Address` | Gatekeeper1155 contract address | | `tokenId` | `bigint` | Token tier ID | | `as` | `'data' \| 'args'` | Output format | ## Gatekeeper Utilities for managing sales and executing purchases on the Gatekeeper protocol. ```ts twoslash /// // ---cut--- import { Gatekeeper } from '@sceneinfrastructure/sdk' ``` ### Encode Functions Functions for encoding calls to be executed via `callGatekeeper`. `registerSale` and `updateSale` can only be called by the token contract. Use `Gatekeeper1155.callGatekeeper` (requires `ADMIN_ROLE` or owner) with the encoded data. During deployment, include the call via `Factory.encodeCallGatekeeper`. #### encodeRegisterSale Encodes a `registerSale` call for registering new sales. ```ts twoslash /// // ---cut--- import { Gatekeeper } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', // Your deployed contract vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, vendor: '0x2345678901234567890123456789012345678901', nonce: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x3456789012345678901234567890123456789012', tokenId: 1n, } as const const data = Gatekeeper.encodeRegisterSale({ saleKey, saleState: { startTime: 1n, endTime: 9999999999n, quantity: 100, price: 1000000n, approvers: [], }, }) ``` ##### Parameters | Parameter | Type | Description | | ----------- | ----------- | -------------------------- | | `saleKey` | `SaleKey` | Sale identifier struct | | `saleState` | `SaleState` | Initial sale configuration | *** #### encodeUpdateSale Encodes an `updateSale` call for modifying existing sales. ```ts twoslash /// // ---cut--- import { Gatekeeper } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', // Your deployed contract vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, vendor: '0x2345678901234567890123456789012345678901', nonce: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x3456789012345678901234567890123456789012', tokenId: 1n, } as const const data = Gatekeeper.encodeUpdateSale({ saleKey, saleState: { startTime: 1n, endTime: BigInt(Math.floor(Date.now() / 1000)), // End now quantity: 0, price: 0n, approvers: [], }, }) ``` *** ### Purchase Functions #### preparePurchase Prepares a purchase transaction for open sales (no approval required). ```ts twoslash /// // ---cut--- import { Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', // Your deployed contract vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, vendor: '0x2345678901234567890123456789012345678901', nonce: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x3456789012345678901234567890123456789012', tokenId: 1n, } as const // Raw calldata const { to, data } = Gatekeeper.preparePurchase({ gatekeeper: gatekeeperAddress[chainId], saleKey, purchaseParams: { recipient: '0x4567890123456789012345678901234567890123', referrer: '0x0000000000000000000000000000000000000000', quantity: 1, }, }) // Viem args const result = Gatekeeper.preparePurchase({ gatekeeper: gatekeeperAddress[chainId], saleKey, purchaseParams: { recipient: '0x4567890123456789012345678901234567890123', referrer: '0x0000000000000000000000000000000000000000', quantity: 1, }, as: 'args', }) ``` ##### Parameters | Parameter | Type | Description | | ---------------- | ------------------ | --------------------------- | | `gatekeeper` | `Address` | Gatekeeper contract address | | `saleKey` | `SaleKey` | Sale identifier | | `purchaseParams` | `PurchaseParams` | Purchase configuration | | `as` | `'data' \| 'args'` | Output format | *** #### preparePurchaseWithApproval Prepares a purchase with EIP-712 approval signature. ```ts twoslash /// // ---cut--- import { Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', // Your deployed contract vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, vendor: '0x2345678901234567890123456789012345678901', nonce: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x3456789012345678901234567890123456789012', tokenId: 1n, } as const const { to, data } = Gatekeeper.preparePurchaseWithApproval({ gatekeeper: gatekeeperAddress[chainId], saleKey, purchaseParams: { recipient: '0x4567890123456789012345678901234567890123', referrer: '0x0000000000000000000000000000000000000000', quantity: 1, }, signatureParams: { deadline: BigInt(Math.floor(Date.now() / 1000) + 900), salt: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', signature: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12', }, }) ``` ##### Parameters | Parameter | Type | Description | | ----------------- | ------------------------- | --------------------------- | | `gatekeeper` | `Address` | Gatekeeper contract address | | `saleKey` | `SaleKey` | Sale identifier | | `purchaseParams` | `PurchaseParams` | Purchase configuration | | `signatureParams` | `ApprovalSignatureParams` | EIP-712 signature data | | `as` | `'data' \| 'args'` | Output format | *** ### Fund Functions #### prepareWithdraw Prepares a withdrawal of accumulated funds. ```ts twoslash /// // ---cut--- import { Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const { to, data } = Gatekeeper.prepareWithdraw({ gatekeeper: gatekeeperAddress[chainId], currency: '0x1111111111111111111111111111111111111111', amount: 1000000n, }) ``` ##### Parameters | Parameter | Type | Description | | ------------ | --------- | --------------------------- | | `gatekeeper` | `Address` | Gatekeeper contract address | | `currency` | `Address` | Currency token address | | `amount` | `bigint` | Amount to withdraw | *** #### prepareWithdrawProtocol Prepares withdrawal of protocol fees (owner only). ```ts twoslash /// // ---cut--- import { Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const { to, data } = Gatekeeper.prepareWithdrawProtocol({ gatekeeper: gatekeeperAddress[chainId], currency: '0x1111111111111111111111111111111111111111', }) ``` *** ### View Functions #### prepareGetPrice Prepares a price calculation query. ```ts twoslash /// // ---cut--- import { Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', // Your deployed contract vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, vendor: '0x2345678901234567890123456789012345678901', nonce: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x3456789012345678901234567890123456789012', tokenId: 1n, } as const const { to, functionName, args } = Gatekeeper.prepareGetPrice({ gatekeeper: gatekeeperAddress[chainId], saleKey, purchaseParams: { recipient: '0x4567890123456789012345678901234567890123', referrer: '0x5678901234567890123456789012345678901234', quantity: 1, }, as: 'args', }) ``` **Returns** (from contract): * `totalPrice` - Total amount due * `subtotal` - Base price * `protocolFee` - Protocol fee amount * `vendorFee` - Vendor fee amount * `referrerFee` - Referrer fee amount * `salesTax` - Sales tax amount *** #### prepareGetSale Prepares a sale state query. ```ts twoslash /// // ---cut--- import { Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', // Your deployed contract vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, vendor: '0x2345678901234567890123456789012345678901', nonce: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x3456789012345678901234567890123456789012', tokenId: 1n, } as const const { to, functionName, args } = Gatekeeper.prepareGetSale({ gatekeeper: gatekeeperAddress[chainId], saleKey, as: 'args', }) ``` *** #### prepareIsSaleRegistered Checks if a sale is registered. ```ts twoslash /// // ---cut--- import { Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', // Your deployed contract vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, vendor: '0x2345678901234567890123456789012345678901', nonce: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x3456789012345678901234567890123456789012', tokenId: 1n, } as const const { to, functionName, args } = Gatekeeper.prepareIsSaleRegistered({ gatekeeper: gatekeeperAddress[chainId], saleKey, as: 'args', }) ``` *** #### prepareDomainSeparator Gets the EIP-712 domain separator. ```ts twoslash /// // ---cut--- import { Gatekeeper, gatekeeperAddress } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const { to, functionName, args } = Gatekeeper.prepareDomainSeparator({ gatekeeper: gatekeeperAddress[chainId], as: 'args', }) ``` ## API Reference Complete API documentation for the Gatekeeper SDK. The SDK is organized into two main categories: * **Writes** - Transaction preparation utilities for state-changing operations (viem-based) * **Reads** - Query methods for indexed blockchain data (Ponder/Drizzle-based) *** ### Writes Transaction preparation utilities for deploying contracts, managing sales, and executing purchases. #### [Factory](/api/factory) Deploy Gatekeeper1155 contracts via the factory pattern. * `prepareDeploy()` - Prepare a deployment transaction * `generateSalt()` - Generate CREATE2 salt * `predictAddress()` - Predict deployment address * `encodeGrantRoles()` - Encode role grants * `encodeSetupNewTier()` - Encode tier creation * `encodeCallGatekeeper()` - Encode Gatekeeper calls #### [Gatekeeper](/api/gatekeeper) Manage sales and execute purchases. * `encodeRegisterSale()` - Register a new sale * `encodeUpdateSale()` - Update sale configuration * `preparePurchase()` - Prepare purchase transaction * `preparePurchaseWithApproval()` - Prepare approval-gated purchase * `prepareWithdraw()` - Withdraw funds * `prepareGetPrice()` - Get price breakdown * `prepareGetSale()` - Get sale state #### [Gatekeeper1155](/api/gatekeeper-1155) Token operations and role management on deployed contracts. * `prepareSetupNewTier()` - Create a new ticket tier * `prepareAdminMint()` - Mint tickets * `prepareAdminStubTickets()` - Check in attendees * `prepareVoidTickets()` - Void tickets * `prepareGrantRoles()` / `prepareRevokeRoles()` - Manage roles *** ### Signing EIP-712 utilities for approvals and ticket intent signatures. #### [Approval](/api/approval) Typed data for purchase approvals. * `getTypedData()` - Generate typed data for signing * `getDeadline()` - Calculate deadline timestamp * `types` - EIP-712 type definitions * `domain` - EIP-712 domain separator #### [TicketIntent](/api/ticket-intent) Typed data for proving ticket ownership. * `types` - EIP-712 type definitions * `domain` - EIP-712 domain separator * `primaryType` - Primary type identifier * `messageSchema` - Zod schema for validation *** ### Reads Query indexed blockchain data via Ponder. All read operations require a configured client. #### [Client](/api/client) Create and configure the query client. * `Client.create()` - Create a client instance * `client.db` - Raw Drizzle query builder * `client.live()` - Subscribe to live updates via SSE * `client.getStatus()` - Get indexer status * `client.schema` - Access Ponder schema tables #### [EventContract](/api/event-contract) Query deployed Gatekeeper1155 contracts. * `get()` - Get a contract by address * `list()` - List contracts with filters * `listByCreator()` - List contracts by creator * `listByOwner()` - List contracts by owner #### [Sale](/api/sale) Query sale configurations. * `get()` - Get a sale by ID * `getByKey()` - Get a sale by composite key * `listByContract()` - List sales for a contract * `listByVendor()` - List sales by vendor * `listActive()` - List currently active sales #### [Tier](/api/tier) Query ticket tier configurations with supply statistics. * `get()` - Get a tier by token ID * `getWithSupply()` - Get tier with supply info * `getStats()` - Get attendance statistics * `listByContract()` - List tiers for a contract * `listByContractWithSupply()` - List tiers with supply data #### [Token](/api/token) Query ERC1155 token data. * `get()` - Get a token by ID * `listByContract()` - List tokens for a contract #### [Balance](/api/balance) Query token holder balances with ticket/stub status. * `get()` - Get a specific balance * `getTokenStatus()` - Get holder's token/stub status * `getHoldings()` - Get all holdings for an address * `isStubbed()` - Check if holder has stubs * `listByToken()` - List holders of a token * `listByHolder()` - List balances for a holder * `hasBalance()` - Check if holder has balance #### [Transfer](/api/transfer) Query transfer history including mints and burns. * `listByToken()` - List transfers for a token * `listByFrom()` - List transfers from an address * `listByTo()` - List transfers to an address * `listMints()` - List mint events * `listBurns()` - List burn events #### [Permission](/api/permission) Query user permissions and role grants. * `get()` - Get permission for a user * `hasRole()` - Check if user has a role * `listByContract()` - List permissions for a contract * `listByUser()` - List permissions for a user * `listByRole()` - List users with a specific role * **Utilities**: `decodeRoles()`, `hasRoleInMask()`, `combineRoles()` #### [Stub](/api/stub) Query stubbing events (check-ins where tickets become stubs). * `listByContract()` - List stubs for a contract * `listByHolder()` - List stubs for a holder * `listByTier()` - List stubs for a tier #### [Void](/api/void) Query voiding events (burns without stub conversion). * `listByContract()` - List voids for a contract * `listByHolder()` - List voids for a holder * `listByTier()` - List voids for a tier *** ### Utilities #### [Errors](/api/errors) Custom error types for SDK operations. #### [Validators](/api/validators) Input validation utilities. *** ### Exported Constants #### Contract Addresses ```ts twoslash /// // ---cut--- import { gatekeeperAddress, gatekeeper1155Address, gatekeeper1155FactoryAddress, } from '@sceneinfrastructure/sdk' ``` #### Role Constants ```ts twoslash /// // ---cut--- import { ADMIN_ROLE, HOST_ROLE, DOOR_ROLE, MINTER_ROLE, } from '@sceneinfrastructure/sdk' ``` #### ABIs ```ts twoslash /// // ---cut--- import { gatekeeperAbi, gatekeeper1155Abi, gatekeeper1155FactoryAbi, } from '@sceneinfrastructure/sdk' ``` #### Protocol Fee ```ts twoslash /// // ---cut--- import { PROTOCOL_FEE_BPS } from '@sceneinfrastructure/sdk' // 100 = 1% ``` import { PermissionDemo } from '../../components/PermissionDemo' ## Permission Query user permissions and role grants on contracts. ```ts twoslash /// // ---cut--- import { Client, Permission, ADMIN_ROLE, HOST_ROLE, DOOR_ROLE, MINTER_ROLE } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) ``` ### Try It ### Methods #### get Get a permission record for a specific user on a contract. ```ts twoslash /// // ---cut--- import { Client, Permission } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const permission = await client.permission.get({ contractAddress: '0x1234567890123456789012345678901234567890' as const, user: '0x2345678901234567890123456789012345678901' as const }) if (permission) { // Decode the role bitmask const roles = Permission.decodeRoles(permission.roles) console.log('Admin:', roles.admin) console.log('Host:', roles.host) console.log('Door:', roles.door) console.log('Minter:', roles.minter) } ``` ##### Parameters | Parameter | Type | Description | | ----------------- | --------- | -------------------- | | `contractAddress` | `Address` | The contract address | | `user` | `Address` | The user address | ##### Returns `Permission | null` - The permission record or null if not found. *** #### hasRole Check if a user has a specific role on a contract. ```ts twoslash /// // ---cut--- import { Client, ADMIN_ROLE, HOST_ROLE, DOOR_ROLE, MINTER_ROLE } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) // Check admin role const isAdmin = await client.permission.hasRole({ contractAddress: '0x1234567890123456789012345678901234567890', user: '0x2345678901234567890123456789012345678901', role: ADMIN_ROLE }) // Check door role (check-in capability) const canCheckIn = await client.permission.hasRole({ contractAddress: '0x1234567890123456789012345678901234567890', user: '0x2345678901234567890123456789012345678901', role: DOOR_ROLE }) ``` ##### Parameters | Parameter | Type | Description | | ----------------- | --------- | -------------------------------------- | | `contractAddress` | `Address` | The contract address | | `user` | `Address` | The user address | | `role` | `bigint` | The role to check (e.g., `ADMIN_ROLE`) | ##### Returns `boolean` - True if the user has the role. *** #### listByContract List all permission records for a contract. ```ts twoslash /// // ---cut--- import { Client, Permission } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const permissions = await client.permission.listByContract({ contractAddress: '0x1234567890123456789012345678901234567890', limit: 50 }) for (const perm of permissions) { const roles = Permission.decodeRoles(perm.roles) console.log(`${perm.userAddress}:`, roles) } ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | ------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Permission[]` - Array of permission records. *** #### listByUser List all contracts where a user has roles. ```ts twoslash /// // ---cut--- import { Client, Permission } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const permissions = await client.permission.listByUser({ user: '0x2345678901234567890123456789012345678901', limit: 50 }) for (const perm of permissions) { const roles = Permission.decodeRoles(perm.roles) console.log(`Contract ${perm.contractAddress}:`, roles) } ``` ##### Parameters | Parameter | Type | Default | Description | | --------- | --------- | ------------ | ------------------------- | | `user` | `Address` | **required** | The user address | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Permission[]` - Array of permission records. *** #### listByRole List users with a specific role on a contract. ```ts twoslash /// // ---cut--- import { Client, ADMIN_ROLE, DOOR_ROLE } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) // Find all admins const admins = await client.permission.listByRole({ contractAddress: '0x1234567890123456789012345678901234567890', role: ADMIN_ROLE }) for (const perm of admins) { console.log('Admin:', perm.userAddress) } // Find all door staff const doorStaff = await client.permission.listByRole({ contractAddress: '0x1234567890123456789012345678901234567890', role: DOOR_ROLE }) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | ------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `role` | `bigint` | **required** | The role to filter by | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Permission[]` - Array of permission records for users with the role. *** ### Utility Functions #### Permission.decodeRoles Decode a role bitmask into individual role flags. ```ts twoslash /// // ---cut--- import { Client, Permission } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const contractAddress = '0x1234567890123456789012345678901234567890' const user = '0x2345678901234567890123456789012345678901' const permission = await client.permission.get({ contractAddress, user }) if (permission) { const roles = Permission.decodeRoles(permission.roles) // Returns: // { // admin: boolean, // host: boolean, // door: boolean, // minter: boolean // } if (roles.admin) { console.log('User is an admin') } } ``` *** #### Permission.hasRoleInMask Check if a bitmask contains a specific role. ```ts twoslash /// // ---cut--- import { Client, Permission, ADMIN_ROLE, HOST_ROLE } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const permission = await client.permission.get({ contractAddress: '0x1234567890123456789012345678901234567890', user: '0x2345678901234567890123456789012345678901' }) if (permission) { const hasAdmin = Permission.hasRoleInMask(permission.roles, ADMIN_ROLE) const hasHost = Permission.hasRoleInMask(permission.roles, HOST_ROLE) } ``` *** #### Permission.combineRoles Combine multiple roles into a single bitmask. ```ts twoslash /// // ---cut--- import { Permission, ADMIN_ROLE, HOST_ROLE, MINTER_ROLE } from '@sceneinfrastructure/sdk' // Grant admin and minter roles const roles = Permission.combineRoles([ADMIN_ROLE, MINTER_ROLE]) // roles = 9n (ADMIN=1 | MINTER=8) ``` *** ### Types #### Permission ```ts twoslash /// // ---cut--- import type { Address, Hex } from 'viem' type Permission = { contractAddress: Address userAddress: Address roles: bigint // Role bitmask createdAt: bigint createdAtBlock: bigint createdAtLogIndex: number creationTxHash: Hex updatedAt: bigint updatedAtBlock: bigint updatedAtLogIndex: number updatedTxHash: Hex | null } ``` | Field | Type | Description | | ----------------- | --------- | ------------------------ | | `contractAddress` | `Address` | The contract address | | `userAddress` | `Address` | The user address | | `roles` | `bigint` | Bitmask of granted roles | *** #### DecodedRoles ```ts twoslash /// // ---cut--- type DecodedRoles = { admin: boolean // ADMIN_ROLE = 1 host: boolean // HOST_ROLE = 2 door: boolean // DOOR_ROLE = 4 minter: boolean // MINTER_ROLE = 8 } ``` *** ### Role Constants ```ts twoslash /// // ---cut--- import { ADMIN_ROLE, HOST_ROLE, DOOR_ROLE, MINTER_ROLE } from '@sceneinfrastructure/sdk' // Values: // ADMIN_ROLE = 1n (0x01) - Full contract control // HOST_ROLE = 2n (0x02) - Event management // DOOR_ROLE = 4n (0x04) - Check-in capability // MINTER_ROLE = 8n (0x08) - Token minting ``` #### Role Capabilities | Role | Value | Capabilities | | ------------- | ----- | --------------------------------- | | `ADMIN_ROLE` | `1n` | Full control, grant/revoke roles | | `HOST_ROLE` | `2n` | Manage event settings | | `DOOR_ROLE` | `4n` | Check in attendees (stub tickets) | | `MINTER_ROLE` | `8n` | Mint new tokens | *** ### Schema #### Zod Validation ```ts twoslash /// // ---cut--- import { Permission } from '@sceneinfrastructure/sdk' const data = {} // Data from API or external source const result = Permission.schema.safeParse(data) if (result.success) { console.log('Valid permission:', result.data) } ``` import { SaleDemo } from '../../components/SaleDemo' ## Sale Sale types, schemas, and utilities for the Gatekeeper protocol. ```ts twoslash /// // ---cut--- import { Sale, SaleKey } from '@sceneinfrastructure/sdk' ``` ### Try It ### Types #### Sale Complete sale configuration returned from the indexer. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) // Sale objects are returned from indexer queries const saleKeyId = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as const const sale = await client.sale.get({ saleKeyId }) if (sale) { console.log('Price:', sale.price) console.log('Quantity:', sale.quantity) console.log('Vendor:', sale.vendorAddress) } ``` | Field | Type | Description | | ----------------------- | ----------- | --------------------------- | | `contractAddress` | `Address` | Gatekeeper1155 contract | | `tokenId` | `bigint` | Token tier ID | | `currencyAddress` | `Address` | Payment currency | | `fundsRecipientAddress` | `Address` | Funds destination | | `vendorAddress` | `Address` | Vendor address | | `vendorFeeBps` | `number` | Vendor fee (basis points) | | `referralFeeBps` | `number` | Referral fee (basis points) | | `salesTaxBps` | `number` | Sales tax (basis points) | | `nonce` | `bigint` | Unique nonce | | `price` | `bigint` | Price per unit | | `quantity` | `number` | Available quantity | | `startTime` | `bigint` | Sale start timestamp | | `endTime` | `bigint` | Sale end timestamp | | `approvers` | `Address[]` | Authorized approvers | Additional indexer metadata: * `saleKeyId` - keccak256 hash of the `SaleKey` * `gatekeeperAddress` - Gatekeeper contract that registered the sale * `saleType` - Currency label emitted by the indexer * `createdAt`, `updatedAt` (plus block/log/tx fields) - indexer timestamps *** #### SaleKey The struct that uniquely identifies a sale in the Gatekeeper contract. ```ts twoslash /// // ---cut--- import type { Sale } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', // Your deployed contract tokenId: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x2345678901234567890123456789012345678901', vendor: '0x3456789012345678901234567890123456789012', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, nonce: 1n, } as const satisfies Sale.SaleKey ``` | Field | Type | Description | | ---------------- | --------- | ---------------- | | `token` | `Address` | Contract address | | `tokenId` | `bigint` | Token ID | | `currency` | `Address` | Currency address | | `fundsRecipient` | `Address` | Funds recipient | | `vendor` | `Address` | Vendor address | | `vendorFeeBps` | `number` | Vendor fee BPS | | `referralFeeBps` | `number` | Referral fee BPS | | `salesTaxBps` | `number` | Sales tax BPS | | `nonce` | `bigint` | Unique nonce | `SaleKey` is the immutable identity of a sale. Any field change produces a new `saleKeyId`, so keep the same key when calling `updateSale`. Use `nonce` to create multiple sales for the same token. *** #### SaleState Mutable sale configuration stored in the contract. ```ts twoslash /// // ---cut--- import type { Sale } from '@sceneinfrastructure/sdk' const saleState: Sale.SaleState = { startTime: 1n, endTime: 9999999999n, quantity: 100, price: 10_000000n, approvers: ['0x1234567890123456789012345678901234567890'], } ``` | Field | Type | Description | | ----------- | ----------- | -------------------- | | `startTime` | `bigint` | Start timestamp | | `endTime` | `bigint` | End timestamp | | `quantity` | `number` | Available quantity | | `price` | `bigint` | Price per unit | | `approvers` | `Address[]` | Authorized approvers | `startTime` must be non-zero; `0n` is treated as "uninitialized" onchain. *** #### PurchaseParams Parameters for executing a purchase. ```ts twoslash /// // ---cut--- import type { Sale } from '@sceneinfrastructure/sdk' const purchaseParams: Sale.PurchaseParams = { recipient: '0x1234567890123456789012345678901234567890', referrer: '0x0000000000000000000000000000000000000000', quantity: 2, } ``` | Field | Type | Description | | ----------- | --------- | ------------------ | | `recipient` | `Address` | Token recipient | | `referrer` | `Address` | Referrer (or zero) | | `quantity` | `number` | Number to purchase | *** #### ApprovalSignatureParams EIP-712 signature parameters for approved purchases. ```ts twoslash /// // ---cut--- import type { Sale } from '@sceneinfrastructure/sdk' const signatureParams: Sale.ApprovalSignatureParams = { deadline: BigInt(Math.floor(Date.now() / 1000) + 900), salt: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', signature: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12', } ``` | Field | Type | Description | | ----------- | -------- | -------------------- | | `deadline` | `bigint` | Signature expiration | | `salt` | `Hex` | Random bytes32 | | `signature` | `Hex` | ECDSA signature | *** #### PurchaseResult Price breakdown returned by `getPrice`. ```ts twoslash /// // ---cut--- import type { Sale } from '@sceneinfrastructure/sdk' const result: Sale.PurchaseResult = { totalPrice: 10_200000n, subtotal: 10_000000n, protocolFee: 100000n, vendorFee: 50000n, referrerFee: 25000n, salesTax: 25000n, } ``` *** ### Functions #### toSaleKey Converts a full `Sale` object to a `SaleKey` struct. ```ts twoslash /// // ---cut--- import { Client, Sale, SaleKey } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) // Get a sale from the indexer using its saleKeyId const saleKeyId = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as const const sale = await client.sale.get({ saleKeyId }) if (sale) { // Convert to SaleKey for contract interactions const saleKey = Sale.toSaleKey(sale) console.log('SaleKey:', saleKey) } ``` *** ### SaleKey Module #### generateId Generates the keccak256 hash of a SaleKey struct. ```ts twoslash /// // ---cut--- import { SaleKey } from '@sceneinfrastructure/sdk' const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', // Your deployed contract tokenId: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x2345678901234567890123456789012345678901', vendor: '0x3456789012345678901234567890123456789012', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, nonce: 1n, } as const const id = SaleKey.generateId(saleKey) console.log('Sale Key ID:', id) ``` ##### Returns `Hex` - keccak256 hash (bytes32) *** ### Schemas Zod schemas for runtime validation of sale data. ```ts twoslash /// // ---cut--- import { Sale } from '@sceneinfrastructure/sdk' ``` #### schema The main schema for validating complete sale configurations. ```ts twoslash /// // ---cut--- import { Sale } from '@sceneinfrastructure/sdk' const data = { contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n, currencyAddress: '0xded683C101b0171aA42d90F5388b7A44a7D7c355', fundsRecipientAddress: '0x2345678901234567890123456789012345678901', vendorAddress: '0x3456789012345678901234567890123456789012', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, nonce: 1n, price: 10_000000n, quantity: 100, startTime: 1n, endTime: 9999999999n, approvers: [], } const result = Sale.schema.safeParse(data) if (result.success) { console.log('Valid sale:', result.data.price) } else { console.error('Validation errors:', result.error.issues) } ``` *** #### saleKeySchema Validates `SaleKey` structs for contract interactions. ```ts twoslash /// // ---cut--- import { Sale } from '@sceneinfrastructure/sdk' const saleKey = Sale.saleKeySchema.parse({ token: '0x1234567890123456789012345678901234567890', tokenId: 1n, currency: '0xded683C101b0171aA42d90F5388b7A44a7D7c355', fundsRecipient: '0x2345678901234567890123456789012345678901', vendor: '0x3456789012345678901234567890123456789012', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, nonce: 1n, }) ``` | Field | Type | Validation | | ---------------- | --------- | ---------------------- | | `token` | `Address` | Valid Ethereum address | | `tokenId` | `bigint` | BigInt | | `currency` | `Address` | Valid Ethereum address | | `fundsRecipient` | `Address` | Valid Ethereum address | | `vendor` | `Address` | Valid Ethereum address | | `vendorFeeBps` | `number` | 0 to 4,294,967,295 | | `referralFeeBps` | `number` | 0 to 4,294,967,295 | | `salesTaxBps` | `number` | 0 to 4,294,967,295 | | `nonce` | `bigint` | BigInt | *** #### saleStateSchema Validates mutable sale state for registration and updates. ```ts twoslash /// // ---cut--- import { Sale } from '@sceneinfrastructure/sdk' const saleState = Sale.saleStateSchema.parse({ startTime: 1n, endTime: 9999999999n, quantity: 100, price: 10_000000n, approvers: ['0x1234567890123456789012345678901234567890'], }) ``` | Field | Type | Validation | | ----------- | ----------- | ------------------------ | | `startTime` | `bigint` | BigInt | | `endTime` | `bigint` | BigInt | | `quantity` | `number` | 0 to 4,294,967,295 | | `price` | `bigint` | BigInt | | `approvers` | `Address[]` | Array of valid addresses | *** #### purchaseParamsSchema Validates purchase parameters. ```ts twoslash /// // ---cut--- import { Sale } from '@sceneinfrastructure/sdk' const params = Sale.purchaseParamsSchema.parse({ recipient: '0x1234567890123456789012345678901234567890', referrer: '0x0000000000000000000000000000000000000000', quantity: 2, }) ``` | Field | Type | Validation | | ----------- | --------- | ---------------------- | | `recipient` | `Address` | Valid Ethereum address | | `referrer` | `Address` | Valid Ethereum address | | `quantity` | `number` | 1 to 65,535 | *** #### approvalSignatureParamsSchema Validates EIP-712 signature parameters for approved purchases. ```ts twoslash /// // ---cut--- import { Sale } from '@sceneinfrastructure/sdk' const sigParams = Sale.approvalSignatureParamsSchema.parse({ deadline: BigInt(Math.floor(Date.now() / 1000) + 900), salt: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', signature: '0x...', }) ``` | Field | Type | Validation | | ----------- | -------- | -------------------------- | | `deadline` | `bigint` | BigInt | | `salt` | `Hex` | Valid hex string (bytes32) | | `signature` | `Hex` | Valid hex string | *** #### purchaseResultSchema Validates price breakdown results from `getPrice`. ```ts twoslash /// // ---cut--- import { Sale } from '@sceneinfrastructure/sdk' const result = Sale.purchaseResultSchema.parse({ totalPrice: 10_200000n, subtotal: 10_000000n, protocolFee: 100000n, vendorFee: 50000n, referrerFee: 25000n, salesTax: 25000n, }) ``` | Field | Type | Validation | | ------------- | -------- | ---------- | | `totalPrice` | `bigint` | BigInt | | `subtotal` | `bigint` | BigInt | | `protocolFee` | `bigint` | BigInt | | `vendorFee` | `bigint` | BigInt | | `referrerFee` | `bigint` | BigInt | | `salesTax` | `bigint` | BigInt | *** ### Read Operations Query sale configurations from the indexer. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) ``` #### get Get a sale by its key ID. ```ts twoslash /// // ---cut--- import { Client, SaleKey } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const chainId = 8453 // Base const saleKey = { token: '0x1234567890123456789012345678901234567890', tokenId: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x2345678901234567890123456789012345678901', vendor: '0x3456789012345678901234567890123456789012', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, nonce: 1n, } as const const saleKeyId = SaleKey.generateId(saleKey) const sale = await client.sale.get({ saleKeyId }) if (sale) { console.log('Price:', sale.price) console.log('Quantity:', sale.quantity) } ``` ##### Parameters | Parameter | Type | Description | | ----------- | ----- | --------------------------------- | | `saleKeyId` | `Hex` | The keccak256 hash of the SaleKey | ##### Returns `Sale | null` - The sale configuration or null if not found. *** #### getByKey Get a sale by its SaleKey struct (convenience method). ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const chainId = 8453 // Base const sale = await client.sale.getByKey({ token: '0x1234567890123456789012345678901234567890', tokenId: 1n, currency: '0x1111111111111111111111111111111111111111', fundsRecipient: '0x2345678901234567890123456789012345678901', vendor: '0x3456789012345678901234567890123456789012', vendorFeeBps: 0, referralFeeBps: 0, salesTaxBps: 0, nonce: 1n, }) ``` ##### Parameters Takes a `SaleKey` struct directly instead of the hash. ##### Returns `Sale | null` - The sale configuration or null if not found. *** #### listByContract List all sales for a contract. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const sales = await client.sale.listByContract({ contractAddress: '0x1234567890123456789012345678901234567890', limit: 20 }) for (const sale of sales) { console.log(`Token ${sale.tokenId}: ${sale.price} per ticket`) } // Filter to only active sales const activeSales = await client.sale.listByContract({ contractAddress: '0x1234567890123456789012345678901234567890', active: true }) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | ------------------------ | ------------- | --------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `active` | `boolean` | - | Filter to only active sales | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | | `orderBy` | `'createdAt' \| 'price'` | `'createdAt'` | Sort field | | `order` | `'asc' \| 'desc'` | `'desc'` | Sort direction | ##### Returns `Sale[]` - Array of sale configurations. *** #### listByVendor List all sales where an address is the vendor. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const sales = await client.sale.listByVendor({ vendor: '0x2345678901234567890123456789012345678901', limit: 20 }) for (const sale of sales) { console.log(`Contract ${sale.contractAddress}: ${sale.vendorFeeBps} bps fee`) } ``` ##### Parameters | Parameter | Type | Default | Description | | --------- | --------- | ------------ | ------------------------- | | `vendor` | `Address` | **required** | The vendor address | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Sale[]` - Array of sale configurations. *** #### listActive List currently active sales. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) // All active sales across all contracts const activeSales = await client.sale.listActive({ limit: 50 }) // Active sales for a specific contract const contractActiveSales = await client.sale.listActive({ contractAddress: '0x1234567890123456789012345678901234567890', limit: 20 }) for (const sale of contractActiveSales) { const now = BigInt(Math.floor(Date.now() / 1000)) const endsIn = sale.endTime - now console.log(`Sale ends in ${endsIn} seconds`) } ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------- | --------------------------- | | `contractAddress` | `Address` | - | Filter to specific contract | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Sale[]` - Array of currently active sales (within start/end time window). import { StubDemo } from '../../components/StubDemo' ## Stub Query stubbing events (check-ins where tickets are exchanged for stubs). ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) ``` ### Try It ### Overview When an attendee checks in, their ticket tokens (odd IDs like 1, 3, 5) are exchanged for stub tokens (even IDs like 2, 4, 6). This is called "stubbing" - the ticket is consumed and a stub is minted as proof of attendance. The stub namespace queries these exchange events. ### Methods #### listByContract List all stubbing events for a contract. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const stubs = await client.stub.listByContract({ contractAddress: '0x1234567890123456789012345678901234567890', limit: 50 }) for (const stub of stubs) { console.log(`${stub.fromAddress} stubbed ${stub.amount} tickets`) console.log(` Ticket ID: ${stub.burnTokenId} -> Stub ID: ${stub.mintTokenId}`) } ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | ------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Stub[]` - Array of stubbing events. *** #### listByHolder List stubbing events for a specific holder. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const stubs = await client.stub.listByHolder({ holderAddress: '0x2345678901234567890123456789012345678901', limit: 50 }) for (const stub of stubs) { console.log(`Checked in ${stub.amount} tickets at ${stub.contractAddress}`) } // Filter by contract const contractStubs = await client.stub.listByHolder({ holderAddress: '0x2345678901234567890123456789012345678901', contractAddress: '0x1234567890123456789012345678901234567890' }) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | --------------------------- | | `holderAddress` | `Address` | **required** | The holder address | | `contractAddress` | `Address` | - | Filter to specific contract | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Stub[]` - Array of stubbing events. *** #### listByTier List stubbing events for a specific tier. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const stubs = await client.stub.listByTier({ contractAddress: '0x1234567890123456789012345678901234567890', tierId: 1n, // The ticket token ID limit: 50 }) console.log(`${stubs.length} check-ins for tier 1`) let totalStubbed = 0n for (const stub of stubs) { totalStubbed += stub.amount } console.log(`Total stubbed: ${totalStubbed}`) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | ----------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `tierId` | `bigint` | **required** | The tier ID (ticket token ID) | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Stub[]` - Array of stubbing events for the tier. *** ### Types #### Stub ```ts twoslash /// // ---cut--- import type { Address, Hex } from 'viem' type Stub = { id: string contractAddress: Address fromAddress: Address burnTokenId: bigint // Ticket token ID (odd: 1, 3, 5...) mintTokenId: bigint // Stub token ID (even: 2, 4, 6...) amount: bigint // Number of tokens stubbed createdAt: bigint createdAtBlock: bigint createdAtLogIndex: number creationTxHash: Hex updatedAt: bigint updatedAtBlock: bigint updatedAtLogIndex: number updatedTxHash: Hex | null } ``` | Field | Type | Description | | ----------------- | --------- | -------------------------- | | `id` | `string` | Unique event identifier | | `contractAddress` | `Address` | The contract address | | `fromAddress` | `Address` | The holder who checked in | | `burnTokenId` | `bigint` | The ticket token ID burned | | `mintTokenId` | `bigint` | The stub token ID minted | | `amount` | `bigint` | Number of tokens exchanged | | `creationTxHash` | `Hex` | Transaction hash | *** ### Token ID Relationship Tickets and stubs have a predictable ID relationship: ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) // Ticket IDs are odd: 1, 3, 5, 7... // Stub IDs are even: 2, 4, 6, 8... // Stub ID = Ticket ID + 1 const ticketId = 1n const stubId = ticketId + 1n // 2n // Or from a stub event: const stubs = await client.stub.listByContract({ contractAddress: '0x1234567890123456789012345678901234567890', limit: 1 }) if (stubs.length > 0) { const stub = stubs[0] const tierId = stub.burnTokenId // The original ticket tier } ``` *** ### Schema #### Zod Validation ```ts twoslash /// // ---cut--- import { Stub } from '@sceneinfrastructure/sdk' const data = { id: 'stub_123', contractAddress: '0x1234567890123456789012345678901234567890', fromAddress: '0x2345678901234567890123456789012345678901', burnTokenId: 1n, mintTokenId: 2n, amount: 1n, createdAt: 1700000000n, createdAtBlock: 100000n, createdAtLogIndex: 0, creationTxHash: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', updatedAt: 1700000000n, updatedAtBlock: 100000n, updatedAtLogIndex: 0, updatedTxHash: null, } const result = Stub.schema.safeParse(data) if (result.success) { console.log('Valid stub event:', result.data) } ``` *** ### Related * [Balance](/api/balance) - Check if users have stubs via `getTokenStatus()` or `isStubbed()` * [Tier](/api/tier) - Get stub counts via `getStats()` * [Void](/api/void) - For burns without stub conversion ## TicketIntent EIP-712 typed data for ticket intent signatures. ```ts twoslash /// // ---cut--- import { TicketIntent } from '@sceneinfrastructure/sdk' ``` ### Overview Ticket intents are signed messages that cryptographically prove a user owns a specific ticket. The ticket holder signs an EIP-712 message containing their token ID, quantity, and an expiry deadline. This signature can then be verified to confirm ownership without requiring a transaction. Common use cases: * Generating QR codes for event check-in * Proving ticket ownership to third parties * Time-limited verification of ticket holdings ### Types #### IntentMessage The message payload for ticket intent signatures. ```ts twoslash /// // ---cut--- import { TicketIntent } from '@sceneinfrastructure/sdk' const message: TicketIntent.IntentMessage = { tokenId: 1n, quantity: 2n, deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour } ``` | Field | Type | Description | | ---------- | -------- | --------------------- | | `tokenId` | `bigint` | The token tier ID | | `quantity` | `bigint` | Number of tickets | | `deadline` | `bigint` | Unix timestamp expiry | *** ### Functions #### Signing a Ticket Intent Use the exported constants with viem's `signTypedData`: ```ts import { TicketIntent } from '@sceneinfrastructure/sdk' import { createWalletClient, http } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { base } from 'viem/chains' const account = privateKeyToAccount('0x...') const walletClient = createWalletClient({ account, chain: base, transport: http(), }) const message: TicketIntent.IntentMessage = { tokenId: 1n, quantity: 2n, deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), } const signature = await walletClient.signTypedData({ domain: { ...TicketIntent.domain, chainId: 8453n, // Base verifyingContract: '0x1234567890123456789012345678901234567890', }, types: TicketIntent.types, primaryType: TicketIntent.primaryType, message, }) ``` *** ### Constants #### types EIP-712 type definitions for TicketIntent signatures. ```ts twoslash /// // ---cut--- import { TicketIntent } from '@sceneinfrastructure/sdk' // TicketIntent.types includes: // - EIP712Domain type // - TicketIntent struct type ``` *** #### domain Base domain separator for TicketIntent signatures. Add `chainId` and `verifyingContract` at runtime. ```ts twoslash /// // ---cut--- import { TicketIntent } from '@sceneinfrastructure/sdk' const contractAddress = '0x1234567890123456789012345678901234567890' as const const fullDomain = { ...TicketIntent.domain, chainId: 8453n, verifyingContract: contractAddress, } ``` | Field | Value | | --------- | ---------------- | | `name` | `'TicketIntent'` | | `version` | `'1.0.0'` | *** #### primaryType The primary type identifier for TicketIntent signatures. ```ts twoslash /// // ---cut--- import { TicketIntent } from '@sceneinfrastructure/sdk' console.log(TicketIntent.primaryType) // 'TicketIntent' ``` *** ### Schemas #### messageSchema Zod schema for validating TicketIntent messages. Coerces string/number inputs to bigint. ```ts twoslash /// // ---cut--- import { TicketIntent } from '@sceneinfrastructure/sdk' const message = TicketIntent.messageSchema.parse({ tokenId: '1', quantity: 2, deadline: Date.now() + 3600000, }) ``` import { TierDemo } from '../../components/TierDemo' ## Tier Query ticket tier configurations. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) ``` ### Try It ### Methods #### get Get a tier by contract address and token ID. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const tier = await client.tier.get({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n }) if (tier) { console.log('Token URI:', tier.tokenURI) console.log('Stub URI:', tier.stubURI) console.log('Max supply:', tier.quantity) } ``` ##### Parameters | Parameter | Type | Description | | ----------------- | --------- | ------------------------------------------- | | `contractAddress` | `Address` | The contract address | | `tokenId` | `bigint` | The tier token ID (odd numbers are tickets) | ##### Returns `Tier | null` - The tier data or null if not found. *** #### getWithSupply Get a tier with current supply information. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const tier = await client.tier.getWithSupply({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n }) if (tier) { console.log('Max supply:', tier.supply.maxSupply) console.log('Minted:', tier.supply.minted) console.log('Remaining:', tier.supply.remaining) console.log('Stubbed:', tier.supply.stubbed) console.log('Active (unstubbed):', tier.supply.active) } ``` ##### Parameters | Parameter | Type | Description | | ----------------- | --------- | -------------------- | | `contractAddress` | `Address` | The contract address | | `tokenId` | `bigint` | The tier token ID | ##### Returns `TierWithSupply | null` - Tier with supply data or null if not found. *** #### getStats Get statistics for a tier. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const stats = await client.tier.getStats({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n }) if (stats) { console.log('Total sold:', stats.sold) console.log('Total stubbed:', stats.stubbed) console.log('Stub rate:', stats.stubRate) // e.g., 75 = 75% console.log('Remaining:', stats.remaining) } ``` ##### Parameters | Parameter | Type | Description | | ----------------- | --------- | -------------------- | | `contractAddress` | `Address` | The contract address | | `tokenId` | `bigint` | The tier token ID | ##### Returns `TierStats | null` - Statistics or null if tier not found. *** #### listByContract List all tiers for a contract. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const tiers = await client.tier.listByContract({ contractAddress: '0x1234567890123456789012345678901234567890', limit: 20, orderBy: 'tokenId', order: 'asc' }) for (const tier of tiers) { console.log(`Tier ${tier.tokenId}: ${tier.tokenURI}`) } ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | -------------------------- | ------------- | ------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | | `orderBy` | `'createdAt' \| 'tokenId'` | `'createdAt'` | Sort field | | `order` | `'asc' \| 'desc'` | `'desc'` | Sort direction | ##### Returns `Tier[]` - Array of tiers. *** #### listByContractWithSupply List all tiers for a contract with supply information. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const tiers = await client.tier.listByContractWithSupply({ contractAddress: '0x1234567890123456789012345678901234567890', limit: 20 }) for (const tier of tiers) { console.log(`Tier ${tier.tokenId}:`) console.log(` Minted: ${tier.supply.minted}/${tier.supply.maxSupply}`) console.log(` Active: ${tier.supply.active}`) } ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | ------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `TierWithSupply[]` - Array of tiers with supply data. *** ### Types #### Tier ```ts twoslash /// // ---cut--- import type { Address, Hex } from 'viem' type Tier = { contractAddress: Address tokenId: bigint tokenURI: string stubTokenId: bigint stubURI: string quantity: bigint // Max supply (0 = unlimited) createdAt: bigint createdAtBlock: bigint createdAtLogIndex: number creationTxHash: Hex updatedAt: bigint updatedAtBlock: bigint updatedAtLogIndex: number updatedTxHash: Hex | null } ``` | Field | Type | Description | | ------------- | -------- | --------------------------------- | | `tokenId` | `bigint` | Ticket token ID (odd: 1, 3, 5...) | | `stubTokenId` | `bigint` | Stub token ID (even: 2, 4, 6...) | | `tokenURI` | `string` | Metadata URI for tickets | | `stubURI` | `string` | Metadata URI for stubs | | `quantity` | `bigint` | Max supply (0 = unlimited) | *** #### TierSupply ```ts twoslash /// // ---cut--- type TierSupply = { maxSupply: bigint // From tier.quantity minted: bigint // Total minted remaining: bigint | null // maxSupply - minted (null if unlimited) stubbed: bigint // Number converted to stubs voided: bigint // Number voided (refunds, cancellations) active: bigint // minted - stubbed - voided } ``` *** #### TierStats ```ts twoslash /// // ---cut--- type TierStats = { sold: bigint // Total sold (minted) stubbed: bigint // Total stubbed (checked in) stubRate: number // Percentage stubbed (0-100) remaining: bigint | null // Remaining supply (null if unlimited) } ``` *** #### TierWithSupply ```ts type TierWithSupply = Tier & { supply: TierSupply } ``` *** ### Schema #### Zod Validation ```ts twoslash /// // ---cut--- import { Tier } from '@sceneinfrastructure/sdk' const data = {} // Data from API or external source const result = Tier.schema.safeParse(data) if (result.success) { console.log('Valid tier:', result.data) } ``` import { TokenDemo } from '../../components/TokenDemo' ## Token ERC1155 Token types and utilities. ```ts twoslash /// // ---cut--- import { Token } from '@sceneinfrastructure/sdk' ``` ### Try It ### Types #### Token Represents the state of a token tier in a Gatekeeper1155 contract. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) // Token objects are returned from indexer queries const token = await client.token.get({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n, }) if (token) { console.log('Token URI:', token.tokenURI) console.log('Max supply:', token.maxSupply) console.log('Total minted:', token.totalMinted) } ``` | Field | Type | Description | | ----------------- | --------- | ----------------------------------- | | `contractAddress` | `Address` | The Gatekeeper1155 contract address | | `tokenId` | `bigint` | The token tier ID | | `tokenURI` | `string` | Metadata URI for the token | | `maxSupply` | `bigint` | Maximum supply (0 = unlimited) | | `totalMinted` | `bigint` | Number of tokens minted | *** ### Schemas #### schema Zod schema for validating and parsing Token data. ```ts twoslash /// // ---cut--- import { Token } from '@sceneinfrastructure/sdk' const data = {} // Data from API // Parse token data from API const result = Token.schema.safeParse(data) if (result.success) { const available = result.data.maxSupply - result.data.totalMinted console.log('Available tickets:', available) } ``` ##### Example: Calculate Availability ```ts twoslash /// // ---cut--- import { Token } from '@sceneinfrastructure/sdk' function getAvailability(token: Token.Token): bigint { if (token.maxSupply === 0n) { return BigInt(Number.MAX_SAFE_INTEGER) // Unlimited } return token.maxSupply - token.totalMinted } function isSoldOut(token: Token.Token): boolean { if (token.maxSupply === 0n) return false return token.totalMinted >= token.maxSupply } ``` *** ### Read Operations Query token data from the indexer. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) ``` #### get Get a token by contract address and token ID. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const token = await client.token.get({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n }) if (token) { console.log('Token URI:', token.tokenURI) console.log('Max supply:', token.maxSupply) console.log('Total minted:', token.totalMinted) } ``` ##### Parameters | Parameter | Type | Description | | ----------------- | --------- | -------------------- | | `contractAddress` | `Address` | The contract address | | `tokenId` | `bigint` | The token ID | ##### Returns `Token | null` - The token data or null if not found. *** #### listByContract List all tokens for a contract. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) const tokens = await client.token.listByContract({ contractAddress: '0x1234567890123456789012345678901234567890', limit: 50 }) for (const token of tokens) { console.log(`Token ${token.tokenId}: ${token.tokenURI}`) console.log(` Supply: ${token.totalMinted}/${token.maxSupply || 'unlimited'}`) } ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | -------------------------- | ------------- | ------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | | `orderBy` | `'createdAt' \| 'tokenId'` | `'createdAt'` | Sort field | | `order` | `'asc' \| 'desc'` | `'desc'` | Sort direction | ##### Returns `Token[]` - Array of token records. import { TransferDemo } from '../../components/TransferDemo' ## Transfer Query ERC1155 transfer history. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) ``` ### Try It ### Methods #### listByToken List transfers for a specific token. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const transfers = await client.transfer.listByToken({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n, limit: 50 }) for (const transfer of transfers) { const type = transfer.fromAddress === null ? 'MINT' : transfer.toAddress === null ? 'BURN' : 'TRANSFER' console.log(`${type}: ${transfer.quantity} tokens`) } ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | ----------------- | ------------ | ------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `tokenId` | `bigint` | **required** | The token ID | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | | `order` | `'asc' \| 'desc'` | `'desc'` | Sort by creation time | ##### Returns `Transfer[]` - Array of transfer records. *** #### listByFrom List transfers sent from a specific address. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const sent = await client.transfer.listByFrom({ from: '0x2345678901234567890123456789012345678901', limit: 50 }) for (const transfer of sent) { console.log(`Sent ${transfer.quantity} of token ${transfer.tokenId}`) } // Filter by contract const sentFromContract = await client.transfer.listByFrom({ from: '0x2345678901234567890123456789012345678901', contractAddress: '0x1234567890123456789012345678901234567890' }) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | --------------------------- | | `from` | `Address` | **required** | The sender address | | `contractAddress` | `Address` | - | Filter to specific contract | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Transfer[]` - Array of transfer records. *** #### listByTo List transfers received by a specific address. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const received = await client.transfer.listByTo({ to: '0x2345678901234567890123456789012345678901', limit: 50 }) for (const transfer of received) { console.log(`Received ${transfer.quantity} of token ${transfer.tokenId}`) } // Filter by contract const receivedFromContract = await client.transfer.listByTo({ to: '0x2345678901234567890123456789012345678901', contractAddress: '0x1234567890123456789012345678901234567890' }) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | --------------------------- | | `to` | `Address` | **required** | The recipient address | | `contractAddress` | `Address` | - | Filter to specific contract | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Transfer[]` - Array of transfer records. *** #### listMints List mint events (transfers from the zero address). ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const mints = await client.transfer.listMints({ contractAddress: '0x1234567890123456789012345678901234567890', limit: 50 }) for (const mint of mints) { console.log(`Minted ${mint.quantity} of token ${mint.tokenId} to ${mint.toAddress}`) } // Filter by token ID const tierMints = await client.transfer.listMints({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n }) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | ------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `tokenId` | `bigint` | - | Filter to specific token | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Transfer[]` - Array of mint records (where `fromAddress` is null). *** #### listBurns List burn events (transfers to the zero address). ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://ponder.mesh.xyz' }) const burns = await client.transfer.listBurns({ contractAddress: '0x1234567890123456789012345678901234567890', limit: 50 }) for (const burn of burns) { console.log(`Burned ${burn.quantity} of token ${burn.tokenId} from ${burn.fromAddress}`) } // Filter by token ID const tierBurns = await client.transfer.listBurns({ contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n }) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | ------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `tokenId` | `bigint` | - | Filter to specific token | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Transfer[]` - Array of burn records (where `toAddress` is null). *** ### Types #### Transfer ```ts twoslash /// // ---cut--- import type { Address, Hex } from 'viem' type Transfer = { id: string contractAddress: Address tokenId: bigint fromAddress: Address | null // null for mints toAddress: Address | null // null for burns quantity: bigint createdAt: bigint createdAtBlock: bigint createdAtLogIndex: number creationTxHash: Hex updatedAt: bigint updatedAtBlock: bigint updatedAtLogIndex: number updatedTxHash: Hex | null } ``` | Field | Type | Description | | ----------------- | ----------------- | ---------------------------- | | `id` | `string` | Unique transfer identifier | | `contractAddress` | `Address` | The contract address | | `tokenId` | `bigint` | The token ID | | `fromAddress` | `Address \| null` | Sender (null for mints) | | `toAddress` | `Address \| null` | Recipient (null for burns) | | `quantity` | `bigint` | Number of tokens transferred | | `creationTxHash` | `Hex` | Transaction hash | #### Transfer Types * **Mint**: `fromAddress === null` - New tokens created * **Burn**: `toAddress === null` - Tokens destroyed * **Transfer**: Both addresses present - Standard transfer *** ### Schema #### Zod Validation ```ts twoslash /// // ---cut--- import { Transfer } from '@sceneinfrastructure/sdk' const data = { id: 'transfer_123', contractAddress: '0x1234567890123456789012345678901234567890', tokenId: 1n, fromAddress: null, toAddress: '0x2345678901234567890123456789012345678901', quantity: 1n, createdAt: 1700000000n, createdAtBlock: 100000n, createdAtLogIndex: 0, creationTxHash: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', updatedAt: 1700000000n, updatedAtBlock: 100000n, updatedAtLogIndex: 0, updatedTxHash: null, } const result = Transfer.schema.safeParse(data) if (result.success) { console.log('Valid transfer:', result.data) } ``` ## Validators Zod schemas for validating Ethereum primitives. ```ts twoslash /// // ---cut--- import { Validators } from '@sceneinfrastructure/sdk' ``` ### Schemas #### ethAddress Validates and transforms Ethereum addresses. Uses [ox](https://github.com/wevm/ox) for validation. ```ts twoslash /// // ---cut--- import { Validators } from '@sceneinfrastructure/sdk' const result = Validators.ethAddress.safeParse('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045') if (result.success) { console.log('Valid address:', result.data) } // Invalid address const invalid = Validators.ethAddress.safeParse('0xinvalid') // invalid.success === false ``` *** #### ethTxHash Validates Ethereum transaction hashes (66-character hex strings). ```ts twoslash /// // ---cut--- import { Validators } from '@sceneinfrastructure/sdk' const txHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' const result = Validators.ethTxHash.safeParse(txHash) if (result.success) { console.log('Valid transaction hash') } ``` *** #### hash Validates 66-character hash strings (bytes32). ```ts twoslash /// // ---cut--- import { Validators } from '@sceneinfrastructure/sdk' const bytes32 = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' const result = Validators.hash.safeParse(bytes32) ``` *** #### hex Validates and transforms hex strings with `0x` prefix. ```ts twoslash /// // ---cut--- import { Validators } from '@sceneinfrastructure/sdk' const result = Validators.hex.safeParse('0xdeadbeef') if (result.success) { console.log('Valid hex:', result.data) // '0xdeadbeef' } ``` *** #### uint256 Coerces values to bigint for uint256 fields. ```ts twoslash /// // ---cut--- import { Validators } from '@sceneinfrastructure/sdk' // Accepts strings, numbers, or bigints const fromString = Validators.uint256.parse('1000000') const fromNumber = Validators.uint256.parse(1000000) const fromBigint = Validators.uint256.parse(1000000n) // All result in: 1000000n ``` *** ### Usage with Zod Combine with other Zod schemas for complex validation: ```ts twoslash /// // ---cut--- import { z } from 'zod' import { Validators } from '@sceneinfrastructure/sdk' const transferSchema = z.object({ from: Validators.ethAddress, to: Validators.ethAddress, amount: Validators.uint256, txHash: Validators.ethTxHash.optional(), }) const transfer = transferSchema.parse({ from: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', to: '0x1234567890123456789012345678901234567890', amount: '1000000000000000000', }) ``` import { VoidDemo } from '../../components/VoidDemo' ## Void Query voiding events (ticket burns without stub conversion). ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: '...' }) ``` ### Try It ### Overview Voiding is when tickets are burned directly without creating stubs. This is different from stubbing (check-in), where tickets are exchanged for stub tokens. Use cases for voiding: * Canceling tickets without marking as attended * Refund scenarios * Administrative removal of tickets For check-in events (ticket to stub exchange), use the [Stub](/api/stub) namespace instead. ### Methods #### listByContract List all voiding events for a contract. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) const voids = await client.void.listByContract({ contractAddress: '0x1234567890123456789012345678901234567890', limit: 50 }) for (const v of voids) { console.log(`${v.holderAddress} had ${v.amount} tickets voided`) console.log(` Tier: ${v.tierId}`) } // Filter by tier const tier1Voids = await client.void.listByContract({ contractAddress: '0x1234567890123456789012345678901234567890', tierId: 1n }) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | ------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `tierId` | `bigint` | - | Filter to specific tier | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Void[]` - Array of voiding events. *** #### listByHolder List voiding events for a specific holder. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) const voids = await client.void.listByHolder({ holderAddress: '0x2345678901234567890123456789012345678901', limit: 50 }) for (const v of voids) { console.log(`${v.amount} tickets voided from ${v.contractAddress}`) } // Filter by contract const contractVoids = await client.void.listByHolder({ holderAddress: '0x2345678901234567890123456789012345678901', contractAddress: '0x1234567890123456789012345678901234567890' }) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | --------------------------- | | `holderAddress` | `Address` | **required** | The holder address | | `contractAddress` | `Address` | - | Filter to specific contract | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Void[]` - Array of voiding events. *** #### listByTier List voiding events for a specific tier. ```ts twoslash /// // ---cut--- import { Client } from '@sceneinfrastructure/sdk' const client = Client.create({ url: 'https://dev.ponder.mesh.ing/sql' }) const voids = await client.void.listByTier({ contractAddress: '0x1234567890123456789012345678901234567890', tierId: 1n, limit: 50 }) let totalVoided = 0n for (const v of voids) { totalVoided += v.amount } console.log(`Total voided from tier 1: ${totalVoided}`) ``` ##### Parameters | Parameter | Type | Default | Description | | ----------------- | --------- | ------------ | ------------------------- | | `contractAddress` | `Address` | **required** | The contract address | | `tierId` | `bigint` | **required** | The tier ID | | `limit` | `number` | `50` | Maximum results to return | | `offset` | `number` | `0` | Number of results to skip | ##### Returns `Void[]` - Array of voiding events for the tier. *** ### Types #### Void ```ts twoslash /// // ---cut--- import type { Address, Hex } from 'viem' type Void = { id: string contractAddress: Address holderAddress: Address tierId: bigint amount: bigint createdAt: bigint createdAtBlock: bigint createdAtLogIndex: number creationTxHash: Hex updatedAt: bigint updatedAtBlock: bigint updatedAtLogIndex: number updatedTxHash: Hex | null } ``` | Field | Type | Description | | ----------------- | --------- | ------------------------------------ | | `id` | `string` | Unique event identifier | | `contractAddress` | `Address` | The contract address | | `holderAddress` | `Address` | The holder whose tickets were voided | | `tierId` | `bigint` | The tier ID | | `amount` | `bigint` | Number of tickets voided | | `creationTxHash` | `Hex` | Transaction hash | *** ### Schema #### Zod Validation ```ts twoslash /// // ---cut--- import { Void } from '@sceneinfrastructure/sdk' const data = {} // Data to validate const result = Void.schema.safeParse(data) if (result.success) { console.log('Valid void event:', result.data) } ``` *** ### Void vs Stub | Event | Action | Result | | ------------------- | -------------------------- | ---------------------- | | **Stub** (Check-in) | Ticket burned, stub minted | Holder gets stub token | | **Void** | Ticket burned directly | No stub token | Use stubs for attendance tracking (check-ins). Use voids for administrative actions like cancellations or refunds. *** ### Related * [Stub](/api/stub) - For check-in events (ticket to stub exchange) * [Transfer](/api/transfer) - For all transfer events including burns * [Balance](/api/balance) - Check current token balances