Smart Contracts API

Since mainnet block 150 every contract transaction is signed by the player: the signer of a call is the caller the contract sees (host_get_caller) and pays the fee; the signer of a deployment is the deployer. The old node-signed endpoints are retired: POST /api/v2/contract/deploy returns 410 Gone and POST /api/v2/contract/call is a dry run only (and is not reachable on the public mainnet edge). Use the endpoints below, or rc.contracts in @rougechain/sdk 1.9.0+.

Signed requests use the standard /api/v2/* envelope { payload, signature, public_key }: signature is ML-DSA-65 over the JSON of payload with keys sorted, payload.from must equal public_key, and payload.timestamp (ms) must be within 5 minutes of the node clock.

Publish (Deploy) Contract

POST /api/v2/contract/publish

Payload fieldTypeRequiredDescription
typestring✅"contract_deploy"
fromstring✅Deployer's signing public key (hex)
wasmstring✅Base64-encoded WASM bytecode (max 1 MiB, must export memory)
noncestring✅Random string, at least 8 characters
timestampnumber✅Milliseconds since the epoch

Response:

{ "success": true, "txId": "…", "address": "<40 hex chars>", "fee": 10 }

Publish fee: 10 XRGE flat. The contract is installed when the tx is mined. Its address is fixed by the signed fields, so it is known before the block: hex(sha256("rougechain/contract/v2" ‖ from ‖ 0x00 ‖ nonce ‖ 0x00 ‖ sha256(wasm))[0..20]).

Execute (Call) Contract

A state-changing call, included in a block.

POST /api/v2/contract/execute

Payload fieldTypeRequiredDescription
typestring✅"contract_call"
fromstring✅Caller's signing public key (hex)
contractAddrstring✅Contract address (hex, lower case)
methodstring✅Method name
argsany JSON❌Arguments ({} when omitted)
gasLimitnumber✅Integer 1 – 10,000,000
attachobject❌From block 190: { "symbol": "XRGE" | "<TOKEN>", "amount": <positive integer> }. Pays the contract. See below
timestampnumber✅Milliseconds since the epoch
noncestring✅Random string

Response:

{
  "success": true,
  "txId": "…",
  "fee": 0.05,
  "preview": { "returnData": { }, "gasUsed": 31234, "events": [] }
}

Call fee: gasLimit × 0.000001 XRGE (the signed limit, charged up front). The node dry-runs the call first and refuses it ("call would fail: …", nothing charged) if it would fail or needs more gas than the limit. The authoritative run happens in the block: if the call reverts there (because state changed in between), it is still included and charged, and its receipt (GET /api/tx/:txId/receipt) reports "status": {"Failed": "<error>"} instead of "Success".

Payable calls: attach (from block 190)

"attach": { "symbol": "XRGE", "amount": 500000000 }
FieldTypeDescription
attach.symbolstring"XRGE" or a token symbol (1–32 letters, digits, _, -; upper-cased by the node)
attach.amountintegerPositive JSON integer: quanta for XRGE (1 XRGE = 1,000,000,000 quanta), raw units for a token. Decimals and strings are rejected
  • attach is part of the signed payload.
  • The payment moves to the contract only if the call succeeds. If the call fails or traps, the payment stays with the caller, but the gas fee is still charged.
  • The contract reads the payment with host_get_attached_amount() and host_get_attached_symbol(out, cap). It refuses a payment by failing the call.
  • The node refuses the request up front if the caller can't cover the gas fee plus an XRGE payment ("insufficient XRGE for the gas fee and the attached payment: …"), or the token amount ("insufficient <SYM> for the attached payment: …"), or if the dry run with the payment would fail.
  • Before block 190, the request fails with "payable contract calls (attach) are not active yet". A malformed attach returns "attach.amount must be a positive integer (quanta for XRGE, raw units for tokens)" or "attach.symbol must be a token symbol".

Query Contract (read-only)

A free dry run against the live state. Nothing is signed, charged or committed.

POST /api/contract/:addr/query

FieldTypeRequiredDescription
methodstring✅Method name
argsany JSON❌Arguments
callerstring❌Public key the contract sees as host_get_caller. Required with attach
attachobject❌Preview a paid call: same { symbol, amount } as execute (integer quanta for XRGE, raw token units). The contract sees the payment as it would in a block (from block 190; before that, the payment is ignored in the preview)

Response:

{ "success": true, "returnData": { }, "gasUsed": 812, "events": [], "error": null }

Get Contract Metadata

GET /api/contract/:addr

Returns contract metadata: address, deployer, code hash, creation timestamp, WASM size.

{
  "success": true,
  "contract": {
    "address": "86fe93e2...",
    "deployer": "test-deployer",
    "code_hash": "a1b2c3...",
    "wasm_size": 711,
    "created_at": 1774401569985
  }
}

Read Contract Storage

Full State Dump

GET /api/contract/:addr/state

Returns all key-value pairs in the contract's persistent storage.

{
  "success": true,
  "state": {
    "count": "42",
    "owner": "alice"
  },
  "count": 2
}

Single Key Lookup

GET /api/contract/:addr/state?key=<hex_key>

Reads a single key from storage.

{
  "success": true,
  "key": "636f756e74",
  "value": "3432",
  "valueUtf8": "42"
}

Get Contract Events

GET /api/contract/:addr/events?limit=50&before=<height>&tx=<txhash>

Returns indexed events emitted by the contract, newest first. limit is 1–1000 (default 50), before returns only events from blocks below that height (paging), and tx returns only the events of one transaction.

{
  "success": true,
  "events": [
    {
      "contract_addr": "86fe93e2...",
      "topic": "transfer",
      "data": "{\"from\":\"alice\",\"to\":\"bob\",\"amount\":100}",
      "block_height": 620,
      "tx_hash": "c3d4e5f6..."
    }
  ],
  "count": 1
}

Live Events (WebSocket)

Connect to wss://<node>/api/ws and send { "subscribe": ["contract:<addr>"] }. After each block is accepted you receive one frame per event:

{ "type": "contract_event", "contract_addr": "…", "topic": "move",
  "data": "…", "block_height": 1234, "tx_hash": "…" }

List All Contracts

GET /api/contracts

Returns all deployed contracts with their metadata.

{
  "success": true,
  "contracts": [
    {
      "address": "86fe93e2...",
      "deployer": "test-deployer",
      "code_hash": "a1b2c3...",
      "wasm_size": 711,
      "created_at": 1774401569985
    }
  ],
  "count": 1
}