Cross-Chain Contract Calls
A smart contract on one chain (BTC/LTC/DOGE) asynchronously invokes a method on
a contract deployed on another chain and receives the outcome via a callback.
Built from the platform’s existing primitives: the emission pipeline
(same-chain emit.execute), the ATTEST request/callback lifecycle, the
federation’s confirmation-gated PBFT, the quorum-signed hub-DB mirror with
deterministic injection (the cross-chain DEX settlement transport), and ANCHOR
recoverability.
Architecture
Chain X (source) Hub federation Chain Y (target)
──────────────── ────────────── ────────────────
contract calls polls getpendingcrosschaincalls;
xchain.emit.crossExecute ──► waits CONF[X] confirmations;
→ XCALL v0 action row every peer re-verifies the
(derived from the user's request against its OWN X
tx — no extra on-chain tx) indexer, then signs (2f+1)
│
▼
cross_chain_calls row ──► indexers verify sigs vs the
(phase='dispatch'), cross_chain capability snapshot,
mirrored to every indexer inject XEXEC at the first block
≥ effective_time (ordered by
(snapshot_block, call_id),
≤25/block): depth-0 EXECUTE,
gasCeiling = gas_limit,
crossCallable allowlist
┌─────────────────────────────┘
▼
X indexers verify sigs, ◄── polls getcrosschaincallresult;
inject the callback waits CONF[Y] depth; peers
EXECUTE into the re-verify the outcome against
requesting contract their OWN Y indexer, sign the
phase='result' row, mirror it
Zero per-call on-chain transactions. The only chain footprint is the user’s original transaction on X (the XCALL request is an emitted action row derived from it) plus the periodic ANCHOR archive on DOGE, amortized across many calls.
Trust model
The 2f+1 cross_chain capability quorum is the authority that tells chain Y
“this call happened on X” — Y cannot read X’s chain. This is the same trust
that releases cross-chain DEX escrow, but with a larger potential blast radius
(invoking contract methods vs releasing escrowed funds). It is bounded by:
crossCallableopt-in — a contract must export acrossCallablearray naming the methods reachable cross-chain. A forged dispatch can only reach methods the target consciously exposed.- Params-only v1 — no token value rides the call.
- Local signature verification everywhere — no indexer ever acts on a mirror row without verifying its 2f+1 Ed25519 signatures against the mirrored, BTC-anchored capability snapshot. Mirror equivocation degrades to censorship, which the deadline bounds.
- Independent peer re-verification — a hub follower only co-signs a dispatch/result after re-fetching it from its OWN indexer for that chain; a Byzantine leader cannot collect a quorum for a call no chain made.
Liveness vs safety: a dead or censoring federation can only delay or expire
calls (the expired callback is derived from block height alone, hub-free) —
it cannot forge them.
Finality and irreversibility
The federation relays a request only after it is buried CONF[source] deep
(BTC 6 / LTC 12 / DOGE 60, the cross-chain swap thresholds), and relays a
result only after the injected execution is CONF[target] deep. A
target-chain execution cannot be retracted from the source chain. A source
reorg deeper than the confirmation gate after the target executed is outside
the security model — the same posture cross-chain DEX settlement takes on a
confirmed give-side. (Defense-in-depth retraction exists for the sub-depth
window: relay rows are marked retracted and broadcast as mirror deletions, and
indexers that have not yet injected skip them.)
Latency
Inherent, not incidental: CONF[X] + hub round + mirror grace + Y block + execution + CONF[Y] + hub round + mirror grace + X block — minutes to tens of
minutes depending on the chain pair. Contracts must be designed fully async:
emit the call, return, and handle the outcome in the callback.
Determinism rules (consensus-critical)
- Every indexer applies relay rows at the same block: the
cross_chain_callsmirror has its own sync barrier (waitForCallSync, with the stream-watermark quiet-table escape) plus snapshot-presence gating, mirroring the match barriers. - Injection order is
(snapshot_block, call_id)— quorum-agreed row content, identical in every hub DB, so the order does not depend on which hub an indexer mirrors (the per-hub AUTO_INCREMENTidis provenance only, though ANCHOR still archives it); the per-block cap carries overflow forward — never drops. - Result delivery and deadline expiry share an exactly-once interlock on the request’s status; both are block-height-driven.
- The injected execution’s synthetic TX_HASH is chain/network-namespaced
(
sha256('XCALL:'+network+':'+chain+':'+call_id)) so anything it emits derives collision-free identifiers. - Reorg: target-side injections and source-side callbacks are anchored to
rollback-able action rows; the request’s terminal flip is reset by the
rollback pass via
resolved_block, so replays re-deliver identically.
Wire/spec details
Formats, canonical signing strings, statuses, gas buckets, and the lifecycle
state machine: actions/XCALL.md. Constants:
constants.js. Developer-facing API:
developer-guide/Smart_Contract_Development.md (§ Calling contracts on other
chains).
Deliberately out of scope (v1)
- Gas refunds for unused target-side gas (would require trusting/settling hub-reported usage).
- Token transfer riding the call (compose with cross-chain DEX settlement).
- Synchronous cross-chain reads or return values (callback pattern only).
- Calls from DEPLOY constructors.