Drawn - Architecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ Docker Container │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Frontend (Port 5173) │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ Next.js + Tailwind + React │ │ │
│ │ │ - Contract Dashboard │ │ │
│ │ │ - Stats Display │ │ │
│ │ │ - GraphiQL Link │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────┬────────────────────────────────┘ │
│ │ │
│ │ GraphQL Queries │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Linera Node Service (Port 8080) │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ GraphQL API │ │ │
│ │ │ - Query: totalMinted, nextTokenId │ │ │
│ │ │ - Mutations: mintSticker, updateScore, etc. │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────┬────────────────────────────────┘ │
│ │ │
│ │ Contract Calls │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Drawn Smart Contract │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ Contract (contract.rs) - Business Logic │ │ │
│ │ │ ┌────────────────────────────────────────────────┐ │ │ │
│ │ │ │ Operations: │ │ │ │
│ │ │ │ • MintSticker(owner, uri, type) │ │ │ │
│ │ │ │ • UpdateScore(tokenId, score) │ │ │ │
│ │ │ │ • AllocateReward(player, amount) │ │ │ │
│ │ │ │ • ClaimRewards() │ │ │ │
│ │ │ └────────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ State (state.rs) - Data Persistence │ │ │
│ │ │ ┌────────────────────────────────────────────────┐ │ │ │
│ │ │ │ • next_token_id: RegisterView<u64> │ │ │ │
│ │ │ │ • stickers: MapView<u64, Sticker> │ │ │ │
│ │ │ │ • players: MapView<String, PlayerData> │ │ │ │
│ │ │ │ • total_minted: RegisterView<u64> │ │ │ │
│ │ │ └────────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ Service (service.rs) - Read-only Queries │ │ │
│ │ │ ┌────────────────────────────────────────────────┐ │ │ │
│ │ │ │ • totalMinted() -> u64 │ │ │ │
│ │ │ │ • nextTokenId() -> u64 │ │ │ │
│ │ │ └────────────────────────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────┬───────────────────────────────┘ │
│ │ │
│ │ State Updates │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Linera Local Network (Ports 9001, 13001) │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ • Validator │ │ │
│ │ │ • Faucet (http://localhost:8080) │ │ │
│ │ │ • Chain & Block Storage │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
User → Frontend → GraphQL Mutation → Contract.execute_operation()
→ State.stickers.insert() → State.players.update() → Block Created
User → Frontend → GraphQL Query → Service.handle_query()
→ State.total_minted.get() → Response
User → GraphQL Mutation → Contract.execute_operation()
→ State.get_sticker() → State.players.update()
→ Auto-allocate rewards (1:1) → Block Created
Port
Service
Purpose
5173
Next.js Frontend
User interface
8080
Linera Node Service
GraphQL API + Faucet
9001
Validator Proxy
Internal network communication
13001
Validator
Blockchain consensus
Deployment Flow (run.bash)
1. Start local network
└─> linera net up --with-faucet
2. Initialize wallet
└─> linera wallet init
3. Request chain with tokens
└─> linera wallet request-chain
4. Build contract (Rust → WASM)
└─> cargo build --target wasm32-unknown-unknown
5. Publish bytecode
└─> linera publish-bytecode drawn_{contract,service}.wasm
6. Create application instance
└─> linera create-application <bytecode-id>
7. Start node service
└─> linera service --port 8080
8. Start frontend
└─> pnpm dev --port 5173
Drawn/
├── contracts/
│ ├── src/
│ │ ├── lib.rs # ABI + Operation enum
│ │ ├── state.rs # State management (Views)
│ │ ├── contract.rs # Business logic + tests
│ │ └── service.rs # GraphQL service
│ └── Cargo.toml # Rust dependencies
├── frontend/
│ ├── pages/
│ │ ├── _app.js # App wrapper
│ │ └── index.js # Main dashboard
│ ├── styles/
│ │ └── globals.css # Tailwind imports
│ └── package.json # Node dependencies
├── Dockerfile # Container setup
├── compose.yaml # Docker Compose config
└── run.bash # Deployment script
Language : Rust 1.86
Framework : Linera SDK 0.15.5
Build Target : wasm32-unknown-unknown
Testing : cargo test
State : RegisterView, MapView
Framework : Next.js 14
Styling : Tailwind CSS 3.4
Package Manager : pnpm
Port : 5173
Container : Docker
Orchestration : Docker Compose
Network : Linera localnet
API : GraphQL (via Linera service)
struct Sticker {
token_id : u64 ,
owner : String ,
metadata_uri : String ,
sticker_type : String ,
minted_at : u64 ,
}
struct PlayerData {
address : String ,
total_score : u64 ,
stickers_owned : Vec < u64 > ,
pending_rewards : u64 ,
}
type Query {
totalMinted : Int !
nextTokenId : Int !
}
type Mutation {
mintSticker (owner : String ! , metadataUri : String ! , stickerType : String ! ): Int !
updateScore (tokenId : Int ! , score : Int ! ): Boolean !
allocateReward (player : String ! , amount : Int ! ): Boolean !
claimRewards : Int !
}
Owner Authentication : Currently uses string addresses - should be enhanced with proper Linera address verification
Access Control : Admin operations (allocateReward) should verify caller permissions
Input Validation : Metadata URIs should follow IPFS standards
Reward Claims : ClaimRewards needs caller authentication implementation
Enhanced Queries : Add sticker lookup and player data queries
Events : Emit events for minting, scoring, rewards
Cross-Chain : Enable sticker trading across chains
Collections : Group stickers into sets with completion bonuses
Marketplace : Add sticker trading between players
Achievements : Track player milestones and badges