Skip to main content
This guide picks up where Build and test a contract leaves off. You have a compiled contract in <contract>/artifacts/<contract>_modules.json and a funded DevNet wallet from the Quickstart. The full deployment lifecycle uses five gen client commands:
CommandWhat it does
pushUploads contract code, returns a contract_code_id
deployInstantiates the code, runs #[deploy], returns a contract address and a root_component_id
installInstalls a non-root component on the deployed instance
build-activation + sign-and-submit-activationCalls a state-changing method
viewCalls a read-only method

Prerequisites

Stash the active account in an env var; every command below uses it:
ACCOUNT=$(gen wallet --json show --account-address-only | jq -r .result.account)

Step 1: Push the contract code

gen client --json push \
  --file my-contract/artifacts/my_contract_modules.json \
  --account $ACCOUNT
Output:
{
  "ok": true,
  "result": {
    "activation_id": "0x...",
    "contract_code_id": "grd@..."
  }
}
Save the contract_code_id; you need it for deploy.

Step 2: Deploy an instance

gen client --json deploy \
  --contract-code-id <contract_code_id> \
  --account $ACCOUNT
If your contract’s #[deploy] takes parameters, pass them as JSON via --deploy-params:
# Example: a contract whose deploy takes max_increment: u64
gen client --json deploy \
  --contract-code-id <contract_code_id> \
  --account $ACCOUNT \
  --deploy-params '{"max_increment":10}'
For deploy parameters that take a GvmContract argument (composing with another deployed contract), see Composing with another contract below. The result includes two fields you need:
  • contract: the bech32m address of the deployed instance.
  • root_component_id: a 4-part, comma-separated string. This is the value you pass as --component-id to call methods. The single contract address is not enough for method calls; you need the 4-part form.

Step 3: Install components

If your contract defines non-root components (component_type_index ≥ 1), install each one explicitly:
gen client --json install \
  --contract <contract> \
  --account $ACCOUNT \
  --component-type-index 1
Skip this step for contracts that only have a root component.

Step 4: Call a state-changing method

Two steps: build the unsigned activation, then sign and submit it.
UNSIGNED=$(gen client --json build-activation \
  --component-id "<root_component_id>" \
  --method set_value \
  --params '{"new_value":42}' \
  | jq -r .result.unsigned_activation_hex)

gen client --json sign-and-submit-activation \
  --unsigned-activation "$UNSIGNED"
The result’s outcome_result is the method’s return value, Borsh-encoded as hex.

Step 5: Call a view method

gen client --json view \
  --component-id "<root_component_id>" \
  --method get_value
The result’s decoded field shows the return value as a string.

Composing with another contract

Some contracts’ #[deploy] takes a GvmContract argument pointing at a contract you’ve already deployed (for example, the example-contracts walkthrough does this in Steps 4, 6, 10, and 11). The CLI rejects bech32m strings or hex bytes for this parameter; it accepts only one specific nested-tuple JSON shape.

The JSON shape

{
  "ft_contract": {
    "deployer":        [[[ <32-byte entity_id as u8 int array> ]]],
    "component_index": [ <push_index as u32> ]
  }
}
The triple-nested tuple wraps the 32-byte entity_id (as an array of u8 integers, not a hex string), and a single-element tuple wraps the u32 push index.

Deriving the values from a bech32m address

gen client push and deploy return addresses in the form grd@1... that encode (32-byte entity_id, u32 push_index little-endian). Until gen client decode ships, you can extract them with this Python decoder:
def decode_gen_address(addr: str) -> tuple[list[int], int]:
    CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
    pos = addr.rfind("1")
    data = [CHARSET.index(c) for c in addr[pos + 1:]][:-6]  # strip checksum
    acc = bits = 0
    out: list[int] = []
    for v in data:
        acc = (acc << 5) | v
        bits += 5
        while bits >= 8:
            bits -= 8
            out.append((acc >> bits) & 0xff)
    entity = out[:32]
    push_index = int.from_bytes(bytes(out[32:36]), "little")
    return entity, push_index
Usage:
entity, push_index = decode_gen_address("grd@1y857zx7s8x2ekmk3h8psf4p9t8fuj5ha296e90y8967qplmf7yes2qqqqqmx454c")
import json
print(json.dumps({
    "ft_contract": {
        "deployer":        [[[entity]]],
        "component_index": [push_index],
    }
}))

Worked example

After deploying a fungible-token instance (Step 10 of the walkthrough composes with this):
# Push and deploy the FT
gen client --json push \
  --file contract-libs/genesis-framework/fungible-token/artifacts/fungible_token_modules.json \
  --account $ACCOUNT
# → contract_code_id: grd@<ft-code>

gen client --json deploy \
  --contract-code-id <ft-code> \
  --account $ACCOUNT \
  --deploy-params '{"name":"MyToken","symbol":"MTK","decimals":[6],"uris":{"icon_uri":null,"website_uri":null},"max_supply":null,"issuer":null}'
# → contract: grd@1y857...mf7yes2qqqqq...   (this is your FT's deployed address)

# Decode that address with the Python snippet above to get (entity, push_index),
# then deploy Step 10's treasury referencing it:
gen client --json deploy \
  --contract-code-id <step10-code> \
  --account $ACCOUNT \
  --deploy-params '{"ft_contract":{"deployer":[[[ENTITY_BYTES]]],"component_index":[PUSH_INDEX]}}'

Troubleshooting

  • error: unexpected argument '--rpc-url'. Some gen client subcommands take --rpc-url after client, others take it after the subcommand. If unsure, set the URL once with gen config set rpc-url and drop the flag.
  • Invalid GvmComponentId format: expected 2 or 4 bech32m parts. --component-id needs the full 4-part comma-separated root_component_id from deploy, not the single contract address.
  • Type mismatch for 'X': expected object, got string. --deploy-params is rejecting a typed SDK argument. See Composing with another contract for the JSON shape.

What’s next