Skip to main content
SDK boundarypush / deploy / install are CLI-only at v0.13.0 of gen-rpc-client. Deployment is a one-time setup step; Rust apps run it via the gen CLI (or a build script that shells out), then consume the deployed contract from Rust via client.get_component(...) and the patterns in the next lesson.
In this lesson you’ll deploy simple-token, the two-component token contract built in the Contract Development workshop. You don’t have to write it from scratch here — you’ll consume the compiled artifacts the workshop produces.

What you should already have

This lesson is standalone, but the smoothest path through it assumes the previous lessons left you with: The four setup blocks below restore each of those independently. Paste only the ones you need.

The CLI binary

export GEN=gen
"$GEN" --version
You should see gen 0.14.0 or later. If command not found, install per Environment setup.

The RPC endpoint

Make sure a local validator is running (gen service validator local --embedded-config in another terminal), then:
export SERVER_URL="http://127.0.0.1:30001"

The wallet

If you completed lessons 1–2, resolve alice’s account:
export ALICE_NAME=alice
export ALICE=$("$GEN" wallet --rpc-url "$SERVER_URL" --json show "$ALICE_NAME" | jq -r '.result.account')
echo "$ALICE"
If $ALICE is empty or null, you don’t have a wallet by that name yet. Create and fund one:
"$GEN" wallet --rpc-url "$SERVER_URL" --json create "$ALICE_NAME" --plaintext
export ALICE=$("$GEN" wallet --rpc-url "$SERVER_URL" --json show "$ALICE_NAME" | jq -r '.result.account')
"$GEN" wallet --rpc-url "$SERVER_URL" --json faucet --wallet "$ALICE_NAME" --amount 1000000000

The contract artifacts

The Contract Development workshop’s gen genie build simple-token/simple-token produces five files under the contract’s artifacts/ folder:
simple_token_modules.json
simple_token_0_root.o
simple_token_0_root.abi.json
simple_token_1_holder.o
simple_token_1_holder.abi.json
Point $ARTIFACTS at that folder. Inside the devcontainer:
export ARTIFACTS=/workspaces/my-contracts-repo/my-contracts-repo/simple-token/simple-token/artifacts
On a native install with the workshop cloned under your home directory:
export ARTIFACTS="$HOME/my-contracts-repo/simple-token/simple-token/artifacts"
ls "$ARTIFACTS"
The listing should show the five files above. If it doesn’t, rebuild from inside the workshop checkout:
gen genie build simple-token/simple-token

The steps to a live contract

Deploying on the Grid is two activations for a single-module contract, three or more for multi-module contracts like this one:
  1. Push — upload the compiled code to a contract_code_id on-chain.
  2. Deploy — instantiate it under your account, producing a contract ID and an auto-installed root component you can call immediately.
  3. Install — wire each additional non-root module (here, holder) into its own callable component, on whatever account you want to own it.
The push/deploy split exists for a reason: pushed code is reusable. Multiple parties can deploy independent instances of the same pushed contract code — that’s the basis of every multi-tenant primitive on the Grid.

Steps

1

Push the contract code

Upload the compiled artifacts:
export PUSH_JSON=$("$GEN" client --rpc-url "$SERVER_URL" --json push --wallet "$ALICE_NAME" --account "$ALICE" --file "$ARTIFACTS/simple_token_modules.json")
Extract the contract code ID:
export CONTRACT_CODE_ID=$(jq -r '.result.contract_code_id' <<<"$PUSH_JSON")
echo "Pushed: $CONTRACT_CODE_ID"
The pushed bytes live on-chain, addressed by contract_code_id (a single bech32m string of the form grd@...). This is storage you are paying for, per byte, per month, in stablecoin cents. For simple-token’s compiled size, that’s well under a cent per month — the economic feasibility of which is the whole reason this design lives on-chain at all.
Receipt · 1 activation + 1 storage allocation · 0.001¢ USDG one-time + <0.01¢/month storage · ~10 ms
  • Failed to read manifest file: No such file or directory$ARTIFACTS doesn’t point at a real folder in this shell. ls "$ARTIFACTS" should list the five files above; if it doesn’t, re-export $ARTIFACTS.
  • ElfParseError: missing symbol: _data_end_value — the artifacts were built with an older toolchain than the validator accepts. Rebuild: gen genie build simple-token/simple-token from the workshop checkout, then retry.
