[ONGOING] OP-PIP-128: Pyth Core Sunset — Fee Zeroing & Balance Repatriation

Abstract

Upgrade Pyth Core contracts to v1.4.6 on chains running older versions, set protocol fees to zero across all EVM chains, and repatriate accumulated fee balances to Pythian Council EVM multisigs ahead of the Pythnet sunset (per OP-PIP-100).

This proposal executes up to three governance actions per chain:

  1. UpgradeContract → v1.4.6 (action 0) — Where needed, upgrade to enable WithdrawFee
  2. SetFee → 0 (action 3) — Eliminate protocol fees
  3. WithdrawFee → PC multisig (action 9) — Transfer accumulated balances (where multisig coverage exists)

Rationale

Per the Pyth Core to Pyth Pro Migration (OP-PIP-100), Pythnet is sunsetting by end of August 2026. As part of this transition:

  1. Contract upgrades are required on 29 chains running pre-April 2025 versions that lack the WithdrawFee governance action (commit 63bbd2d)
  2. Fee zeroing ensures no additional protocol fees accrue during the wind-down period
  3. Balance repatriation moves accumulated DAO revenue to PC custody per the Cross-Chain Fee Repatriation Framework (OP-PIP-125)

The proposal covers ~84 EVM mainnet chains where Pyth Core is deployed.

Description

Part 1: UpgradeContract → v1.4.6 (29 Chains)

Execute GovernanceAction.UpgradeContract to v1.4.6 on chains running contract versions predating April 2025. This adds the WithdrawFee capability required for Part 3.

New implementation contracts (v1.4.6) will be deployed on each of the 29 chains before the proposal is created. Each UpgradeContract payload contains only the 20-byte address of that chain’s new implementation, so verifying this proposal means verifying that the bytecode at those addresses matches this repository.

Chains Requiring Upgrade:

| Chain           | Chain ID             |
| --------------- | -------------------- |
| Abstract        | abstract             |
| ApeChain        | apechain_mainnet     |
| Arbitrum        | arbitrum             |
| Avalanche       | avalanche            |
| Base            | base                 |
| Berachain       | berachain_mainnet    |
| Blast           | blast                |
| BSC             | bsc                  |
| Celo            | celo                 |
| Core Blockchain | coredao              |
| Cronos          | cronos               |
| Ethereum        | ethereum             |
| Flow EVM        | flow_mainnet         |
| Gnosis          | gnosis               |
| Hemi            | hemi_mainnet         |
| HyperEVM        | hyperevm             |
| Ink             | kraken_ink_mainnet   |
| Linea           | linea                |
| Mantle          | mantle               |
| OP Mainnet      | optimism             |
| opBNB           | opbnb                |
| Polygon         | polygon              |
| Scroll          | scroll               |
| Sei             | sei_evm_mainnet      |
| Soneium         | soneium              |
| Sonic           | fantom_sonic_mainnet |
| Unichain        | unichain             |
| World Chain     | worldchain           |
| zkSync          | zksync               |

Part 2: SetFee → 0 (All Chains)

Execute GovernanceAction.SetFee with newFee = 0 on all 84 mainnet chains via Wormhole executor governance.

Part 3: WithdrawFee → PC Multisig (35 Chains)

Execute GovernanceAction.WithdrawFee on chains where the Pythian Council has deployed Safe multisigs per OP-PIP-125. Withdrawal amounts will be specified per-chain based on accrued balances at proposal creation time.

Chains with WithdrawFee (34):

