SDK boundary —
push / 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.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
genCLI on yourPATH— set up in Environment setup. - A reachable RPC endpoint — either a local validator on
127.0.0.1:30001(from lesson 1) or DevNet credentials (from lesson 3). - A funded wallet named
alice— created in lesson 1, funded by faucet in lesson 2. - Built
simple-tokenartifacts — produced bygen genie build simple-token/simple-tokenafter Contract Development · Adding Functionality.
The CLI binary
gen 0.14.0 or later. If command not found, install per
Environment setup.
The RPC endpoint
- Local
- DevNet
Make sure a local validator is running (
gen service validator local --embedded-config in another terminal), then:The wallet
If you completed lessons 1–2, resolvealice’s account:
$ALICE is empty or null, you don’t have a wallet by that name yet.
Create and fund one:
The contract artifacts
The Contract Development workshop’sgen genie build simple-token/simple-token
produces five files under the contract’s artifacts/ folder:
$ARTIFACTS at that folder. Inside the devcontainer:
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:- Push — upload the compiled code to a
contract_code_idon-chain. - Deploy — instantiate it under your account, producing a
contractID and an auto-installedrootcomponent you can call immediately. - Install — wire each additional non-root module (here,
holder) into its own callable component, on whatever account you want to own it.
Steps
Push the contract code
Upload the compiled artifacts:Extract the 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 msTroubleshooting
Troubleshooting
Failed to read manifest file: No such file or directory—$ARTIFACTSdoesn’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-tokenfrom the workshop checkout, then retry.
Deploy an instance
Instantiate the pushed code:Extract the contract ID and 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 msTroubleshooting
Troubleshooting
Invalid bech32m or wrong payload length—$CONTRACT_CODE_IDisnullor empty, usually because the push step in the same shell failed. Printecho "$PUSH_JSON" | jq .and fix the push error first.
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:- (a) Instantiates a fresh component of that module’s code with its
own
component_id, distinct fromrootand from any other holder. Reads and writes against$HOLDER_COMPONENT_IDare 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 sameinstallagainst$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 msWhat 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
| Step | Produces | Reusable? |
|---|---|---|
push | contract_code_id — the compiled bytes | Yes. Anyone can deploy this code. |
deploy | contract ID + auto-installed root component | Per-instance. |
install | additional non-root component_ids on a deployed contract | Per-instance, per-account, per-module. |
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.
