← Back

Developers

Token Claimer is an open-source protocol on LUKSO that lets anyone deploy token and NFT drop campaigns. All contracts are verified on-chain.

Architecture

The system uses a factory pattern. Two factory contracts (one for LSP7 tokens, one for LSP8 NFTs) deploy individual drop contracts. Each drop is its own contract owned by the creator.

LSP7DropFactoryLSP8DropFactory
↓ deploys
LSP7ClaimableTokenLSP8ClaimableNFT
↓ claimer calls claim()
Tokens / NFTs minted to claimer

Verified Contracts

All contracts are verified and readable on the LUKSO block explorer.

Mainnet(Chain ID: 42)
Testnet(Chain ID: 4201)
LSP26 Follower Registry(same on both networks)

How It Works

1

Creator deploys a drop

Calls createTokenDrop() or createNFTDrop() on the factory. This deploys a fresh contract and transfers ownership to the creator.

2

Creator configures the drop

Enable claiming, set a time window, add token-gating requirements (hold LSP7/LSP8 tokens), require a minimum follower count via LSP26, or set a secret codeword.

3

Users claim tokens or NFTs

Eligible users call claim() or claimWithCode(). The contract validates all requirements, then mints tokens/NFTs directly to the claimer. Each address can only claim once.

Contract Interface

Factory — Create Drops

// LSP7 (Fungible Tokens)
function createTokenDrop(
  string name,
  string symbol,
  uint256 amountPerClaim,
  uint256 maxSupply,       // 0 = unlimited
  bool isNonDivisible      // true = 0 decimals, false = 18
) returns (address)

// LSP8 (NFTs)
function createNFTDrop(
  string name,
  string symbol,
  uint256 maxSupply,       // 0 = unlimited
  uint256 amountPerClaim   // max 50 per claim
) returns (address)

Factory — Query Drops

// LSP7
function totalCampaigns() view returns (uint256)
function getCampaigns(uint256 offset, uint256 limit)
  view returns (Campaign[] memory)

// LSP8
function totalNFTCampaigns() view returns (uint256)
function getNFTCampaigns(uint256 offset, uint256 limit)
  view returns (Campaign[] memory)

// Campaign struct
struct Campaign {
  address contractAddr;
  address creator;
  string name;
  string symbol;
  uint256 createdAt;
}

Drop — Owner Management

function setClaimEnabled(bool enabled)
function setClaimWindow(uint256 startTime, uint256 endTime)
function setAmountPerClaim(uint256 amount)
function setRequirements(
  address[] tokens,
  uint256[] minBalances,
  uint256 minFollowers     // LSP26 follower count
)
function setCodeword(string word)  // stored as keccak256 hash
function clearCodeword()

Drop — Claiming

function claim()
function claimWithCode(string codeword)

Drop — View Functions

function claimEnabled() view returns (bool)
function isClaimActive() view returns (bool)
function hasClaimed(address) view returns (bool)
function meetsRequirements(address)
  view returns (bool tokenOk, bool followersOk)
function getTokenRequirements()
  view returns (address[] tokens, uint256[] minBalances)
function requiredFollowers() view returns (uint256)
function maxSupply() view returns (uint256)
function amountPerClaim() view returns (uint256)
function codewordEnabled() view returns (bool)
function owner() view returns (address)

// LSP7 only
function totalClaimers() view returns (uint256)
function totalClaimedAmount() view returns (uint256)
function decimals() view returns (uint8)

// LSP8 only
function totalClaimed() view returns (uint256)

Custom Errors

ClaimNotActive()
AlreadyClaimed()
OutsideClaimWindow()
MaxSupplyReached()
InsufficientTokenBalance()
InsufficientFollowers()
WrongCodeword()
ReentrancyGuard()

LSP Standards Used

LSP7

Digital Asset (Fungible Token)

LUKSO's standard for fungible tokens. Similar to ERC-20 but with force parameter on transfers and metadata stored in ERC725Y (no name()/symbol() view functions).

LSP8

Identifiable Digital Asset (NFT)

LUKSO's NFT standard. Token IDs are bytes32 (not uint256). Drop contracts auto-increment IDs starting at 1.

LSP26

Follower System

On-chain social graph. Drops can require claimers to have a minimum number of followers. Registry is at 0xf01103E5a9909Fc0DBe8166dA7085e0285daDDcA.

LSP4

Digital Asset Metadata

Token metadata (name, description, icon) stored on-chain as VerifiableURI pointing to IPFS JSON. Creators can set this after deployment via the manage page.

Integration Examples

Using ethers.js v6 to interact with the contracts.

Query all drops from a factory

import { JsonRpcProvider, Contract } from "ethers";

const rpc = new JsonRpcProvider("https://rpc.mainnet.lukso.network");
const factory = new Contract(
  "0x9b132E764f92c6E6F5E91E276E310758C33dB08F",
  [
    "function totalCampaigns() view returns (uint256)",
    "function getCampaigns(uint256,uint256) view returns " +
      "(tuple(address contractAddr, address creator, " +
      "string name, string symbol, uint256 createdAt)[])",
  ],
  rpc
);

const total = await factory.totalCampaigns();
const campaigns = await factory.getCampaigns(0, total);

campaigns.forEach(c => {
  console.log(c.name, c.contractAddr);
});

Check eligibility for a user

const drop = new Contract(dropAddress, [
  "function isClaimActive() view returns (bool)",
  "function hasClaimed(address) view returns (bool)",
  "function meetsRequirements(address) view returns " +
    "(bool tokenOk, bool followersOk)",
], rpc);

const active = await drop.isClaimActive();
const claimed = await drop.hasClaimed(userAddress);
const { tokenOk, followersOk } = await drop.meetsRequirements(
  userAddress
);

const canClaim = active && !claimed && tokenOk && followersOk;

Claim tokens via a Universal Profile

import { BrowserProvider, Contract } from "ethers";

// Get the UP browser extension provider
const provider = new BrowserProvider(window.lukso);
const signer = await provider.getSigner();

const drop = new Contract(dropAddress, [
  "function claim() external",
  "function claimWithCode(string) external",
], signer);

// Simple claim
const tx = await drop.claim();
await tx.wait();

// Or claim with codeword
const tx2 = await drop.claimWithCode("secret");
await tx2.wait();

Detect token type (LSP7 vs LSP8)

// LSP7 has decimals(), LSP8 does not
async function detectTokenType(contract) {
  try {
    await contract.decimals();
    return "LSP7"; // Fungible token
  } catch {
    return "LSP8"; // NFT
  }
}