2

Deploy an instance

Instantiate the pushed code:
export DEPLOY_JSON=$("$GEN" client --rpc-url "$SERVER_URL" --json deploy --wallet "$ALICE_NAME" --contract-code-id "$CONTRACT_CODE_ID" --account "$ALICE" --deploy-params '{}')
Extract the contract ID and root component ID:
export CONTRACT=$(jq -r '.result.contract' <<<"$DEPLOY_JSON")
export ROOT_COMPONENT_ID=$(jq -r '.result.root_component_id' <<<"$DEPLOY_JSON")
echo "Deployed: $CONTRACT"
echo "Root component: $ROOT_COMPONENT_ID"
Deploy gives you a new contract instance owned by alice’s account, with its root module auto-installed. simple-token’s deploy() takes no parameters, hence '{}'. For contracts whose deploy() takes arguments, you’d pass them as JSON here.$ROOT_COMPONENT_ID is a 4-part, comma-separated bech32m string, not a single identifier. That’s the form lesson 6’s view and activation calls take as --component-id.
Receipt · 1 activation · 0.001¢ USDG · ~10 ms
  • Invalid bech32m or wrong payload length$CONTRACT_CODE_ID is null or empty, usually because the push step in the same shell failed. Print echo "$PUSH_JSON" | jq . and fix the push error first.
3

Install the holder component

simple-token has a holder module at component_type_index: 1. Each user who wants a wallet under this token installs their own holder:
export HOLDER_JSON=$("$GEN" client --rpc-url "$SERVER_URL" --json install --wallet "$ALICE_NAME" --contract "$CONTRACT" --account "$ALICE" --component-type-index 1)
export HOLDER_COMPONENT_ID=$(jq -r '.result.component_id' <<<"$HOLDER_JSON")
echo "Alice's holder: $HOLDER_COMPONENT_ID"
Each install does three things:
  • (a) Instantiates a fresh component of that module’s code with its own component_id, distinct from root and from any other holder. Reads and writes against $HOLDER_COMPONENT_ID are isolated to alice — they don’t touch root’s state or anyone else’s holder.
  • (b) Attaches that component to --account. Install is per-account configuration: bob’s holder will get its own component id by running the same install against $BOB.
  • (c) Costs exactly one activation (~0.001¢ USDG) per install. Adding components is opt-in and metered, not bundled into deploy — so multi-module contracts have a deterministic, per-module install cost rather than a single “deploy everything” charge.
Receipt · 1 activation · 0.001¢ USDG · ~10 ms

What just happened

You went from a folder of compiled artifacts to a callable, on-chain token contract for a total of three activations and three thousandths of a cent, plus a fraction of a cent per month for storage. This is the economic claim that makes “full on-chain” tractable on the Grid. The same contract on Ethereum mainnet would cost tens of dollars to deploy and storage would push you off-chain almost immediately. Here it lives on-chain in full, and anyone can verify its state without trusting your server.

The split: push vs deploy vs install

StepProducesReusable?
pushcontract_code_id — the compiled bytesYes. Anyone can deploy this code.
deploycontract ID + auto-installed root componentPer-instance.
installadditional non-root component_ids on a deployed contractPer-instance, per-account, per-module.
This is why a single simple-token push can have many independent deployments (one per ecosystem, per app, per stablecoin), and why a multi-module contract lets each consumer pick which modules to install. Pushing once, deploying many, and installing only the modules you need is the canonical pattern.

What’s next

Call your contract

Read with view. Write with the build-and-sign pattern from the previous chapter.