Seismic Book
  • Welcome
  • introduction
    • Why
    • What
    • How
  • onboarding
    • Installation
    • Quickstart
    • Tutorial
      • Setting Up Your Walnut App Project
        • Verify devtool installation
        • Create project structure and monorepo workspace
        • Initialize the contracts subdirectory
        • Initialize the CLI subdirectory
      • Writing, testing and deploying the contract
        • Chapter 1: Making the Kernel
        • Chapter 2: Making the Shell and revealing the Kernel
        • Chapter 3: Reset Mechanism, Rounds, and a more conditional Kernel Reveal
        • Chapter 4: Testing your Walnut contract
        • Deploying your contract
      • Interacting with the contract via a CLI
        • Quick primer: seismic-viem
        • Chapter 1: Defining the constants and utilities
        • Chapter 2: Writing the core app
        • Chapter 3: Bringing it all together
      • Understanding the Walnut contract
  • core
    • Basics
      • suint / sint
      • saddress
      • sbool
    • Collections
    • Clients
  • Appendix
    • Devnet
Powered by GitBook

Contact us

  • Telegram
  • Twitter
  • Email
On this page
  • Set Up Environment Variables
  • Write index.ts
  • Execute the main() function
  • Running the CLI
Export as PDF
  1. onboarding
  2. Tutorial
  3. Interacting with the contract via a CLI

Chapter 3: Bringing it all together

Now that we’ve built the core logic for interacting with the Walnut contract, it’s time to tie everything together into a CLI that runs a multiplayer game session with two players - Alice and Bob. In this chapter, you’ll set up the environment variables for multiple players and write index.ts to simulate gameplay. Estimated time: ~20 minutes

Set Up Environment Variables

Before running the Walnut game, we need to define environment variables that store important configurations such as the RPC URL, chain ID, and player private keys.

Create a .env in packages/cli :

touch .env

Open .env and paste the following:

CHAIN_ID=31337
RPC_URL=http://127.0.0.1:8545
ALICE_PRIVKEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
BOB_PRIVKEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d

What’s Happening Here?

• CHAIN_ID=31337 : 31337is the default chain ID for sanvil(your local Seismic node).

• RPC_URL=http://127.0.0.1:8545 : This is the RPC URL for interacting with the local Seismic node.

• ALICE_PRIVKEYand BOB_PRIVKEY : These are Alice and Bob’s private keys, allowing them to play the game. (These again are standard test keys provided by sanvil)

Write index.ts

Now, we’ll create the main entry point for our game session. This file will simulate gameplay, initializing players and having them interact with the Walnut contract. Open packages/cli/src/index.tsand follow these steps:

Import Dependencies

Import the required libraries to read environment variables, define network configurations, and interact with the Walnut contract.

import dotenv from 'dotenv'
import { join } from 'path'
import { seismicDevnet } from 'seismic-viem'
import { anvil } from 'viem/chains'

import { CONTRACT_DIR, CONTRACT_NAME } from '../lib/constants'
import { readContractABI, readContractAddress } from '../lib/utils'
import { App } from './app'

// Load environment variables from .env file
dotenv.config()

Define the main() function

This function initializes the contract and player wallets, then runs the game session.

async function main() {
  if (!process.env.CHAIN_ID || !process.env.RPC_URL) {
    console.error('Please set your environment variables.')
    process.exit(1)
  }

Read Contract Details

The contract’s ABI and deployed address are read from files generated during deployment.

  const broadcastFile = join(
    CONTRACT_DIR,
    'broadcast',
    `${CONTRACT_NAME}.s.sol`,
    process.env.CHAIN_ID,
    'run-latest.json'
  )
  const abiFile = join(
    CONTRACT_DIR,
    'out',
    `${CONTRACT_NAME}.sol`,
    `${CONTRACT_NAME}.json`
  )

Select the blockchain network

Determine whether to use the local sanvilnode (31337) or the Seismic devnet.

  const chain =
    process.env.CHAIN_ID === anvil.id.toString() ? anvil : seismicDevnet

Define players

Assign Alice and Bob as players with private keys stored in .env .

  const players = [
    { name: 'Alice', privateKey: process.env.ALICE_PRIVKEY! },
    { name: 'Bob', privateKey: process.env.BOB_PRIVKEY! },
  ]

Initialize the Game App

Create an App instance to interact with the Walnut contract.

  const app = new App({
    players,
    wallet: {
      chain,
      rpcUrl: process.env.RPC_URL!,
    },
    contract: {
      abi: readContractABI(abiFile),
      address: readContractAddress(broadcastFile),
    },
  })

  await app.init()

Simulate the game round by round

The following logic executes two rounds of gameplay between Alice and Bob.

Round 1 - Alice Plays

  console.log('=== Round 1 ===')
  await app.reset('Alice')
  await app.shake('Alice', 2)
  await app.hit('Alice')
  await app.shake('Alice', 4)
  await app.hit('Alice')
  await app.shake('Alice', 1)
  await app.hit('Alice')
  await app.look('Alice')

Round 2 - Bob Plays

  console.log('=== Round 2 ===')
  await app.reset('Bob')
  await app.hit('Bob')
  await app.shake('Bob', 1)
  await app.hit('Bob')
  await app.shake('Bob', 1)
  await app.hit('Bob')

  // Bob looks at the number in round 2
  await app.look('Bob')

Alice Tries to Look in Round 2 (we expect this to fail since she has contributed in round 1 but not round 2)

  // Alice tries to look in round 2, should fail by reverting
  console.log('=== Testing Access Control ===')
  console.log("Attempting Alice's look() in Bob's round (should revert)")
  try {
    await app.look('Alice')
    console.error('❌ Expected look() to revert but it succeeded')
    process.exit(1)
  } catch (error) {
    console.log('✅ Received expected revert')
  }
}

Execute the main() function

This ensures that the script runs when executed.

main()

Running the CLI

Now, run the CLI from packages/cli by running:

bun dev

You should see something like this as the output:

=== Round 1 ===
- Player Alice writing shake()
- Player Alice writing hit()
- Player Alice writing shake()
- Player Alice writing hit()
- Player Alice writing shake()
- Player Alice writing hit()
- Player Alice reading look()
- Player Alice sees number: 7n
=== Round 2 ===
- Player Bob writing reset()
- Player Bob writing hit()
- Player Bob writing shake()
- Player Bob writing hit()
- Player Bob writing shake()
- Player Bob writing hit()
- Player Bob reading look()
- Player Bob sees number: 3n
=== Testing Access Control ===
- Attempting Alice's look() in Bob's round (should revert)
✅ Received expected revert

This output logs the events during two rounds of gameplay in the Walnut contract, showing interactions by Alice and Bob, along with a revert error when Alice attempts to call look()in Round 2.

PreviousChapter 2: Writing the core appNextUnderstanding the Walnut contract

Last updated 2 months ago

The entire index.ts file can be found

Congratulations! You've reached the end of the tutorial. You can find the code for the entire project .

here
here