A trust-minimized blockchain vending machine implementation that accepts stablecoin payments and issues participation tokens to customers.
This project implements a decentralized vending machine smart contract system with the following features:
- Multi-track vending system: Configurable number of tracks (default: 3) for different products
- Stablecoin payments: Accepts multiple dollar-denominated tokens (USDC, USDT, DAI)
- Participation tokens: Issues ERC20 voting tokens proportional to purchase amounts
- Sequential dispensing: Mimics physical vending machine behavior with front-item vending
- Role-based access control: Separate roles for operators and treasury management
The main vending machine contract that handles:
- Track management (loading products, setting prices, restocking)
- Payment token configuration
- Product vending with sequential dispensing
- Revenue withdrawal
An ERC20 token with voting capabilities that:
- Implements ERC20Votes for governance participation
- Includes permit functionality for gasless approvals
- Mints tokens proportional to customer purchases
# Install dependencies
yarn install
# Build contracts
yarn build
# Run tests
yarn test# 1. Configure environment variables
cp .env.example .env
# Edit .env with your RPC URLs and private keys
# 2. Deploy to Sepolia testnet
forge script script/DeployVendingMachine.s.sol:DeployVendingMachine \
--rpc-url $SEPOLIA_RPC \
--private-key $PRIVATE_KEY \
--broadcast \
--verify \
-vvvThe project includes automated deployment workflows:
-
Configure Repository Secrets:
DEPLOYER_PRIVATE_KEY- Private key for deploymentsSEPOLIA_RPC_URL- Sepolia RPC endpointETHERSCAN_API_KEY- For contract verification
-
Trigger Deployment:
- Manual: Go to Actions tab → "Deploy to Sepolia" → Run workflow
- Automatic: On pull requests and pushes to
mainbranch
See docs/DEPLOYMENT.md for detailed deployment instructions.
loadTrack(trackId, product, initialStock)- Load a single track with productloadMultipleTracks(trackIds[], products[], initialStocks[])- Batch load multiple tracksrestockTrack(trackId, additionalStock)- Add more items to a tracksetTrackPrice(trackId, dollarPrice)- Update product price for a trackconfigurePaymentTokens(tokens[])- Set accepted stablecoins
vendFromTrack(trackId, token, recipient)- Purchase item from specific track
To purchase from the VendingMachine on Sepolia testnet:
# Prerequisites: Get Sepolia USDC
# You need Sepolia USDC tokens. The token address is:
# 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238
# Set environment variables
export PRIVATE_KEY=your_private_key
export SEPOLIA_RPC=your_sepolia_rpc_url
export VENDING_MACHINE_ADDRESS=deployed_contract_address
# Option 1: Use the convenience script
./scripts/purchase-sepolia.sh
# Option 2: Use forge directly
forge script script/PurchaseFromVendingMachine.s.sol:PurchaseFromVendingMachine \
--rpc-url $SEPOLIA_RPC \
--broadcast \
-vvvThe purchase script will:
- Check your USDC balance
- Approve the VendingMachine to spend USDC
- Purchase from track 1 (Coca Cola)
- Display the vote tokens received
withdrawRevenue(token, to, amount)- Withdraw collected payments
Run the comprehensive test suite:
# Run all tests
yarn test
# Run specific test contract
forge test --match-contract VendingMachineTest
# Run with gas reporting
forge test --gas-reportThe system follows a modular architecture:
- Access Control: Role-based permissions for operators and treasury
- Reentrancy Protection: Guards against reentrancy attacks on vending function
- Sequential Dispensing: Tracks maintain FIFO inventory with automatic front-item vending
- Token Integration: Safe ERC20 operations using OpenZeppelin's SafeERC20
- Uses OpenZeppelin's battle-tested contracts for core functionality
- Implements reentrancy guards on state-changing functions
- Role-based access control for administrative functions
- Input validation on all external functions
- Safe math operations (Solidity 0.8.20+)
MIT