> For the complete documentation index, see [llms.txt](https://docs.gravity.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gravity.xyz/research-and-development/native-oracle.md).

# Native Oracle

Cross-chain data has always been the soft spot of L1 design. Whether it's an asset bridge, a price feed, or any other piece of off-chain truth, the data has to enter the chain *somewhere* — and where that "somewhere" lives determines who you have to trust.

Most chains push that responsibility outside their consensus. An external oracle network signs the data, or a separate committee of bridge signers attests to deposits. The chain itself stays clean, but a new trust assumption is bolted on the side. If that side-channel breaks, the chain keeps running — but the data is wrong.

Gravity L1 collapses this. The same validators that produce blocks under AptosBFT also observe external data, vote on it, and write it to L1. There is no external oracle network. No multisig committee. The bridge isn't a separate service; it's a contract that receives data **already committed by the validator set**.

This is what "native" means in **Native Oracle**: the validator-attestation pipeline is part of the chain's state machine, not a service running next to it. The security of any data that lands on Native Oracle is the security of the chain itself — same validator set, same BFT thresholds, same finality window.

This post walks through the primitive, the one application built on it today (the Ethereum → Gravity L1 asset bridge), and the design space it opens up.

***

## The Core Abstraction

Native Oracle is a single contract deployed at a fixed system address on Gravity L1:

```
NativeOracle  →  0x0000000000000000000000000001625F4000
```

It exposes one core operation, callable only by `SYSTEM_CALLER` — a privileged consensus-time identity, *not* a normal account:

```solidity
function record(
    uint32  sourceType,        // 0 = BLOCKCHAIN; more reserved
    uint256 sourceId,          // e.g. chain ID
    uint128 nonce,             // must equal currentNonce + 1
    uint256 blockNumber,       // source-side provenance
    bytes   payload,           // opaque blob
    uint256 callbackGasLimit
) external;
```

A `recordBatch(...)` variant exists for delivering multiple events from the same source in one transaction.

Three design choices are worth pulling out:

**1. Replay protection is centralized.** Native Oracle enforces `nonce == currentNonce + 1` per `(sourceType, sourceId)` — strictly sequential, no gaps. An old message can never be replayed because the contract has already moved past its nonce. Application-level handlers don't need to maintain their own seen-nonce maps; in fact, the bridge receiver explicitly drops that work and leaves only a `__deprecated_processedNonces` storage slot for layout compatibility.

**2. Callbacks are routed, not pulled.** Each `(sourceType, sourceId)` pair can register a callback contract. When data is recorded, Native Oracle calls `onOracleEvent(...)` on the registered handler with a caller-specified gas limit. Resolution is two-layer: a default handler per source type, optionally overridden by a specialized handler for a specific `sourceId`. Governance manages the registry.

**3. Callbacks are fail-safe.** The handler returns `shouldStore: bool` — a handler that has fully consumed the payload (applied it to its own state) can return `false` to skip persistence and save gas. If the handler reverts or runs out of gas, Native Oracle catches it, emits `CallbackFailed(reason)`, and stores the payload anyway. The recording succeeds regardless. The data is committed by consensus; the application logic is best-effort.

The result is a clean separation: **Native Oracle is responsible for&#x20;*****truth*** (consensus-attested, replay-safe), **and the application contract is responsible for&#x20;*****meaning*** (decoding and acting on the payload).

***

## The Consensus Layer

Each Gravity validator runs an oracle observer alongside its block-production duties. For every registered task, the observer:

1. Polls the source endpoint (today, an Ethereum RPC) for the relevant events.
2. Restricts itself to **finalized** state — only events past Ethereum finality are eligible, eliminating reorg surprises.
3. Tracks a cursor (last scanned block) and a last-processed nonce, persisted to disk for fast restart.
4. Emits its observed batch into the consensus layer.

These observations then flow through the *same* AptosBFT machinery that orders normal transactions. Validators sign their observations; a quorum certificate forms; the agreed batch is committed in-line with regular block production. **There is no separate consensus instance, no separate signer set, no separate liveness assumption.**

When the batch is committed, the system call `NativeOracle.recordBatch(...)` is constructed and dispatched as a SYSTEM\_CALLER transaction, landing the data on L1 in the same block-execution flow as everything else.

This is what gives the bridge its security guarantee: **a deposit is finalized on Gravity when ⅔+ of validators have attested to its Ethereum finalization**, enforced by the same BFT thresholds the network uses to produce blocks. There is no separate bridge to compromise.

***

## End-to-End Today: ETH → Gravity Asset Bridge

The first application wired end-to-end through Native Oracle is the G token bridge: lock G on Ethereum, receive native G on Gravity. Here's the full path.

![Native Oracle at launch: Ethereum → Gravity L1 asset bridge via validator attestation](/files/V1uipnnxGHHXRzA9f5GH)

### Step 1 — Ethereum side

The user calls `GBridgeSender.bridgeToGravity(amount, recipient)` (or `bridgeToGravityWithPermit(...)` for gasless approval). The sender:

* Escrows `amount` G tokens (ERC-20) in itself.
* Calls `GravityPortal.send(abi.encode(amount, recipient))` with an ETH fee.

`GravityPortal.send` uses a compact 36-byte header format:

```
payload = sender (20 bytes) || nonce (16 bytes) || message
```

This trims the typical 128+ bytes of `abi.encode` overhead down to 36, charged as:

```
fee = baseFee + (36 + message.length) * feePerByte   // wei, paid in ETH
```

The portal rejects both under- and over-payment (`InsufficientFee` / `ExcessiveFee`) to keep fees deterministic. It then assigns a monotonic `uint128` nonce and emits:

```solidity
event MessageSent(
    uint128 indexed nonce,
    uint256 indexed block_number,
    bytes payload
);
```

### Step 2 — Validator observation & attestation

Every Gravity validator's observer polls Ethereum for `MessageSent` events, filtered on the GravityPortal address, between its cursor and the latest finalized block. New events with `nonce > last_processed_nonce` are accepted and emitted into AptosBFT.

The BFT layer reaches agreement on the observed batch in the same finality window as normal transactions. There's no delayed settlement — the validators that order blocks also order observations.

### Step 3 — On-chain commit

The agreed batch becomes a `SYSTEM_CALLER` transaction calling:

```solidity
nativeOracle.recordBatch(
    /* sourceType        */ 0,                  // BLOCKCHAIN
    /* sourceId          */ 1,                  // Ethereum mainnet
    /* nonces            */ [...],              // strictly sequential
    /* blockNumbers      */ [...],              // provenance from MessageSent
    /* payloads          */ [...],              // raw PortalMessage bytes
    /* callbackGasLimits */ [...]
);
```

Native Oracle enforces `nonces[i] == currentNonce + 1` for each entry, reverting `NonceNotSequential(...)` on any gap or duplicate. For each accepted record, it resolves the callback — `(sourceType=0, sourceId=1) → GBridgeReceiver` — and invokes it.

### Step 4 — Callback & native mint

`GBridgeReceiver` extends `BlockchainEventHandler` and implements `IOracleCallback`. On invocation it:

1. Decodes the PortalMessage: `(sender, messageNonce, message) = decode(payload)`.
2. Verifies `sourceId == trustedSourceId` (Ethereum mainnet) — rejects with `InvalidSourceChain(...)` otherwise.
3. Verifies `sender == trustedBridge` (the deployed `GBridgeSender`) — defense in depth.
4. Decodes the application message: `(amount, recipient) = abi.decode(message, (uint256, address))`.
5. Calls the native mint precompile:

```
NativeMintPrecompile  →  0x0000000000000000000000000001625F5000
```

The precompile mints native G tokens directly to the recipient — **no wrapper, no synthetic asset.** The user holds the L1's native gas asset, just as if it had been minted at genesis.

Total trip from `bridgeToGravity` call to native G in the recipient's address: one Ethereum-finality window plus one Gravity finality window. No external relayer, no wrapped representation, no separate signer set.

***

## Reliability & Recovery

A few mechanics that matter for operators and integrators:

* **Finalized-only polling.** Observers only ingest events past Ethereum finality. Reorgs on the source chain cannot land on Native Oracle — by definition, they cannot land on a finalized block.
* **Persisted cursors.** Each observer writes `(cursor_block, last_processed_nonce)` to disk per source. After a crash or restart, the observer resumes from its checkpoint instead of rescanning from genesis.
* **On-chain reconciliation.** On startup, the observer reads `NativeOracle.getLatestNonce(sourceType, sourceId)`. If the on-chain nonce is ahead of the local checkpoint (e.g., the validator was offline while peers committed), the observer fast-forwards both cursor and last-processed state. No duplicate delivery, no missed events.
* **Exactly-once delivery, structurally.** Combining (a) sequential nonces in the contract, (b) strictly monotonic filtering in the observer, and (c) finalized-only polling: each `MessageSent` event maps to at most one `record(...)` call across the whole network. There is no per-handler dedup logic to get wrong.
* **Fail-safe callback semantics.** A bug in the callback contract — revert, out-of-gas, wrong logic — does not block the oracle. The record is committed, `CallbackFailed(reason)` is emitted, and operators can patch the handler (via governance-managed callback registration) without losing data.

***

## Live Contract Map

| Chain      | Contract               | Address                                           |
| ---------- | ---------------------- | ------------------------------------------------- |
| Ethereum   | `GravityPortal`        | `0x76cf8526Fa9461e50B2c6702a7246ce6915f6E53`      |
| Ethereum   | `GBridgeSender`        | `0xE82c61Ac9Ec2041b493118051afa4F18a55dC876`      |
| Ethereum   | `G` (ERC-20)           | `0x9C7BEBa8F6eF6643aBd725e45a4E8387eF260649`      |
| Gravity L1 | `NativeOracle`         | `0x0000000000000000000000000001625F4000` (system) |
| Gravity L1 | `NativeMintPrecompile` | `0x0000000000000000000000000001625F5000` (system) |

Source addresses for the system precompiles are fixed; ABIs are available in the [gravity\_chain\_core\_contracts](https://github.com/Galxe/gravity_chain_core_contracts) repository.

***

## Beyond the Bridge: What the Primitive Enables

![Native Oracle as a primitive: asset bridge, perp DEX oracle, payments / x402, on-chain AI agent, and generic messaging — five application lanes around one consensus-attested core](/files/9K9NeGFYH3FN9szCGSlR)

Native Oracle isn't *the* bridge — the bridge is the first application built on Native Oracle. The primitive itself is just `(sourceType, sourceId, nonce, payload, callback)`; everything above that line is policy. The substrate that today routes Ethereum deposits to a mint precompile can route any externally attested event to any callback governance approves. The primitive doesn't change; only the tuples registered on top of it do.

To make that concrete, here are application shapes the primitive supports **by design**, not by deployment. Each is a different `(sourceType, sourceId, callback)` registration:

**Asset bridges (today).** Validators observe `MessageSent` events on Ethereum; the callback decodes `(amount, recipient)` and mints native G. More chains under `sourceType = 0` are a governance registration, not a code change. Bidirectional flow — burn on Gravity, release on Ethereum — is the symmetric callback on the same primitive.

**Perp DEX oracles.** Mark price, index price, and funding-rate updates require fresh, consensus-attested data — exactly what most perp DEXes today pay an external oracle network for. A registered price-feed source, observed by validators and routed to the perp protocol's callback, removes that external dependency. Liquidations execute against prices the chain itself agreed to, not prices a side network signed.

**Payments and x402.** The bridge is one shape of payment; payments in general are another. A payer authorizes on chain A, validators attest it on Gravity, a callback credits the payee — same primitive, different decoder. HTTP-402-style pay-per-call services can use this to verify "the client paid X on chain Y" inside the same finality window that serves the request: no off-chain receipt, no trusted indexer.

**On-chain AI agents.** Agents acting on-chain need verifiable inputs they can react to — prices, balances on other chains, the result of off-chain computation they didn't perform themselves. Native Oracle is the substrate they read from. An agent does not have to trust an oracle network the chain itself doesn't trust; whatever the chain sees, the agent sees, in the same finality window.

**Arbitrary application messaging.** The PortalMessage format already carries an opaque `message` field. Fungible-token transfer is the first decoder; arbitrary cross-chain calls are a small step from there.

The connective thread: every one of these uses the same `(sourceType, sourceId, nonce, payload, callback)` shape, the same AptosBFT machinery, the same fail-safe callback resolution. Adding a new application doesn't mean adding a new trust assumption — it means writing a callback contract and asking governance to register it. The security envelope doesn't expand; only what lives inside it does.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.gravity.xyz/research-and-development/native-oracle.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
