Tic-Tac-Toe Demo

This repository demonstrates account abstraction on Sonic using both EIP-7702 and ERC-4337 standards through a fully functional on-chain tic-tac-toe game.

It showcases how Sonic is upgrading its infrastructure to support Ethereum's account abstraction features, enabling gasless transactions, smart account capabilities, and enhanced user experiences.

Quick Start

  1. Sonic Testnet Deploy your own contracts using Hardhat Ignition (chain ID 14601)

  2. Frontend Demo Requires a running bundler for UserOperation submission

  3. Bot Demo Interacts directly with deployed contracts

Table of Contents

Key Features

🎮 On-Chain Tic-Tac-Toe Game

  • Fully decentralized 5x5 tic-tac-toe game with 4-in-a-row win condition

  • Prize token rewards (10 tokens for winners, one token for draws)

  • Time-limited moves (24-hour timeout protection)

  • Upgradeable smart contract using UUPS proxy pattern

🔐 EIP-7702 Account Abstraction

  • Transform regular EOAs into smart accounts without changing addresses

  • Enable transaction batching and gas sponsorship for existing wallets

  • Demonstrate SET_CODE_TX_TYPE transaction format

📦 ERC-4337 UserOperations

  • Complete implementation of UserOperation flow

  • EntryPoint interaction for validation and execution

  • Support for both MetaMask integration and programmatic signing

  • Gas estimation and fee management

💰 Paymaster Infrastructure

  • TargetCheckingPaymaster: Sponsors gas for whitelisted contract interactions

  • SignatureCheckingPaymaster: Validates authorized transactions with signature verification

  • Gas-free gameplay for users

🤖 AI Game Bot

  • Automated tic-tac-toe player using min-max algorithm

  • Monitors blockchain events and responds to game moves

  • Configurable joining delay and strategy selection

  • Complete event subscription and game state management

🌐 Frontend Demo

  • Vue.js web interface for game interaction

  • Two authentication methods:

  • MetaMask integration (requires pre-configured delegation)

  • Private key input (sets up delegation automatically)

  • Real-time UserOperation construction and submission

Architecture Overview

Understanding Account Abstraction

EIP-7702: Protocol-Level Account Abstraction

EIP-7702 introduces a new transaction type that allows EOAs to delegate their execution logic to smart contracts:

  • No Address Change EOAs maintain their original addresses

  • Code Delegation EOA delegates code execution to a specified smart contract

  • Native Batching Execute multiple operations in a single transaction

  • Backward Compatible Works with existing infrastructure

  • Persistent Until Changed Delegation remains active until explicitly updated

Key Innovation: The SET_CODE_TX_TYPE transaction format with authorization lists enables EOAs to act as smart accounts without permanent migration.

ERC-4337: Smart Account Framework

ERC-4337 provides a complete account abstraction solution without protocol changes:

  • UserOperations Pseudo-transactions that encapsulate user intent

  • Bundlers Aggregate and submit UserOps to the blockchain

  • EntryPoint Singleton contract for validation and execution

  • Paymasters Services that sponsor gas fees

Key components:

  1. Sender ERC-4337 compatible smart contract wallet

  2. UserOperation Contains nonce, calldata, gas limits, and signatures

  3. Bundler Collects UserOps and submits them on-chain

  4. Paymaster Covers transaction fees on behalf of users

How They Work Together

EIP-7702 and ERC-4337 are complementary, not competing standards:

  1. EIP-7702 upgrades an EOA to execute smart contract code

  2. The upgraded EOA implements ERC-4337's IAccount interface

  3. The account can now receive UserOperations through EntryPoint

  4. Paymasters can sponsor gas for the upgraded account

  5. Users benefit from both simple EOA interactions AND smart account features

Project Components

Smart Contracts (/contracts)

  • TicTacToe.sol: Main game contract with UUPS upgradeability

  • PrizeToken.sol: ERC20 token for game rewards

  • Simple7702Account.sol: EIP-7702 compatible account implementation (in /contracts/testing)

  • TargetCheckingPaymaster.sol: Paymaster that sponsors specific contract calls

  • SignatureCheckingPaymaster.sol: Paymaster with signature validation

  • Note: EntryPoint contract is imported from @account-abstraction/contracts

Backend Services (/cmd)

senduserop

  • Demonstrates UserOperation creation and submission

  • EIP-7702 authorization signing

  • Gas estimation and fee calculation

  • Direct EntryPoint interaction

tictactoebot

  • Automated game player with an AI strategy

  • Event monitoring and subscription

  • Minimax algorithm implementation

  • Keystore management

Frontend (/frontenddemo)

  • Vue.js single-page application

  • UserOperation construction in the browser

  • MetaMask and private key support

  • Real-time game interaction

Testing (/test)

  • Comprehensive contract testing

  • Paymaster validation tests

  • Game logic verification

  • Gas usage optimization

Installation & Setup

Prerequisites

  • Node.js 18+ and npm

  • Go 1.24+ (yes, 1.24 is correct - see go.mod)

  • Git

  • A funded wallet for deploying contracts and running the bundler

Clone Repository

git clone <repository-url>
cd TicTacToeDemo

Install Dependencies

# Install Node.js dependencies
npm install

# Install Go dependencies
go mod download

# Install frontend dependencies
cd frontenddemo
npm install
cd ..

Environment Configuration

Create a .env file in the root directory:

# Required for deployment
PRIVATE_KEY=your_private_key_here_without_0x_prefix

Notes:

  • Private key should be without the '0x' prefix

  • Sonic RPC URL is already configured in hardhat.config.ts

