[V136] feat: add BNB Smart Chain (BSC)

Summary

Part 4 of the V136 chain stack (00 to 06), tracked in #325. This MR targets v136/02-bifrost-evm-hardening. Review only the commits on this branch; the parent branches have their own MRs.

This branch adds BNB Smart Chain: chain BSC, gas asset BSC.BNB, router MAYAChain_Router 4.1, and a BSC client on the generic EVM client. BSC is the first chain behind the chain-activation gate from branch 00. Every BSC path stays inert until the network version reaches 1.136.0.

It also creates the shared V136 address functions (NewAddressV136, IsChainV136, getChainV136, getNetworkV136), which branches 04 to 06 extend.

Changes

Consensus ([V136])

  • 6b630ab74 common:
    • BSCChain and BNBBEP20Asset (BSC.BNB); the legacy Beacon BNBChain / BNB.BNB are untouched.
    • Every BSC case in an unversioned predicate is reached only when BSCChain.IsActivated(): GetEVMChains (so IsEVM, AddressPrefix), ChainsThatCanBundleMultipleBlocks, GetGasAsset, GetGasUnits (centigwei, 1e11), DustThreshold (10_000 = 0.0001 BNB), ApproximateBlockMilliseconds (450), InboundNotes.
    • PubKey.GetAddress returns NoAddress for an inactive chain, before the cache.
    • V136 address set with 1.136.0 dispatch. BSC needs no new parsing rule, so three functions delegate to the V135 bodies; getChainV136 adds BSC after ETH. NewAddressV130, IsChainV130, getChainV129, getNetworkV130 move unchanged to address_archive.go.
    • migrateLegacy1e8TransactionFeeRate (v127 migrations only) skips inactive chains, so a replay with BSC in AllChains is identical.
  • ff6a4fe88 Token list: empty below 1.136.0. From 1.136.0: Binance-Peg USDT 0x55d398326f99059fF775485246999027B3197955 and USDC 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d, both 18 decimals (same as THORNode's list).
  • 29f882e30 SUPPORT_CHAINS_VCURRENT gains BSC (gated at 1.136.0 by 00). getRouterChains adds BSC at 1.136.0. BSC routers ship empty on every network; mainnet/stagenet are filled in a later release that gates the address on the network version, and the first router is then set with MimirUpgradeContractBSC=1, as for ARB. MaxBSCGas = 0.5 BNB.
  • a3b389663 handler_tss handleV136: a router chain without a chain contract stays out of a new vault. Without it, a keygen that reached consensus on BSC before the router was set would create a vault that ignores router deposits and cannot sign BSC outbounds. handleV135 moves unchanged to handler_tss_archive.go.
  • 40628aa48 migrateStoreV136 removes the stale mainnet BSC.BNB pool: Staged, zero balances, 20000000 pending CACAO (0.002 CACAO) from one LP. It acts only on that exact shape, refunds the pending CACAO from Asgard, deletes the records and emits the events, in one cache context. Mainnet, stagenet and mocknet run it; regtest only with mimir V136TestStalePool.
  • 333c1338b getChainV136 lists BSC only once it is activated, so an unrecognised address never maps to BSC on a 1.135 network.

Bifrost ([Version-unspecific])

  • bc973cde2 BSC client on the generic EVM client:
    • Finality: finalized tag (BEP-126) with 3 confirmations as fallback.
    • Chain id 56 on mainnet/stagenet, 1397 on mocknet.
    • Receipts via eth_getBlockReceipts.
    • Non-vault native transfers below dust are dropped (THORNode parity; only BSC has a non-zero EVM threshold).
    • Config: bsc block (disabled: true, reorg rescan 600 blocks, gas resolution 5 centigwei, USDT/USDC whitelist), env binds, metrics label.
    • BSC is removed from haltchain_known_thorchain_chains.
    • A coinless observation is split on its own chain, not the legacy BNBChain.
  • 31eede9fc BSC confirmations are a built-in rule (finalized, 3), so branch 02's pinning stops an operator override on mainnet/stagenet. THORNode hardcodes 3.

Mocknet, simulation, docs, tests

  • eba2153be init_chain restored IFS to the literal "IFS", so later expansions split on those letters. BSC was the first chain name to hit it (bug since 2022).
  • a6a56ab55 Mocknet binance-smart service: ethereum/client-go:v1.16.3 pinned by digest, dev genesis with chain id 1397. Router 4.1 is deployed at genesis.
  • 32b85ac92 BSC in the static simulation suite.
  • b6d0cea4b docs/chains/bsc.md (parameters, activation, launch runbook, user notes), docs/mimir.md (MAXGAS-<CHAIN>, first-router rule), docs/newchain.md.
  • 21d04142f BSC mainnet header hash fixture for the reorg check. 8b5aaf2ce THORChain halts of BSC no longer propagate.
  • a9ae3c5dd The bsc config block resets username / password, so BSC does not inherit the BTC daemon credentials (see Review follow-up).
  • bc7523a34 Regression suites bsc/bsc-swaps, bsc/bsc-activation, bsc/bsc-stale-pool, and regenerated exports.

Versioning / consensus impact

  • [V136] commits change state-machine behaviour from 1.136.0: chain predicates, address functions, token list, support and router chains, handler_tss, router upgrade (upgradeContractV136), store migration 136.
  • Archived unchanged: NewAddressV130, IsChainV130, getChainV129, getNetworkV130 (common/address_archive.go); handleV135 (x/mayachain/handler_tss_archive.go); upgradeContractV109 (x/mayachain/router_upgrade_controller_archive.go). No #check-lint-warning.
  • Below 1.136.0, every BSC predicate answers as for an unknown chain, and BSC observations fail ValidateBasic (code 6).
  • Export changes all come from the genesis single-node vault, which is built from SUPPORT_CHAINS_VCURRENT and now lists BSC. Affected: the vault chain list, the resulting block hashes, and gas_used +15 to +360 on txs touching that vault. Exports were regenerated with EXPORT=1, not hand-edited.

THORNode references and parity

  • Parity audit of 02 + 03 against THORNode cd6c065108. The bifrost findings are fixed on branch 02 (A1 0fced2f7b4, A2 cc8097283b, A3 89c6fc0f64, A5 5294ac17fc, A6 1df4d86490, D1-D3). The BSC confirmations rule is fixed here.
  • BSC confirmations: THOR 4a8c22818 hardcodes 3. MAYA waits for the finalized tag and votes 3.
  • Dust filter matches THOR 6a474b90c5.
  • Separate MRs on develop: signer-cache expiry (!880 (merged), THOR a759cb4f99; BSC is covered through evm/) and RPC auth (!881 (merged), THOR 337d8f9317).
  • Intentional divergences:
    • router 4.1: no direct-ERC20 inbound, no memoless outbound;
    • centigwei gas units, resolution 5;
    • cancel txs observed as ETH.ETH, the same as ARB;
    • a two-token whitelist, no aggregators, no short codes.
  • MAYA-only:
    • handler_tss router guard;
    • stale-pool migration;
    • activation gate;
    • a unique mocknet chain id;
    • BSC in the simulation (THOR excludes it).

Tests

  • Unit tests:
    • activation-rule table: every predicate for BSC at 1.135.0 with enforcement on, and with the version unset, equals the answer for an unknown chain;
    • V136 address functions and the GetAddress guard;
    • MsgObservedTxIn/Out fail with code 6 at 1.135.0 and pass at 1.136.0;
    • token list, support and router chains, handler_tss guard;
    • migrateStoreV136: refund, idempotent rerun, untouched other shapes, Asgard shortfall;
    • BSC client: dust, receipts method, finality, chain id, header hash.
  • make test, lint (gofumpt, versions, handlers, managers, analyze, erc20s) pass.
  • Regression, full EXPORT=1 run on this branch: 144/144, 0 failures, exports unchanged from the committed ones.
  • Mocknet: full static simulation passes (214 actors, 46 min), including the BSC actors. Run on 2026-10-17. It ran before the THOR-parity fixes; the regression run above includes them. The run found the IFS bug and a --networkid flag geth rejects with --dev; both are fixed here.

Deferred / open items

  • Keygen chain-set preflight and churn-out (THOR 25989b1f7e, 161ba2adaf): consensus, planned for V137. Meanwhile, the runbook checks that BSC is enabled on every Ready node before the first BSC churn.
  • "Missing tx out" security-event exemption for BSC failed-outbound placeholders: consensus event, V137. Until then each reverted BSC outbound emits a security event (pre-existing for ARB).
  • Scanner prefetch for 0.45 s blocks (THOR bd66c5254, f6abd5730): measure scan lag on a mainnet node first.
  • Mainnet/stagenet bscNewRouter after deployment, gated on the network version of that release. node-launcher and Midgard changes.
  • Quote confirmations for BSC (querier_quotes.go is ETH-only), hardhat fee-on-transfer tests, smoke bsc.py.
  • When !881 (merged) lands: the bsc config section needs username: "" and password: "", like the other EVM chains.

Operator notes

Launch runbook in docs/chains/bsc.md, in order:

  1. Before the release is published, set HaltBSCChain=1 and HaltBSCTrading=1 and verify them on /mayachain/mimir (both are unset on mainnet and stagenet today).
  2. The network reaches 1.136.0; verify both halts are still set and clear the stale HALTBSCCHAINAGG.
  3. Deploy and verify the router, ship bscNewRouter in a release that gates it on the network version, then set MimirUpgradeContractBSC=1.
  4. Set MAXGAS-BSC=1000000.
  5. Operators set BSC_DISABLED=false (chain id 56).
  6. Churn, unhalt, seed the pool, unhalt trading.

migrateStoreV136 refunds the mainnet stale-pool LP 0.002 CACAO automatically at the upgrade.

CI follow-up

  • 0f00294a1 drops the duplicate H1 from docs/chains/bsc.md (markdownlint MD025, the front matter has the title) and applies prettier.

Review follow-up

  • a9ae3c5dd resets username / password in the bsc config block. The default-chain anchor carries the BTC daemon credentials, and with the EVM RPC auth from !881 (merged) an EVM chain that keeps them sends them as basic auth. The !881 (merged) config test (EVM chains from common.GetEVMChains) covers BSC once both branches are merged.
  • The docs/newchain.md activation intro suggestion is in !883 (4a628e537).

Review follow-up (round 4)

  • 0effed2b3: upgradeContractV136: from 1.136.0 a MimirUpgradeContract<CHAIN> for a router chain with no new router in the binary is deleted instead of staying set until a later release fills in the address (BSC everywhere, AVAX on mainnet/stagenet). upgradeContractV109 moves unchanged to the archive. The bscNewRouter comment and the docs require the address release to be version-gated.
  • 8220b85f3: runbook: set and verify HaltBSCChain=1 / HaltBSCTrading=1 before the release and before BSC is enabled (they are unset on mainnet and stagenet). The later step only verifies them and clears HALTBSCCHAINAGG. HaltBSCChain stops observation but not the first BSC churn.
  • a5b2dee6a: BSC reorg recovery and restart tests on the shipped BSC scanner config (eth_getBlockReceipts, block-hash check, 600-block rescan). The reorg and version-lookup fixes they exercise are on !885 and !883.

Follow-up after review round 4

  • 572b65740: upgradeContractV136 also deletes MimirUpgradeContract<CHAIN> when the new router is already the chain's router (nothing left to do) and when the old router is not the chain's router (this release cannot upgrade it; set the mimir again after a release with the right old router). Before, the mimir stayed set and the check logged every block. Below 1.136.0 nothing changes. Test: TestRouterUpgradeStuckMimir (1.135.0 keeps the mimir, 1.136.0 deletes it, in both cases).
Edited by Bitol Maya

Merge request reports

Loading
Loading