Skip to main content
This guide walks through building Lotteria, an onchain lottery machine, as a working example of how to build a full-stack appchain on Initia. It covers writing Move modules, connecting a frontend with InterwovenKit, and deploying to your rollup. The full source code is available in the Lotteria reference repository. This is a demo repository built as a reference for hackathon participants. If you get stuck on any step, from writing Move modules to wiring up your frontend, use the reference implementation as a working example to compare against.

Prerequisites

Before you begin, make sure you have completed the Set Up Your Appchain setup, including:
  • A running appchain via weave init
  • initiad and minitiad installed and in your PATH. This should already be installed when you set up your appchain
  • OPinit Executor and IBC Relayer running connected to your appchain
  • A funded Weave Gas Station account imported into your local keyring

Project Structure

1. Move Modules

Move is the smart contract language for the MoveVM track on Initia. In Lotteria, the Move module handles all the core lottery logic: creating rounds, buying tickets, drawing winners, and distributing prizes.

Setting Up the Move Package

Create a move/ directory in your project root with the following structure:
The lottery module depends on a helper module lottery_random for onchain randomness (winner selection). Both source files are required for compilation. Copy both from the reference repo.
Your Move.toml defines the package metadata, dependencies, and the address your module will be published under. You need to convert your Initia address to its hex representation:
Then configure Move.toml:
move/Move.toml
The InitiaStdlib dependency gives you access to core modules like coin, signer, event, and object, which are essential building blocks for any onchain application.

Writing the Lottery Module

The lottery module manages the full lifecycle of a lottery round. Here is a breakdown of the key components. Module declaration and imports:
Data structures: Define the resources that will live onchain. A lottery needs to track the current round state, ticket purchases, and prize pool:
The has key ability means this struct can be stored as a top-level resource in global storage, which is how Move manages persistent onchain state. Core entry functions: Entry functions are the public interface that users and frontends call via transactions:
  • initialize: Creates the initial lottery config and first draw
  • buy_ticket: Validates six picked numbers and transfers the ticket price
  • execute_draw: Generates winning numbers for a draw
  • finalize_draw: Calculates prize amounts per winner tier
  • get_ticket_price: A view function that returns the configured ticket price without modifying state
Replace the Reference Metadata Address: The Metadata object address in lottery.move is specific to the token used by the reference deployment and will not exist on your local rollup. Before building, query the metadata object for the token you want to use for ticket payments:
Replace every hardcoded object::address_to_object<Metadata>(@0x...) occurrence in lottery.move with the returned metadata address.
View functions: View functions let the frontend query onchain state without submitting a transaction:

Building and Deploying the Module

Build the module to check for compilation errors:
Deploy to your running appchain:
Use minitiad (not initiad) here because you are deploying to your Layer 2 rollup, not the Initia L1.
The fee denom (umin above) depends on the fee token you configured during weave init. To confirm the correct denom for your rollup, run:
and use the denom shown for your rollup’s native token.
To verify the deployment, query a view function:
Required Empty Args: Even for view functions that take no arguments, --args '[]' is required. Omitting it will cause a CLI parse error.
To execute a function, for example to initialize the module state after deployment:

2. Using InterwovenKit

InterwovenKit is a React library that provides components and hooks for connecting dApps to Initia and Interwoven Rollups. It handles wallet connections, transaction signing, and chain interactions out of the box.

Installation

Install the InterwovenKit React package:
If you are starting from scratch, scaffold a new React project with Vite:

Setting Up the Provider

Wrap your application with InterwovenKitProvider. This is the top-level context that makes wallet state and transaction methods available throughout your component tree. In your main.tsx or App.tsx:

Connecting a Wallet

The useInterwovenKit hook provides everything you need for wallet connection. Here is a simplified wallet connection component based on the wallet controls in Lotteria’s Header.tsx:
When no wallet is connected, the component renders a “Connect” button that calls openConnect to launch the wallet connection drawer. Once connected, it displays the user’s Initia username (if they have one) or a truncated version of their address. Clicking the button calls openWallet to open the wallet detail panel.

Depositing Assets

Users need tokens on your appchain to interact with it. InterwovenKit provides a built-in deposit flow that handles cross-chain bridging. Here is the openDeposit pattern used by Lotteria:
The openDeposit method opens a drawer that lets users bridge tokens from Initia L1 (or other connected chains) to your appchain. The denoms array specifies which token denominations are accepted. The chainId should match your appchain’s chain ID.
If you want to support deposits from Initia L1, include uinit in the allowed denoms. Initia L1 uses initiation-2 on testnet and interwoven-1 on mainnet.
The component only renders when a wallet is connected (address is truthy), since depositing requires an active wallet session.

Sending Transactions

To interact with your Move module from the frontend, use requestTxSync to construct and send transactions:
The requestTxSync method prompts the user’s wallet to sign and broadcast the transaction. It returns the transaction hash on success.

Querying Onchain State

To query and read data using the view functions of your Move module, you can utilize the chain’s REST endpoint as follows:
You can wrap this in a React hook with polling or use a library like TanStack Query for automatic refetching.

3. Connecting to the Chain

Chain Configuration

Your appchain runs locally after weave init. The default endpoints are: These endpoints are what your frontend uses to query state and broadcast transactions.

Bridging Assets via IBC

Once the IBC Relayer is running (see Set Up Your Appchain), you can bridge INIT tokens from the L1 testnet to your appchain. This is how users fund their accounts on your rollup. The bridge flow works through IBC transfer channels that the relayer establishes between Initia L1 and your rollup. Users can send tokens from L1 to your chain, and the tokens arrive as IBC-denominated assets.

Frontend Development Workflow

A practical development loop looks like this:
  1. Write or update your Move module in move/sources/
  2. Build and deploy with minitiad move deploy
  3. Test the module via CLI with minitiad tx move execute and minitiad query move view
  4. Build your frontend components using InterwovenKit hooks
  5. Run the frontend dev server and test end-to-end

Summary

Building an appchain on Initia with the Move track involves three layers:
  1. Move modules define your onchain logic. Write your structs and entry functions, build with initiad move build, and deploy with minitiad move deploy.
  2. InterwovenKit connects your React frontend to the chain. Wrap your app in InterwovenKitProvider, use useInterwovenKit for wallet connection and requestTxSync for transactions.
  3. Chain connectivity is handled by the Interwoven Stack. Your local rollup exposes RPC and REST endpoints, and the IBC Relayer enables cross-chain token transfers.
For the full working example, check out the Lotteria repository. Happy building.