Skip to Content
Welcome to RitoSwap's documentation!
Colored KeysOverview

Colored Keys Smart Contracts

Version: 0.2.0
Solidity: 0.8.20
OpenZeppelin: 5.3.0

Overview

Colored Keys is an ERC-721 NFT system with two main features: on-chain SVG generation and a one-token-per-wallet restriction. All metadata and images are generated directly in the smart contract without external dependencies.

The system consists of two contracts:

  • KeyToken.sol - Base ERC-721 with on-chain SVG generation
  • OnePerWalletKeyToken.sol - Production contract that restricts ownership to one token per wallet

Quick Start

git clone https://github.com/ritovision/ritoswap.git cd ritoswap/colored-keys pnpm install pnpm compile

Configure environment variables:

cp .env.example .env # Add your PRIVATE_KEY, RECEIVER_ADDRESS, and API keys

Contract Specifications

MetricValueNotes
Runtime Bytecode18,565 bytes75.5% of EIP-170 limit
Deployment Gas4,127,918Measured on testnet
Mint Gas~247,000Including color generation and storage
Transfer Gas~83,000Standard ERC-721 plus balance check

Core Features

On-Chain Generation

  • SVG images generated directly in contract code
  • Deterministic colors based on token ID, block data, and minter address
  • Complete metadata generated without external dependencies
  • Base64-encoded data URIs for full compatibility

One-Token-Per-Wallet Restriction

The production contract enforces ownership limits through an _update function override:

function _update(address to, uint256 tokenId, address auth) internal override returns (address) { address from = _ownerOf(tokenId); if (to != address(0) && balanceOf(to) > 0 && from != to) { revert WouldExceedMaxTokensPerWallet(to); } return super._update(to, tokenId, auth); }

This check intercepts all token movements (minting, transferring, burning) while allowing self-transfers.

Color Generation Algorithm

Colors are generated using keccak256 hashing with multiple seeds:

backgroundColor = generateColor(tokenId, block.timestamp, minter); keyColor = generateColor(tokenId * 2, block.number, minter);

If colors are identical, the key color is inverted to ensure contrast. Note: this only detects exact matches, not visual similarity.

Deployment

Network Commands

# Local development pnpm deploy:hardhat pnpm deploy:local-blockchain # Testnets pnpm deploy:sepolia # Mainnet (uses Viem v2, limited scripts) pnpm deploy:mainnet

Contract addresses are automatically saved to ContractAddresses/{network}.json in the repository root.

Mainnet deployment uses Viem v2 for enhanced reliability. Mainnet interaction is primarily intended through the dApp, so only deployment and verification scripts are provided.

Contract Verification

pnpm verify:sepolia # Requires ETHERSCAN_API_KEY pnpm verify:mainnet # Requires ETHERSCAN_API_KEY

Contract Interaction

Basic operations using the deployed contract addresses:

# Mint one token (enforced by contract) pnpm mint:sepolia # Check ownership and supply pnpm check-supply:sepolia # Burn token (allows minting a new one) pnpm burn:sepolia # Transfer to RECEIVER_ADDRESS pnpm transfer:sepolia

Testing

The contracts have comprehensive test coverage including unit tests, gas profiling, and security analysis.

# Run all tests pnpm test # Generate coverage report pnpm test:coverage # Gas consumption analysis pnpm test:gas # Security analysis pnpm slither pnpm mythril pnpm echidna

Coverage Results

ContractStatementsBranchesFunctionsLines
OnePerWalletKeyToken100%100%100%100%
KeyToken95.83%100%88.89%96.15%
SVGGenerator100%100%100%100%
ColorGenerator57.89%16.67%66.67%62.5%

Lower coverage in ColorGenerator is due to private utility functions tested indirectly through public interfaces.

Architecture

The contracts use standard OpenZeppelin inheritance with minimal custom logic:

ERC721 + ERC721Enumerable + ERC721Burnable KeyToken (adds color generation and SVG creation) OnePerWalletKeyToken (adds ownership restriction)

Supporting libraries:

  • ColorGenerator.sol - Pure functions for color generation and hex conversion
  • SVGGenerator.sol - Programmatic SVG construction

Key Functions

Public Functions

  • mint() - Mint one token (checks one-per-wallet rule)
  • mintBatch(uint256) - Batch minting (limited to quantity=1 in production)
  • burn(uint256) - Burn owned token
  • tokenURI(uint256) - Returns complete Base64-encoded JSON metadata
  • getTokenColors(uint256) - Returns background and key colors
  • getTokenOfOwner(address) - Returns tokenId and ownership status

View Functions

  • balanceOf(address) - Token count (always 0 or 1 in production)
  • tokensOfOwner(address) - Array of owned tokens
  • totalSupply() - Current supply

Technical Details

Storage Pattern

Each token stores minimal data:

struct ColorData { string backgroundColor; // Generated hex color string keyColor; // Generated hex color address minter; // Original minter uint256 mintedAt; // Block timestamp }

All other properties (SVG, metadata) are computed on-demand.

SVG Generation

The SVG creates a simple key design with four elements:

  • Background rectangle (200x200)
  • Key ring (circle at 60,100 with radius 20)
  • Key shaft (rectangle from 80,95)
  • Two teeth elements (positioned at x=145 and x=165)

Integration

dApp Integration

The contracts integrate with the RitoSwap dApp through standard ERC-721 interfaces plus custom view functions.

Local Blockchain Support

For local development, contracts can be deployed to the custom local blockchain:

Local Blockchain Setup Guide

Documentation

Limitations

  • Color similarity detection: Only checks for identical colors, not visual similarity
  • Batch minting: Limited to quantity=1 in production contract
  • No upgradability: Contracts are immutable by design
  • One per address: Restriction applies to addresses, not wallets (one wallet can control multiple addresses)

Development Features

The base KeyToken contract includes mintBatch(uint256) for development testing of color generation across multiple tokens. In production, this is overridden to enforce quantity=1.

Summary

Colored Keys provides a simple ERC-721 implementation with on-chain generation and ownership restrictions. The contracts prioritize simplicity and security over complex features, using proven OpenZeppelin foundations with minimal custom logic. The one-token-per-wallet mechanism creates scarcity while the on-chain generation ensures permanent availability.