Chain            | PC Multisig
-----------------|--------------------------------------------
0G               | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Abstract         | 0x146a7a66c008E15E543b78aa3DeB39eaecD8fBcd
ApeChain         | 0xaC400398aD744836a30Fb833Ea6A1b2Cb21505D9
Arbitrum         | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Avalanche        | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Base             | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Berachain        | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Blast            | 0x36e9f482712ADDcddE83b3326116D46bc578e918
BNB Chain        | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Celo             | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
CoreDAO          | 0x146a7a66c008E15E543b78aa3DeB39eaecD8fBcd
Cronos           | 0xaC400398aD744836a30Fb833Ea6A1b2Cb21505D9
Ethereum         | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Gnosis Chain     | 0x36e9f482712ADDcddE83b3326116D46bc578e918
Hemi             | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
HyperEVM         | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Injective EVM    | 0x0694D676980856658e727341D7c9Ef87b490ae7D
Ink              | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Linea            | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Mantle           | 0x36e9f482712ADDcddE83b3326116D46bc578e918
MegaETH          | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Mezo             | 0x85Ab7030e8130775eA2C5F70CafBc8e8c55f2666
Monad            | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
opBNB            | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Optimism         | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Plasma           | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Polygon          | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Scroll           | 0x36e9f482712ADDcddE83b3326116D46bc578e918
Sei EVM          | 0x36e9f482712ADDcddE83b3326116D46bc578e918
Soneium          | 0x36e9f482712ADDcddE83b3326116D46bc578e918
Sonic            | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Unichain         | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
Worldchain       | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935
zkSync           | 0xD879B2c9e70CA8e4bED04223D95f45F87F33B935

Execution Order: Instructions are ordered within the proposal so that, for each chain, UpgradeContract and SetFee execute before WithdrawFee. The Pyth contract enforces strictly increasing Wormhole sequence numbers for governance instructions, so on every chain the upgrade is guaranteed to land before the withdrawal that depends on it.

Implementation

Governance payloads encode amounts as a uint64 value and uint64 expo pair, where the contract computes amount = value × 10^expo. A SetFee payload is value || expo (this proposal uses 0 || 0), and a WithdrawFee payload is targetAddress (20 bytes) || value || expo. See parseSetFeePayload and parseWithdrawFeePayload in PythGovernanceInstructions.sol.

The proposal executes via Wormhole governance using the existing instruction types:

// PythGovernanceInstructions.sol
enum GovernanceAction {
    UpgradeContract,    // 0
    // ...
    SetFee,             // 3
    // ...
    WithdrawFee         // 9
}

struct SetFeePayload {
    uint newFee;            // Set to 0
}

struct WithdrawFeePayload {
    address targetAddress;  // PC multisig
    uint256 fee;         // Per-chain amount (see table above)
}

Proposal ID: Pyth Network

Verification

To verify this proposal:

  1. Clone the pyth-crosschain repository:
git clone <https://github.com/pyth-network/pyth-crosschain.git>
cd pyth-crosschain
pnpm install && pnpm turbo build --filter @pythnetwork/contract-manager
  1. Review the fee configuration file (to be generated) containing all chain IDs, Pyth contract addresses, and multisig targets.
  2. Verify the proposal payload:
cd contract_manager
pnpm tsx scripts/check_proposal.ts --cluster mainnet-beta --proposal <proposal_id>

Verify the upgrade implementations: check_proposal.ts prints the code digest of the implementation contract each UpgradeContract instruction points at. Compare it against the digest built from source:

cd target_chains/ethereum/contracts
forge build
cat out/PythUpgradable.sol/PythUpgradable.json | jq -r .deployedBytecode.object | tr -d '\r\n' | cast keccak

The digests must match for all standard EVM chains.

Note: zkSync and Abstract are ZK-stack chains compiled with zksolc, so their implementation bytecode intentionally differs from the forge build.

  1. Cross-reference:
    1. Contract addresses against Pyth Core EVM chainlist
    2. Multisig addresses against Pythian Council deployment documentation or verify multisig addresses on-chain: each listed multisig must be deployed Safe on its chain with the Pythian Council’s 5-of-8 configuration:
cast code <multisig> --rpc-url <$RPC>          # must return Safe proxy bytecode, not 0x
cast call <multisig> "getThreshold()(uint256)" # must return 5
cast call <multisig> "getOwners()(address[])"  # must match the PC signer set on Ethereum

References