Skip to content

Jalaa532/ArbiDrop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ArbiDrop — Limited NFT Drop on Arbitrum

10 NFTs for demo purposes on Arbitrum.
On-chain randomness (block timestamp) · Three phases · No reveal needed · IPFS metadata.


Deployed on Arbitrum One:
0x27ccb5c8da8ae0e2c8b349a0a7d14627429c9569

Built using QuantumAILab/ARBuilder

Architecture

nft_drop/
├── packages/
│   ├── contract/      # Stylus (Rust) ERC-721 — ArbiDrop
│   ├── frontend/      # Next.js 14 · wagmi v2 · RainbowKit · DaisyUI
│   ├── indexer/       # The Graph subgraph — Transfer + Mint events
│   └── oracle/        # Chainlink VRF v2.5 consumer (Solidity, optional)
├── Makefile
└── package.json

Drop Phases

Phase Window Price Max per wallet
1 — ALLOWLIST First 15 min 0.000001 ETH 2
2 — PUBLIC Next 15 min 0.000001 ETH 1
3 — CLOSED After 30 min

TokenIds are drawn at random from a remaining pool using a swap-pop pattern. The block-timestamp entropy is used by default; swap in the VRF oracle for production-grade randomness.


Prerequisites

Tool Install
Rust + cargo-stylus cargo install cargo-stylus
Foundry (cast) https://getfoundry.sh
Node.js ≥ 18 https://nodejs.org
Graph CLI npm i -g @graphprotocol/graph-cli

1 · Deploy the Contract

cd packages/contract

# Copy and fill in env
cp ../.env.example .env
# PRIVATE_KEY=0x...
# RPC_URL=https://sepolia-rollup.arbitrum.io/rpc
# https://arb1.arbitrum.io/rpc

# Check + deploy + initialize
bash deploy.sh

# The deployed address is written to deployed_address.txt

After deployment:

# Add wallets to the allowlist
cast send $CONTRACT "addToAllowlist(address)" 0xYOUR_WALLET \
  --private-key $PRIVATE_KEY --rpc-url $RPC_URL

# Set the drop start timestamp (Unix seconds)
cast send $CONTRACT "setStartTimestamp(uint256)" $(date +%s) \
  --private-key $PRIVATE_KEY --rpc-url $RPC_URL

2 · Deploy the VRF Oracle (optional mainnet hardening)

cd packages/oracle
cp .env.example .env
# Fill in PRIVATE_KEY and VRF_SUBSCRIPTION_ID

npm install
npm run compile
npm run deploy

Then add the deployed address as a consumer on https://vrf.chain.link.


3 · Deploy the Subgraph

cd packages/indexer
npm install

# Update subgraph.yaml:
#   source.address → your deployed contract address
#   source.startBlock → deployment block number

npm run codegen
npm run build

# Authenticate with The Graph Studio
graph auth --studio YOUR_DEPLOY_KEY

npm run deploy

4 · Run the Frontend

cd packages/frontend
cp .env.example .env.local

# Fill in:
#   NEXT_PUBLIC_CONTRACT_ADDRESS=0x...
#   NEXT_PUBLIC_WALLET_CONNECT_ID=...  (cloud.walletconnect.com)
#   NEXT_PUBLIC_SUBGRAPH_URL=https://api.studio.thegraph.com/query/.../arbidrop/...

npm install
npm run dev

Open http://localhost:3000.


5 · Makefile shortcuts

make contract-test      # Run Rust unit tests
make contract-deploy    # Build + deploy contract
make frontend-dev       # Start Next.js dev server
make indexer-deploy     # Deploy subgraph
make oracle-deploy      # Deploy VRF consumer

Frontend Features

  • Phase banner — ALLOWLIST / PUBLIC MINT / SOLD OUT / CLOSED with color coding
  • Countdown timer — live client-side tick to next phase boundary
  • Mint progress7 / 10 minted with a progress bar
  • Wallet eligibility — allowlist status, mints remaining in current phase
  • Mint panel — one-click mint with correct ETH value, error/success toasts
  • Gallery — last 12 minted NFTs pulled from the subgraph, refreshes every 30s
  • Leaderboard — top 5 wallets by mint count, refreshes every 60s

Contract Public Interface

Function Description
initialize() One-time setup; fills the 10-token demo pool
mint() payable Mint one NFT; assigns random tokenId
currentPhase() → uint256 0=closed, 1=allowlist, 2=public
dropStartTime() → uint256 Unix ts when allowlist phase begins
secondsUntilNextPhase() Seconds to next phase boundary
isAllowlisted(addr) Check allowlist status
mintedBy(addr) How many tokens minted by address
totalSupply() Total minted so far
maxSupply() 10
tokenUri(tokenId) ipfs://QmPlaceholderCID/{id}.json
setStartTimestamp(ts) Owner only
addToAllowlist(addr) Owner only
withdraw(to, amount) Owner only

Events (subgraph triggers)

Event Signature
Transfer Transfer(indexed address from, indexed address to, indexed uint256 tokenId)
Mint Mint(indexed address minter, indexed uint256 tokenId, uint256 phase)

IPFS Metadata

Update BASE_URI once metadata is pinned:

# Contract
cast send $CONTRACT "setBaseUri(string)" "ipfs://QmYourRealCID/" \
  --private-key $PRIVATE_KEY --rpc-url $RPC_URL

# Frontend — update NEXT_PUBLIC_CONTRACT_ADDRESS and rebuild

Expected metadata format ({tokenId}.json):

{
  "name": "ArbiDrop #42",
  "description": "Early Arbitrum community member NFT",
  "image": "ipfs://QmYourImageCID/42.png",
  "attributes": [
    { "trait_type": "Phase", "value": "Allowlist" }
  ]
}

Security Notes

  • The on-chain random selection uses block.timestamp % pool_size. This is fine for a low-stakes community drop but is miner-manipulable. For a high-value drop, integrate the ArbiDropVRF oracle (two-step mint pattern).
  • The only_owner guard is lightweight; consider adding a Ownable2Step-style pattern for mainnet.
  • Always audit before deploying to mainnet.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors