Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions template/runtime/RUNTIME_VERSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,30 @@ to `spec_version` / `transaction_version` must add a row here in the same PR.
The genesis reset (`69d1b837`) set `spec_version` back to 1 and
`transaction_version` to 1 for the public testnet launch.

### Next spec — pending
### spec 20 — tx 7 — [Unreleased]

Not yet versioned: needs a `spec_version` bump (transaction version unchanged)
before `setCode`.
EVM precompiles: the Balances precompile only runs as a direct call and meters
by weight, and every Orbinum precompile address holds code, so Solidity
high-level calls reach them. `transaction_version` is unchanged. Ships with
`pallet-evm-precompile-balances` 0.2.0.

- **Balances precompile (`0x…0802`)** refuses static calls, delegated calls
(DELEGATECALL, CALLCODE) and calls carrying value. Under DELEGATECALL the
debited `context.caller` was the user who called the delegating contract, so
any contract could move its caller's ORB. Calls are charged the transfer's
dispatch weight up front, failing ones included, instead of a flat 5,000 gas,
and every failure reverts with an `Error(string)` reason.

### spec 19 — tx 7 — [Unreleased]
- **Precompile code.** `0x0400`–`0x0403`, `0x0801` and `0x0802` hold the stub
`0x60006000fd` (`PUSH1 0 PUSH1 0 REVERT`), which never runs: with no code at
those addresses, a Solidity high-level call to a function returning nothing
reverted at its code-size check before reaching the precompile. Genesis sets
it; `migrations::PrecompileCode` sets it on a live chain (idempotent).
- **Migrations.** `Migrations` holds only `PrecompileCode`. The spec 19
migrations (`RelayerFeesToPool`, `RemoveRelayerPallet`, `MoveStrandedEvmTips`)
ran on testnet with spec 19 and are removed, with their tests, so no later
upgrade runs them again.

### spec 19 — tx 7 — 2026-10-13 (`v0.5.0`)

A validator is its account and session keys, nothing else: the EVM relayer layer
is removed. Private spends are submitted only unsigned, and every spend's fee and
Expand Down
13 changes: 13 additions & 0 deletions template/runtime/src/genesis_config_preset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ pub(super) fn build_genesis(
code: vec![0x00],
},
);
for address in
crate::precompiles::FrontierPrecompiles::<crate::Runtime>::addresses_with_code()
{
map.insert(
address,
fp_evm::GenesisAccount {
nonce: U256::zero(),
balance: U256::zero(),
storage: Default::default(),
code: crate::precompiles::PRECOMPILE_CODE.to_vec(),
},
);
}
map
};

Expand Down
8 changes: 2 additions & 6 deletions template/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,7 @@ pub type SignedPayload = generic::SignedPayload<RuntimeCall, SignedExtra>;

/// Runs on upgrade, oldest first. Drop an entry once every live chain has passed its
/// version: a migration that can no longer run could be re-armed by mistake.
pub type Migrations = (
migrations::RelayerFeesToPool,
migrations::RemoveRelayerPallet,
migrations::MoveStrandedEvmTips,
);
pub type Migrations = (migrations::PrecompileCode,);

pub type Executive = frame_executive::Executive<
Runtime,
Expand Down Expand Up @@ -162,7 +158,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: Cow::Borrowed("orbinum"),
impl_name: Cow::Borrowed("orbinum"),
authoring_version: 1,
spec_version: 19,
spec_version: 20,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 7,
Expand Down
233 changes: 0 additions & 233 deletions template/runtime/src/migrations.rs

This file was deleted.

12 changes: 12 additions & 0 deletions template/runtime/src/migrations/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! Runtime-level storage migrations, run by `Executive` on upgrade (`Migrations`).

mod precompile_code;

pub use precompile_code::PrecompileCode;

use crate::Runtime;
use frame_support::weights::Weight;

fn db_weight(reads: u64, writes: u64) -> Weight {
<Runtime as frame_system::Config>::DbWeight::get().reads_writes(reads, writes)
}
47 changes: 47 additions & 0 deletions template/runtime/src/migrations/precompile_code.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! Code at the Orbinum precompile addresses, for a chain created before genesis
//! set it.

use super::db_weight;
use crate::Runtime;
use frame_support::{traits::OnRuntimeUpgrade, weights::Weight};

#[cfg(feature = "try-runtime")]
use alloc::vec::Vec;

/// Stores [`PRECOMPILE_CODE`](crate::precompiles::PRECOMPILE_CODE) at every
/// precompile that should carry it and has none, as genesis does for a new
/// chain. Idempotent: an address that already holds code is left alone.
pub struct PrecompileCode;

impl OnRuntimeUpgrade for PrecompileCode {
fn on_runtime_upgrade() -> Weight {
let mut reads = 0u64;
let mut written = 0u64;
for address in crate::precompiles::FrontierPrecompiles::<Runtime>::addresses_with_code() {
reads += 1;
if pallet_evm::AccountCodes::<Runtime>::contains_key(address) {
continue;
}
// Only an inner-create origin check can fail, and none is passed.
let _ = pallet_evm::Pallet::<Runtime>::create_account(
address,
crate::precompiles::PRECOMPILE_CODE.to_vec(),
None,
);
written += 1;
}
log::info!(target: "runtime::migrations", "stored code at {written} precompile address(es)");
// Per write: the account's providers read and write, the code and its metadata.
db_weight(reads.saturating_add(written), written.saturating_mul(3))
}

#[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
frame_support::ensure!(
crate::precompiles::FrontierPrecompiles::<Runtime>::addresses_with_code()
.all(|a| !pallet_evm::AccountCodes::<Runtime>::get(a).is_empty()),
"a precompile address has no code"
);
Ok(())
}
}
Loading
Loading