Deployment

Compile Contracts

npx hardhat compile

Deploy Contracts

Deploy to Sonic testnet (chain ID 14601):

# Deploy complete TicTacToe system
npx hardhat ignition deploy ./ignition/modules/TicTacToe.ts --network sonic-testnet

# Deploy paymasters separately (if needed)
npx hardhat ignition deploy ./ignition/modules/TargetCheckingPaymaster.ts --network sonic-testnet
npx hardhat ignition deploy ./ignition/modules/SignatureCheckingPaymaster.ts --network sonic-testnet

Verify Contracts

# For Sonic testnet
npx hardhat ignition verify --network sonic-testnet

Running the Demo

1. Start the Bundler

The bundler aggregates UserOperations and submits them to the EntryPoint:

# Clone the bundler (if not already done)
git clone https://github.com/eth-infinitism/bundler
cd bundler


# Configure and run
yarn install
yarn run bundler --network https://rpc.soniclabs.com/ \
                 --mnemonic path/to/mnemonic.txt \
                 --entryPoint 0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108 \
                 --unsafe

The bundler RPC will be exposed at: http://localhost:3000/rpc

Notes:

  • Use --unsafe flag for RPCs that don't support debug_traceCall (most public RPCs)

  • The mnemonic account needs sufficient balance to send transactions (0.1 S recommended)

2. Start the TicTacToe Bot

The bot automatically plays games and demonstrates event monitoring:

# Build the bot
cd cmd/tictactoebot
go build


# Run with configuration
./tictactoebot \
  --rpcUrl https://rpc.soniclabs.com/ \
  --gameAddress 0xYourGameAddress \
  --keystore path/to/keystore.json \
  --joiningDelay 30s

Bot flags:

  • --gameAddress: TicTacToe contract address

  • --startBlock: Block number to start event scanning (default: 1)

  • --joiningDelay: Time to wait before joining new games (default: 1 minute)

  • --blocksPerQuery: Max block range for event queries (default: 10000)

  • --keystore: Path to keystore JSON file

  • --keystorePass: Path to keystore passphrase file (optional)

3. Launch Frontend

cd frontenddemo
npm run dev

Open http://localhost:5173 in your browser.

Testing

Run the comprehensive test suite:

# Run all tests
npx hardhat test

# Run with gas reporting
REPORT_GAS=true npx hardhat test

# Run with detailed traces
npx hardhat test --fulltrace

# Run specific test file
npx hardhat test test/TicTacToe.ts

Technical Deep Dive

UserOperation Structure

struct PackedUserOperation {
    address sender;               // Smart account address
    uint256 nonce;               // Anti-replay parameter
    bytes initCode;              // Account creation code (if needed)
    bytes callData;              // Execution calldata
    bytes32 accountGasLimits;    // Verification and call gas limits
    uint256 preVerificationGas;  // Gas for bundler overhead
    bytes32 gasFees;            // maxPriorityFee and maxFeePerGas
    bytes paymasterAndData;      // Paymaster address and data
    bytes signature;             // Account signature
}

EIP-7702 Authorization

auth := types.SetCodeAuthorization{
    ChainID: chainId,
    Address: accountImplementation,  // Smart account code
    Nonce:   accountNonce,
}
signedAuth := SignSetCode(privateKey, auth)

Paymaster Validation

The TargetCheckingPaymaster validates:

  1. Account code hash matches whitelist

  2. The target contract is approved

  3. Gas fees are within limits

Game Mechanics

  • Board Size: 5x5 grid

  • Win Condition: 4 stones in a row

  • Rewards: 10 tokens for the winner, one token each for the draw

  • Timeout: 24 hours per move

  • Storage: Packed uint256 for efficient gas usage

Developer Resources

Key Concepts

Account abstraction benefits:

  • 🔒 Enhanced security with smart account features

  • ⛽ Gas sponsorship for better UX

  • 📦 Transaction batching for efficiency

  • 🔑 Flexible authentication methods

  • 🔄 Social recovery and multi-sig support

Implementation patterns:

  1. Delegation Pattern EOA delegates to smart contract logic

  2. Validation Pattern Custom signature validation in accounts

  3. Sponsorship Pattern Paymasters cover gas costs

  4. Batching Pattern Multiple operations in a single transaction

API References

Smart account interface:

interface IAccount {
    function validateUserOp(
        PackedUserOperation calldata userOp,
        bytes32 userOpHash,
        uint256 missingAccountFunds
    ) external returns (uint256 validationData);
}

Paymaster interface:

interface IPaymaster {
    function validatePaymasterUserOp(
        PackedUserOperation calldata userOp,
        bytes32 userOpHash,
        uint256 maxCost
    ) external returns (bytes memory context, uint256 validationData);
}

Troubleshooting

Common issues and solutions:

  1. "AA25 invalid account nonce": Account nonce mismatch - check EntryPoint.getNonce()

  2. "AA33 reverted": Paymaster validation failed - check gas limits

  3. "AA51 prefund too low": Insufficient paymaster deposit

  4. "Not from self or EntryPoint": Invalid caller for account execution

Additional setup issues:

  1. Frontend bundler connection: Update bundler URL in Vue components if not using default

  2. Go version error: Ensure Go 1.24+ is installed (check with go version)

  3. Contract deployment fails: Ensure wallet has sufficient funds and correct network is selected

  4. Bot can't join games: Check keystore file permissions and passphrase

  5. Bundler errors: Ensure the mnemonic account has funds for gas

Further Reading

Last updated