Gear Builtin Actors
Goal
Treat builtin actors as a distinct call target: runtime-provided pseudo-programs at hardcoded ActorIds, reached via the normal msg::send_for_reply family but with request and reply types defined in the gbuiltin-* helper crates, not in a Sails IDL.
Inputs
../../references/gear-builtin-actors.md— actor catalog, ActorId derivation, per-actor request/response shapes, calling pattern../../references/gear-messaging-and-replies.md— reply semantics forsend_for_reply../../references/gear-gas-reservations-and-waitlist.md— gas budgeting and waitlist interaction when awaiting builtin replies../../references/gear-gstd-api-and-syscalls.md— the underlyinggstd::msgAPI family
Route Here When
- a feature needs staking, nominating, proxy management, BLS12-381 pairings, or ETH bridging from inside a Sails program
- a
send_for_replytargets a hardcoded non-programActorIdand the reply does not decode as a Sails route - a reply bytes stream is expected to decode as a
gbuiltin_*::Responserather than a Sails-routed payload - a builder is unsure whether to call a builtin directly from a service handler or wrap it behind a dedicated broker service
- a gtest or local-smoke run fails at the builtin call site with gas or ED errors
- a runtime upgrade changes the set of registered builtins and existing hardcoded
ActorIdconstants need revalidation
Working Model
- Confirm the actor is registered in the target runtime. For Vara, the authoritative tuple is
BuiltinActorsingear/vara/runtime/vara/src/lib.rs(IDs 1 = BLS12-381, 2 = staking, 3 = ETH bridge, 4 = proxy). IDs are runtime-version-bound. - Pick the correct
gbuiltin-*helper crate:gbuiltin-staking,gbuiltin-proxy,gbuiltin-bls381,gbuiltin-eth-bridge. Import itsRequest(andResponsewhere defined). - Derive the
ActorIdfrom the builtin ID viahash((b"built/in", id).encode()), or take the stable hex from the reference. Store as aconst ActorIdin the module that owns the call. - Call from an async handler:
msg::send_bytes_for_reply(ACTOR_ID, &payload[..], value, reply_deposit)?.await. Value and reply deposit follow Substrate ED rules for the builtin's underlying pallet. - Classify the reply: success reply decodes as
gbuiltin_*::Response(where one is defined) or is empty for fire-and-forget variants; an error reply surfaces as the.awaitErr arm and carries aBuiltinActorErrorpayload. - Budget gas against the builtin's declared
max_gas()weight; block allowance failures manifest as error replies, not panics. - For first-time interactions with a new builtin address, confirm ED is minted (runtime migrations handle this for shipped builtins — only relevant on fresh chains or new actors).
- When a service wraps a builtin (broker pattern), keep the wrapping service responsible for idempotency, reply routing, and state reconciliation — the builtin itself has no memory, no
read_state, and no code.
Guardrails
- Do not hardcode
ActorIdliterals without citing the runtime file or usinghash((b"built/in", id).encode())— IDs are runtime-version-bound and change with registration changes. - Do not expect Sails Header framing on builtin replies. Decode with
Response::decode(&mut &bytes[..])from the matchinggbuiltin-*crate, not with a generated Sails client. - Do not call builtins from a sync handler that cannot await; use
#[gstd::async_main]or an async service method. - Do not treat builtin calls as free. Gas is charged against the sender's block allowance, and ED rules apply to any
valuetransferred. - Do not confuse builtin IDs with actor IDs in broker examples; the ID is a
u64registration key, theActorIdis its hashed account. - If the task is really about adding a new builtin actor to the runtime itself, stop — that is runtime-maintenance work inside the gear repo, not Sails application work covered by this pack.
- If the target chain is Vara.eth or ethexe, stop — the builtin-actor set differs and routes through a different pack.