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

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

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):

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:

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 for details.

Updating a Sale

Update an existing sale's configuration:

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:

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:

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