From 300c30e4adf8791c773735277cfc00d1e7fbffa0 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xKitsune@protonmail.com> Date: Wed, 15 Jul 2026 19:57:47 -0400 Subject: [PATCH 1/6] feat: add zone-native fee manager --- crates/contracts/src/lib.rs | 2 +- crates/contracts/src/precompiles/mod.rs | 7 +- .../src/precompiles/zone_fee_manager.rs | 15 + crates/evm/src/executor.rs | 177 +-------- crates/evm/src/fee_manager.rs | 103 +++++ crates/evm/src/lib.rs | 24 +- crates/l1/src/state/provider.rs | 8 +- crates/node/assets/zone-dev-genesis.json | 12 +- crates/node/src/genesis.rs | 11 +- crates/precompiles/src/lib.rs | 3 + crates/precompiles/src/tempo_state.rs | 7 +- crates/precompiles/src/zone_fee_manager.rs | 373 ++++++++++++++++++ crates/primitives/src/constants.rs | 11 + docs/ZONES.md | 5 +- invariants.md | 5 +- .../src/interfaces/IZoneFeeManager.sol | 18 + specs/spec.md | 33 +- xtask/src/generate_zone_genesis.rs | 15 +- 18 files changed, 621 insertions(+), 208 deletions(-) create mode 100644 crates/contracts/src/precompiles/zone_fee_manager.rs create mode 100644 crates/evm/src/fee_manager.rs create mode 100644 crates/precompiles/src/zone_fee_manager.rs create mode 100644 specs/ref-impls/src/interfaces/IZoneFeeManager.sol diff --git a/crates/contracts/src/lib.rs b/crates/contracts/src/lib.rs index 4f70cb428..ae7714f9b 100644 --- a/crates/contracts/src/lib.rs +++ b/crates/contracts/src/lib.rs @@ -6,7 +6,7 @@ //! accepts batch proofs, and processes withdrawals back to L1 recipients. //! - **ZoneOutbox** — deployed on the Zone L2. Collects user withdrawal requests, builds //! withdrawal hash chains, and exposes `LastBatch` state for proof generation. -//! - **ZoneInbox**, **TempoState**, **ZoneTxContext** — Zone L2 predeploys. +//! - **ZoneInbox**, **TempoState**, **ZoneTxContext**, **ZoneFeeManager** — Zone L2 predeploys. //! - **ZoneFactory**, **SwapAndDepositRouter** — deployed on Tempo L1. #![cfg_attr(not(feature = "std"), no_std)] diff --git a/crates/contracts/src/precompiles/mod.rs b/crates/contracts/src/precompiles/mod.rs index bd20bb0a6..f11564c39 100644 --- a/crates/contracts/src/precompiles/mod.rs +++ b/crates/contracts/src/precompiles/mod.rs @@ -2,6 +2,7 @@ pub mod common; pub mod swap_and_deposit_router; pub mod tempo_state; pub mod zone_factory; +pub mod zone_fee_manager; pub mod zone_inbox; pub mod zone_outbox; pub mod zone_portal; @@ -11,6 +12,7 @@ pub use common::*; pub use swap_and_deposit_router::*; pub use tempo_state::*; pub use zone_factory::*; +pub use zone_fee_manager::*; pub use zone_inbox::*; pub use zone_outbox::*; pub use zone_portal::*; @@ -21,6 +23,7 @@ pub use zone_tx_context::*; // contracts crate, e.g. `tempo_zone_contracts::TEMPO_STATE_ADDRESS`. pub use zone_primitives::constants::{ EMPTY_SENTINEL, MAX_WITHDRAWAL_GAS_LIMIT, NO_QUEUE_INDEX, PORTAL_ADMIN_SLOT, - PORTAL_PENDING_SEQUENCER_SLOT, PORTAL_SEQUENCER_SLOT, TEMPO_STATE_ADDRESS, ZONE_CONFIG_ADDRESS, - ZONE_INBOX_ADDRESS, ZONE_OUTBOX_ADDRESS, ZONE_TOKEN_ADDRESS, ZONE_TX_CONTEXT_ADDRESS, + PORTAL_PENDING_SEQUENCER_SLOT, PORTAL_SEQUENCER_SLOT, PORTAL_TOKEN_CONFIGS_SLOT, + TEMPO_STATE_ADDRESS, ZONE_CONFIG_ADDRESS, ZONE_FEE_MANAGER_ADDRESS, ZONE_INBOX_ADDRESS, + ZONE_OUTBOX_ADDRESS, ZONE_TOKEN_ADDRESS, ZONE_TX_CONTEXT_ADDRESS, }; diff --git a/crates/contracts/src/precompiles/zone_fee_manager.rs b/crates/contracts/src/precompiles/zone_fee_manager.rs new file mode 100644 index 000000000..2d79f3b9e --- /dev/null +++ b/crates/contracts/src/precompiles/zone_fee_manager.rs @@ -0,0 +1,15 @@ +//! `ZoneFeeManager` — Zone L2 protocol fee manager. + +crate::sol! { + #[derive(Debug)] + contract IZoneFeeManager { + event UserTokenSet(address indexed user, address indexed token); + event FeesDistributed(address indexed sequencer, address indexed token, uint256 amount); + + function userTokens(address user) external view returns (address); + function collectedFees(address sequencer, address token) external view returns (uint256); + function setUserToken(address token) external; + function distributeFees(address sequencer, address token) external; + function isEnabledToken(address token) external view returns (bool); + } +} diff --git a/crates/evm/src/executor.rs b/crates/evm/src/executor.rs index 46bd3a728..aa51dd908 100644 --- a/crates/evm/src/executor.rs +++ b/crates/evm/src/executor.rs @@ -12,14 +12,10 @@ use alloy_evm::{ }; use reth_evm::block::StateDB; use reth_revm::Inspector; -use revm::context::{ContextTr, JournalTr, Transaction}; use tempo_chainspec::TempoChainSpec; use tempo_evm::{TempoBlockExecutionCtx, TempoReceiptBuilder}; -use tempo_precompiles::{ - TIP_FEE_MANAGER_ADDRESS, storage::actions::StorageActions, tip_fee_manager::TipFeeManager, -}; use tempo_primitives::{TempoReceipt, TempoTxEnvelope, TempoTxType}; -use tempo_revm::{TempoStateAccess, evm::TempoContext}; +use tempo_revm::evm::TempoContext; use crate::{ZoneEvm, tx_context}; @@ -50,32 +46,6 @@ where ), } } - - /// Overrides `validatorTokens[beneficiary]` to match the resolved fee token - /// so the handler skips FeeAMM. - fn override_validator_token(&mut self) { - let ctx = self.inner.evm.ctx_mut(); - let fee_payer = ctx.tx.fee_payer().unwrap_or(ctx.tx.caller()); - let spec = ctx.cfg.spec; - - let fee_token = match ctx.journaled_state.get_fee_token( - &ctx.tx, - fee_payer, - spec, - StorageActions::disabled(), - ) { - Ok(token) => token, - Err(_) => return, - }; - - let beneficiary = ctx.block.beneficiary; - let slot = TipFeeManager::new().validator_tokens[beneficiary].slot(); - - let _ = ctx.journal_mut().load_account(TIP_FEE_MANAGER_ADDRESS); - let _ = - ctx.journal_mut() - .sstore(TIP_FEE_MANAGER_ADDRESS, slot, fee_token.into_word().into()); - } } impl<'a, DB, I> BlockExecutor for ZoneBlockExecutor<'a, DB, I> @@ -98,10 +68,6 @@ where ) -> Result { let (tx_env, recovered) = tx.into_parts(); - // Override the validator's fee token preference to match this - // transaction's resolved fee token, so the handler skips FeeAMM. - self.override_validator_token(); - let _tx_hash_guard = tx_context::set_current_tx_hash(*recovered.tx().tx_hash()); self.inner .execute_transaction_without_commit((tx_env, recovered)) @@ -129,144 +95,3 @@ where self.inner.receipts() } } - -#[cfg(test)] -mod tests { - use alloy_primitives::{Address, U256}; - use tempo_precompiles::{ - DEFAULT_FEE_TOKEN, TIP_FEE_MANAGER_ADDRESS, - storage::{ContractStorage, Handler, StorageCtx, hashmap::HashMapStorageProvider}, - test_util::TIP20Setup, - tip_fee_manager::{TipFeeManager, amm::PoolKey}, - }; - - /// Simulates the zone executor's per-tx validator token override and runs - /// the full fee lifecycle across multiple TIP-20 tokens, verifying: - /// - /// 1. Default validator token is PATH_USD (no explicit preference set). - /// 2. No FeeAMM liquidity exists for any token pair. - /// 3. Paying fees in betaUSD, gammaUSD, and pathUSD all succeed when the - /// validator token is overridden per-tx. - /// 4. Fees are credited in the user's token (no conversion). - /// 5. FeeAMM pool reserves remain zero throughout. - #[test] - fn multi_token_fees_with_validator_override() -> eyre::Result<()> { - let mut storage = HashMapStorageProvider::new(1); - let admin = Address::random(); - let user = Address::random(); - let sequencer = Address::random(); - - StorageCtx::enter(&mut storage, || { - // Deploy three tokens. - let path_usd = TIP20Setup::create("PathUSD", "pUSD", admin) - .with_issuer(admin) - .with_mint(user, U256::from(10_000_000u64)) - .with_approval(user, TIP_FEE_MANAGER_ADDRESS, U256::MAX) - .apply()?; - let beta_usd = TIP20Setup::create("BetaUSD", "bUSD", admin) - .with_issuer(admin) - .with_mint(user, U256::from(10_000_000u64)) - .with_approval(user, TIP_FEE_MANAGER_ADDRESS, U256::MAX) - .apply()?; - let gamma_usd = TIP20Setup::create("GammaUSD", "gUSD", admin) - .with_issuer(admin) - .with_mint(user, U256::from(10_000_000u64)) - .with_approval(user, TIP_FEE_MANAGER_ADDRESS, U256::MAX) - .apply()?; - - let fee_manager = TipFeeManager::new(); - - // 1. Validator token defaults to PATH_USD. - assert_eq!( - fee_manager.get_validator_token(sequencer)?, - DEFAULT_FEE_TOKEN - ); - - // 2. No FeeAMM pools exist. - for (a, b) in [ - (beta_usd.address(), DEFAULT_FEE_TOKEN), - (gamma_usd.address(), DEFAULT_FEE_TOKEN), - (beta_usd.address(), gamma_usd.address()), - ] { - let pool = fee_manager.pools[PoolKey::new(a, b).get_id()].read()?; - assert_eq!(pool.reserve_user_token, 0); - assert_eq!(pool.reserve_validator_token, 0); - } - - // 3. Three transactions, each paying in a different token. - let txs = [ - ( - beta_usd.address(), - U256::from(5_000u64), - U256::from(3_000u64), - ), - ( - gamma_usd.address(), - U256::from(8_000u64), - U256::from(7_000u64), - ), - ( - path_usd.address(), - U256::from(4_000u64), - U256::from(2_000u64), - ), - ]; - - let mut fee_manager = TipFeeManager::new(); - for (token, max, used) in &txs { - // Zone executor override: validatorTokens[sequencer] = fee_token. - fee_manager.validator_tokens[sequencer].write(*token)?; - - fee_manager.collect_fee_pre_tx(user, *token, *max, sequencer, false)?; - fee_manager.collect_fee_post_tx(user, *used, *max - *used, *token, sequencer)?; - } - - // 4. Fees credited per-token — no conversion happened. - for (token, _, used) in &txs { - let collected = fee_manager.collected_fees[sequencer][*token].read()?; - assert_eq!(collected, *used, "fees should be credited in {token}"); - } - - // 5. FeeAMM pools still empty — never touched. - for (a, b) in [ - (beta_usd.address(), DEFAULT_FEE_TOKEN), - (gamma_usd.address(), DEFAULT_FEE_TOKEN), - (beta_usd.address(), gamma_usd.address()), - ] { - let pool = fee_manager.pools[PoolKey::new(a, b).get_id()].read()?; - assert_eq!( - pool.reserve_user_token, 0, - "pool {a}-{b} user reserve should be 0" - ); - assert_eq!( - pool.reserve_validator_token, 0, - "pool {a}-{b} validator reserve should be 0" - ); - } - - Ok(()) - }) - } - - /// Validator token slot computation is deterministic and the storage - /// write produces the expected value when read back via TipFeeManager. - #[test] - fn validator_token_slot_roundtrip() -> eyre::Result<()> { - let mut storage = HashMapStorageProvider::new(1); - let sequencer = Address::random(); - let token = Address::random(); - - StorageCtx::enter(&mut storage, || { - let mut fee_manager = TipFeeManager::new(); - - // Write via the Mapping handler (what the executor does via journal sstore). - fee_manager.validator_tokens[sequencer].write(token)?; - - // Read back via TipFeeManager API. - let read_back = fee_manager.get_validator_token(sequencer)?; - assert_eq!(read_back, token); - - Ok(()) - }) - } -} diff --git a/crates/evm/src/fee_manager.rs b/crates/evm/src/fee_manager.rs new file mode 100644 index 000000000..39b9a2dd5 --- /dev/null +++ b/crates/evm/src/fee_manager.rs @@ -0,0 +1,103 @@ +//! Adapter between Tempo's protocol fee hooks and the zone fee-manager precompile. + +use alloy_primitives::{Address, U256}; +use alloy_sol_types::SolCall; +use revm::{Database, context::Journal}; +use tempo_chainspec::hardfork::TempoHardfork; +use tempo_precompiles::{ + DEFAULT_FEE_TOKEN, + error::Result, + storage::{Handler, StorageActions}, +}; +use tempo_revm::{ProtocolFeeManager, TempoStateAccess, TempoTx, TempoTxEnv}; +use tempo_zone_contracts::{IZoneFeeManager, ZONE_FEE_MANAGER_ADDRESS}; +use zone_l1::state::L1StateProvider; +use zone_precompiles::ZoneFeeManager; + +/// Zone implementation of Tempo's internal protocol fee hooks. +#[derive(Debug, Clone)] +pub(crate) struct ZoneProtocolFeeManager { + provider: L1StateProvider, +} + +impl ZoneProtocolFeeManager { + pub(crate) const fn new(provider: L1StateProvider) -> Self { + Self { provider } + } +} + +impl ProtocolFeeManager for ZoneProtocolFeeManager { + fn get_fee_token( + &self, + journal: &mut Journal, + tx: &TempoTxEnv, + fee_payer: Address, + spec: TempoHardfork, + actions: StorageActions, + ) -> Result
{ + if let Some(token) = tx.fee_token() { + return Ok(token); + } + + // A direct preference update pays in the newly selected token, matching + // the behavior of Tempo's public fee manager. + if !tx.is_aa() + && fee_payer == tx.caller() + && let Some((kind, input)) = tx.calls().next() + && kind.to() == Some(&ZONE_FEE_MANAGER_ADDRESS) + && let Ok(call) = IZoneFeeManager::setUserTokenCall::abi_decode(input) + { + return Ok(call.token); + } + + let preferred = journal.with_read_only_storage_ctx(spec, actions.clone(), || { + ZoneFeeManager::new().user_tokens[fee_payer].read() + })?; + if !preferred.is_zero() { + return Ok(preferred); + } + + // Preserve Tempo's TIP-20 call inference and default-token behavior for + // users that have not selected a zone preference. + journal.get_fee_token(tx, fee_payer, spec, actions) + } + + fn get_validator_token( + &self, + _journal: &mut Journal, + _beneficiary: Address, + _spec: TempoHardfork, + _actions: StorageActions, + ) -> Result
{ + // Zones never negotiate a validator token or enter an AMM route. + Ok(DEFAULT_FEE_TOKEN) + } + + fn collect_fee_pre_tx( + &self, + fee_payer: Address, + user_token: Address, + max_amount: U256, + _beneficiary: Address, + _skip_liquidity_check: bool, + ) -> Result
{ + ZoneFeeManager::new().collect_fee_pre_tx(&self.provider, fee_payer, user_token, max_amount) + } + + fn collect_fee_post_tx( + &self, + fee_payer: Address, + actual_spending: U256, + refund_amount: U256, + fee_token: Address, + beneficiary: Address, + ) -> Result { + ZoneFeeManager::new().collect_fee_post_tx( + fee_payer, + actual_spending, + refund_amount, + fee_token, + beneficiary, + ) + } +} diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index eff4cbe65..413e9a0e2 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -8,6 +8,7 @@ #![allow(unnameable_types)] mod executor; +mod fee_manager; pub mod precompiles; mod tx_context; mod zone_evm; @@ -16,10 +17,11 @@ pub use zone_evm::{ZoneEvm, contract_creation::validate_transaction}; use crate::{ executor::ZoneBlockExecutor, + fee_manager::ZoneProtocolFeeManager, precompiles::{ AES_GCM_DECRYPT_ADDRESS, AesGcmDecrypt, CHAUM_PEDERSEN_VERIFY_ADDRESS, ChaumPedersenVerify, SequencerExt, TempoState, ZONE_TIP20_FACTORY_ADDRESS, ZONE_TIP403_PROXY_ADDRESS, - ZoneTip20Token, ZoneTip403ProxyRegistry, ZoneTokenFactory, + ZoneFeeManager, ZoneTip20Token, ZoneTip403ProxyRegistry, ZoneTokenFactory, }, tx_context::ZoneTxContext, }; @@ -49,13 +51,14 @@ use tempo_payload_types::TempoExecutionData; use tempo_precompiles::{ ACCOUNT_KEYCHAIN_ADDRESS, NONCE_PRECOMPILE_ADDRESS, PrecompileEnv, STABLECOIN_DEX_ADDRESS, TIP_FEE_MANAGER_ADDRESS, account_keychain::AccountKeychain, nonce::NonceManager, - storage::actions::StorageActions, storage_credits::NonCreditableSlots, - tip_fee_manager::TipFeeManager, tip20::is_tip20_prefix, + storage::actions::StorageActions, storage_credits::NonCreditableSlots, tip20::is_tip20_prefix, }; use tempo_primitives::{ Block, TempoHeader, TempoPrimitives, TempoReceipt, TempoTxEnvelope, TempoTxType, }; -use tempo_zone_contracts::{TEMPO_STATE_ADDRESS, ZONE_TX_CONTEXT_ADDRESS}; +use tempo_zone_contracts::{ + TEMPO_STATE_ADDRESS, ZONE_FEE_MANAGER_ADDRESS, ZONE_TX_CONTEXT_ADDRESS, +}; use zone_l1::state::{L1StateCache, L1StateProvider, L1StateProviderConfig, PolicyProvider}; type TempoCtx = ::Context; @@ -85,14 +88,18 @@ impl ZoneEvmFactory { fn register_precompiles>>( &self, - mut evm: TempoEvm, + evm: TempoEvm, ) -> TempoEvm { let cfg = evm.ctx().cfg.clone(); + let mut evm = evm.with_fee_manager(ZoneProtocolFeeManager::new(self.l1_provider.clone())); let (_, _, precompiles) = evm.components_mut(); precompiles.apply_precompile(&TEMPO_STATE_ADDRESS, |_| { Some(TempoState::create(self.l1_provider.clone(), &cfg)) }); precompiles.apply_precompile(&ZONE_TX_CONTEXT_ADDRESS, |_| Some(ZoneTxContext::create())); + precompiles.apply_precompile(&ZONE_FEE_MANAGER_ADDRESS, |_| { + Some(ZoneFeeManager::create(self.l1_provider.clone(), &cfg)) + }); precompiles.apply_precompile(&CHAUM_PEDERSEN_VERIFY_ADDRESS, |_| { Some(ChaumPedersenVerify::create(&cfg)) }); @@ -121,7 +128,8 @@ impl ZoneEvmFactory { // // This replaces the upstream `extend_tempo_precompiles` lookup, so we // must also handle the non-TIP-20 Tempo precompiles that are zone-relevant - // (FeeManager, NonceManager, AccountKeychain). + // (NonceManager, AccountKeychain). FeeManager is deliberately disabled + // here because protocol fees use ZoneFeeManager above. // Zone-specific overrides (TIP20Factory, TIP403Proxy) are in the // static map via `apply_precompile` and take priority over this. let zone_cfg = cfg.clone(); @@ -138,9 +146,7 @@ impl ZoneEvmFactory { registry.clone(), sequencer.clone(), )) - } else if *address == TIP_FEE_MANAGER_ADDRESS { - Some(TipFeeManager::create_precompile(&zone_env)) - } else if *address == STABLECOIN_DEX_ADDRESS { + } else if *address == TIP_FEE_MANAGER_ADDRESS || *address == STABLECOIN_DEX_ADDRESS { None } else if *address == NONCE_PRECOMPILE_ADDRESS { Some(NonceManager::create_precompile(&zone_env)) diff --git a/crates/l1/src/state/provider.rs b/crates/l1/src/state/provider.rs index cb5a3fd4d..709f2b0a5 100644 --- a/crates/l1/src/state/provider.rs +++ b/crates/l1/src/state/provider.rs @@ -18,7 +18,7 @@ use alloy_transport::layers::RetryBackoffLayer; use eyre::Result; use tempo_alloy::TempoNetwork; use tracing::{debug, info, warn}; -use zone_precompiles::{L1StorageReader, SequencerExt}; +use zone_precompiles::{L1StorageReader, SequencerExt, ZoneConfigReader}; use super::cache::L1StateCache; use crate::{abi::PORTAL_SEQUENCER_SLOT, rpc::rpc_connection_config}; @@ -293,6 +293,12 @@ impl L1StorageReader for L1StateProvider { } } +impl ZoneConfigReader for L1StateProvider { + fn zone_portal_address(&self) -> Address { + self.portal_address + } +} + impl SequencerExt for L1StateProvider { fn latest_sequencer(&self) -> Option
{ self.get_latest_sequencer().ok() diff --git a/crates/node/assets/zone-dev-genesis.json b/crates/node/assets/zone-dev-genesis.json index 59db06c6f..6441c468c 100644 --- a/crates/node/assets/zone-dev-genesis.json +++ b/crates/node/assets/zone-dev-genesis.json @@ -33,6 +33,11 @@ "code": "0x6080806040526004361015610012575f80fd5b5f3560e01c9081631beb1ab814610561575080631fbb25ad1461051d5780633488ce0d146102dd5780635c1bba381461023b5780636d46e98714610193578063a21de6d91461014f5763e202d99514610069575f80fd5b34610138575f366003190112610138576040516381e3da6b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260026024830152602090829060449082907f0000000000000000000000001c00000000000000000000000000000000000000165afa8015610144575f9061010d575b6040516001600160a01b039091168152602090f35b506020813d60201161013c575b816101276020938361067c565b8101031261013857602090516100f8565b5f80fd5b3d915061011a565b6040513d5f823e3d90fd5b34610138575f366003190112610138576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610138576020366003190112610138576004356001600160a01b0381169081900361013857604051630b83774760e31b8152602081600481305afa908115610144575f916101f6575b506040516001600160a01b039091169091148152602090f35b90506020813d602011610233575b816102116020938361067c565b810103126101385751906001600160a01b0382168203610138579060206101dd565b3d9150610204565b34610138575f366003190112610138576040516381e3da6b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301525f6024830152602090829060449082907f0000000000000000000000001c00000000000000000000000000000000000000165afa8015610144575f9061010d576040516001600160a01b039091168152602090f35b34610138575f366003190112610138576040516381e3da6b60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038181166004840152600760248401527f0000000000000000000000001c00000000000000000000000000000000000000169190602082604481865afa918215610144575f926104e9575b5081156104da576040516020810190600782526020815261038f60408261067c565b519020915f1981019081116104c6578060011b90808204600214901517156104c65782018092116104c65760018201908183116104c6576040516381e3da6b60e01b81526001600160a01b03821660048201526024810193909352602083604481875afa928315610144575f9361048e575b506040516381e3da6b60e01b81526001600160a01b03909116600482015260248101919091529160209083908180604481015b03915afa918215610144575f92610459575b5060ff6040928351928352166020820152f35b91506020823d602011610486575b816104746020938361067c565b810103126101385790519060ff610446565b3d9150610467565b919092506020823d6020116104be575b816104ab6020938361067c565b8101031261013857905191610434610401565b3d915061049e565b634e487b7160e01b5f52601160045260245ffd5b630c322fb560e31b5f5260045ffd5b9091506020813d602011610515575b816105056020938361067c565b810103126101385751908361036d565b3d91506104f8565b34610138575f366003190112610138576040517f0000000000000000000000001c000000000000000000000000000000000000006001600160a01b03168152602090f35b34610138576020366003190112610138576004356001600160a01b0381169190829003610138576105fe9181602080930191825260086040820152604081526105ab60608261067c565b5190206040516381e3da6b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166004820152602481019190915291829081906044820190565b03817f0000000000000000000000001c000000000000000000000000000000000000006001600160a01b03165afa8015610144575f90610649575b60209060ff604051911615158152f35b506020813d602011610674575b816106636020938361067c565b810103126101385760209051610639565b3d9150610656565b90601f8019910116810190811067ffffffffffffffff82111761069e57604052565b634e487b7160e01b5f52604160045260245ffdfea2646970667358221220fdb09622e8f7b1599cbada7d6552a3d891d6e220116c42bbb8308f891598515264736f6c63430008230033", "nonce": "0x1" }, + "0x1c00000000000000000000000000000000000006": { + "balance": "0x0", + "code": "0xef", + "nonce": "0x0" + }, "0x20c0000000000000000000000000000000000000": { "balance": "0x0", "code": "0xef", @@ -101,11 +106,8 @@ }, "0xfeec000000000000000000000000000000000000": { "balance": "0x0", - "code": "0xef", - "nonce": "0x0", - "storage": { - "0xabd7b398c2237712843e3e780dcd40dfb99446b30666f04c025da4efa5ce5177": "0x0000000000000000000000000000000000000000000000000000000000000000" - } + "code": "0x", + "nonce": "0x0" } }, "baseFeePerGas": "0x2540be400", diff --git a/crates/node/src/genesis.rs b/crates/node/src/genesis.rs index 123bb87d8..79d6e099a 100644 --- a/crates/node/src/genesis.rs +++ b/crates/node/src/genesis.rs @@ -20,6 +20,8 @@ const TEMPO_STATE_ADDRESS: Address = address!("0x1c00000000000000000000000000000 const ZONE_INBOX_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000001"); /// ZoneConfig predeploy address. const ZONE_CONFIG_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000003"); +/// ZoneFeeManager predeploy address. +const ZONE_FEE_MANAGER_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000006"); /// `tempoPortal` immutable occurrences in ZoneInbox deployed bytecode. const ZONE_INBOX_PORTAL_IMMUTABLES: usize = 4; @@ -148,7 +150,14 @@ mod tests { #[test] fn template_parses() { - genesis_template().unwrap(); + let genesis = genesis_template().unwrap(); + assert!( + genesis.alloc[&ZONE_FEE_MANAGER_ADDRESS] + .code + .as_ref() + .is_some_and(|code| !code.is_empty()), + "ZoneFeeManager missing from genesis template" + ); } #[test] diff --git a/crates/precompiles/src/lib.rs b/crates/precompiles/src/lib.rs index 32427d908..6547618b9 100644 --- a/crates/precompiles/src/lib.rs +++ b/crates/precompiles/src/lib.rs @@ -16,6 +16,7 @@ //! - **TIP-20 Factory** ([`tip20_factory`]) — zone-side TIP-20 token factory. //! - **TIP-403 Proxy** ([`tip403_proxy`]) — read-only TIP-403 registry proxy. //! - **Zone TIP-20** ([`ztip20`]) — policy-aware TIP-20 wrapper. +//! - **Zone Fee Manager** ([`zone_fee_manager`]) — multi-token fees without FeeAMM. #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::too_many_arguments)] @@ -44,6 +45,7 @@ pub mod policy; pub mod tempo_state; pub mod tip20_factory; pub mod tip403_proxy; +pub mod zone_fee_manager; pub mod ztip20; pub use aes_gcm::{AES_GCM_DECRYPT_ADDRESS, AesGcmDecrypt}; @@ -51,6 +53,7 @@ pub use chaum_pedersen::{CHAUM_PEDERSEN_VERIFY_ADDRESS, ChaumPedersenVerify}; pub use tempo_state::{L1StorageReader, TempoState}; pub use tip20_factory::{ZONE_TIP20_FACTORY_ADDRESS, ZoneTokenFactory}; pub use tip403_proxy::{ZONE_TIP403_PROXY_ADDRESS, ZoneTip403ProxyRegistry}; +pub use zone_fee_manager::{ZoneConfigReader, ZoneFeeManager}; pub use ztip20::{SequencerExt, ZoneTip20Token}; use revm::precompile::PrecompileError; diff --git a/crates/precompiles/src/tempo_state.rs b/crates/precompiles/src/tempo_state.rs index 555cf2ba5..7bae83b0a 100644 --- a/crates/precompiles/src/tempo_state.rs +++ b/crates/precompiles/src/tempo_state.rs @@ -3,7 +3,7 @@ //! Replaces the Solidity TempoState predeploy at `0x1c00...0000` while //! preserving the zone-facing checkpoint and Tempo storage read ABI. -use alloc::vec::Vec; +use alloc::{format, vec::Vec}; use alloy_consensus::BlockHeader; use alloy_evm::precompiles::DynPrecompile; @@ -63,6 +63,11 @@ impl TempoState { Ok(()) } + /// Returns the finalized Tempo block number that zone state reads are bound to. + pub fn finalized_block_number(&self) -> tempo_precompiles::Result { + self.tempo_block_number.read() + } + fn write_checkpoint( &mut self, header_rlp: &[u8], diff --git a/crates/precompiles/src/zone_fee_manager.rs b/crates/precompiles/src/zone_fee_manager.rs new file mode 100644 index 000000000..43d7836e9 --- /dev/null +++ b/crates/precompiles/src/zone_fee_manager.rs @@ -0,0 +1,373 @@ +//! Zone-specific protocol fee manager. +//! +//! Zone fees never use Tempo's FeeAMM. The transaction's resolved fee token is +//! checked against the portal registry at the finalized [`TempoState`] checkpoint, +//! escrowed for execution, and credited to the sequencer in that same token. + +use alloc::string::ToString; +use alloy_evm::precompiles::DynPrecompile; +use alloy_primitives::{Address, U256, keccak256}; +use alloy_sol_types::{SolError, SolValue}; +use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult}; +use tempo_precompiles::{ + DelegateCallNotAllowed, charge_input_cost, dispatch, + error::{Result, TempoPrecompileError}, + mutate_void, + storage::{Handler, Mapping, StorageCtx, evm::EvmPrecompileStorageProvider}, + tip20::{ITIP20, TIP20Token, validate_usd_currency}, + view, +}; +use tempo_precompiles_macros::contract; +use tempo_zone_contracts::{IZoneFeeManager, PORTAL_TOKEN_CONFIGS_SLOT, ZONE_FEE_MANAGER_ADDRESS}; + +use crate::{L1StorageReader, TempoState}; + +/// L1 state access required to resolve [`ZoneConfig`](https://github.com/tempoxyz/tempo-zones) +/// token enablement at the zone's finalized Tempo checkpoint. +pub trait ZoneConfigReader: L1StorageReader { + /// Address of the ZonePortal whose registry backs ZoneConfig. + fn zone_portal_address(&self) -> Address; + + /// Apply the same `_tokenConfigs[token].enabled` lookup as `ZoneConfig.isEnabledToken`. + fn is_enabled_token( + &self, + token: Address, + block_number: u64, + ) -> core::result::Result { + let slot = keccak256((token, PORTAL_TOKEN_CONFIGS_SLOT).abi_encode()); + let value = self.read_l1_storage(self.zone_portal_address(), slot, block_number)?; + Ok(value[31] != 0) + } +} + +/// Zone fee manager storage. +/// +/// This layout is owned by the zone implementation. In particular, no Tempo +/// `TipFeeManager` storage slots are read or overwritten. +#[contract(addr = ZONE_FEE_MANAGER_ADDRESS)] +pub struct ZoneFeeManager { + user_tokens: Mapping, + collected_fees: Mapping>, +} + +impl ZoneFeeManager { + /// Initializes the precompile account marker in genesis. + pub fn initialize(&mut self) -> Result<()> { + self.__initialize() + } + + fn map_reader_error(error: PrecompileError) -> TempoPrecompileError { + TempoPrecompileError::Fatal(error.to_string()) + } + + fn is_enabled(&self, provider: &P, token: Address) -> Result { + let block_number = TempoState::new().finalized_block_number()?; + provider + .is_enabled_token(token, block_number) + .map_err(Self::map_reader_error) + } + + fn ensure_enabled(&self, provider: &P, token: Address) -> Result<()> { + if !self.is_enabled(provider, token)? { + return Err( + tempo_precompiles::tip_fee_manager::FeeManagerError::invalid_token().into(), + ); + } + Ok(()) + } + + /// Returns the fee token preference stored for `user`. + pub fn user_token(&self, user: Address) -> Result
{ + self.user_tokens[user].read() + } + + /// Returns fees accrued to `sequencer` in `token`. + pub fn collected_fees(&self, sequencer: Address, token: Address) -> Result { + self.collected_fees[sequencer][token].read() + } + + /// Stores a user's zone fee token preference after registry and currency validation. + pub fn set_user_token( + &mut self, + provider: &P, + sender: Address, + token: Address, + ) -> Result<()> { + self.ensure_enabled(provider, token)?; + validate_usd_currency(token)?; + + if self.user_tokens[sender].read()? == token { + return Ok(()); + } + self.user_tokens[sender].write(token)?; + self.emit_event(IZoneFeeManager::UserTokenSet { + user: sender, + token, + }) + } + + /// Collects the maximum fee before execution without consulting FeeAMM state. + pub fn collect_fee_pre_tx( + &mut self, + provider: &P, + fee_payer: Address, + fee_token: Address, + max_amount: U256, + ) -> Result
{ + self.ensure_enabled(provider, fee_token)?; + + let mut token = TIP20Token::from_address(fee_token)?; + // Tempo's specialized fee-transfer helpers use the canonical fee-manager + // address as protocol custody. The public precompile and all accounting at + // that address remain disabled on zones. + token.ensure_transfer_authorized(fee_payer, tempo_precompiles::TIP_FEE_MANAGER_ADDRESS)?; + token.transfer_fee_pre_tx(fee_payer, max_amount)?; + Ok(fee_token) + } + + /// Refunds unused gas and credits the sequencer in the user's fee token. + pub fn collect_fee_post_tx( + &mut self, + fee_payer: Address, + actual_spending: U256, + refund_amount: U256, + fee_token: Address, + sequencer: Address, + ) -> Result { + let mut token = TIP20Token::from_address(fee_token)?; + token.transfer_fee_post_tx(fee_payer, refund_amount, actual_spending)?; + + if !actual_spending.is_zero() { + self.collected_fees[sequencer][fee_token].sinc(actual_spending)?; + } + Ok(actual_spending) + } + + /// Transfers a sequencer's accrued fees out of protocol custody. + pub fn distribute_fees(&mut self, sequencer: Address, token: Address) -> Result<()> { + StorageCtx.set_tip1060_storage_credit_minting(false); + + let amount = self.collected_fees[sequencer][token].read()?; + if amount.is_zero() { + return Ok(()); + } + self.collected_fees[sequencer][token].write(U256::ZERO)?; + + let mut tip20 = TIP20Token::from_address(token)?; + // `transfer_fee_pre_tx` escrows here inside TIP-20 storage; ZoneFeeManager + // owns the corresponding sequencer ledger and is the only enabled fee API. + tip20.transfer( + tempo_precompiles::TIP_FEE_MANAGER_ADDRESS, + ITIP20::transferCall { + to: sequencer, + amount, + }, + )?; + self.emit_event(IZoneFeeManager::FeesDistributed { + sequencer, + token, + amount, + }) + } + + /// Wraps the public ZoneFeeManager ABI for EVM registration. + pub fn create( + provider: P, + cfg: &revm::context::CfgEnv, + ) -> DynPrecompile { + let spec = cfg.spec; + let amsterdam_eip8037_enabled = cfg.enable_amsterdam_eip8037; + let gas_params = cfg.gas_params.clone(); + + DynPrecompile::new_stateful( + PrecompileId::Custom("ZoneFeeManager".into()), + move |input| { + if !input.is_direct_call() { + return Ok(PrecompileOutput::revert( + 0, + SolError::abi_encode(&DelegateCallNotAllowed {}).into(), + input.reservoir, + )); + } + + let mut storage = EvmPrecompileStorageProvider::new( + input.internals, + input.gas, + input.reservoir, + spec, + amsterdam_eip8037_enabled, + input.is_static, + gas_params.clone(), + ); + + StorageCtx::enter(&mut storage, || { + Self::new().call_with_provider(&provider, input.data, input.caller) + }) + }, + ) + } + + fn call_with_provider( + &mut self, + provider: &P, + calldata: &[u8], + msg_sender: Address, + ) -> PrecompileResult { + if let Some(error) = charge_input_cost(&mut self.storage, calldata) { + return error; + } + + dispatch!(calldata, |call| match call { + IZoneFeeManager::IZoneFeeManagerCalls { + userTokens(call) => view(call, |call| self.user_token(call.user)), + collectedFees(call) => view(call, |call| { + self.collected_fees(call.sequencer, call.token) + }), + isEnabledToken(call) => view(call, |call| self.is_enabled(provider, call.token)), + setUserToken(call) => mutate_void(call, msg_sender, |sender, call| { + self.set_user_token(provider, sender, call.token) + }), + distributeFees(call) => mutate_void(call, msg_sender, |_, call| { + self.distribute_fees(call.sequencer, call.token) + }), + } + }) + } +} + +#[cfg(test)] +mod tests { + use alloc::vec::Vec; + + use alloy_primitives::{B256, address}; + use tempo_precompiles::{ + TIP_FEE_MANAGER_ADDRESS, + storage::{ContractStorage, StorageCtx, hashmap::HashMapStorageProvider}, + test_util::TIP20Setup, + tip_fee_manager::{TipFeeManager, amm::PoolKey}, + tip20::{ITIP20, TIP20Token}, + }; + + use super::*; + + type TestResult = core::result::Result<(), Box>; + + #[derive(Clone)] + struct MockZoneConfig { + portal: Address, + enabled: Vec
, + } + + impl L1StorageReader for MockZoneConfig { + fn read_l1_storage( + &self, + account: Address, + slot: B256, + block_number: u64, + ) -> core::result::Result { + assert_eq!(account, self.portal); + assert_eq!(block_number, 0); + + let enabled = self + .enabled + .iter() + .any(|token| keccak256((*token, PORTAL_TOKEN_CONFIGS_SLOT).abi_encode()) == slot); + Ok(B256::from(U256::from(enabled as u8).to_be_bytes())) + } + } + + impl ZoneConfigReader for MockZoneConfig { + fn zone_portal_address(&self) -> Address { + self.portal + } + } + + #[test] + fn collects_enabled_tokens_without_touching_fee_amm() -> TestResult { + let mut storage = HashMapStorageProvider::new(1); + let admin = Address::random(); + let user = Address::random(); + let sequencer = Address::random(); + + StorageCtx::enter(&mut storage, || { + let alpha = TIP20Setup::create("Alpha USD", "aUSD", admin) + .with_issuer(admin) + .with_mint(user, U256::from(10_000u64)) + .apply()?; + let beta = TIP20Setup::create("Beta USD", "bUSD", admin) + .with_issuer(admin) + .with_mint(user, U256::from(10_000u64)) + .apply()?; + let provider = MockZoneConfig { + portal: address!("0x0000000000000000000000000000000000001234"), + enabled: vec![alpha.address(), beta.address()], + }; + let mut manager = ZoneFeeManager::new(); + + for (token, max, used) in [ + (alpha.address(), U256::from(2_000), U256::from(1_250)), + (beta.address(), U256::from(3_000), U256::from(2_500)), + ] { + manager.collect_fee_pre_tx(&provider, user, token, max)?; + manager.collect_fee_post_tx(user, used, max - used, token, sequencer)?; + + assert_eq!(manager.collected_fees(sequencer, token)?, used); + assert_eq!( + TIP20Token::from_address(token)? + .balance_of(ITIP20::balanceOfCall { account: user })?, + U256::from(10_000) - used + ); + assert_eq!( + TIP20Token::from_address(token)?.balance_of(ITIP20::balanceOfCall { + account: TIP_FEE_MANAGER_ADDRESS, + })?, + used + ); + + manager.distribute_fees(sequencer, token)?; + assert_eq!(manager.collected_fees(sequencer, token)?, U256::ZERO); + assert_eq!( + TIP20Token::from_address(token)? + .balance_of(ITIP20::balanceOfCall { account: sequencer })?, + used + ); + } + + let pool = TipFeeManager::new().pools + [PoolKey::new(alpha.address(), beta.address()).get_id()] + .read()?; + assert_eq!(pool.reserve_user_token, 0); + assert_eq!(pool.reserve_validator_token, 0); + Ok(()) + }) + } + + #[test] + fn rejects_tokens_disabled_in_zone_config() -> TestResult { + let mut storage = HashMapStorageProvider::new(1); + let admin = Address::random(); + let user = Address::random(); + + StorageCtx::enter(&mut storage, || { + let token = TIP20Setup::create("Disabled USD", "dUSD", admin) + .with_issuer(admin) + .with_mint(user, U256::from(10_000u64)) + .apply()?; + let provider = MockZoneConfig { + portal: Address::random(), + enabled: Vec::new(), + }; + + let error = ZoneFeeManager::new() + .collect_fee_pre_tx(&provider, user, token.address(), U256::from(1_000)) + .unwrap_err(); + assert!(matches!(error, TempoPrecompileError::FeeManagerError(_))); + assert_eq!( + TIP20Token::from_address(token.address())? + .balance_of(ITIP20::balanceOfCall { account: user })?, + U256::from(10_000) + ); + Ok(()) + }) + } +} diff --git a/crates/primitives/src/constants.rs b/crates/primitives/src/constants.rs index 57ef86d5b..d9e3a8f84 100644 --- a/crates/primitives/src/constants.rs +++ b/crates/primitives/src/constants.rs @@ -35,6 +35,10 @@ pub const CONTRACT_DEPLOYER_ALLOWLIST: &[Address] = &[]; /// ZoneTxContext precompile address on Zone L2. pub const ZONE_TX_CONTEXT_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000005"); +/// ZoneFeeManager precompile address on Zone L2. +pub const ZONE_FEE_MANAGER_ADDRESS: Address = + address!("0x1c00000000000000000000000000000000000006"); + /// Chaum-Pedersen verification precompile address. pub const CHAUM_PEDERSEN_VERIFY_ADDRESS: Address = address!("0x1C00000000000000000000000000000000000100"); @@ -66,6 +70,13 @@ pub const PORTAL_PENDING_SEQUENCER_SLOT: B256 = { B256::new(bytes) }; +/// ZonePortal storage slot 8: `_tokenConfigs` mapping. +pub const PORTAL_TOKEN_CONFIGS_SLOT: B256 = { + let mut bytes = [0u8; 32]; + bytes[31] = 8; + B256::new(bytes) +}; + // --------------------------------------------------------------------------- // Storage slot constants for the proof system // --------------------------------------------------------------------------- diff --git a/docs/ZONES.md b/docs/ZONES.md index 68c4fd5ca..84559ebf0 100644 --- a/docs/ZONES.md +++ b/docs/ZONES.md @@ -511,7 +511,7 @@ graph TB subgraph L2["Zone L2 Node"] direction TB Tasks["Sequencer Tasks
• L1 subscriber (deposit backfill + live)
• Zone engine (L1-driven block building)
• Zone monitor (batch submission to L1)
• Withdrawal processor (L1 queue drain)"] - Predeploys["Predeploys
0x1c00…0000 TempoState
0x1c00…0001 ZoneInbox
0x1c00…0002 ZoneOutbox
0x1c00…0003 ZoneConfig
0x1c00…0004 TempoStateReader
0x20C0…0000 pathUSD"] + Predeploys["Predeploys
0x1c00…0000 TempoState
0x1c00…0001 ZoneInbox
0x1c00…0002 ZoneOutbox
0x1c00…0003 ZoneConfig
0x1c00…0004 TempoStateReader
0x1c00…0005 ZoneTxContext
0x1c00…0006 ZoneFeeManager
0x20C0…0000 pathUSD"] end Portal -- "WSS subscription
(deposits, headers)" --> Tasks @@ -530,7 +530,7 @@ Zones inherit the Tempo L1 EVM but replace, disable, or pass through each precom | TIP-20 tokens | `0x20C0…` prefix | **Replaced** — routed through `ZoneTip20Token`, which adds privacy (caller-scoped reads), fixed gas for transfers, bridge-auth for mint/burn, and TIP-403 policy enforcement via the L1-synced cache. | | TIP20Factory | `0x20FC…0000` | **Replaced** — `ZoneTokenFactory` exposes only `enableToken(address, name, symbol, currency)`, called by ZoneInbox during `advanceTempo` to initialize bridged tokens. | | TIP403Registry | `0x403C…0000` | **Replaced** — read-only `ZoneTip403ProxyRegistry` serves authorization queries from a cache-first, L1-RPC-fallback provider. Mutating calls (`createPolicy`, `modifyPolicyWhitelist`, etc.) revert — policy state is managed on L1. | -| TipFeeManager | `0xfeec…0000` | **Present** — the precompile is still registered, but its liquidity pools are not used by transactions. The zone executor overrides `validatorTokens` to match each transaction's fee token, so the FeeAMM swap path is bypassed and fees are collected directly in the user's token. | +| TipFeeManager | `0xfeec…0000` | **Disabled** — protocol fee hooks are routed to the zone-native `ZoneFeeManager`; the L1 FeeAMM and validator-token preference are unavailable. | | StablecoinDEX | `0xdec0…0000` | **Disabled** — not registered on zones, so the address behaves like an empty account. Users on zones can trade on the StablecoinDEX on Tempo via the bridge. | | NonceManager | `0x4E4F…0000` | **Unchanged** — same implementation as L1, runs locally on zone state. | | ValidatorConfig (legacy) | `0xCCCC…0000` | **Not registered** — zones do not run validators, so the precompile is not loaded. | @@ -545,6 +545,7 @@ Zones inherit the Tempo L1 EVM but replace, disable, or pass through each precom |------------|---------|-------------| | TempoStateReader | `0x1c00…0004` | Reads L1 contract storage from zone contracts via the L1 state cache. | | ZoneTxContext | `0x1c00…0005` | Exposes the hash of the currently executing zone transaction (`currentTxHash`), used by ZoneOutbox for authenticated withdrawals. | +| ZoneFeeManager | `0x1c00…0006` | Accepts any portal-enabled fee token, skips AMM/liquidity routing, and accounts sequencer fees in the token paid by the user. | | ChaumPedersenVerify | `0x1c00…0100` | Verifies DLOG equality proofs for ECDH key exchange (encrypted deposits). | | AesGcmDecrypt | `0x1c00…0101` | AES-256-GCM authenticated decryption (encrypted deposit payloads). | diff --git a/invariants.md b/invariants.md index af4068120..b6125608b 100644 --- a/invariants.md +++ b/invariants.md @@ -20,7 +20,7 @@ for auditors, invariant/fuzz test authors, and production monitoring. | `TEMPO-ZONE-CHAIN-ID-UNIQUE` | Each live zone uses the chain ID derived from its zone ID, and no two live zones share a chain ID | 🟡 | Cross-zone replay protection fails; signed transactions may be valid on more than one zone | | `TEMPO-ZONE-PORTAL-PAIRING` | A `ZoneFactory` registry entry maps one zone ID to exactly one portal, and that portal uses the factory's shared messenger | 🟡 | Deposits, withdrawals, callbacks, and config reads can target different trust domains | | `TEMPO-ZONE-GENESIS-BINDING` | Portal `blockHash`, `genesisTempoBlockNumber`, and emitted zone creation parameters match the zone genesis file | 🔴 | The zone may prove batches from a different genesis state than the portal expects | -| `TEMPO-ZONE-PREDEPLOY-ADDRESSES` | `TempoState`, `ZoneInbox`, `ZoneOutbox`, `ZoneConfig`, `TempoStateReader`, and `ZoneTxContext` exist at their fixed addresses | 🔴 | System calls can be redirected or missing, invalidating mint/burn, proofs, and Tempo reads | +| `TEMPO-ZONE-PREDEPLOY-ADDRESSES` | `TempoState`, `ZoneInbox`, `ZoneOutbox`, `ZoneConfig`, `TempoStateReader`, `ZoneTxContext`, and `ZoneFeeManager` exist at their fixed addresses | 🔴 | System calls can be redirected or missing, invalidating mint/burn, fee collection, proofs, and Tempo reads | ### Access Control and Configuration @@ -36,6 +36,9 @@ for auditors, invariant/fuzz test authors, and production monitoring. | ID | Assertion | Crit | Impact | |---|---|---|---| +| `TEMPO-ZONE-FEE-TOKEN-ENABLED` | Every nonzero gas fee is paid in a token enabled by the portal registry at the zone's finalized Tempo checkpoint | 🔴 | The zone can create unbacked demand or accept an asset outside its configured trust domain | +| `TEMPO-ZONE-FEE-NO-SWAP` | Zone fee settlement credits the sequencer in exactly the token paid by the user and never reads or mutates FeeAMM pool state | 🟡 | Fee accounting can depend on unavailable liquidity or diverge between execution and proving | + | `TEMPO-ZONE-TOKEN-ENABLEMENT-APPEND-ONLY` | Once enabled, a token remains enabled and remains in the append-only enabled token list | 🔴 | Withdrawals can be disabled after deposits, breaking the non-custodial bridge guarantee | | `TEMPO-ZONE-TOKEN-DEPOSIT-PAUSE-ONLY` | Pausing a token only disables new deposits; withdrawals for enabled tokens remain requestable and processable | 🔴 | Admin can lock users inside the zone by pausing deposits | | `TEMPO-ZONE-MESSENGER-AUTH` | The shared messenger only relays when `msg.sender == ZoneFactory.zones(zoneId).portal` | 🟡 | A caller can spoof a source zone or invoke receiver callbacks outside the portal-controlled withdrawal path | diff --git a/specs/ref-impls/src/interfaces/IZoneFeeManager.sol b/specs/ref-impls/src/interfaces/IZoneFeeManager.sol new file mode 100644 index 000000000..77828c8ad --- /dev/null +++ b/specs/ref-impls/src/interfaces/IZoneFeeManager.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +// ZoneFeeManager precompile address (0x1c00...0006). +address constant ZONE_FEE_MANAGER = 0x1c00000000000000000000000000000000000006; + +/// @title IZoneFeeManager +/// @notice Zone-native fee manager with no AMM or validator-token preference. +interface IZoneFeeManager { + event UserTokenSet(address indexed user, address indexed token); + event FeesDistributed(address indexed sequencer, address indexed token, uint256 amount); + + function userTokens(address user) external view returns (address); + function collectedFees(address sequencer, address token) external view returns (uint256); + function setUserToken(address token) external; + function distributeFees(address sequencer, address token) external; + function isEnabledToken(address token) external view returns (bool); +} diff --git a/specs/spec.md b/specs/spec.md index 0440fd35f..1a811bcfd 100644 --- a/specs/spec.md +++ b/specs/spec.md @@ -77,6 +77,7 @@ - [Proof Requirements](#proof-requirements) - [Zone Precompiles](#zone-precompiles) - [TIP-20 Token Precompile](#tip-20-token-precompile) + - [Zone Fee Manager](#zone-fee-manager) - [Chaum-Pedersen Verify](#chaum-pedersen-verify) - [AES-GCM Decrypt](#aes-gcm-decrypt) - [Contracts and Interfaces](#contracts-and-interfaces) @@ -89,6 +90,7 @@ - [IZoneInbox](#izoneinbox) - [IZoneOutbox](#izoneoutbox) - [IZoneConfig](#izoneconfig) + - [IZoneFeeManager](#izonefeemanager) - [TIP-403 Registry](#tip-403-registry) - [Network Upgrades and Hard Fork Activation](#network-upgrades-and-hard-fork-activation) @@ -262,7 +264,7 @@ The shared `ZoneMessenger` relays withdrawal callbacks for all zones created by ### Zone Predeploys -Each zone has five system contracts deployed at genesis at fixed addresses: +Each zone has six system contracts deployed at genesis at fixed addresses: | Predeploy | Address | Purpose | |-----------|---------|---------| @@ -271,9 +273,12 @@ Each zone has five system contracts deployed at genesis at fixed addresses: | [`ZoneOutbox`](#izoneoutbox) | `0x1c00...0002` | Handles withdrawal requests and batch finalization. Sole burn authority. | | [`ZoneConfig`](#izoneconfig) | `0x1c00...0003` | Central configuration. Reads the sequencer address and token registry from Tempo via `TempoState`. | | `ZoneTxContext` | `0x1c00...0005` | Provides the current transaction hash to system contracts (used by `ZoneOutbox` for `senderTag` computation). | +| `ZoneFeeManager` | `0x1c00...0006` | Collects gas fees in any token enabled by `ZoneConfig`, without AMM routing, and credits the sequencer in that token. | `ZoneConfig` reads the sequencer address and token registry from the portal on Tempo via `TempoState` storage reads, making Tempo the single source of truth for zone configuration. See [Tempo State Reads](#tempo-state-reads) for details. +`ZoneFeeManager` replaces Tempo's `TipFeeManager` for protocol fee collection. Before execution it requires the resolved fee token to be enabled in the portal registry at the finalized `TempoState` checkpoint and escrows the maximum fee. After execution it refunds unused gas and credits the actual spend to the block sequencer in the same token. Zones never inspect FeeAMM pools, select a validator token, or swap gas fees. + ### Zone Token Model Contract creation is disabled on zones (`CREATE` and `CREATE2` revert). All TIP-20 tokens on a zone are representations of Tempo tokens, deployed at the same address as on Tempo. When the sequencer enables a token on the portal, the zone's TIP-20 factory precompile (at `0x20Fc000000000000000000000000000000000000`) provisions a TIP-20 token precompile at that address. The factory is called by `ZoneInbox` during `advanceTempo` and is not user-accessible. @@ -1426,7 +1431,7 @@ The proof must validate: ## Zone Precompiles -Zones have three categories of precompiles: TIP-20 token precompiles (one per enabled token) and two cryptographic precompiles for encrypted deposit verification. +Zones have TIP-20 token precompiles (one per enabled token), a zone-native fee manager, and two cryptographic precompiles for encrypted deposit verification. ### TIP-20 Token Precompile @@ -1436,6 +1441,14 @@ Each enabled TIP-20 token is deployed as a precompile at the same address as on - Transfer-family operations (`transfer`, `transferFrom`, `approve`) charge a fixed 100,000 gas. - `mint` is restricted to `ZoneInbox`, `burn` is restricted to `ZoneOutbox`. +### Zone Fee Manager + +| | | +|---|---| +| **Address** | `0x1c00000000000000000000000000000000000006` | + +The zone EVM invokes this precompile's deterministic fee hooks before and after every charged transaction. It accepts a fee token only when the portal's `_tokenConfigs[token].enabled` value is true at the block number finalized in `TempoState`. The pre-transaction hook escrows the maximum fee. The post-transaction hook refunds the unused portion and credits the actual spend to the sequencer under the same token. It never reads or modifies FeeAMM pools. + ### Chaum-Pedersen Verify | | | @@ -2043,6 +2056,22 @@ interface IZoneConfig { Reads the sequencer address, token registry, and encryption key from the portal on Tempo via `TempoState` storage reads. +### IZoneFeeManager + +Address: `0x1c00000000000000000000000000000000000006` + +```solidity +interface IZoneFeeManager { + function userTokens(address user) external view returns (address); + function collectedFees(address sequencer, address token) external view returns (uint256); + function setUserToken(address token) external; + function distributeFees(address sequencer, address token) external; + function isEnabledToken(address token) external view returns (bool); +} +``` + +The protocol fee hooks share this precompile's storage and registry-validation logic, so fee collection is part of normal journaled EVM execution and is reproduced by the prover. + ### TIP-403 Registry Deployed at the same address as on Tempo. Read-only on the zone. Its `isAuthorized(policyId, account)` function reads policy state from Tempo via `TempoState.readTempoStorageSlot()`. Zone-side TIP-20 transfers call this automatically. diff --git a/xtask/src/generate_zone_genesis.rs b/xtask/src/generate_zone_genesis.rs index cf480eaee..2b7f8abfc 100644 --- a/xtask/src/generate_zone_genesis.rs +++ b/xtask/src/generate_zone_genesis.rs @@ -33,19 +33,19 @@ use tempo_precompiles::{ nonce::NonceManager, stablecoin_dex::StablecoinDEX, storage::{StorageActions, StorageCtx}, - tip_fee_manager::TipFeeManager, tip20::{ISSUER_ROLE, ITIP20, TIP20Token}, tip20_factory::TIP20Factory, tip403_registry::TIP403Registry, }; use tempo_primitives::TempoHeader; use tempo_revm::{TempoBlockEnv, TempoTxEnv}; -use zone_precompiles::{TempoState as NativeTempoState, ZoneTokenFactory}; +use zone_precompiles::{TempoState as NativeTempoState, ZoneFeeManager, ZoneTokenFactory}; const TEMPO_STATE_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000000"); const ZONE_INBOX_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000001"); const ZONE_OUTBOX_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000002"); const ZONE_CONFIG_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000003"); +const ZONE_FEE_MANAGER_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000006"); const DEPLOYER: Address = address!("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); @@ -140,7 +140,7 @@ impl GenerateZoneGenesis { initialize_tip403_registry(&mut evm)?; initialize_tip20_factory(&mut evm)?; create_path_usd_token(&mut evm, self.admin)?; - initialize_fee_manager(&mut evm)?; + initialize_zone_fee_manager(&mut evm)?; initialize_stablecoin_dex(&mut evm)?; initialize_nonce_manager(&mut evm)?; initialize_account_keychain(&mut evm)?; @@ -194,6 +194,7 @@ impl GenerateZoneGenesis { ("ZoneConfig", ZONE_CONFIG_ADDRESS), ("ZoneInbox", ZONE_INBOX_ADDRESS), ("ZoneOutbox", ZONE_OUTBOX_ADDRESS), + ("ZoneFeeManager", ZONE_FEE_MANAGER_ADDRESS), ] { let account = db .cache @@ -558,8 +559,8 @@ fn create_path_usd_token(evm: &mut TempoEvm>, admin: Address) - Ok(()) } -/// Initialize the TipFeeManager precompile. -fn initialize_fee_manager(evm: &mut TempoEvm>) -> eyre::Result<()> { +/// Initialize the ZoneFeeManager precompile. +fn initialize_zone_fee_manager(evm: &mut TempoEvm>) -> eyre::Result<()> { let ctx = evm.ctx_mut(); StorageCtx::enter_evm( &mut ctx.journaled_state, @@ -568,13 +569,13 @@ fn initialize_fee_manager(evm: &mut TempoEvm>) -> eyre::Result< &ctx.tx, StorageActions::disabled(), || { - let mut fee_manager = TipFeeManager::new(); + let mut fee_manager = ZoneFeeManager::new(); fee_manager .initialize() .expect("Could not init fee manager"); }, ); - println!("Initialized TipFeeManager"); + println!("Initialized ZoneFeeManager"); Ok(()) } From 3faa8c50bd2c5d5f4277d85c5d018f653212d8d5 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xKitsune@protonmail.com> Date: Wed, 15 Jul 2026 20:09:29 -0400 Subject: [PATCH 2/6] fix: use canonical fee manager address --- crates/contracts/src/lib.rs | 2 +- crates/evm/src/lib.rs | 10 +++++----- crates/node/assets/zone-dev-genesis.json | 12 +++++------- crates/node/src/genesis.rs | 4 ++-- crates/precompiles/src/zone_fee_manager.rs | 9 ++------- crates/primitives/src/constants.rs | 2 +- docs/ZONES.md | 5 ++--- invariants.md | 2 +- specs/ref-impls/src/interfaces/IZoneFeeManager.sol | 4 ++-- specs/spec.md | 7 +++---- xtask/src/generate_zone_genesis.rs | 2 +- 11 files changed, 25 insertions(+), 34 deletions(-) diff --git a/crates/contracts/src/lib.rs b/crates/contracts/src/lib.rs index ae7714f9b..c37d0c955 100644 --- a/crates/contracts/src/lib.rs +++ b/crates/contracts/src/lib.rs @@ -6,7 +6,7 @@ //! accepts batch proofs, and processes withdrawals back to L1 recipients. //! - **ZoneOutbox** — deployed on the Zone L2. Collects user withdrawal requests, builds //! withdrawal hash chains, and exposes `LastBatch` state for proof generation. -//! - **ZoneInbox**, **TempoState**, **ZoneTxContext**, **ZoneFeeManager** — Zone L2 predeploys. +//! - **ZoneInbox**, **TempoState**, **ZoneTxContext**, **ZoneFeeManager** — Zone L2 system contracts and precompiles. //! - **ZoneFactory**, **SwapAndDepositRouter** — deployed on Tempo L1. #![cfg_attr(not(feature = "std"), no_std)] diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index 413e9a0e2..6b6b28181 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -50,8 +50,8 @@ use tempo_evm::{ use tempo_payload_types::TempoExecutionData; use tempo_precompiles::{ ACCOUNT_KEYCHAIN_ADDRESS, NONCE_PRECOMPILE_ADDRESS, PrecompileEnv, STABLECOIN_DEX_ADDRESS, - TIP_FEE_MANAGER_ADDRESS, account_keychain::AccountKeychain, nonce::NonceManager, - storage::actions::StorageActions, storage_credits::NonCreditableSlots, tip20::is_tip20_prefix, + account_keychain::AccountKeychain, nonce::NonceManager, storage::actions::StorageActions, + storage_credits::NonCreditableSlots, tip20::is_tip20_prefix, }; use tempo_primitives::{ Block, TempoHeader, TempoPrimitives, TempoReceipt, TempoTxEnvelope, TempoTxType, @@ -128,8 +128,8 @@ impl ZoneEvmFactory { // // This replaces the upstream `extend_tempo_precompiles` lookup, so we // must also handle the non-TIP-20 Tempo precompiles that are zone-relevant - // (NonceManager, AccountKeychain). FeeManager is deliberately disabled - // here because protocol fees use ZoneFeeManager above. + // (NonceManager, AccountKeychain). The upstream FeeManager lookup is + // disabled because ZoneFeeManager occupies the same address in the static map. // Zone-specific overrides (TIP20Factory, TIP403Proxy) are in the // static map via `apply_precompile` and take priority over this. let zone_cfg = cfg.clone(); @@ -146,7 +146,7 @@ impl ZoneEvmFactory { registry.clone(), sequencer.clone(), )) - } else if *address == TIP_FEE_MANAGER_ADDRESS || *address == STABLECOIN_DEX_ADDRESS { + } else if *address == ZONE_FEE_MANAGER_ADDRESS || *address == STABLECOIN_DEX_ADDRESS { None } else if *address == NONCE_PRECOMPILE_ADDRESS { Some(NonceManager::create_precompile(&zone_env)) diff --git a/crates/node/assets/zone-dev-genesis.json b/crates/node/assets/zone-dev-genesis.json index 6441c468c..59db06c6f 100644 --- a/crates/node/assets/zone-dev-genesis.json +++ b/crates/node/assets/zone-dev-genesis.json @@ -33,11 +33,6 @@ "code": "0x6080806040526004361015610012575f80fd5b5f3560e01c9081631beb1ab814610561575080631fbb25ad1461051d5780633488ce0d146102dd5780635c1bba381461023b5780636d46e98714610193578063a21de6d91461014f5763e202d99514610069575f80fd5b34610138575f366003190112610138576040516381e3da6b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260026024830152602090829060449082907f0000000000000000000000001c00000000000000000000000000000000000000165afa8015610144575f9061010d575b6040516001600160a01b039091168152602090f35b506020813d60201161013c575b816101276020938361067c565b8101031261013857602090516100f8565b5f80fd5b3d915061011a565b6040513d5f823e3d90fd5b34610138575f366003190112610138576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610138576020366003190112610138576004356001600160a01b0381169081900361013857604051630b83774760e31b8152602081600481305afa908115610144575f916101f6575b506040516001600160a01b039091169091148152602090f35b90506020813d602011610233575b816102116020938361067c565b810103126101385751906001600160a01b0382168203610138579060206101dd565b3d9150610204565b34610138575f366003190112610138576040516381e3da6b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301525f6024830152602090829060449082907f0000000000000000000000001c00000000000000000000000000000000000000165afa8015610144575f9061010d576040516001600160a01b039091168152602090f35b34610138575f366003190112610138576040516381e3da6b60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038181166004840152600760248401527f0000000000000000000000001c00000000000000000000000000000000000000169190602082604481865afa918215610144575f926104e9575b5081156104da576040516020810190600782526020815261038f60408261067c565b519020915f1981019081116104c6578060011b90808204600214901517156104c65782018092116104c65760018201908183116104c6576040516381e3da6b60e01b81526001600160a01b03821660048201526024810193909352602083604481875afa928315610144575f9361048e575b506040516381e3da6b60e01b81526001600160a01b03909116600482015260248101919091529160209083908180604481015b03915afa918215610144575f92610459575b5060ff6040928351928352166020820152f35b91506020823d602011610486575b816104746020938361067c565b810103126101385790519060ff610446565b3d9150610467565b919092506020823d6020116104be575b816104ab6020938361067c565b8101031261013857905191610434610401565b3d915061049e565b634e487b7160e01b5f52601160045260245ffd5b630c322fb560e31b5f5260045ffd5b9091506020813d602011610515575b816105056020938361067c565b810103126101385751908361036d565b3d91506104f8565b34610138575f366003190112610138576040517f0000000000000000000000001c000000000000000000000000000000000000006001600160a01b03168152602090f35b34610138576020366003190112610138576004356001600160a01b0381169190829003610138576105fe9181602080930191825260086040820152604081526105ab60608261067c565b5190206040516381e3da6b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166004820152602481019190915291829081906044820190565b03817f0000000000000000000000001c000000000000000000000000000000000000006001600160a01b03165afa8015610144575f90610649575b60209060ff604051911615158152f35b506020813d602011610674575b816106636020938361067c565b810103126101385760209051610639565b3d9150610656565b90601f8019910116810190811067ffffffffffffffff82111761069e57604052565b634e487b7160e01b5f52604160045260245ffdfea2646970667358221220fdb09622e8f7b1599cbada7d6552a3d891d6e220116c42bbb8308f891598515264736f6c63430008230033", "nonce": "0x1" }, - "0x1c00000000000000000000000000000000000006": { - "balance": "0x0", - "code": "0xef", - "nonce": "0x0" - }, "0x20c0000000000000000000000000000000000000": { "balance": "0x0", "code": "0xef", @@ -106,8 +101,11 @@ }, "0xfeec000000000000000000000000000000000000": { "balance": "0x0", - "code": "0x", - "nonce": "0x0" + "code": "0xef", + "nonce": "0x0", + "storage": { + "0xabd7b398c2237712843e3e780dcd40dfb99446b30666f04c025da4efa5ce5177": "0x0000000000000000000000000000000000000000000000000000000000000000" + } } }, "baseFeePerGas": "0x2540be400", diff --git a/crates/node/src/genesis.rs b/crates/node/src/genesis.rs index 79d6e099a..9258f3398 100644 --- a/crates/node/src/genesis.rs +++ b/crates/node/src/genesis.rs @@ -20,8 +20,8 @@ const TEMPO_STATE_ADDRESS: Address = address!("0x1c00000000000000000000000000000 const ZONE_INBOX_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000001"); /// ZoneConfig predeploy address. const ZONE_CONFIG_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000003"); -/// ZoneFeeManager predeploy address. -const ZONE_FEE_MANAGER_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000006"); +/// ZoneFeeManager precompile address. +const ZONE_FEE_MANAGER_ADDRESS: Address = address!("0xfeEC000000000000000000000000000000000000"); /// `tempoPortal` immutable occurrences in ZoneInbox deployed bytecode. const ZONE_INBOX_PORTAL_IMMUTABLES: usize = 4; diff --git a/crates/precompiles/src/zone_fee_manager.rs b/crates/precompiles/src/zone_fee_manager.rs index 43d7836e9..e1b05dea0 100644 --- a/crates/precompiles/src/zone_fee_manager.rs +++ b/crates/precompiles/src/zone_fee_manager.rs @@ -117,10 +117,7 @@ impl ZoneFeeManager { self.ensure_enabled(provider, fee_token)?; let mut token = TIP20Token::from_address(fee_token)?; - // Tempo's specialized fee-transfer helpers use the canonical fee-manager - // address as protocol custody. The public precompile and all accounting at - // that address remain disabled on zones. - token.ensure_transfer_authorized(fee_payer, tempo_precompiles::TIP_FEE_MANAGER_ADDRESS)?; + token.ensure_transfer_authorized(fee_payer, self.address)?; token.transfer_fee_pre_tx(fee_payer, max_amount)?; Ok(fee_token) } @@ -154,10 +151,8 @@ impl ZoneFeeManager { self.collected_fees[sequencer][token].write(U256::ZERO)?; let mut tip20 = TIP20Token::from_address(token)?; - // `transfer_fee_pre_tx` escrows here inside TIP-20 storage; ZoneFeeManager - // owns the corresponding sequencer ledger and is the only enabled fee API. tip20.transfer( - tempo_precompiles::TIP_FEE_MANAGER_ADDRESS, + self.address, ITIP20::transferCall { to: sequencer, amount, diff --git a/crates/primitives/src/constants.rs b/crates/primitives/src/constants.rs index d9e3a8f84..a2e41a81b 100644 --- a/crates/primitives/src/constants.rs +++ b/crates/primitives/src/constants.rs @@ -37,7 +37,7 @@ pub const ZONE_TX_CONTEXT_ADDRESS: Address = address!("0x1c000000000000000000000 /// ZoneFeeManager precompile address on Zone L2. pub const ZONE_FEE_MANAGER_ADDRESS: Address = - address!("0x1c00000000000000000000000000000000000006"); + address!("0xfeEC000000000000000000000000000000000000"); /// Chaum-Pedersen verification precompile address. pub const CHAUM_PEDERSEN_VERIFY_ADDRESS: Address = diff --git a/docs/ZONES.md b/docs/ZONES.md index 84559ebf0..67c997d3a 100644 --- a/docs/ZONES.md +++ b/docs/ZONES.md @@ -511,7 +511,7 @@ graph TB subgraph L2["Zone L2 Node"] direction TB Tasks["Sequencer Tasks
• L1 subscriber (deposit backfill + live)
• Zone engine (L1-driven block building)
• Zone monitor (batch submission to L1)
• Withdrawal processor (L1 queue drain)"] - Predeploys["Predeploys
0x1c00…0000 TempoState
0x1c00…0001 ZoneInbox
0x1c00…0002 ZoneOutbox
0x1c00…0003 ZoneConfig
0x1c00…0004 TempoStateReader
0x1c00…0005 ZoneTxContext
0x1c00…0006 ZoneFeeManager
0x20C0…0000 pathUSD"] + Predeploys["Predeploys
0x1c00…0000 TempoState
0x1c00…0001 ZoneInbox
0x1c00…0002 ZoneOutbox
0x1c00…0003 ZoneConfig
0x1c00…0004 TempoStateReader
0x1c00…0005 ZoneTxContext
0x20C0…0000 pathUSD"] end Portal -- "WSS subscription
(deposits, headers)" --> Tasks @@ -530,7 +530,7 @@ Zones inherit the Tempo L1 EVM but replace, disable, or pass through each precom | TIP-20 tokens | `0x20C0…` prefix | **Replaced** — routed through `ZoneTip20Token`, which adds privacy (caller-scoped reads), fixed gas for transfers, bridge-auth for mint/burn, and TIP-403 policy enforcement via the L1-synced cache. | | TIP20Factory | `0x20FC…0000` | **Replaced** — `ZoneTokenFactory` exposes only `enableToken(address, name, symbol, currency)`, called by ZoneInbox during `advanceTempo` to initialize bridged tokens. | | TIP403Registry | `0x403C…0000` | **Replaced** — read-only `ZoneTip403ProxyRegistry` serves authorization queries from a cache-first, L1-RPC-fallback provider. Mutating calls (`createPolicy`, `modifyPolicyWhitelist`, etc.) revert — policy state is managed on L1. | -| TipFeeManager | `0xfeec…0000` | **Disabled** — protocol fee hooks are routed to the zone-native `ZoneFeeManager`; the L1 FeeAMM and validator-token preference are unavailable. | +| TipFeeManager | `0xfeec…0000` | **Replaced** — `ZoneFeeManager` accepts any portal-enabled fee token and accounts fees in the token paid, without validator-token preferences or FeeAMM routing. | | StablecoinDEX | `0xdec0…0000` | **Disabled** — not registered on zones, so the address behaves like an empty account. Users on zones can trade on the StablecoinDEX on Tempo via the bridge. | | NonceManager | `0x4E4F…0000` | **Unchanged** — same implementation as L1, runs locally on zone state. | | ValidatorConfig (legacy) | `0xCCCC…0000` | **Not registered** — zones do not run validators, so the precompile is not loaded. | @@ -545,7 +545,6 @@ Zones inherit the Tempo L1 EVM but replace, disable, or pass through each precom |------------|---------|-------------| | TempoStateReader | `0x1c00…0004` | Reads L1 contract storage from zone contracts via the L1 state cache. | | ZoneTxContext | `0x1c00…0005` | Exposes the hash of the currently executing zone transaction (`currentTxHash`), used by ZoneOutbox for authenticated withdrawals. | -| ZoneFeeManager | `0x1c00…0006` | Accepts any portal-enabled fee token, skips AMM/liquidity routing, and accounts sequencer fees in the token paid by the user. | | ChaumPedersenVerify | `0x1c00…0100` | Verifies DLOG equality proofs for ECDH key exchange (encrypted deposits). | | AesGcmDecrypt | `0x1c00…0101` | AES-256-GCM authenticated decryption (encrypted deposit payloads). | diff --git a/invariants.md b/invariants.md index b6125608b..08572ae4f 100644 --- a/invariants.md +++ b/invariants.md @@ -20,7 +20,7 @@ for auditors, invariant/fuzz test authors, and production monitoring. | `TEMPO-ZONE-CHAIN-ID-UNIQUE` | Each live zone uses the chain ID derived from its zone ID, and no two live zones share a chain ID | 🟡 | Cross-zone replay protection fails; signed transactions may be valid on more than one zone | | `TEMPO-ZONE-PORTAL-PAIRING` | A `ZoneFactory` registry entry maps one zone ID to exactly one portal, and that portal uses the factory's shared messenger | 🟡 | Deposits, withdrawals, callbacks, and config reads can target different trust domains | | `TEMPO-ZONE-GENESIS-BINDING` | Portal `blockHash`, `genesisTempoBlockNumber`, and emitted zone creation parameters match the zone genesis file | 🔴 | The zone may prove batches from a different genesis state than the portal expects | -| `TEMPO-ZONE-PREDEPLOY-ADDRESSES` | `TempoState`, `ZoneInbox`, `ZoneOutbox`, `ZoneConfig`, `TempoStateReader`, `ZoneTxContext`, and `ZoneFeeManager` exist at their fixed addresses | 🔴 | System calls can be redirected or missing, invalidating mint/burn, fee collection, proofs, and Tempo reads | +| `TEMPO-ZONE-PREDEPLOY-ADDRESSES` | `TempoState`, `ZoneInbox`, `ZoneOutbox`, `ZoneConfig`, `TempoStateReader`, and `ZoneTxContext` exist at their fixed addresses | 🔴 | System calls can be redirected or missing, invalidating mint/burn, proofs, and Tempo reads | ### Access Control and Configuration diff --git a/specs/ref-impls/src/interfaces/IZoneFeeManager.sol b/specs/ref-impls/src/interfaces/IZoneFeeManager.sol index 77828c8ad..a4c79368d 100644 --- a/specs/ref-impls/src/interfaces/IZoneFeeManager.sol +++ b/specs/ref-impls/src/interfaces/IZoneFeeManager.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; -// ZoneFeeManager precompile address (0x1c00...0006). -address constant ZONE_FEE_MANAGER = 0x1c00000000000000000000000000000000000006; +// ZoneFeeManager replaces Tempo's fee manager at the canonical precompile address. +address constant ZONE_FEE_MANAGER = 0xfeEC000000000000000000000000000000000000; /// @title IZoneFeeManager /// @notice Zone-native fee manager with no AMM or validator-token preference. diff --git a/specs/spec.md b/specs/spec.md index 1a811bcfd..e37701bcb 100644 --- a/specs/spec.md +++ b/specs/spec.md @@ -264,7 +264,7 @@ The shared `ZoneMessenger` relays withdrawal callbacks for all zones created by ### Zone Predeploys -Each zone has six system contracts deployed at genesis at fixed addresses: +Each zone has five system contracts deployed at genesis at fixed addresses: | Predeploy | Address | Purpose | |-----------|---------|---------| @@ -273,7 +273,6 @@ Each zone has six system contracts deployed at genesis at fixed addresses: | [`ZoneOutbox`](#izoneoutbox) | `0x1c00...0002` | Handles withdrawal requests and batch finalization. Sole burn authority. | | [`ZoneConfig`](#izoneconfig) | `0x1c00...0003` | Central configuration. Reads the sequencer address and token registry from Tempo via `TempoState`. | | `ZoneTxContext` | `0x1c00...0005` | Provides the current transaction hash to system contracts (used by `ZoneOutbox` for `senderTag` computation). | -| `ZoneFeeManager` | `0x1c00...0006` | Collects gas fees in any token enabled by `ZoneConfig`, without AMM routing, and credits the sequencer in that token. | `ZoneConfig` reads the sequencer address and token registry from the portal on Tempo via `TempoState` storage reads, making Tempo the single source of truth for zone configuration. See [Tempo State Reads](#tempo-state-reads) for details. @@ -1445,7 +1444,7 @@ Each enabled TIP-20 token is deployed as a precompile at the same address as on | | | |---|---| -| **Address** | `0x1c00000000000000000000000000000000000006` | +| **Address** | `0xfeEC000000000000000000000000000000000000` | The zone EVM invokes this precompile's deterministic fee hooks before and after every charged transaction. It accepts a fee token only when the portal's `_tokenConfigs[token].enabled` value is true at the block number finalized in `TempoState`. The pre-transaction hook escrows the maximum fee. The post-transaction hook refunds the unused portion and credits the actual spend to the sequencer under the same token. It never reads or modifies FeeAMM pools. @@ -2058,7 +2057,7 @@ Reads the sequencer address, token registry, and encryption key from the portal ### IZoneFeeManager -Address: `0x1c00000000000000000000000000000000000006` +Address: `0xfeEC000000000000000000000000000000000000` ```solidity interface IZoneFeeManager { diff --git a/xtask/src/generate_zone_genesis.rs b/xtask/src/generate_zone_genesis.rs index 2b7f8abfc..3dd21349f 100644 --- a/xtask/src/generate_zone_genesis.rs +++ b/xtask/src/generate_zone_genesis.rs @@ -45,7 +45,7 @@ const TEMPO_STATE_ADDRESS: Address = address!("0x1c00000000000000000000000000000 const ZONE_INBOX_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000001"); const ZONE_OUTBOX_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000002"); const ZONE_CONFIG_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000003"); -const ZONE_FEE_MANAGER_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000006"); +const ZONE_FEE_MANAGER_ADDRESS: Address = address!("0xfeEC000000000000000000000000000000000000"); const DEPLOYER: Address = address!("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); From 3a8f8d9e780681af9ea6987323455e3a098442d9 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xKitsune@protonmail.com> Date: Wed, 15 Jul 2026 20:24:06 -0400 Subject: [PATCH 3/6] fmt: forge fmt --- specs/ref-impls/src/interfaces/IZoneFeeManager.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/ref-impls/src/interfaces/IZoneFeeManager.sol b/specs/ref-impls/src/interfaces/IZoneFeeManager.sol index a4c79368d..453d494fc 100644 --- a/specs/ref-impls/src/interfaces/IZoneFeeManager.sol +++ b/specs/ref-impls/src/interfaces/IZoneFeeManager.sol @@ -7,6 +7,7 @@ address constant ZONE_FEE_MANAGER = 0xfeEC000000000000000000000000000000000000; /// @title IZoneFeeManager /// @notice Zone-native fee manager with no AMM or validator-token preference. interface IZoneFeeManager { + event UserTokenSet(address indexed user, address indexed token); event FeesDistributed(address indexed sequencer, address indexed token, uint256 amount); @@ -15,4 +16,5 @@ interface IZoneFeeManager { function setUserToken(address token) external; function distributeFees(address sequencer, address token) external; function isEnabledToken(address token) external view returns (bool); + } From d555931760f72374b75ae28cf81a2cab4ab490a4 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xKitsune@protonmail.com> Date: Fri, 17 Jul 2026 17:41:07 -0400 Subject: [PATCH 4/6] fix: harden zone fee token handling --- crates/evm/src/fee_manager.rs | 22 ++- crates/evm/src/lib.rs | 22 ++- crates/l1/src/lib.rs | 6 +- crates/l1/src/subscriber.rs | 22 ++- crates/l1/src/tests.rs | 27 +++ crates/node/assets/zone-dev-genesis.json | 8 +- crates/node/src/genesis.rs | 4 +- crates/precompiles/src/zone_fee_manager.rs | 204 ++++++++++++++++++-- specs/ref-impls/src/tempo/ZonePortal.sol | 8 +- specs/ref-impls/test/tempo/ZonePortal.t.sol | 16 ++ 10 files changed, 299 insertions(+), 40 deletions(-) diff --git a/crates/evm/src/fee_manager.rs b/crates/evm/src/fee_manager.rs index 45926d745..05a1ea413 100644 --- a/crates/evm/src/fee_manager.rs +++ b/crates/evm/src/fee_manager.rs @@ -11,23 +11,29 @@ use tempo_precompiles::{ }; use tempo_revm::{ProtocolFeeManager, TempoStateAccess, TempoTx, TempoTxEnv}; use tempo_zone_contracts::{IZoneFeeManager, ZONE_FEE_MANAGER_ADDRESS}; -use zone_precompiles::{ZoneConfigReader, ZoneFeeManager}; +use zone_l1::state::PolicyProvider; +use zone_precompiles::{ZoneConfigReader, ZoneFeeManager, ZoneTip403ProxyRegistry}; /// Zone implementation of Tempo's internal protocol fee hooks. #[derive(Clone)] pub(crate) struct ZoneProtocolFeeManager

{ provider: P, + registry: Option>, } impl

core::fmt::Debug for ZoneProtocolFeeManager

{ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("ZoneProtocolFeeManager").finish_non_exhaustive() + f.debug_struct("ZoneProtocolFeeManager") + .finish_non_exhaustive() } } impl

ZoneProtocolFeeManager

{ - pub(crate) const fn new(provider: P) -> Self { - Self { provider } + pub(crate) const fn new( + provider: P, + registry: Option>, + ) -> Self { + Self { provider, registry } } } @@ -90,7 +96,13 @@ where _beneficiary: Address, _skip_liquidity_check: bool, ) -> Result

{ - ZoneFeeManager::new().collect_fee_pre_tx(&self.provider, fee_payer, user_token, max_amount) + ZoneFeeManager::new().collect_fee_pre_tx( + &self.provider, + self.registry.as_ref(), + fee_payer, + user_token, + max_amount, + ) } fn collect_fee_post_tx( diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index 5537c2e03..d44be03ed 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -21,7 +21,8 @@ use crate::{ precompiles::{ AES_GCM_DECRYPT_ADDRESS, AesGcmDecrypt, CHAUM_PEDERSEN_VERIFY_ADDRESS, ChaumPedersenVerify, SequencerExt, TempoState, ZONE_TIP20_FACTORY_ADDRESS, ZONE_TIP403_PROXY_ADDRESS, - ZoneConfigReader, ZoneFeeManager, ZoneTip20Token, ZoneTip403ProxyRegistry, ZoneTokenFactory, + ZoneConfigReader, ZoneFeeManager, ZoneTip20Token, ZoneTip403ProxyRegistry, + ZoneTokenFactory, }, tx_context::ZoneTxContext, }; @@ -96,14 +97,25 @@ where evm: TempoEvm, ) -> TempoEvm { let cfg = evm.ctx().cfg.clone(); - let mut evm = evm.with_fee_manager(ZoneProtocolFeeManager::new(self.l1_reader.clone())); + let registry = self + .policy_provider + .clone() + .map(ZoneTip403ProxyRegistry::new); + let mut evm = evm.with_fee_manager(ZoneProtocolFeeManager::new( + self.l1_reader.clone(), + registry.clone(), + )); let (_, _, precompiles) = evm.components_mut(); precompiles.apply_precompile(&TEMPO_STATE_ADDRESS, |_| { Some(TempoState::create(self.l1_reader.clone(), &cfg)) }); precompiles.apply_precompile(&ZONE_TX_CONTEXT_ADDRESS, |_| Some(ZoneTxContext::create())); precompiles.apply_precompile(&ZONE_FEE_MANAGER_ADDRESS, |_| { - Some(ZoneFeeManager::create(self.l1_reader.clone(), &cfg)) + Some(ZoneFeeManager::create( + self.l1_reader.clone(), + registry.clone(), + &cfg, + )) }); precompiles.apply_precompile(&CHAUM_PEDERSEN_VERIFY_ADDRESS, |_| { Some(ChaumPedersenVerify::create(&cfg)) @@ -114,10 +126,6 @@ where precompiles.apply_precompile(&ZONE_TIP20_FACTORY_ADDRESS, |_| { Some(ZoneTokenFactory::create(&cfg)) }); - let registry = self - .policy_provider - .clone() - .map(ZoneTip403ProxyRegistry::new); let sequencer: Arc = Arc::new(self.l1_reader.clone()); if let Some(provider) = self.policy_provider.clone() { diff --git a/crates/l1/src/lib.rs b/crates/l1/src/lib.rs index ad8033386..5b19785c6 100644 --- a/crates/l1/src/lib.rs +++ b/crates/l1/src/lib.rs @@ -70,7 +70,7 @@ use crate::{ abi::{ EncryptedDeposit as AbiEncryptedDeposit, EncryptedDepositPayload as AbiEncryptedDepositPayload, PORTAL_PENDING_SEQUENCER_SLOT, - PORTAL_SEQUENCER_SLOT, + PORTAL_SEQUENCER_SLOT, PORTAL_TOKEN_CONFIGS_SLOT, ZonePortal::{ self, DepositMade, EncryptedDepositMade, SequencerTransferStarted, SequencerTransferred, TokenEnabled, WithdrawalBounceBack, ZonePortalEvents, @@ -102,6 +102,6 @@ pub(crate) use event::EnqueueOutcome; pub(crate) use queue::PendingDeposits; #[cfg(test)] pub(crate) use subscriber::{ - LocalTempoCheckpointReader, address_to_storage_value, apply_sequencer_events_to_cache, - verify_receipts, + LocalTempoCheckpointReader, address_to_storage_value, apply_enabled_tokens_to_cache, + apply_sequencer_events_to_cache, verify_receipts, }; diff --git a/crates/l1/src/subscriber.rs b/crates/l1/src/subscriber.rs index d6cab03a1..ab73c5a70 100644 --- a/crates/l1/src/subscriber.rs +++ b/crates/l1/src/subscriber.rs @@ -664,11 +664,17 @@ impl L1Subscriber { /// Write decoded portal state changes into the shared L1 cache at the /// confirmed block height. fn apply_portal_state_events(&self, block_number: u64, portal_events: &L1PortalEvents) { - if portal_events.sequencer_events.is_empty() { + if portal_events.sequencer_events.is_empty() && portal_events.enabled_tokens.is_empty() { return; } let mut cache = self.config.l1_state_cache.write(); + apply_enabled_tokens_to_cache( + &mut cache, + self.config.portal_address, + block_number, + &portal_events.enabled_tokens, + ); apply_sequencer_events_to_cache( &mut cache, self.config.portal_address, @@ -756,6 +762,20 @@ pub(crate) fn verify_receipts( Ok(()) } +pub(crate) fn apply_enabled_tokens_to_cache( + cache: &mut L1StateCacheInner, + portal_address: Address, + block_number: u64, + enabled_tokens: &[EnabledToken], +) { + // TokenEnabled sets both packed booleans in TokenConfig to true. + let enabled_config = B256::from(U256::from(0x0101).to_be_bytes()); + for enabled in enabled_tokens { + let slot = keccak256((enabled.token, PORTAL_TOKEN_CONFIGS_SLOT).abi_encode()); + cache.set(portal_address, slot, block_number, enabled_config); + } +} + pub(crate) fn apply_sequencer_events_to_cache( cache: &mut L1StateCacheInner, portal_address: Address, diff --git a/crates/l1/src/tests.rs b/crates/l1/src/tests.rs index 4c792ea78..41a7249da 100644 --- a/crates/l1/src/tests.rs +++ b/crates/l1/src/tests.rs @@ -644,6 +644,33 @@ fn test_apply_sequencer_events_to_cache_sets_pending_sequencer() { ); } +#[test] +fn test_apply_enabled_tokens_to_cache_supersedes_an_older_disabled_value() { + let portal_address = address!("0x0000000000000000000000000000000000000ABC"); + let token = address!("0x20C00000000000000000000000000000000000A1"); + let slot = keccak256((token, PORTAL_TOKEN_CONFIGS_SLOT).abi_encode()); + let mut cache = L1StateCacheInner::new(HashSet::from([portal_address])); + cache.set(portal_address, slot, 41, B256::ZERO); + + apply_enabled_tokens_to_cache( + &mut cache, + portal_address, + 42, + &[EnabledToken { + token, + name: "Alpha USD".into(), + symbol: "aUSD".into(), + currency: "USD".into(), + }], + ); + + assert_eq!(cache.get(portal_address, slot, 41), Some(B256::ZERO)); + assert_eq!( + cache.get(portal_address, slot, 42), + Some(B256::from(U256::from(0x0101).to_be_bytes())) + ); +} + #[test] fn test_apply_sequencer_events_to_cache_accept_clears_pending_sequencer() { let portal_address = address!("0x0000000000000000000000000000000000000ABC"); diff --git a/crates/node/assets/zone-dev-genesis.json b/crates/node/assets/zone-dev-genesis.json index 81afd1970..4ae6b4b60 100644 --- a/crates/node/assets/zone-dev-genesis.json +++ b/crates/node/assets/zone-dev-genesis.json @@ -20,17 +20,17 @@ }, "0x1c00000000000000000000000000000000000001": { "balance": "0x0", - "code": "0x60806040526004361015610011575f80fd5b5f3560e01c80631fbb25ad146100945780632d4884821461008f57806379502c551461008a57806382648c3b14610085578063857e85f81461008057806386516ec01461007b578063a21de6d9146100765763bffa55d514610071575f80fd5b610356565b610312565b610252565b61018a565b610150565b61010c565b6100e6565b346100d8575f3660031901126100d8577f0000000000000000000000001c000000000000000000000000000000000000006001600160a01b03166080908152602090f35b5f80fd5b5f9103126100d857565b346100d8575f3660031901126100d85760206001600160401b0360015416604051908152f35b346100d8575f3660031901126100d8576040517f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03168152602090f35b346100d8575f3660031901126100d85760205f54604051908152f35b6001600160a01b038116036100d857565b35906101888261016c565b565b346100d85760403660031901126100d85760206001600160801b036101e86004356101b48161016c565b602435906101c18261016c565b60018060a01b03165f526002845260405f209060018060a01b03165f5260205260405f2090565b5416604051908152f35b9181601f840112156100d8578235916001600160401b0383116100d8576020808501948460051b0101116100d857565b9181601f840112156100d8578235916001600160401b0383116100d8576020808501948460071b0101116100d857565b346100d85760803660031901126100d8576004356001600160401b0381116100d857366023820112156100d8578060040135906001600160401b0382116100d85736602483830101116100d8576024356001600160401b0381116100d8576102be9036906004016101f2565b6044929192356001600160401b0381116100d8576102e0903690600401610222565b91606435946001600160401b0386116100d8576103109661030760249736906004016101f2565b97909601610c10565b005b346100d8575f3660031901126100d8576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100d85760203660031901126100d8576004356103738161016c565b6001600160a01b0381165f8181526002602081815260408084203380865281845291852054959094529190526001600160801b0390921692916103db916103cb91905b9060018060a01b03165f5260205260405f2090565b80546001600160801b0319169055565b6001600160a01b031690813b156100d8576040516340c10f1960e01b8152915f838061040b853360048401610aad565b038183855af19283156104915761047393610477575b506040516001600160801b038316815233907fffd3bbab073ab4b2d0792c270104924c14c285a153b9acddabae166395d2eb5c90602090a36040516001600160801b0390911681529081906020820190565b0390f35b806104855f61048b936104e5565b806100dc565b5f610421565b61051e565b634e487b7160e01b5f52604160045260245ffd5b60a081019081106001600160401b038211176104c557604052565b610496565b60c081019081106001600160401b038211176104c557604052565b90601f801991011681019081106001600160401b038211176104c557604052565b908160209103126100d8575161051b8161016c565b90565b6040513d5f823e3d90fd5b908060209392818452848401375f828201840152601f01601f1916010190565b91602061051b938181520191610529565b634e487b7160e01b5f52603260045260245ffd5b91908110156105905760051b81013590607e19813603018212156100d8570190565b61055a565b3561051b8161016c565b903590601e19813603018212156100d857018035906001600160401b0382116100d8576020019181360383136100d857565b9593916105fd9061060b9461051b99979360018060a01b03168952608060208a01526080890191610529565b918683036040880152610529565b926060818503910152610529565b9492909361063761051b979561064594606089526060890191610529565b918683036020880152610529565b926040818503910152610529565b91908110156105905760051b81013590605e19813603018212156100d8570190565b6002111561067f57565b634e487b7160e01b5f52602160045260245ffd5b3560028110156100d85790565b35906001600160801b03821682036100d857565b60ff8116036100d857565b6001600160401b0381116104c557601f01601f191660200190565b35906001600160a01b0319821682036100d857565b35906001600160801b0319821682036100d857565b919060a0838203126100d8576040519061071d826104aa565b8193803583526020810135610731816106b4565b602084015260408101356001600160401b0381116100d857810182601f820112156100d857803591610762836106bf565b9361077060405195866104e5565b838552602084840101116100d8576080935f6020856107b19682899701838601378301015260408601526107a6606082016106da565b6060860152016106ef565b910152565b6020818303126100d8578035906001600160401b0382116100d8570160c0818303126100d857604051916107e9836104ca565b81356107f48161016c565b83526108026020830161017d565b6020840152610813604083016106a0565b60408401526108246060830161017d565b60608401526080820135608084015260a08201356001600160401b0381116100d8576108509201610704565b60a082015290565b90600282101561067f5752565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b92919060a060409161089c866001610858565b60606020870152600180831b038151166060870152600180831b0360208201511660808701526001600160801b03838201511682870152600180831b0360608201511660c0870152608081015160e0870152015160c0610100860152805161012086015260ff6020820151166101408601526109288282015160a06101608801526101c0870190610865565b60608201516001600160a01b0319166101808701526080909101516001600160801b0319166101a0860152930152565b801515036100d857565b3561051b81610958565b634e487b7160e01b5f52601160045260245ffd5b5f19811461098e5760010190565b61096c565b91908110156105905760071b0190565b3561051b816106b4565b908160209103126100d8575161051b81610958565b604051906109d16040836104e5565b600d82526c65636965732d6165732d6b657960981b6020830152565b91906040838203126100d85782516001600160401b0381116100d85783019080601f830112156100d8578151610a22816106bf565b91610a3060405193846104e5565b818352602082850101116100d8576020815f92828096018386015e8301015292015161051b81610958565b92610a919060809360209397969786526bffffffffffffffffffffffff60a01b168386015260a0604086015260a0850190610865565b83810360608501525f815201936001600160801b031916910152565b6001600160a01b0390911681526001600160801b03909116602082015260400190565b908160c09103126100d85760a060405191610aea836104ca565b8035610af58161016c565b83526020810135610b058161016c565b60208401526040810135610b188161016c565b6040840152610b29606082016106a0565b60608401526080810135610b3c8161016c565b6080840152013560a082015290565b60e0909392919360a0610100820195610b64835f610858565b600180831b038151166020840152600180831b036020820151166040840152600180831b0360408201511660608401526001600160801b036060820151166080840152600180831b0360808201511682840152015160c08201520152565b908160209103126100d8575190565b906001600160401b03809116911601906001600160401b03821161098e57565b908160209103126100d857516001600160401b03811681036100d85790565b959194939296903315158061172b575b61171c577f0000000000000000000000001c000000000000000000000000000000000000006001600160a01b031696873b156100d85760405163fe77009960e01b8152915f9183918291610c78919060048401610549565b0381838b5af1801561049157611708575b505f9492945b8181106115ed575050505f54925f915f915b878310610e9f5750505003610e90576040516381e3da6b60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015260056024820152602081604481865afa801561049157610e73575b50805f55610d4e610d326001600160401b038516610d2d6001546001600160401b031690565b610bd1565b6001600160401b03166001600160401b03196001541617600155565b604051631014997960e31b815291602083600481845afa928315610491575f93610e3e575b50602060049160405192838092631d04645f60e01b82525afa8015610491577fd2d2bf1e295f62cd08f0f0ab45818efeaba78b58310526f7b7e9686b8aeded1a926001600160401b03925f92610e09575b50610e0490610ddb6001546001600160401b031690565b6040805198895260208901929092526001600160401b0316908701529116939081906060820190565b0390a3565b610e04919250610e309060203d602011610e37575b610e2881836104e5565b810190610bf1565b9190610dc4565b503d610e1e565b6004919350610e64602091823d8411610e6c575b610e5c81836104e5565b810190610bc2565b939150610d73565b503d610e52565b610e8b9060203d602011610e6c57610e5c81836104e5565b610d07565b6361aba18160e11b5f5260045ffd5b909194610ead868985610653565b95610eb787610693565b610ec081610675565b6111c957610edc610ed4602089018961059f565b810190610ad0565b91604051610f0181610ef360208201948786610b4b565b03601f1981018352826104e5565b51902060808301805191989091610f28906001600160a01b03165b6001600160a01b031690565b610f41575050610f39600192611bd0565b019190610ca1565b6040610f4d9101610962565b15610fa35760208301518351600194610f9e936001600160a01b03938416939092610f9791610f8991606091169301516001600160801b031690565b92516001600160a01b031690565b928b611828565b610f39565b8251610fb990610f1c906001600160a01b031681565b604084018051909291906001600160a01b0316946060810192610fe384516001600160801b031690565b92803b156100d8575f8d946110129260019a836040518096819582946340c10f1960e01b845260048401610aad565b03925af190816111b5575b506111135781517f3c1af70310b8c9d43b0a7207217f398e4a3114f1728987c40addb451399ac87c9290611070906001600160a01b031686516001600160801b031684516001600160a01b031691611b45565b61110b6110ca6110bc6110ae6110a0611092602087015160018060a01b031690565b9a516001600160a01b031690565b94516001600160a01b031690565b97516001600160801b031690565b93516001600160a01b031690565b604080516001600160a01b0398891681526001600160801b03909516602086015296169583019590955260a088901b88900390811695169381906060820190565b0390a4610f39565b5060208101517fd5277bc9597c7da3fab9cdbba4de6005f48b9eb7389cf2389c4ea9eea3172c219190611157906001600160a01b031695516001600160a01b031690565b815161110b9060a090611172906001600160a01b03166110ae565b930151604080516001600160a01b0390981688526001600160801b0390941660208801529286019290925260a088901b8890039081169516939081906060820190565b806104855f6111c3936104e5565b5f61101d565b61120660406111e66111de60208b018b61059f565b8101906107b6565b9381516111fc81610ef360208201948986610889565b5190209801610962565b61159b578585101561158c5761122661121e86610980565b958785610993565b6080830161123481516118a2565b92906020604060a08801936112b9855161125385825192015160ff1690565b988335966112628786016109a3565b8651635f8a996960e01b8152600481019490945260ff9b8c166024850152604484018990528b166064840152608483015290981660a48901529101803560c48801526020013560e487015285908190610104820190565b0381731c000000000000000000000000000000000001005afa938415610491575f9461155c575b508392606094611444575b505050158015611438575b61142b5761130390611b12565b835191939161131c90610f1c906001600160a01b031681565b604083019161133283516001600160801b031690565b95823b156100d8575f806113618e9560019a6040519485809481936340c10f1960e01b83528960048401610aad565b03925af19081611417575b5061137e575050610f9e929150611a8e565b60208501517ffc236d1b4402e76c2b0a882db36ad3a6fb0f5b6b7edc8ae317d993f10434c43692919061110b906113d8906113ca906001600160a01b031698516001600160a01b031690565b96516001600160801b031690565b604080516001600160a01b0390981688526001600160801b03909116602088015286019290925260a088901b8890039081169516939081906060820190565b806104855f611425936104e5565b5f61136c565b50610f9e60019288611a8e565b506040815114156112f6565b518251516040516bffffffffffffffffffffffff197f000000000000000000000000000000000000000000000000000000000000000060601b166020820152603481019290925260548083019190915281525f93506114ce916114bb91906114ad6074836104e5565b6114b56109c2565b90611a22565b9151938401516001600160a01b03191690565b906115076114ec608060408701519601516001600160801b03191690565b60405163f4a7eb1360e01b8152958694859460048601610a5b565b0381731c000000000000000000000000000000000001015afa8015610491575f915f91611538575b505f80806112eb565b905061155691503d805f833e61154e81836104e5565b8101906109ed565b5f61152f565b61157e91945060203d8111611585575b61157681836104e5565b8101906109ad565b925f6112e0565b503d61156c565b6351de8c1f60e01b5f5260045ffd5b6020820151600192610f9e916001600160a01b031681519091906001600160a01b03166115e660606115d760408501516001600160801b031690565b9301516001600160a01b031690565b928b6117ba565b6115fb81838598969861056e565b9061160582610595565b916020810190611615828261059f565b90926040830193611626858561059f565b91909760608601986116388a8861059f565b9061083f60921b3b156100d8575f9561166693604051998a9788976374ae5b3760e11b8952600489016105d1565b03818361083f60921b5af18015610491576001967f4ac4dcc08b0c26c3fb6b58c64c1392b7934b1ce6b0382a5986ea5c3de795e053946116c9946116e8936116f4575b506116d16116c06116b983610595565b958361059f565b9690988361059f565b93909261059f565b9290916040519687968c8060a01b03169987610619565b0390a201949294610c8f565b806104855f611702936104e5565b5f6116a9565b806104855f611716936104e5565b5f610c89565b63bb62587160e01b5f5260045ffd5b50604051630b83774760e31b81526020816004817f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03165afa908115610491575f9161178b575b506001600160a01b0316331415610c20565b6117ad915060203d6020116117b3575b6117a581836104e5565b810190610506565b5f611779565b503d61179b565b92907f4620415fad9c416306a56ca0ee640b3418628a5f2e45ddde3ddf7452a7a654fb92946001600160801b036080936117f583828a611b45565b60405197611804896001610858565b6001600160a01b0390811660208a01529116604088015290811660608701521693a3565b92907f4620415fad9c416306a56ca0ee640b3418628a5f2e45ddde3ddf7452a7a654fb92946001600160801b0360809361186383828a611b45565b60405197611804895f610858565b908160011b918083046002149015171561098e57565b906001820180921161098e57565b9190820180921161098e57565b6118d3906118cd60405160208101906118c481610ef384906007602083019252565b51902091611871565b90611895565b9061195c6118e083611887565b6040516381e3da6b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000818116600484015260248301969096527f0000000000000000000000001c0000000000000000000000000000000000000016949092909190602090849081906044820190565b0381885afa928315610491575f936119ef575b5082156119e0576040516381e3da6b60e01b81526001600160a01b03929092166004830152602482015292602090849060449082905afa928315610491575f936119bc575b509160ff1690565b60ff9193506119d99060203d602011610e6c57610e5c81836104e5565b92906119b4565b63fb1f4a4960e01b5f5260045ffd5b611a0991935060203d602011610e6c57610e5c81836104e5565b915f61196f565b805191908290602001825e015f815290565b91611a766001611a5061051b95611a619560405191602083015260208252611a4b6040836104e5565b611e00565b926040519485916020830190611a10565b8260f81b815203601e198101855201836104e5565b60405190602082015260208152611a4b6040826104e5565b815160408301805160608501517f95705d99ac13cf82894fd274cd871942e6f301c98c186271337e8fedbbb9d7ea93611adf926001600160a01b03928316926001600160801b039091169116611b45565b6020840151935190516040516001600160a01b039586169590928392610e04926001600160801b03909116911683610aad565b90815160408103611b2e57506034602083015160601c92015190565b633fbbeba160e21b5f52600452604060245260445ffd5b9060026007609a1b013b156100d8576040516338b8fb9760e21b81526001600160a01b0392831660048201526001600160801b03919091166024820152911660448201525f816064818360026007609a1b015af1801561049157611ba65750565b5f610188916104e5565b906001600160801b03809116911601906001600160801b03821161098e57565b6040810151611c289190602090611bff90611bf3906001600160a01b0316610f1c565b6001600160401b031690565b60405163a3a124c360e01b81526001600160401b03909116600482015292839081906024820190565b03815f60026007609a1b015af1918215610491575f92611dc9575b508051611c5a90610f1c906001600160a01b031681565b916060820192611c7184516001600160801b031690565b90803b156100d8576040516340c10f1960e01b8152915f918391829084908290611c9f908960048401610aad565b03925af19081611db5575b50611d7e57611d5e611d507f92d91a50ae3561d4edd8eadad5f27f09116faaa4f733bd37682c49b4cbc20e3993611d43611ceb87516001600160801b031690565b82516001600160a01b03165f908152600260205260409020611d2990611d129088906103b6565b91611d2483546001600160801b031690565b611bb0565b6001600160801b03166001600160801b0319825416179055565b516001600160a01b031690565b93516001600160801b031690565b6040516001600160a01b03909216938291611d799183610aad565b0390a2565b90517fbc1cf3ff6e619587619408b396a13775509216474757afb9d29bda1a96f590f09190611d5e906001600160a01b0316611d50565b806104855f611dc3936104e5565b5f611caa565b611de391925060203d6020116117b3576117a581836104e5565b905f611c43565b61051b9392604092825260208201520190611a10565b5f908051604081115f14611f0c57505f611e2260209260405191828092611a10565b039060025afa156104915760205f611e9d610ef3611e918351965b6040519283917f363636363636363636363636363636363636363636363636363636363636363689187f36363636363636363636363636363636363636363636363636363636363636368b18898501611dea565b60405191828092611a10565b039060025afa1561049157611efc7f5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c611e915f93610ef36020968651908560405196879518911889850191606093918352602083015260408201520190565b039060025afa15610491575f5190565b6020808301519490821193509160018414611f7b57505f925b60208210611f65575b6040821016611f4b575b505f611e9d610ef3611e91602094611e3d565b600160409190910360031b1b5f190119909116905f611f38565b935f1960018360200360031b1b01191693611f2e565b6040015192611f2556fea26469706673582212205b68507f3077a8996d1334365c031587ef9cc06931828bf8527cfde98c15015764736f6c63430008230033", + "code": "0x60806040526004361015610011575f80fd5b5f3560e01c80631fbb25ad146100945780632d4884821461008f57806379502c551461008a57806382648c3b14610085578063857e85f81461008057806386516ec01461007b578063a21de6d9146100765763bffa55d514610071575f80fd5b610356565b610312565b610252565b61018a565b610150565b61010c565b6100e6565b346100d8575f3660031901126100d8577f0000000000000000000000001c000000000000000000000000000000000000006001600160a01b03166080908152602090f35b5f80fd5b5f9103126100d857565b346100d8575f3660031901126100d85760206001600160401b0360015416604051908152f35b346100d8575f3660031901126100d8576040517f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03168152602090f35b346100d8575f3660031901126100d85760205f54604051908152f35b6001600160a01b038116036100d857565b35906101888261016c565b565b346100d85760403660031901126100d85760206001600160801b036101e86004356101b48161016c565b602435906101c18261016c565b60018060a01b03165f526002845260405f209060018060a01b03165f5260205260405f2090565b5416604051908152f35b9181601f840112156100d8578235916001600160401b0383116100d8576020808501948460051b0101116100d857565b9181601f840112156100d8578235916001600160401b0383116100d8576020808501948460071b0101116100d857565b346100d85760803660031901126100d8576004356001600160401b0381116100d857366023820112156100d8578060040135906001600160401b0382116100d85736602483830101116100d8576024356001600160401b0381116100d8576102be9036906004016101f2565b6044929192356001600160401b0381116100d8576102e0903690600401610222565b91606435946001600160401b0386116100d8576103109661030760249736906004016101f2565b97909601610c10565b005b346100d8575f3660031901126100d8576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100d85760203660031901126100d8576004356103738161016c565b6001600160a01b0381165f8181526002602081815260408084203380865281845291852054959094529190526001600160801b0390921692916103db916103cb91905b9060018060a01b03165f5260205260405f2090565b80546001600160801b0319169055565b6001600160a01b031690813b156100d8576040516340c10f1960e01b8152915f838061040b853360048401610aad565b038183855af19283156104915761047393610477575b506040516001600160801b038316815233907fffd3bbab073ab4b2d0792c270104924c14c285a153b9acddabae166395d2eb5c90602090a36040516001600160801b0390911681529081906020820190565b0390f35b806104855f61048b936104e5565b806100dc565b5f610421565b61051e565b634e487b7160e01b5f52604160045260245ffd5b60a081019081106001600160401b038211176104c557604052565b610496565b60c081019081106001600160401b038211176104c557604052565b90601f801991011681019081106001600160401b038211176104c557604052565b908160209103126100d8575161051b8161016c565b90565b6040513d5f823e3d90fd5b908060209392818452848401375f828201840152601f01601f1916010190565b91602061051b938181520191610529565b634e487b7160e01b5f52603260045260245ffd5b91908110156105905760051b81013590607e19813603018212156100d8570190565b61055a565b3561051b8161016c565b903590601e19813603018212156100d857018035906001600160401b0382116100d8576020019181360383136100d857565b9593916105fd9061060b9461051b99979360018060a01b03168952608060208a01526080890191610529565b918683036040880152610529565b926060818503910152610529565b9492909361063761051b979561064594606089526060890191610529565b918683036020880152610529565b926040818503910152610529565b91908110156105905760051b81013590605e19813603018212156100d8570190565b6002111561067f57565b634e487b7160e01b5f52602160045260245ffd5b3560028110156100d85790565b35906001600160801b03821682036100d857565b60ff8116036100d857565b6001600160401b0381116104c557601f01601f191660200190565b35906001600160a01b0319821682036100d857565b35906001600160801b0319821682036100d857565b919060a0838203126100d8576040519061071d826104aa565b8193803583526020810135610731816106b4565b602084015260408101356001600160401b0381116100d857810182601f820112156100d857803591610762836106bf565b9361077060405195866104e5565b838552602084840101116100d8576080935f6020856107b19682899701838601378301015260408601526107a6606082016106da565b6060860152016106ef565b910152565b6020818303126100d8578035906001600160401b0382116100d8570160c0818303126100d857604051916107e9836104ca565b81356107f48161016c565b83526108026020830161017d565b6020840152610813604083016106a0565b60408401526108246060830161017d565b60608401526080820135608084015260a08201356001600160401b0381116100d8576108509201610704565b60a082015290565b90600282101561067f5752565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b92919060a060409161089c866001610858565b60606020870152600180831b038151166060870152600180831b0360208201511660808701526001600160801b03838201511682870152600180831b0360608201511660c0870152608081015160e0870152015160c0610100860152805161012086015260ff6020820151166101408601526109288282015160a06101608801526101c0870190610865565b60608201516001600160a01b0319166101808701526080909101516001600160801b0319166101a0860152930152565b801515036100d857565b3561051b81610958565b634e487b7160e01b5f52601160045260245ffd5b5f19811461098e5760010190565b61096c565b91908110156105905760071b0190565b3561051b816106b4565b908160209103126100d8575161051b81610958565b604051906109d16040836104e5565b600d82526c65636965732d6165732d6b657960981b6020830152565b91906040838203126100d85782516001600160401b0381116100d85783019080601f830112156100d8578151610a22816106bf565b91610a3060405193846104e5565b818352602082850101116100d8576020815f92828096018386015e8301015292015161051b81610958565b92610a919060809360209397969786526bffffffffffffffffffffffff60a01b168386015260a0604086015260a0850190610865565b83810360608501525f815201936001600160801b031916910152565b6001600160a01b0390911681526001600160801b03909116602082015260400190565b908160c09103126100d85760a060405191610aea836104ca565b8035610af58161016c565b83526020810135610b058161016c565b60208401526040810135610b188161016c565b6040840152610b29606082016106a0565b60608401526080810135610b3c8161016c565b6080840152013560a082015290565b60e0909392919360a0610100820195610b64835f610858565b600180831b038151166020840152600180831b036020820151166040840152600180831b0360408201511660608401526001600160801b036060820151166080840152600180831b0360808201511682840152015160c08201520152565b908160209103126100d8575190565b906001600160401b03809116911601906001600160401b03821161098e57565b908160209103126100d857516001600160401b03811681036100d85790565b959194939296903315158061172b575b61171c577f0000000000000000000000001c000000000000000000000000000000000000006001600160a01b031696873b156100d85760405163fe77009960e01b8152915f9183918291610c78919060048401610549565b0381838b5af1801561049157611708575b505f9492945b8181106115ed575050505f54925f915f915b878310610e9f5750505003610e90576040516381e3da6b60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015260056024820152602081604481865afa801561049157610e73575b50805f55610d4e610d326001600160401b038516610d2d6001546001600160401b031690565b610bd1565b6001600160401b03166001600160401b03196001541617600155565b604051631014997960e31b815291602083600481845afa928315610491575f93610e3e575b50602060049160405192838092631d04645f60e01b82525afa8015610491577fd2d2bf1e295f62cd08f0f0ab45818efeaba78b58310526f7b7e9686b8aeded1a926001600160401b03925f92610e09575b50610e0490610ddb6001546001600160401b031690565b6040805198895260208901929092526001600160401b0316908701529116939081906060820190565b0390a3565b610e04919250610e309060203d602011610e37575b610e2881836104e5565b810190610bf1565b9190610dc4565b503d610e1e565b6004919350610e64602091823d8411610e6c575b610e5c81836104e5565b810190610bc2565b939150610d73565b503d610e52565b610e8b9060203d602011610e6c57610e5c81836104e5565b610d07565b6361aba18160e11b5f5260045ffd5b909194610ead868985610653565b95610eb787610693565b610ec081610675565b6111c957610edc610ed4602089018961059f565b810190610ad0565b91604051610f0181610ef360208201948786610b4b565b03601f1981018352826104e5565b51902060808301805191989091610f28906001600160a01b03165b6001600160a01b031690565b610f41575050610f39600192611bd0565b019190610ca1565b6040610f4d9101610962565b15610fa35760208301518351600194610f9e936001600160a01b03938416939092610f9791610f8991606091169301516001600160801b031690565b92516001600160a01b031690565b928b611828565b610f39565b8251610fb990610f1c906001600160a01b031681565b604084018051909291906001600160a01b0316946060810192610fe384516001600160801b031690565b92803b156100d8575f8d946110129260019a836040518096819582946340c10f1960e01b845260048401610aad565b03925af190816111b5575b506111135781517f3c1af70310b8c9d43b0a7207217f398e4a3114f1728987c40addb451399ac87c9290611070906001600160a01b031686516001600160801b031684516001600160a01b031691611b45565b61110b6110ca6110bc6110ae6110a0611092602087015160018060a01b031690565b9a516001600160a01b031690565b94516001600160a01b031690565b97516001600160801b031690565b93516001600160a01b031690565b604080516001600160a01b0398891681526001600160801b03909516602086015296169583019590955260a088901b88900390811695169381906060820190565b0390a4610f39565b5060208101517fd5277bc9597c7da3fab9cdbba4de6005f48b9eb7389cf2389c4ea9eea3172c219190611157906001600160a01b031695516001600160a01b031690565b815161110b9060a090611172906001600160a01b03166110ae565b930151604080516001600160a01b0390981688526001600160801b0390941660208801529286019290925260a088901b8890039081169516939081906060820190565b806104855f6111c3936104e5565b5f61101d565b61120660406111e66111de60208b018b61059f565b8101906107b6565b9381516111fc81610ef360208201948986610889565b5190209801610962565b61159b578585101561158c5761122661121e86610980565b958785610993565b6080830161123481516118a2565b92906020604060a08801936112b9855161125385825192015160ff1690565b988335966112628786016109a3565b8651635f8a996960e01b8152600481019490945260ff9b8c166024850152604484018990528b166064840152608483015290981660a48901529101803560c48801526020013560e487015285908190610104820190565b0381731c000000000000000000000000000000000001005afa938415610491575f9461155c575b508392606094611444575b505050158015611438575b61142b5761130390611b12565b835191939161131c90610f1c906001600160a01b031681565b604083019161133283516001600160801b031690565b95823b156100d8575f806113618e9560019a6040519485809481936340c10f1960e01b83528960048401610aad565b03925af19081611417575b5061137e575050610f9e929150611a8e565b60208501517ffc236d1b4402e76c2b0a882db36ad3a6fb0f5b6b7edc8ae317d993f10434c43692919061110b906113d8906113ca906001600160a01b031698516001600160a01b031690565b96516001600160801b031690565b604080516001600160a01b0390981688526001600160801b03909116602088015286019290925260a088901b8890039081169516939081906060820190565b806104855f611425936104e5565b5f61136c565b50610f9e60019288611a8e565b506040815114156112f6565b518251516040516bffffffffffffffffffffffff197f000000000000000000000000000000000000000000000000000000000000000060601b166020820152603481019290925260548083019190915281525f93506114ce916114bb91906114ad6074836104e5565b6114b56109c2565b90611a22565b9151938401516001600160a01b03191690565b906115076114ec608060408701519601516001600160801b03191690565b60405163f4a7eb1360e01b8152958694859460048601610a5b565b0381731c000000000000000000000000000000000001015afa8015610491575f915f91611538575b505f80806112eb565b905061155691503d805f833e61154e81836104e5565b8101906109ed565b5f61152f565b61157e91945060203d8111611585575b61157681836104e5565b8101906109ad565b925f6112e0565b503d61156c565b6351de8c1f60e01b5f5260045ffd5b6020820151600192610f9e916001600160a01b031681519091906001600160a01b03166115e660606115d760408501516001600160801b031690565b9301516001600160a01b031690565b928b6117ba565b6115fb81838598969861056e565b9061160582610595565b916020810190611615828261059f565b90926040830193611626858561059f565b91909760608601986116388a8861059f565b9061083f60921b3b156100d8575f9561166693604051998a9788976374ae5b3760e11b8952600489016105d1565b03818361083f60921b5af18015610491576001967f4ac4dcc08b0c26c3fb6b58c64c1392b7934b1ce6b0382a5986ea5c3de795e053946116c9946116e8936116f4575b506116d16116c06116b983610595565b958361059f565b9690988361059f565b93909261059f565b9290916040519687968c8060a01b03169987610619565b0390a201949294610c8f565b806104855f611702936104e5565b5f6116a9565b806104855f611716936104e5565b5f610c89565b63bb62587160e01b5f5260045ffd5b50604051630b83774760e31b81526020816004817f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03165afa908115610491575f9161178b575b506001600160a01b0316331415610c20565b6117ad915060203d6020116117b3575b6117a581836104e5565b810190610506565b5f611779565b503d61179b565b92907f4620415fad9c416306a56ca0ee640b3418628a5f2e45ddde3ddf7452a7a654fb92946001600160801b036080936117f583828a611b45565b60405197611804896001610858565b6001600160a01b0390811660208a01529116604088015290811660608701521693a3565b92907f4620415fad9c416306a56ca0ee640b3418628a5f2e45ddde3ddf7452a7a654fb92946001600160801b0360809361186383828a611b45565b60405197611804895f610858565b908160011b918083046002149015171561098e57565b906001820180921161098e57565b9190820180921161098e57565b6118d3906118cd60405160208101906118c481610ef384906007602083019252565b51902091611871565b90611895565b9061195c6118e083611887565b6040516381e3da6b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000818116600484015260248301969096527f0000000000000000000000001c0000000000000000000000000000000000000016949092909190602090849081906044820190565b0381885afa928315610491575f936119ef575b5082156119e0576040516381e3da6b60e01b81526001600160a01b03929092166004830152602482015292602090849060449082905afa928315610491575f936119bc575b509160ff1690565b60ff9193506119d99060203d602011610e6c57610e5c81836104e5565b92906119b4565b63fb1f4a4960e01b5f5260045ffd5b611a0991935060203d602011610e6c57610e5c81836104e5565b915f61196f565b805191908290602001825e015f815290565b91611a766001611a5061051b95611a619560405191602083015260208252611a4b6040836104e5565b611e00565b926040519485916020830190611a10565b8260f81b815203601e198101855201836104e5565b60405190602082015260208152611a4b6040826104e5565b815160408301805160608501517f95705d99ac13cf82894fd274cd871942e6f301c98c186271337e8fedbbb9d7ea93611adf926001600160a01b03928316926001600160801b039091169116611b45565b6020840151935190516040516001600160a01b039586169590928392610e04926001600160801b03909116911683610aad565b90815160408103611b2e57506034602083015160601c92015190565b633fbbeba160e21b5f52600452604060245260445ffd5b9060026007609a1b013b156100d8576040516338b8fb9760e21b81526001600160a01b0392831660048201526001600160801b03919091166024820152911660448201525f816064818360026007609a1b015af1801561049157611ba65750565b5f610188916104e5565b906001600160801b03809116911601906001600160801b03821161098e57565b6040810151611c289190602090611bff90611bf3906001600160a01b0316610f1c565b6001600160401b031690565b60405163a3a124c360e01b81526001600160401b03909116600482015292839081906024820190565b03815f60026007609a1b015af1918215610491575f92611dc9575b508051611c5a90610f1c906001600160a01b031681565b916060820192611c7184516001600160801b031690565b90803b156100d8576040516340c10f1960e01b8152915f918391829084908290611c9f908960048401610aad565b03925af19081611db5575b50611d7e57611d5e611d507f92d91a50ae3561d4edd8eadad5f27f09116faaa4f733bd37682c49b4cbc20e3993611d43611ceb87516001600160801b031690565b82516001600160a01b03165f908152600260205260409020611d2990611d129088906103b6565b91611d2483546001600160801b031690565b611bb0565b6001600160801b03166001600160801b0319825416179055565b516001600160a01b031690565b93516001600160801b031690565b6040516001600160a01b03909216938291611d799183610aad565b0390a2565b90517fbc1cf3ff6e619587619408b396a13775509216474757afb9d29bda1a96f590f09190611d5e906001600160a01b0316611d50565b806104855f611dc3936104e5565b5f611caa565b611de391925060203d6020116117b3576117a581836104e5565b905f611c43565b61051b9392604092825260208201520190611a10565b5f908051604081115f14611f0c57505f611e2260209260405191828092611a10565b039060025afa156104915760205f611e9d610ef3611e918351965b6040519283917f363636363636363636363636363636363636363636363636363636363636363689187f36363636363636363636363636363636363636363636363636363636363636368b18898501611dea565b60405191828092611a10565b039060025afa1561049157611efc7f5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c611e915f93610ef36020968651908560405196879518911889850191606093918352602083015260408201520190565b039060025afa15610491575f5190565b6020808301519490821193509160018414611f7b57505f925b60208210611f65575b6040821016611f4b575b505f611e9d610ef3611e91602094611e3d565b600160409190910360031b1b5f190119909116905f611f38565b935f1960018360200360031b1b01191693611f2e565b6040015192611f2556fea2646970667358221220ef81febf9add1a1221f6f089b5c548a7494182f7e1c0cda085f6ecdefffea21964736f6c63430008230033", "nonce": "0x1" }, "0x1c00000000000000000000000000000000000002": { "balance": "0x0", - "code": "0x60806040526004361015610011575f80fd5b5f3560e01c80633406527214610184578063378fa8fa1461017f5780633d1c5a931461017a578063413e9cde1461017557806343c3cb831461017057806348aa41081461016b57806353a8d73914610166578063545525f11461016157806379502c551461015c57806379fa3289146101575780637b9c9aa41461015257806386f47e551461014d578063a3a124c314610148578063a94cd93114610143578063b3b200aa1461013e578063b9d1fd6814610139578063bba9282e14610134578063c37fc8a81461012f578063c9b6ca9b1461012a578063ce7025e914610125578063d93af1d214610120578063e2e3ee5c1461011b5763f490ca9614610116575f80fd5b610d33565b610b8c565b610b6f565b610aea565b610ace565b6109e3565b61086c565b61075e565b6106c6565b610634565b610546565b610529565b6104ec565b610358565b6102d2565b6102ac565b610290565b610238565b61021d565b6101fd565b6101d7565b6101bc565b610197565b5f91031261019357565b5f80fd5b34610193575f3660031901126101935760206001600160801b035f5416604051908152f35b34610193575f36600319011261019357602060405160218152f35b34610193575f3660031901126101935760206001600160401b0360045416604051908152f35b34610193575f36600319011261019357602060025460c01c604051908152f35b34610193575f36600319011261019357602060405160718152f35b34610193575f366003190112610193575f602060405161025781610d69565b828152015260406001546001600160401b0380600254166020845161027b81610d69565b84815201908152835192835251166020820152f35b34610193575f3660031901126101935760206040516104008152f35b34610193575f36600319011261019357602063ffffffff60025460401c16604051908152f35b34610193575f366003190112610193576040517f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03168152602090f35b600435906001600160801b038216820361019357565b604435906001600160801b038216820361019357565b602435906001600160801b038216820361019357565b3461019357602036600319011261019357610371610316565b33151580610416575b61040757670de0b6b3a76400006001600160801b038216116103f8576103f3816103d97f6f864cce5237e12ffc9a99fc6c59af17222c2bbb3457690cc8753ab16b5d715e936001600160801b03166001600160801b03195f5416175f55565b6040516001600160801b0390911681529081906020820190565b0390a1005b630d62f21160e11b5f5260045ffd5b63bb62587160e01b5f5260045ffd5b50604051630b83774760e31b81526020816004817f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03165afa9081156104a5575f91610476575b506001600160a01b031633141561037a565b610498915060203d60201161049e575b6104908183610da5565b810190610de8565b5f610464565b503d610486565b610e00565b600435906001600160401b038216820361019357565b608435906001600160401b038216820361019357565b602435906001600160401b038216820361019357565b3461019357602036600319011261019357602061051861050a6104aa565b61051381611490565b6114d2565b6001600160801b0360405191168152f35b34610193575f366003190112610193576020604051629896808152f35b346101935760203660031901126101935761055f6104aa565b6007609a1b1933016105e7576001600160401b03165f818152600560205260409020546001600160a01b031680156105d8576105d4915f5260056020526105ba60405f206bffffffffffffffffffffffff60a01b8154169055565b6040516001600160a01b0390911681529081906020820190565b0390f35b63c29f0c7160e01b5f5260045ffd5b6303300c7360e31b5f5260045ffd5b6001600160a01b0381160361019357565b9181601f84011215610193578235916001600160401b038311610193576020838186019501011161019357565b346101935760e036600319011261019357600435610651816105f6565b6024359061065e826105f6565b61066661032c565b916064356106726104c0565b60a4359161067f836105f6565b60c435956001600160401b038711610193576106ac6106a56106c4983690600401610607565b3691610e26565b94604051966106bc602089610da5565b5f88526115ea565b005b3461019357610100366003190112610193576004356106e4816105f6565b6024356106f0816105f6565b6106f861032c565b916064356107046104c0565b9060a435610711816105f6565b60c4356001600160401b03811161019357610730903690600401610607565b93909260e435976001600160401b038911610193576107566106c4993690600401610607565b989097610e70565b346101935760203660031901126101935760043563ffffffff8116810361019357331515806107ed575b610407576103f3816107d67f5340f6cf6e1274bffd0c7188c75f885ed6c90cd6b0879a646290a92f84a6dce39363ffffffff60401b6002549160401b169063ffffffff60401b191617600255565b60405163ffffffff90911681529081906020820190565b50604051630b83774760e31b81526020816004817f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03165afa9081156104a5575f9161084d575b506001600160a01b0316331415610788565b610866915060203d60201161049e576104908183610da5565b5f61083b565b34610193575f3660031901126101935760206001600160401b035f5460801c16604051908152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b602081016020825282518091526040820191602060408360051b8301019401925f915b8383106108ea57505050505090565b90919293946020806109d4600193603f1986820301875289519061091681835160018060a01b03169052565b818401516001600160a01b031681850152604082810151908201526060808301516001600160a01b0316908201526080828101516001600160801b03169082015260a0828101516001600160801b03169082015260c082015160c082015261098e60e083015160e08301906001600160401b03169052565b610100828101516001600160401b0316908201526101406109c2610120840151610160610120850152610160840190610894565b92015190610140818403910152610894565b970193019301919392906108db565b34610193575f366003190112610193576003546109ff81610e97565b90610a0d6040519283610da5565b808252601f19610a1c82610e97565b015f5b818110610a705750505f5b818110610a3f57604051806105d485826108b8565b80610a54610a4e600193610ec2565b50610fe6565b610a5e8286610efc565b52610a698185610efc565b5001610a2a565b602090604051610a7f81610d89565b5f81525f838201525f60408201525f60608201525f60808201525f60a08201525f60c08201525f60e08201525f6101008201526060610120820152606061014082015282828701015201610a1f565b34610193575f36600319011261019357602060405161c3508152f35b3461019357606036600319011261019357610b036104d6565b604435906001600160401b0382116101935736602383011215610193578160040135906001600160401b038211610193573660248360051b8501011161019357610b56610b5f9260246105d495016110ee565b90600435611c82565b6040519081529081906020820190565b34610193575f366003190112610193576020600354604051908152f35b3461019357606036600319011261019357600435610ba9816105f6565b610bb1610342565b90604435610bbe816105f6565b6007609a1b1933016105e7576001600160401b037f34ca953f3eed14157d2f660c7e92a5bd9c05be0d61a188830f3bf1cb7d094f9691610d2e5f95610c76610c04610dc6565b6001600160a01b038816815260208101899052604081018990526001600160a01b03851660608201526001600160801b03831660808201528860a08201528860c08201528860e082015288610100820152610c5d610e5c565b610120820152610c6b610e5c565b6101408201526112ae565b865460801c6001600160401b031692610cbd610c918561146e565b5f805467ffffffffffffffff60801b191660809290921b67ffffffffffffffff60801b16919091179055565b604080516001600160a01b0398891681529790911660208801526001600160801b03909116908601525f606086018190526080860181905260a0860181905260c0860181905261012060e0870181905286018190526101406101008701819052860152911692908190610160820190565b0390a3005b34610193575f366003190112610193576020604051670de0b6b3a76400008152f35b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117610d8457604052565b610d55565b61016081019081106001600160401b03821117610d8457604052565b90601f801991011681019081106001600160401b03821117610d8457604052565b60405190610dd661016083610da5565b565b60405190610dd661014083610da5565b908160209103126101935751610dfd816105f6565b90565b6040513d5f823e3d90fd5b6001600160401b038111610d8457601f01601f191660200190565b929192610e3282610e0b565b91610e406040519384610da5565b829481845281830111610193578281602093845f960137010152565b60405190610e6b602083610da5565b5f8252565b97610e89610e91929394959697610dd69b993691610e26565b973691610e26565b966115ea565b6001600160401b038111610d845760051b60200190565b634e487b7160e01b5f52603260045260245ffd5b600354811015610ede5760035f52600960205f20910201905f90565b610eae565b8054821015610ede575f52600960205f20910201905f90565b8051821015610ede5760209160051b010190565b90600182811c92168015610f3e575b6020831014610f2a57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610f1f565b9060405191825f825492610f5b84610f10565b8084529360018116908115610fc45750600114610f80575b50610dd692500383610da5565b90505f9291925260205f20905f915b818310610fa8575050906020610dd6928201015f610f73565b6020919350806001915483858901015201910190918492610f8f565b905060209250610dd694915060ff191682840152151560051b8201015f610f73565b906110e66008610ff4610dc6565b84546001600160a01b031681529360018101546001600160a01b031660208601526002810154604086015260038101546001600160a01b031660608601526110766110666004830154611060611050826001600160801b031690565b6001600160801b031660808a0152565b60801c90565b6001600160801b031660a0870152565b600581015460c08601526110ce6110bd60068301546110ae61109e826001600160401b031690565b6001600160401b031660e08a0152565b60401c6001600160401b031690565b6001600160401b0316610100870152565b6110da60078201610f48565b61012086015201610f48565b610140830152565b906110f881610e97565b916111066040519384610da5565b818352602083019160051b8101903682116101935780925b82841061112c575050505090565b83356001600160401b03811161019357820136601f820112156101935760209161115d839236908481359101610e26565b81520193019261111e565b634e487b7160e01b5f525f60045260245ffd5b5f5b82811061118957505050565b5f8282015560010161117d565b91601f82116111a457505050565b8082116111b057505050565b610dd6925f5260205f20916020601f830160051c92106111db575b601f82910160051c03910161117b565b5f91506111cb565b91909182516001600160401b038111610d845761120a816112048454610f10565b84611196565b6020601f821160011461124957819061123a9394955f9261123e575b50508160011b915f199060031b1c19161790565b9055565b015190505f80611226565b601f1982169061125c845f5260205f2090565b915f5b8181106112965750958360019596971061127e575b505050811b019055565b01515f1960f88460031b161c191690555f8080611274565b9192602060018192868b01518155019401920161125f565b60035468010000000000000000811015610d84578060016112d492016003556003610ee3565b61145557815181546001600160a01b0319166001600160a01b03909116178155610dd6916008906101409060208101516001850180546001600160a01b0319166001600160a01b039092169190911790556040810151600285015560608101516003850180546001600160a01b0319166001600160a01b039092169190911790556113bd6004850161138f61137360808501516001600160801b031690565b82546001600160801b0319166001600160801b03909116178255565b60a08301516001600160801b031681546001600160801b031660809190911b6001600160801b031916179055565b60c0810151600585015561143a600685016114026113e560e08501516001600160401b031690565b825467ffffffffffffffff19166001600160401b03909116178255565b61010083015181546fffffffffffffffff0000000000000000191660409190911b6fffffffffffffffff000000000000000016179055565b61144c610120820151600786016111e3565b015191016111e3565b611168565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b03166001600160401b03811461148b5760010190565b61145a565b6001600160401b03629896809116116114a557565b637d2e4eb560e01b5f5260045ffd5b6001600160401b036001911601906001600160401b03821161148b57565b6001600160401b031661c350016001600160401b03811161148b576001600160401b036001600160801b035f54169116026001600160801b03811690810361148b5790565b90816020910312610193575180151581036101935790565b63ffffffff1663ffffffff811461148b5760010190565b906001600160801b03809116911601906001600160801b03821161148b57565b90816020910312610193575190565b6001600160a01b039182168152911660208201526001600160801b0391821660408201529116606082015260808101919091526001600160401b0391821660a0820152911660c082015261012060e08201819052610dfd9391926115db91840190610894565b91610100818403910152610894565b95969092949693919360018060a01b038816156105d85760405163037d635760e31b81526001600160a01b0388166004820152602081806024810103817f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03165afa9081156104a5575f91611a70575b5015611a615761167083611490565b610400815111611a525761168382611ff7565b600254604081901c63ffffffff16611969575b506116a0836114d2565b976116ab8987611546565b60405163868a2e9d60e01b8152979094906020896004815f60056007609a1b015af19889156104a5575f99611938575b508815611929576040516323b872dd60e01b81523360048201523060248201526001600160801b03871660448201526001600160a01b038b16906020816064815f865af19081156104a5575f916118fa575b50156118eb57803b1561019357604051630852cd8d60e31b81526001600160801b039790971660048801525f908790602490829084905af180156104a5577f34ca953f3eed14157d2f660c7e92a5bd9c05be0d61a188830f3bf1cb7d094f96996118cc97611899926118d1575b506118136117b86117b36004546001600160401b031690565b61146e565b956117d9876001600160401b03166001600160401b03196004541617600455565b6117f4876001600160401b03165f52600560205260405f2090565b80546001600160a01b0319166001600160a01b03909216919091179055565b61181b610dc6565b6001600160a01b038d1681529033602083015260408201526001600160a01b03891660608201526001600160801b038a1660808201526001600160801b038d1660a082015260c081018390526001600160401b03841660e08201526001600160401b03851661010082015285610120820152866101408201526112ae565b5f5460801c6001600160401b03169a6118b4610c918d61146e565b6040519889986001600160401b03339e169c8a611575565b0390a3565b806118df5f6118e593610da5565b80610189565b5f61179a565b6312171d8360e31b5f5260045ffd5b61191c915060203d602011611922575b6119148183610da5565b810190611517565b5f61172d565b503d61190a565b632968ee7f60e21b5f5260045ffd5b61195b91995060203d602011611962575b6119538183610da5565b810190611566565b975f6116db565b503d611949565b61199661198a6001600160401b034316926001600160401b039060801c1690565b6001600160401b031690565b8103611a1b575b506002546119c86119bf606083901c63ffffffff169260401c63ffffffff1690565b63ffffffff1690565b63ffffffff82161015611a0c576119e1611a069161152f565b6002805463ffffffff60601b191660609290921b63ffffffff60601b16919091179055565b5f611696565b63124ab48560e11b5f5260045ffd5b6002805460809290921b67ffffffffffffffff60801b166bffffffffffffffffffffffff60601b199092169190911790555f61199d565b634b8a874d60e11b5f5260045ffd5b631fcf8c4760e11b5f5260045ffd5b611a89915060203d602011611922576119148183610da5565b5f611661565b5f1981019190821161148b57565b604080825282516001600160a01b031690820152929190602090611b629080830151606087015260408101516001600160a01b0316608087015260608101516001600160801b031660a087015260808101516001600160801b031660c087015260a081015160e087015260c08101516001600160401b031661010087015260e08101516001600160401b0316610120870152610120611b4d610100830151610140808a0152610180890190610894565b910151868203603f1901610160880152610894565b930152565b611b718154610f10565b9081611b7b575050565b81601f5f9311600114611b8c575055565b81835260208320611ba991601f0160051c8419019060010161117b565b808252602082209081548360011b9084198560031b1c191617905555565b90611455576008815f610dd693555f60018201555f60028201555f60038201555f60048201555f60058201555f6006820155611c0560078201611b67565b01611b67565b6003545f60035580611c1a5750565b8060090290600982040361148b5760035f5260205f205f5b828110611c3e57505050565b80611c7c600860099385015f81555f60018201555f60028201555f60038201555f60048201555f60058201555f6006820155611c0560078201611b67565b01611c32565b9092915f9333151580611f78575b610407576001600160401b03804316911603611f6957600354808303611f5257508051828103611f3b575081611d79575b5050611cfc611ce0611cdb6002546001600160401b031690565b6114b4565b6001600160401b03166001600160401b03196002541617600255565b817fec4aff46c65f485f4b15e3c2edadda1d57d002995f5aa262a27c76b9a680ec16611d74611d336002546001600160401b031690565b611d3c84600155565b600280546001600160c01b03164260c01b6001600160c01b0319161790556040516001600160401b0390911681529081906020820190565b0390a2565b5f19935090805b611d95575050611d8e611c0b565b5f80611cc1565b611d9e81611a8f565b93611da885610ec2565b50611db290610fe6565b611dbc8685610efc565b518061014083015190611dce91612053565b8151602080840151604080860151905160609290921b6bffffffffffffffffffffffff191692820192835260348083019190915281526001600160a01b039092169391611e1c605482610da5565b51902060608201519091906001600160a01b031660808201516001600160801b031660a08301516001600160801b031660c08401519160e0850151611e67906001600160401b031690565b93610100860151611e7e906001600160401b031690565b95610120015196611e8d610dd8565b6001600160a01b03909a168a5260208a01526001600160a01b031660408901526001600160801b031660608801526001600160801b0316608087015260a08601526001600160401b031660c08501526001600160401b031660e084015261010083015261012082015260405180916020820193611f0a9185611a9d565b03601f1981018252611f1c9082610da5565b51902093611f2990610ec2565b611f3291611bc7565b5f190180611d80565b630db2128560e11b5f52600452602482905260445ffd5b6324a839db60e11b5f52600483905260245260445ffd5b631391e11b60e21b5f5260045ffd5b50604051630b83774760e31b81526020816004817f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03165afa9081156104a5575f91611fd8575b506001600160a01b0316331415611c90565b611ff1915060203d60201161049e576104908183610da5565b5f611fc6565b8051801561204f5760210361204057805115610ede576020810151612026906120229060f81c612084565b1590565b61204057612022602161203a9201516120ef565b61204057565b6361d0136b60e11b5f5260045ffd5b5050565b5161207c575f905b51818103612067575050565b63521a2d4d60e11b5f5260045260245260445ffd5b60719061205b565b60ff1660028114908115612096575090565b600391501490565b3d156120c8573d906120af82610e0b565b916120bd6040519384610da5565b82523d5f602084013e565b606090565b6020815191015190602081106120e1575090565b5f199060200360031b1b1690565b8015801561219b575b61218a575f9081906401000003d0199060079082908181800909086040805160208082018181529282018190526060820152608081019290925263800001e9600160ff1b0360a08301526401000003d01960c08084019190915282529061216060e082610da5565b519060055afa61216e61209e565b9015801561218f575b61218a576121866001916120cd565b1490565b505f90565b50602081511415612177565b506401000003d0198110156120f856fea2646970667358221220a44608fefd2dc3bacd7bbf453e39f69d3da2f0f4d2de02769f9242bd32eec7bc64736f6c63430008230033", + "code": "0x60806040526004361015610011575f80fd5b5f3560e01c80633406527214610184578063378fa8fa1461017f5780633d1c5a931461017a578063413e9cde1461017557806343c3cb831461017057806348aa41081461016b57806353a8d73914610166578063545525f11461016157806379502c551461015c57806379fa3289146101575780637b9c9aa41461015257806386f47e551461014d578063a3a124c314610148578063a94cd93114610143578063b3b200aa1461013e578063b9d1fd6814610139578063bba9282e14610134578063c37fc8a81461012f578063c9b6ca9b1461012a578063ce7025e914610125578063d93af1d214610120578063e2e3ee5c1461011b5763f490ca9614610116575f80fd5b610d33565b610b8c565b610b6f565b610aea565b610ace565b6109e3565b61086c565b61075e565b6106c6565b610634565b610546565b610529565b6104ec565b610358565b6102d2565b6102ac565b610290565b610238565b61021d565b6101fd565b6101d7565b6101bc565b610197565b5f91031261019357565b5f80fd5b34610193575f3660031901126101935760206001600160801b035f5416604051908152f35b34610193575f36600319011261019357602060405160218152f35b34610193575f3660031901126101935760206001600160401b0360045416604051908152f35b34610193575f36600319011261019357602060025460c01c604051908152f35b34610193575f36600319011261019357602060405160718152f35b34610193575f366003190112610193575f602060405161025781610d69565b828152015260406001546001600160401b0380600254166020845161027b81610d69565b84815201908152835192835251166020820152f35b34610193575f3660031901126101935760206040516104008152f35b34610193575f36600319011261019357602063ffffffff60025460401c16604051908152f35b34610193575f366003190112610193576040517f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03168152602090f35b600435906001600160801b038216820361019357565b604435906001600160801b038216820361019357565b602435906001600160801b038216820361019357565b3461019357602036600319011261019357610371610316565b33151580610416575b61040757670de0b6b3a76400006001600160801b038216116103f8576103f3816103d97f6f864cce5237e12ffc9a99fc6c59af17222c2bbb3457690cc8753ab16b5d715e936001600160801b03166001600160801b03195f5416175f55565b6040516001600160801b0390911681529081906020820190565b0390a1005b630d62f21160e11b5f5260045ffd5b63bb62587160e01b5f5260045ffd5b50604051630b83774760e31b81526020816004817f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03165afa9081156104a5575f91610476575b506001600160a01b031633141561037a565b610498915060203d60201161049e575b6104908183610da5565b810190610de8565b5f610464565b503d610486565b610e00565b600435906001600160401b038216820361019357565b608435906001600160401b038216820361019357565b602435906001600160401b038216820361019357565b3461019357602036600319011261019357602061051861050a6104aa565b61051381611490565b6114d2565b6001600160801b0360405191168152f35b34610193575f366003190112610193576020604051629896808152f35b346101935760203660031901126101935761055f6104aa565b6007609a1b1933016105e7576001600160401b03165f818152600560205260409020546001600160a01b031680156105d8576105d4915f5260056020526105ba60405f206bffffffffffffffffffffffff60a01b8154169055565b6040516001600160a01b0390911681529081906020820190565b0390f35b63c29f0c7160e01b5f5260045ffd5b6303300c7360e31b5f5260045ffd5b6001600160a01b0381160361019357565b9181601f84011215610193578235916001600160401b038311610193576020838186019501011161019357565b346101935760e036600319011261019357600435610651816105f6565b6024359061065e826105f6565b61066661032c565b916064356106726104c0565b60a4359161067f836105f6565b60c435956001600160401b038711610193576106ac6106a56106c4983690600401610607565b3691610e26565b94604051966106bc602089610da5565b5f88526115ea565b005b3461019357610100366003190112610193576004356106e4816105f6565b6024356106f0816105f6565b6106f861032c565b916064356107046104c0565b9060a435610711816105f6565b60c4356001600160401b03811161019357610730903690600401610607565b93909260e435976001600160401b038911610193576107566106c4993690600401610607565b989097610e70565b346101935760203660031901126101935760043563ffffffff8116810361019357331515806107ed575b610407576103f3816107d67f5340f6cf6e1274bffd0c7188c75f885ed6c90cd6b0879a646290a92f84a6dce39363ffffffff60401b6002549160401b169063ffffffff60401b191617600255565b60405163ffffffff90911681529081906020820190565b50604051630b83774760e31b81526020816004817f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03165afa9081156104a5575f9161084d575b506001600160a01b0316331415610788565b610866915060203d60201161049e576104908183610da5565b5f61083b565b34610193575f3660031901126101935760206001600160401b035f5460801c16604051908152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b602081016020825282518091526040820191602060408360051b8301019401925f915b8383106108ea57505050505090565b90919293946020806109d4600193603f1986820301875289519061091681835160018060a01b03169052565b818401516001600160a01b031681850152604082810151908201526060808301516001600160a01b0316908201526080828101516001600160801b03169082015260a0828101516001600160801b03169082015260c082015160c082015261098e60e083015160e08301906001600160401b03169052565b610100828101516001600160401b0316908201526101406109c2610120840151610160610120850152610160840190610894565b92015190610140818403910152610894565b970193019301919392906108db565b34610193575f366003190112610193576003546109ff81610e97565b90610a0d6040519283610da5565b808252601f19610a1c82610e97565b015f5b818110610a705750505f5b818110610a3f57604051806105d485826108b8565b80610a54610a4e600193610ec2565b50610fe6565b610a5e8286610efc565b52610a698185610efc565b5001610a2a565b602090604051610a7f81610d89565b5f81525f838201525f60408201525f60608201525f60808201525f60a08201525f60c08201525f60e08201525f6101008201526060610120820152606061014082015282828701015201610a1f565b34610193575f36600319011261019357602060405161c3508152f35b3461019357606036600319011261019357610b036104d6565b604435906001600160401b0382116101935736602383011215610193578160040135906001600160401b038211610193573660248360051b8501011161019357610b56610b5f9260246105d495016110ee565b90600435611c82565b6040519081529081906020820190565b34610193575f366003190112610193576020600354604051908152f35b3461019357606036600319011261019357600435610ba9816105f6565b610bb1610342565b90604435610bbe816105f6565b6007609a1b1933016105e7576001600160401b037f34ca953f3eed14157d2f660c7e92a5bd9c05be0d61a188830f3bf1cb7d094f9691610d2e5f95610c76610c04610dc6565b6001600160a01b038816815260208101899052604081018990526001600160a01b03851660608201526001600160801b03831660808201528860a08201528860c08201528860e082015288610100820152610c5d610e5c565b610120820152610c6b610e5c565b6101408201526112ae565b865460801c6001600160401b031692610cbd610c918561146e565b5f805467ffffffffffffffff60801b191660809290921b67ffffffffffffffff60801b16919091179055565b604080516001600160a01b0398891681529790911660208801526001600160801b03909116908601525f606086018190526080860181905260a0860181905260c0860181905261012060e0870181905286018190526101406101008701819052860152911692908190610160820190565b0390a3005b34610193575f366003190112610193576020604051670de0b6b3a76400008152f35b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117610d8457604052565b610d55565b61016081019081106001600160401b03821117610d8457604052565b90601f801991011681019081106001600160401b03821117610d8457604052565b60405190610dd661016083610da5565b565b60405190610dd661014083610da5565b908160209103126101935751610dfd816105f6565b90565b6040513d5f823e3d90fd5b6001600160401b038111610d8457601f01601f191660200190565b929192610e3282610e0b565b91610e406040519384610da5565b829481845281830111610193578281602093845f960137010152565b60405190610e6b602083610da5565b5f8252565b97610e89610e91929394959697610dd69b993691610e26565b973691610e26565b966115ea565b6001600160401b038111610d845760051b60200190565b634e487b7160e01b5f52603260045260245ffd5b600354811015610ede5760035f52600960205f20910201905f90565b610eae565b8054821015610ede575f52600960205f20910201905f90565b8051821015610ede5760209160051b010190565b90600182811c92168015610f3e575b6020831014610f2a57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610f1f565b9060405191825f825492610f5b84610f10565b8084529360018116908115610fc45750600114610f80575b50610dd692500383610da5565b90505f9291925260205f20905f915b818310610fa8575050906020610dd6928201015f610f73565b6020919350806001915483858901015201910190918492610f8f565b905060209250610dd694915060ff191682840152151560051b8201015f610f73565b906110e66008610ff4610dc6565b84546001600160a01b031681529360018101546001600160a01b031660208601526002810154604086015260038101546001600160a01b031660608601526110766110666004830154611060611050826001600160801b031690565b6001600160801b031660808a0152565b60801c90565b6001600160801b031660a0870152565b600581015460c08601526110ce6110bd60068301546110ae61109e826001600160401b031690565b6001600160401b031660e08a0152565b60401c6001600160401b031690565b6001600160401b0316610100870152565b6110da60078201610f48565b61012086015201610f48565b610140830152565b906110f881610e97565b916111066040519384610da5565b818352602083019160051b8101903682116101935780925b82841061112c575050505090565b83356001600160401b03811161019357820136601f820112156101935760209161115d839236908481359101610e26565b81520193019261111e565b634e487b7160e01b5f525f60045260245ffd5b5f5b82811061118957505050565b5f8282015560010161117d565b91601f82116111a457505050565b8082116111b057505050565b610dd6925f5260205f20916020601f830160051c92106111db575b601f82910160051c03910161117b565b5f91506111cb565b91909182516001600160401b038111610d845761120a816112048454610f10565b84611196565b6020601f821160011461124957819061123a9394955f9261123e575b50508160011b915f199060031b1c19161790565b9055565b015190505f80611226565b601f1982169061125c845f5260205f2090565b915f5b8181106112965750958360019596971061127e575b505050811b019055565b01515f1960f88460031b161c191690555f8080611274565b9192602060018192868b01518155019401920161125f565b60035468010000000000000000811015610d84578060016112d492016003556003610ee3565b61145557815181546001600160a01b0319166001600160a01b03909116178155610dd6916008906101409060208101516001850180546001600160a01b0319166001600160a01b039092169190911790556040810151600285015560608101516003850180546001600160a01b0319166001600160a01b039092169190911790556113bd6004850161138f61137360808501516001600160801b031690565b82546001600160801b0319166001600160801b03909116178255565b60a08301516001600160801b031681546001600160801b031660809190911b6001600160801b031916179055565b60c0810151600585015561143a600685016114026113e560e08501516001600160401b031690565b825467ffffffffffffffff19166001600160401b03909116178255565b61010083015181546fffffffffffffffff0000000000000000191660409190911b6fffffffffffffffff000000000000000016179055565b61144c610120820151600786016111e3565b015191016111e3565b611168565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b03166001600160401b03811461148b5760010190565b61145a565b6001600160401b03629896809116116114a557565b637d2e4eb560e01b5f5260045ffd5b6001600160401b036001911601906001600160401b03821161148b57565b6001600160401b031661c350016001600160401b03811161148b576001600160401b036001600160801b035f54169116026001600160801b03811690810361148b5790565b90816020910312610193575180151581036101935790565b63ffffffff1663ffffffff811461148b5760010190565b906001600160801b03809116911601906001600160801b03821161148b57565b90816020910312610193575190565b6001600160a01b039182168152911660208201526001600160801b0391821660408201529116606082015260808101919091526001600160401b0391821660a0820152911660c082015261012060e08201819052610dfd9391926115db91840190610894565b91610100818403910152610894565b95969092949693919360018060a01b038816156105d85760405163037d635760e31b81526001600160a01b0388166004820152602081806024810103817f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03165afa9081156104a5575f91611a70575b5015611a615761167083611490565b610400815111611a525761168382611ff7565b600254604081901c63ffffffff16611969575b506116a0836114d2565b976116ab8987611546565b60405163868a2e9d60e01b8152979094906020896004815f60056007609a1b015af19889156104a5575f99611938575b508815611929576040516323b872dd60e01b81523360048201523060248201526001600160801b03871660448201526001600160a01b038b16906020816064815f865af19081156104a5575f916118fa575b50156118eb57803b1561019357604051630852cd8d60e31b81526001600160801b039790971660048801525f908790602490829084905af180156104a5577f34ca953f3eed14157d2f660c7e92a5bd9c05be0d61a188830f3bf1cb7d094f96996118cc97611899926118d1575b506118136117b86117b36004546001600160401b031690565b61146e565b956117d9876001600160401b03166001600160401b03196004541617600455565b6117f4876001600160401b03165f52600560205260405f2090565b80546001600160a01b0319166001600160a01b03909216919091179055565b61181b610dc6565b6001600160a01b038d1681529033602083015260408201526001600160a01b03891660608201526001600160801b038a1660808201526001600160801b038d1660a082015260c081018390526001600160401b03841660e08201526001600160401b03851661010082015285610120820152866101408201526112ae565b5f5460801c6001600160401b03169a6118b4610c918d61146e565b6040519889986001600160401b03339e169c8a611575565b0390a3565b806118df5f6118e593610da5565b80610189565b5f61179a565b6312171d8360e31b5f5260045ffd5b61191c915060203d602011611922575b6119148183610da5565b810190611517565b5f61172d565b503d61190a565b632968ee7f60e21b5f5260045ffd5b61195b91995060203d602011611962575b6119538183610da5565b810190611566565b975f6116db565b503d611949565b61199661198a6001600160401b034316926001600160401b039060801c1690565b6001600160401b031690565b8103611a1b575b506002546119c86119bf606083901c63ffffffff169260401c63ffffffff1690565b63ffffffff1690565b63ffffffff82161015611a0c576119e1611a069161152f565b6002805463ffffffff60601b191660609290921b63ffffffff60601b16919091179055565b5f611696565b63124ab48560e11b5f5260045ffd5b6002805460809290921b67ffffffffffffffff60801b166bffffffffffffffffffffffff60601b199092169190911790555f61199d565b634b8a874d60e11b5f5260045ffd5b631fcf8c4760e11b5f5260045ffd5b611a89915060203d602011611922576119148183610da5565b5f611661565b5f1981019190821161148b57565b604080825282516001600160a01b031690820152929190602090611b629080830151606087015260408101516001600160a01b0316608087015260608101516001600160801b031660a087015260808101516001600160801b031660c087015260a081015160e087015260c08101516001600160401b031661010087015260e08101516001600160401b0316610120870152610120611b4d610100830151610140808a0152610180890190610894565b910151868203603f1901610160880152610894565b930152565b611b718154610f10565b9081611b7b575050565b81601f5f9311600114611b8c575055565b81835260208320611ba991601f0160051c8419019060010161117b565b808252602082209081548360011b9084198560031b1c191617905555565b90611455576008815f610dd693555f60018201555f60028201555f60038201555f60048201555f60058201555f6006820155611c0560078201611b67565b01611b67565b6003545f60035580611c1a5750565b8060090290600982040361148b5760035f5260205f205f5b828110611c3e57505050565b80611c7c600860099385015f81555f60018201555f60028201555f60038201555f60048201555f60058201555f6006820155611c0560078201611b67565b01611c32565b9092915f9333151580611f78575b610407576001600160401b03804316911603611f6957600354808303611f5257508051828103611f3b575081611d79575b5050611cfc611ce0611cdb6002546001600160401b031690565b6114b4565b6001600160401b03166001600160401b03196002541617600255565b817fec4aff46c65f485f4b15e3c2edadda1d57d002995f5aa262a27c76b9a680ec16611d74611d336002546001600160401b031690565b611d3c84600155565b600280546001600160c01b03164260c01b6001600160c01b0319161790556040516001600160401b0390911681529081906020820190565b0390a2565b5f19935090805b611d95575050611d8e611c0b565b5f80611cc1565b611d9e81611a8f565b93611da885610ec2565b50611db290610fe6565b611dbc8685610efc565b518061014083015190611dce91612053565b8151602080840151604080860151905160609290921b6bffffffffffffffffffffffff191692820192835260348083019190915281526001600160a01b039092169391611e1c605482610da5565b51902060608201519091906001600160a01b031660808201516001600160801b031660a08301516001600160801b031660c08401519160e0850151611e67906001600160401b031690565b93610100860151611e7e906001600160401b031690565b95610120015196611e8d610dd8565b6001600160a01b03909a168a5260208a01526001600160a01b031660408901526001600160801b031660608801526001600160801b0316608087015260a08601526001600160401b031660c08501526001600160401b031660e084015261010083015261012082015260405180916020820193611f0a9185611a9d565b03601f1981018252611f1c9082610da5565b51902093611f2990610ec2565b611f3291611bc7565b5f190180611d80565b630db2128560e11b5f52600452602482905260445ffd5b6324a839db60e11b5f52600483905260245260445ffd5b631391e11b60e21b5f5260045ffd5b50604051630b83774760e31b81526020816004817f0000000000000000000000001c000000000000000000000000000000000000036001600160a01b03165afa9081156104a5575f91611fd8575b506001600160a01b0316331415611c90565b611ff1915060203d60201161049e576104908183610da5565b5f611fc6565b8051801561204f5760210361204057805115610ede576020810151612026906120229060f81c612084565b1590565b61204057612022602161203a9201516120ef565b61204057565b6361d0136b60e11b5f5260045ffd5b5050565b5161207c575f905b51818103612067575050565b63521a2d4d60e11b5f5260045260245260445ffd5b60719061205b565b60ff1660028114908115612096575090565b600391501490565b3d156120c8573d906120af82610e0b565b916120bd6040519384610da5565b82523d5f602084013e565b606090565b6020815191015190602081106120e1575090565b5f199060200360031b1b1690565b8015801561219b575b61218a575f9081906401000003d0199060079082908181800909086040805160208082018181529282018190526060820152608081019290925263800001e9600160ff1b0360a08301526401000003d01960c08084019190915282529061216060e082610da5565b519060055afa61216e61209e565b9015801561218f575b61218a576121866001916120cd565b1490565b505f90565b50602081511415612177565b506401000003d0198110156120f856fea2646970667358221220fbc967cb10d7a5d946594e2ae57ae4b0ad448cb8697124c0b9433b4769692f4064736f6c63430008230033", "nonce": "0x1" }, "0x1c00000000000000000000000000000000000003": { "balance": "0x0", - "code": "0x6080806040526004361015610012575f80fd5b5f3560e01c9081631beb1ab814610561575080631fbb25ad1461051d5780633488ce0d146102dd5780635c1bba381461023b5780636d46e98714610193578063a21de6d91461014f5763e202d99514610069575f80fd5b34610138575f366003190112610138576040516381e3da6b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260026024830152602090829060449082907f0000000000000000000000001c00000000000000000000000000000000000000165afa8015610144575f9061010d575b6040516001600160a01b039091168152602090f35b506020813d60201161013c575b816101276020938361067c565b8101031261013857602090516100f8565b5f80fd5b3d915061011a565b6040513d5f823e3d90fd5b34610138575f366003190112610138576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610138576020366003190112610138576004356001600160a01b0381169081900361013857604051630b83774760e31b8152602081600481305afa908115610144575f916101f6575b506040516001600160a01b039091169091148152602090f35b90506020813d602011610233575b816102116020938361067c565b810103126101385751906001600160a01b0382168203610138579060206101dd565b3d9150610204565b34610138575f366003190112610138576040516381e3da6b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301525f6024830152602090829060449082907f0000000000000000000000001c00000000000000000000000000000000000000165afa8015610144575f9061010d576040516001600160a01b039091168152602090f35b34610138575f366003190112610138576040516381e3da6b60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038181166004840152600760248401527f0000000000000000000000001c00000000000000000000000000000000000000169190602082604481865afa918215610144575f926104e9575b5081156104da576040516020810190600782526020815261038f60408261067c565b519020915f1981019081116104c6578060011b90808204600214901517156104c65782018092116104c65760018201908183116104c6576040516381e3da6b60e01b81526001600160a01b03821660048201526024810193909352602083604481875afa928315610144575f9361048e575b506040516381e3da6b60e01b81526001600160a01b03909116600482015260248101919091529160209083908180604481015b03915afa918215610144575f92610459575b5060ff6040928351928352166020820152f35b91506020823d602011610486575b816104746020938361067c565b810103126101385790519060ff610446565b3d9150610467565b919092506020823d6020116104be575b816104ab6020938361067c565b8101031261013857905191610434610401565b3d915061049e565b634e487b7160e01b5f52601160045260245ffd5b630c322fb560e31b5f5260045ffd5b9091506020813d602011610515575b816105056020938361067c565b810103126101385751908361036d565b3d91506104f8565b34610138575f366003190112610138576040517f0000000000000000000000001c000000000000000000000000000000000000006001600160a01b03168152602090f35b34610138576020366003190112610138576004356001600160a01b0381169190829003610138576105fe9181602080930191825260086040820152604081526105ab60608261067c565b5190206040516381e3da6b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166004820152602481019190915291829081906044820190565b03817f0000000000000000000000001c000000000000000000000000000000000000006001600160a01b03165afa8015610144575f90610649575b60209060ff604051911615158152f35b506020813d602011610674575b816106636020938361067c565b810103126101385760209051610639565b3d9150610656565b90601f8019910116810190811067ffffffffffffffff82111761069e57604052565b634e487b7160e01b5f52604160045260245ffdfea26469706673582212203429d776cda058be2d19a4198c33fa6c2b89f0e67020df0330b8fbe4c1307cf964736f6c63430008230033", + "code": "0x6080806040526004361015610012575f80fd5b5f3560e01c9081631beb1ab814610561575080631fbb25ad1461051d5780633488ce0d146102dd5780635c1bba381461023b5780636d46e98714610193578063a21de6d91461014f5763e202d99514610069575f80fd5b34610138575f366003190112610138576040516381e3da6b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260026024830152602090829060449082907f0000000000000000000000001c00000000000000000000000000000000000000165afa8015610144575f9061010d575b6040516001600160a01b039091168152602090f35b506020813d60201161013c575b816101276020938361067c565b8101031261013857602090516100f8565b5f80fd5b3d915061011a565b6040513d5f823e3d90fd5b34610138575f366003190112610138576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610138576020366003190112610138576004356001600160a01b0381169081900361013857604051630b83774760e31b8152602081600481305afa908115610144575f916101f6575b506040516001600160a01b039091169091148152602090f35b90506020813d602011610233575b816102116020938361067c565b810103126101385751906001600160a01b0382168203610138579060206101dd565b3d9150610204565b34610138575f366003190112610138576040516381e3da6b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301525f6024830152602090829060449082907f0000000000000000000000001c00000000000000000000000000000000000000165afa8015610144575f9061010d576040516001600160a01b039091168152602090f35b34610138575f366003190112610138576040516381e3da6b60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038181166004840152600760248401527f0000000000000000000000001c00000000000000000000000000000000000000169190602082604481865afa918215610144575f926104e9575b5081156104da576040516020810190600782526020815261038f60408261067c565b519020915f1981019081116104c6578060011b90808204600214901517156104c65782018092116104c65760018201908183116104c6576040516381e3da6b60e01b81526001600160a01b03821660048201526024810193909352602083604481875afa928315610144575f9361048e575b506040516381e3da6b60e01b81526001600160a01b03909116600482015260248101919091529160209083908180604481015b03915afa918215610144575f92610459575b5060ff6040928351928352166020820152f35b91506020823d602011610486575b816104746020938361067c565b810103126101385790519060ff610446565b3d9150610467565b919092506020823d6020116104be575b816104ab6020938361067c565b8101031261013857905191610434610401565b3d915061049e565b634e487b7160e01b5f52601160045260245ffd5b630c322fb560e31b5f5260045ffd5b9091506020813d602011610515575b816105056020938361067c565b810103126101385751908361036d565b3d91506104f8565b34610138575f366003190112610138576040517f0000000000000000000000001c000000000000000000000000000000000000006001600160a01b03168152602090f35b34610138576020366003190112610138576004356001600160a01b0381169190829003610138576105fe9181602080930191825260086040820152604081526105ab60608261067c565b5190206040516381e3da6b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166004820152602481019190915291829081906044820190565b03817f0000000000000000000000001c000000000000000000000000000000000000006001600160a01b03165afa8015610144575f90610649575b60209060ff604051911615158152f35b506020813d602011610674575b816106636020938361067c565b810103126101385760209051610639565b3d9150610656565b90601f8019910116810190811067ffffffffffffffff82111761069e57604052565b634e487b7160e01b5f52604160045260245ffdfea2646970667358221220b0b92c9155d6cce2d55408925208eeec080cfcc4d5ae4e31b14bc6a9ecd183a964736f6c63430008230033", "nonce": "0x1" }, "0x20c0000000000000000000000000000000000000": { @@ -138,5 +138,5 @@ "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x42", "timestamp": "0x0", - "zoneFactoryBytecode": "0x60808060405234610138575f805463ffffffff191660011781556003600755600680546001600160a01b03191633908117909155907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a36101a28181016001600160401b03811183821017610124578291614c96833903905ff08015610119576001600160a01b03165f8181526003602052604090819020805460ff19166001179055600480546001600160a01b031916909217909155516105a2808201906001600160401b03821183831017610124576020918391614e3883393081520301905ff0801561011957600580546001600160a01b0319166001600160a01b0392909216919091179055604051614b59908161013d8239f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fdfe6080806040526004361015610012575f80fd5b5f905f3560e01c90816301b290d314610edc575080632b7ac3f314610eb45780632d45796a146104815780633cb747bf1461045857806374134d371461043a57806376628f83146103f85780638da5cb5b146103cf57806390b7f6fd14610174578063eee83499146101275763f2fde38b1461008c575f80fd5b34610124576020366003190112610124576004356001600160a01b03811690819003610122576006546001600160a01b0381169033829003610113578215610104576001600160a01b03191682176006557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6349e27cff60e01b8452600484fd5b6330cd747160e01b8452600484fd5b505b80fd5b50346101245780600319360112610124575f1963ffffffff825416019063ffffffff82116101605760208263ffffffff60405191168152f35b634e487b7160e01b81526011600452602490fd5b50346101245760203660031901126101245760043563ffffffff81168091036101225760606101206040516101a881610f2e565b84815284602082015284604082015284838201528460808201528460a08201528460c08201528460e0820152846101008201520152815260016020526040812090604051906101f682610f2e565b825463ffffffff811683526001600160a01b03602091821c811691840191825260018501548116604080860191825260028701548316606087019081526003880154841660808801908152600489015490941660a08801908152600589015460c0890190815260068a015460e08a0190815260078b01546001600160401b03166101008b0190815294516008909b018054989b989597919692959394919391929189918b91906102a582610fb1565b80855291600181169081156103b15750600114610377575b5050036102ca9089610f4a565b6101208a81019889526040805160208082529c5163ffffffff168d8201529c516001600160a01b03908116918e01919091529151821660608d01529151811660808c01529151821660a08b015291511660c0890152905160e0880152905161010087015290516001600160401b031690850152516101408085015280516101608501819052849390929183910161018085015e8183016101800152601f1990601f01168101036101800190f35b8c5260208c208c92505b81831061039657505081016020015f806102bd565b80602092948385600194549201015201910190918a92610381565b9150506020925060ff191682840152151560051b8201015f806102bd565b50346101245780600319360112610124576006546040516001600160a01b039091168152602090f35b5034610124576020366003190112610124576004356001600160a01b038116908190036101225760408260ff9260209452600384522054166040519015158152f35b5034610124578060031936011261012457602060405162e4e1c08152f35b50346101245780600319360112610124576005546040516001600160a01b039091168152602090f35b5034610c3c576020366003190112610c3c576001600160401b0360043511610c3c5761010060043536036003190112610c3c576006546001600160a01b03163303610ea5576104d4600435600401610f1a565b6040516335ec42c960e01b81526001600160a01b03909116600482015260208160248161083f60921b5afa908115610c31575f91610e6a575b5015610e5b576001600160a01b03610529600435602401610f1a565b1615610e4c576001600160a01b03610545600435604401610f1a565b1615610e3d576001600160a01b03610561600435606401610f1a565b165f52600360205260ff60405f20541615610e2e5762e4e1c05a10610e1f575f5463ffffffff80821614610e1057600163ffffffff82160163ffffffff8111610dfc5763ffffffff1663ffffffff198216175f5560075460018101808211610dfc57600755606081610c54575050604051606b60f91b6020820152602560fa1b60218201523060601b6022820152600160ff1b603682015260178152610608603782610f4a565b60018060a01b03906020815191012016604051613b3a8082018281106001600160401b03821117610c40578291610fea833903905ff0908115610c3157610653600435600401610f1a565b6005546001600160a01b031661066d600435602401610f1a565b61067b604460043501610f1a565b610689606460043501610f1a565b9061069860c460043501610f6b565b926106ad60e460043501600435600401610f7f565b91906001600160a01b038a163b15610c3c576001600160401b0363ffffffff96610144968d9686955f9b6040519d8e9c8d9b633fd2490760e01b8d521660048c015260018060a01b031660248b015260448a015260018060a01b0316606489015260018060a01b0316608488015260018060a01b031660a48701526084600435013560c48701521660e485015261012061010485015281610124850152848401378181018301859052601f01601f19168101030181836001600160a01b0388165af18015610c3157610c1c575b506001600160a01b03821603610bc057610798600435600401610f1a565b6107a6602460043501610f1a565b6107b4604460043501610f1a565b906001600160401b036107cb606460043501610f1a565b6107d960c460043501610f6b565b906107ee60e460043501600435600401610f7f565b959094604051976107fe89610f2e565b63ffffffff8b1689526001600160a01b038a811660208b015290811660408a0152908116606089015290811660808801521660a0860152600435608481013560c087015260a4013560e0860152166101008401526001600160401b038211610ba85785906040519261087a6020601f19601f8401160185610f4a565b8084523681830111610bbc57806020928386013783010152610120820190815263ffffffff848116865260016020818152604080892086518154848901516001600160c01b031990911691909616179490921b640100000000600160c01b03169390931781559184015190820180546001600160a01b03199081166001600160a01b0393841617909155606085015160028401805483169184169190911790556080850151600384018054831691841691909117905560a0850151600484018054909216921691909117905560c0830151600582015560e083015160068201556101009092015160078301805467ffffffffffffffff19166001600160401b0392831617905590518051918211610ba8576109986008840154610fb1565b601f8111610b53575b50602090601f8311600114610ae257828793604098936008938a979592610ad7575b50508160011b915f199060031b1c1916179101555b6001600160a01b0383168152600260205220805460ff19166001179055610a026004803501610f1a565b6001600160401b03610a18602460043501610f1a565b610a26604460043501610f1a565b610a34606460043501610f1a565b90610a4360c460043501610f6b565b88516001600160a01b03968716815293861660208501529085168389015290841660608301526004356084810135608084015260a4013560a08301529190911660c08201529082169063ffffffff8416907fa0d8b561b80ba334b8b367f1fd53ab8d8719fa1c348a8de4bbfd8bbffa90b3379060e090a3825163ffffffff9290921682526001600160a01b03166020820152f35b015190505f806109c3565b9060088401875280872091875b601f1985168110610b3b57508360409894936008936001938b9896601f19811610610b23575b505050811b019101556109d8565b01515f1960f88460031b161c191690555f8080610b15565b91926020600181928685015181550194019201610aef565b828111156109a15760088401875260208720601f840160051c9060208510610ba0575b81601f9101920160051c0390875b828110610b925750506109a1565b808960019284015501610b84565b889150610b76565b634e487b7160e01b86526041600452602486fd5b8280fd5b60405162461bcd60e51b815260206004820152602e60248201527f506f7274616c2061646472657373206d69736d61746368202d206e6f6e63652060448201526d3a3930b1b5b4b7339032b93937b960911b6064820152608490fd5b610c299194505f90610f4a565b5f925f61077a565b6040513d5f823e3d90fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b607f8211610ca65750604051606b60f91b6020820152602560fa1b60218201523060601b602282015260f89190911b6001600160f81b031916603682015260178152610ca1603782610f4a565b610608565b60ff8211610cfd575060405160d760f81b6020820152602560fa1b60218201523060601b6022820152608160f81b603682015260f89190911b6001600160f81b031916603782015260188152610ca1603882610f4a565b61ffff8211610d555750604051601b60fb1b6020820152602560fa1b60218201523060601b6022820152604160f91b603682015260f09190911b6001600160f01b031916603782015260198152610ca1603982610f4a565b62ffffff8211610dae575060405160d960f81b6020820152602560fa1b60218201523060601b6022820152608360f81b603682015260e89190911b6001600160e81b0319166037820152601a8152610ca1603a82610f4a565b604051606d60f91b6020820152602560fa1b60218201523090911b6022820152602160fa1b603682015260e09190911b6001600160e01b0319166037820152601b8152610ca1603b82610f4a565b634e487b7160e01b5f52601160045260245ffd5b63e558bfa360e01b5f5260045ffd5b6307099c5360e21b5f5260045ffd5b63baa3de5f60e01b5f5260045ffd5b633c9f858360e21b5f5260045ffd5b630b5eba9f60e41b5f5260045ffd5b63c1ab6dc160e01b5f5260045ffd5b90506020813d602011610e9d575b81610e8560209383610f4a565b81010312610c3c57518015158103610c3c575f61050d565b3d9150610e78565b6330cd747160e01b5f5260045ffd5b34610c3c575f366003190112610c3c576004546040516001600160a01b039091168152602090f35b34610c3c576020366003190112610c3c576004356001600160a01b0381169190829003610c3c576020915f526002825260ff60405f20541615158152f35b356001600160a01b0381168103610c3c5790565b61014081019081106001600160401b03821117610c4057604052565b90601f801991011681019081106001600160401b03821117610c4057604052565b356001600160401b0381168103610c3c5790565b903590601e1981360301821215610c3c57018035906001600160401b038211610c3c57602001918136038313610c3c57565b90600182811c92168015610fdf575b6020831014610fcb57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610fc056fe60808060405234601557613b20908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f905f3560e01c9081625d97ef146126af57508062ebfdb41461262557806309a0a234146123575780630d0c743e14611f235780630e18b68114611e945780630e86bbdc14611dff5780632678224714611dd657806327c71b5014611d505780632b7ac3f314611d275780632c37826e14611cfd5780632dfdf0b514611cd65780633488ce0d14611c6b57806337f981d814611c1657806339dc015d14611aad5780633cb747bf14611a845780633fd24907146117d05780634256ce38146117b257806345c5d2fb14611794578063580bbcfc1461176a5780635c1bba3814611743578063652ef10b14611568578063748538d91461152957806375829def146114a45780638081a8b21461148657806383a146b31461146b578063857e85f81461140f57806386f47e55146113f15780638976248d146113c557806394ce88b0146113a7578063959f47c3146112d35780639a97784b146112305780639ecd4cc0146111f8578063b01f22e414610bed578063b02690f214610bb5578063be2a63ee14610b8b578063bf8e244014610b07578063bffa55d514610a48578063c13a2d4b146109cf578063c690908a146108db578063cfaabb4d146108b1578063d179978a1461088d578063e09eae10146107fb578063e202d995146107d2578063e4643e8514610745578063e471784914610727578063e84abe69146106fd578063ecf79a4e146106d6578063ef10b1871461038e578063f22a195e14610370578063f490ca961461034d578063f706cfbf1461032f578063f851a44014610306578063fb2f1206146102e55763fe136c4e1461026e575f80fd5b346102e25760203660031901126102e2576040809161028b612855565b8160208451610299816129c4565b82815201526001600160a01b031681526008602052208151906102bb826129c4565b5460ff6020818316151593848152019160081c161515815282519182525115156020820152f35b80fd5b50346102e257806003193601126102e257602060065460c01c604051908152f35b50346102e257806003193601126102e2576001546040516001600160a01b039091168152602090f35b50346102e257806003193601126102e2576020604051620186a08152f35b50346102e257806003193601126102e2576020604051670de0b6b3a76400008152f35b50346102e257806003193601126102e2576020600454604051908152f35b50346102e25760a03660031901126102e25760043560243560ff8116908181036106d2576044359060ff82168092036106ce5784546001600160a01b031633036106bf576103db90613856565b156106b0576103e983613892565b156106b057836080602092604051848101903082528760408201528660608201526060815261041884826129df565b51902090604051918252848201526064356040820152608435606082015282805260015afa156106a557825183806401000003d019600781878181800909086040516020810191602083526020604083015260206060830152608082015263400000f4600160fe1b0360a08201526401000003d01960c082015260c081526104a160e0826129df565b519060055afa6104af61354b565b908061069a575b15610665576104c490613870565b6002831460018216150361063b575b6040519060208201908582526040830152604082526104f36060836129df565b905190206001600160a01b03918216801592909116908215610630575b5050610621576001600160401b0343169060405161052d816129a9565b83815260208101908282526040810190848252600754600160401b81101561060d5780600161055f9201600755612acd565b9190916105f95760ff916001915181550192511668ffffffffffffffff008354925160081b169168ffffffffffffffffff191617179055600754905f1982019182116105e557917f82b5f4090f18a082bc8156b956154bfe0319307f5e5a7e903ef33f14ad2cb17e9391608093604051938452602084015260408301526060820152a180f35b634e487b7160e01b85526011600452602485fd5b634e487b7160e01b88526004889052602488fd5b634e487b7160e01b88526041600452602488fd5b6392536faf60e01b8352600483fd5b141590505f80610510565b6401000003d019036401000003d0198111156104d357634e487b7160e01b85526011600452602485fd5b60405162461bcd60e51b815260206004820152600d60248201526c1b5bd9195e1c0819985a5b1959609a1b6044820152606490fd5b5060208151146104b6565b6040513d84823e3d90fd5b632f0be49b60e11b8452600484fd5b6314f8a09f60e21b8552600485fd5b8480fd5b8380fd5b50346102e257806003193601126102e25760206001600160801b0360035416604051908152f35b50346102e257806003193601126102e25760206001600160401b0360065460401c16604051908152f35b50346102e257806003193601126102e2576020600954604051908152f35b50346102e257806003193601126102e2576002546001600160a01b03811690811580156107c8575b6107b95782546001600160a01b0319808216841785559091166002556001600160a01b03167f9945af900637995146537e5ea7a7be455dfd08bbb46fdd79e9c3a2bad0c4eef88380a380f35b63d71402d960e01b8352600483fd5b508133141561076d565b50346102e257806003193601126102e2576002546040516001600160a01b039091168152602090f35b50346102e25760203660031901126102e2576108156128d5565b81546001600160a01b0316330361087e57600680546001600160c01b031660c083901b6001600160c01b0319161790556040516001600160401b0390911681527f66bcd750662bb66118e25a8e421ae73974634d9af2d44fb9e600d250917fe69090602090a180f35b6314f8a09f60e21b8252600482fd5b50346102e257806003193601126102e257602063ffffffff60115416604051908152f35b50346102e25760203660031901126102e25760406020916004358152600d83522054604051908152f35b50346102e25760203660031901126102e2576108f5612855565b6001546001600160a01b031633036109c0576001600160a01b0381168083526008602052604083205460ff166109b1576040516335ec42c960e01b8152600481019190915260208160248161083f60921b5afa9081156109a6578391610977575b5015610968576109659061369e565b80f35b631fcf8c4760e11b8252600482fd5b610999915060203d60201161099f575b61099181836129df565b810190612a24565b5f610956565b503d610987565b6040513d85823e3d90fd5b632909298f60e11b8352600483fd5b637bfa4b9f60e01b8252600482fd5b50346102e25760403660031901126102e2576004356001600160401b038111610a44576101406003198236030112610a445781546001600160a01b0316330361087e57601054610a3557610a2e90600160105560243590600401612c9a565b8060105580f35b630b5c738160e01b8252600482fd5b5080fd5b50346102e25760203660031901126102e257610a62612855565b6001600160a01b038116808352600a60208181526040808620335f818152918452828220548689529484528288208183529093522080546001600160801b03191690556001600160801b0390911692610abc91849161394b565b15610af857602092506040518281527fffd3bbab073ab4b2d0792c270104924c14c285a153b9acddabae166395d2eb5c843392a3604051908152f35b63be51ced360e01b8352600483fd5b50346102e25760203660031901126102e257610b21612855565b81546001600160a01b03169033829003610b7c57600280546001600160a01b0319166001600160a01b03929092169182179055907f12cf9e4d760566fb6a3e0193406f2295fc4b53f2b60817145a61f29e470cf7d68380a380f35b6314f8a09f60e21b8352600483fd5b50346102e257806003193601126102e25760206001600160401b0360065460801c16604051908152f35b50346102e25760203660031901126102e2576020610bd4600435612c5a565b905460405160039290921b1c6001600160a01b03168152f35b50346102e25760a03660031901126102e257610c07612855565b906024356001600160801b0381168103610a44576001600160401b0360643511610a445760a060643536036003190112610a4457610c43612881565b906001600160a01b038216156111e957610c5c846132dc565b604051639c4bad2960e01b81526020816004816001600160a01b0389165afa9081156111b15783610ccb9260209287916111bc575b506040516337de09eb60e11b81526001600160401b0390911660048201526001600160a01b03909116602482015291829081906044820190565b038161100f60921b5afa9081156111b1578491611192575b501561118357610cff610cfa602460643501612bff565b613856565b1561117457610d1360643560040135613892565b15611174576040610d2e604460643501606435600401612c0d565b90500361114957610d40604435612b6b565b50156110b757610d509084613367565b9060405192610d5e8461297a565b6001600160a01b0386811685523360208601526001600160801b03841660408087019190915290821660608601526044356080860152519360a085016001600160401b038111868210176110a3576040526064356004013585526024606435013560ff8116810361109f5760208601526001600160401b03604460643501351161109b5736606435604481013501602301121561109b57610e0a60643560448101350160040135612c3f565b610e1760405191826129df565b606435604481013501600481013580835236602491909201011161109f5760643560448101350160048101359060240160208301376064803560448101358101600401358301602001899052604088019290925201356001600160a01b03198116900361109b57606480350135606086015260846064350135946001600160801b03198616958681036110975792611054886080610fcb6001600160401b03968660209e99847f4debdbd2891da07eb466d2025028a25aab636034cf3c941afd1025a0c590a07a9c99015260a082019081526005549060405194602093869485019750600188526060604086015260018060a01b038151168286015260018060a01b0360208201511660a08601526001600160801b0360408201511660c086015260018060a01b0360608201511660e08601520151610100840152519060c061012084015281516101408401528f60ff9083015116610160840152610f8d604083015160a06101808601526101e0850190612a00565b6060808401516001600160a01b0319166101a08601526080909301516001600160801b0319166101c08501529183015203601f1981018352826129df565b51902098899760ff610fdc8a613515565b956001600160801b03610ff3602460643501612bff565b9181611009604460643501606435600401612c0d565b969097506040519c60018060a01b03168d521660208c01521660408a015260443560608a01526064356004013560808a01521660a088015261016060c0880152610160870191612a3c565b6064803501356001600160a01b03191660e08601526101008501979097526001600160a01b031661012084015216610140820152339381900390a3604051908152f35b8780fd5b8580fd5b8680fd5b634e487b7160e01b87526041600452602487fd5b826007546044351015611134576110cf604435612acd565b5090600160443501918260443511611120576001600160401b03600181816110f8606497612acd565b5094015460081c1692015460081c169063034dff0f60e01b8352604435600452602452604452fd5b634e487b7160e01b82526011600452602482fd5b637f455d3760e11b8152604435600452602490fd5b60448361115f8260643501606435600401612c0d565b632331a96360e11b8352600452506040602452fd5b632f0be49b60e11b8352600483fd5b6354cfe65960e01b8352600483fd5b6111ab915060203d60201161099f5761099181836129df565b5f610ce3565b6040513d86823e3d90fd5b6111dc9150833d85116111e2575b6111d481836129df565b810190612be0565b5f610c91565b503d6111ca565b638dc30b3960e01b8352600483fd5b50346102e25760203660031901126102e25760406001600160401b0361121f600435612b6b565b835191151582529091166020820152f35b50346102e25760203660031901126102e257600435908060408051611254816129a9565b82815282602082015201526007548210156112c157606061127483612acd565b506001600160401b03604051611289816129a9565b60ff600184549485845201549183604060208301928486168452019360081c1683526040519485525116602084015251166040820152f35b602491637f455d3760e11b8252600452fd5b50346102e257806003193601126102e2576040519080600e54906112f682612942565b80855291600181169081156113805750600114611336575b6113328461131e818603826129df565b604051918291602083526020830190612a00565b0390f35b600e81525f516020613aab5f395f51905f52939250905b8082106113665750909150810160200161131e8261130e565b91926001816020925483858801015201910190929161134d565b60ff191660208087019190915292151560051b8501909201925061131e915083905061130e565b50346102e257806003193601126102e2576020600b54604051908152f35b50346102e257806003193601126102e25760206113e0612b46565b6001600160801b0360405191168152f35b50346102e257806003193601126102e2576020604051629896808152f35b50346102e25760403660031901126102e257604061142b612855565b9161143461286b565b9260018060a01b03168152600a602052209060018060a01b03165f5260205260206001600160801b0360405f205416604051908152f35b50346102e257806003193601126102e25760206113e0612afd565b50346102e257806003193601126102e2576020600c54604051908152f35b50346102e25760203660031901126102e2576114be612855565b6001546001600160a01b0316903382900361151a57600f80546001600160a01b0319166001600160a01b03929092169182179055907fe5cd1c804f1c9cc6d7009e4c0fb532f0e2d8863524c3323a6b3790c3f80bf25c8380a380f35b637bfa4b9f60e01b8352600483fd5b50346102e25760203660031901126102e25760209060ff906040906001600160a01b03611554612855565b168152600884522054166040519015158152f35b50346102e25760203660031901126102e2576004356001600160401b038111610a4457611599903690600401612915565b8254909291906001600160a01b0316330361087e576001600160401b03831161172f576115c7600e54612942565b601f81116116d3575b508192601f8111600114611645578083947ff4e00967b25e707df96d88676243b33be84847ef27615af8ef91290b52294fc6949161163a575b508160011b905f198360031b1c191617600e555b611634604051928392602084526020840191612a3c565b0390a180f35b90508201355f611609565b600e8352601f198116935f516020613aab5f395f51905f5290845b8681106116bb5750827ff4e00967b25e707df96d88676243b33be84847ef27615af8ef91290b52294fc69596106116a2575b5050600181811b01600e5561161d565b8301355f19600384901b60f8161c191690555f80611692565b90916020600181928588013581550193019101611660565b838111156115d057600e8352601f840160051c9060208510611727575b601f82910160051c0390835b82811061170a5750506115d0565b80855f516020613aab5f395f51905f5260019385010155016116fc565b8391506116f0565b634e487b7160e01b82526041600452602482fd5b50346102e257806003193601126102e257546040516001600160a01b039091168152602090f35b50346102e257806003193601126102e25760206001600160401b0360125460a01c16604051908152f35b50346102e257806003193601126102e2576020600554604051908152f35b50346102e257806003193601126102e2576020600754604051908152f35b50346102e2576101203660031901126102e25760043563ffffffff8116809103610a44576117fc61286b565b906044356001600160a01b03811681036106d2576064356001600160a01b03811692908390036106ce5761182e612881565b60a4356001600160a01b038116919082900361109f5761184c6128eb565b90610104356001600160401b038111611a805761186d903690600401612915565b969095735af1ffffffffffffffffffffffffffffffffffff193301611a71576012549560ff8760e01c16611a6357601180546001600160c01b0319169190911760209290921b640100000000600160c01b0316919091179055600180546001600160a01b03199081169290921790558854166001600160a01b039190911617875560c4356004556001600160e81b03199092161760a09190911b67ffffffffffffffff60a01b1617600160e01b176012556001600160401b038211611a4f57611937600e54612942565b601f81116119f3575b508390601f831160011461198057610965939291859183611975575b50508160011b915f199060031b1c191617600e5561369e565b013590505f8061195c565b600e85525f516020613aab5f395f51905f5291601f198416865b8181106119db5750916001939185610965979694106119c2575b505050811b01600e5561369e565b01355f19600384901b60f8161c191690555f80806119b4565b9193602060018192878701358155019501920161199a565b8281111561194057600e8552601f830160051c9060208410611a47575b601f82910160051c0390855b828110611a2a575050611940565b80875f516020613aab5f395f51905f526001938501015501611a1c565b859150611a10565b634e487b7160e01b84526041600452602484fd5b62dc149f60e41b8b5260048bfd5b631966391b60e11b8a5260048afd5b8880fd5b50346102e257806003193601126102e257601154604051602091821c6001600160a01b03168152f35b50346102e25760203660031901126102e257611ac76128d5565b906007549081158015611bbc575b611ba05780915f19810190811161112057836001600160401b0384929116925b818310611b2657606083611b0881612acd565b509060ff600183549301541660405192835260208301526040820152f35b9091828101808211611b8c5760018101809111611b8c5760011c90846001600160401b036001611b5585612acd565b50015460081c1611611b6a5750915b90611af5565b92505f19810190811115611b6457634e487b7160e01b82526011600452602482fd5b634e487b7160e01b83526011600452602483fd5b63302c66ab60e21b81526001600160401b038316600452602490fd5b5060075415611c0257600781527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6895460081c6001600160401b0390811690841610611ad5565b634e487b7160e01b81526032600452602490fd5b50346102e25760203660031901126102e2576020906040906001600160a01b03611c3e612855565b16815260088352205460ff81169081611c5d575b506040519015158152f35b60ff915060081c165f611c52565b50346102e257806003193601126102e257600754908115611cc7575f198201918211611cb3576040611c9c83612acd565b5060ff600182549201541682519182526020820152f35b634e487b7160e01b81526011600452602490fd5b630c322fb560e31b8152600490fd5b50346102e257806003193601126102e25760206001600160401b0360065416604051908152f35b50346102e257806003193601126102e25760206001600160401b0360035460801c16604051908152f35b50346102e257806003193601126102e2576012546040516001600160a01b039091168152602090f35b50346102e25760203660031901126102e257611d6a612855565b6001546001600160a01b031633036109c0576001600160a01b03168082526008602052604082205460ff161561096857808252600860205260408220805461ff00191690557feb225a736fbfee3f85ccb72bdf84ff0396ab358b7970e2cc351ab3e3fd92358d8280a280f35b50346102e257806003193601126102e257600f546040516001600160a01b039091168152602090f35b50346102e25760203660031901126102e2576004356001600160801b038116809103610a445781546001600160a01b0316330361087e57670de0b6b3a76400008111611e85576020817fc62141e607d6fcbf7d11fd2b6d8e18e5ebef6d3fff8136ca98822801abbaea38926001600160801b03196003541617600355604051908152a180f35b630d62f21160e11b8252600482fd5b50346102e257806003193601126102e257600f546001600160a01b0381169081158015611f19575b611f0a57600180546001600160a01b031980821685179092559116600f556001600160a01b03167ff8ccb027dfcd135e000e9d45e6cc2d662578a8825d4c45b5e32e0adf67e79ec68380a380f35b63058d9a1b60e01b8352600483fd5b5081331415611ebc565b50346102e2576101603660031901126102e257611f3e6128d5565b6024356001600160401b038116918282036106d25760403660431901126106d25760803660831901126106d2576101043591610124356001600160401b03811161109b57611f90903690600401612915565b9094610144356001600160401b03811161109757611fb2903690600401612915565b88546001600160a01b03169190338390036123485760443593600454850361233957601254906001600160401b038916976001600160401b038360a01c1689106122fa578161231857505087944388116123095761200f8861357a565b945b85156122fa5761201f612a5c565b98600654996001600160401b03808c60401c169116148015906122d7575b80156122bb575b6122ac57908d96959493929163ffffffff6011541696600354996001600160401b038b60801c16995060018a01926001600160401b038411612295576001600160401b0393929184916040519b632d8a30cd60e21b8d5260048d015260248c01521660448a0152606489015216608487015260a486015260c4850152606435968760e486015260843561010486015260a4359b8c61012487015260c435916001600160401b03831680930361229157602095879561213d8f946121509489979688976101448901526001600160401b0361211c6128eb565b166101648901526101848801526101e06101a48801526101e4870191612a3c565b848103600319016101c486015291612a3c565b03916001600160a01b03165afa908115612286578991612267575b50156122585767ffffffffffffffff60801b9061218790612a9c565b60801b16906001600160401b0360801b1916176003556004556fffffffffffffffff00000000000000006121b9612a72565b60401b16916001600160401b0360801b9060801b169077ffffffffffffffffffffffffffffffff0000000000000000191617176006557f5a66941dc92cb865480c966eff640c02b1d00d544b74332fd67c6f1cbfccdf39608061221b836135e8565b936001600160401b03600354831c1693600454906001600160401b0360065460401c1691604051938452602084015260408301526060820152a380f35b6309bde33960e01b8852600488fd5b612280915060203d60201161099f5761099181836129df565b5f61216b565b6040513d8b823e3d90fd5b8e80fd5b50505060248f634e487b7160e01b81526011600452fd5b633d9dffbd60e01b8e5260048efd5b506122c4612a72565b6001600160401b03808c16911611612044565b506122e0612a72565b6001600160401b03806122f1612a5c565b1691161061203d565b632705871360e01b8d5260048dfd5b632705871360e01b8c5260048cfd5b8882979211156122fa574387116122fa57612333909661357a565b94612011565b6309bde33960e01b8b5260048bfd5b6314f8a09f60e21b8a5260048afd5b50346102e25760a03660031901126102e257612371612855565b61237961286b565b906123826128ab565b6064359061238e612881565b6001600160a01b038116959093908615612616576123ab826132dc565b604051639c4bad2960e01b81526001600160a01b03831695906020816004818a5afa9081156109a65783916125f7575b506040516337de09eb60e11b81526001600160401b03821660048201526001600160a01b038916602482015260208160448161100f60921b5afa9081156111b15784916125d8575b50156111835760405163b389e30560e01b81526001600160401b03821660048201526001600160a01b038916602482015260208160448161100f60921b5afa9081156111b15784916125b9575b5015611183576040516337de09eb60e11b81526001600160401b039190911660048201526001600160a01b0391909116602482015260208160448161100f60921b5afa9081156106a557829161259a575b501561258b5750946001600160401b03926124e66001600160801b0393602098613367565b939061252c82604051996124f98b61297a565b898b52338c8c015260018060a01b0316968760408c015216988960608201528560808201528460a0820152600554613490565b9761253689613515565b956040519889528a8901526040880152166060860152608085015260a08401521660c0820152817ffee50869847a4d073bfd83ec655472febff62beb46febf4ec48752f0686affd160e03393a3604051908152f35b6354cfe65960e01b8152600490fd5b6125b3915060203d60201161099f5761099181836129df565b5f6124c1565b6125d2915060203d60201161099f5761099181836129df565b5f612470565b6125f1915060203d60201161099f5761099181836129df565b5f612423565b612610915060203d6020116111e2576111d481836129df565b5f6123db565b638dc30b3960e01b8152600490fd5b50346102e25760203660031901126102e25761263f612855565b6001546001600160a01b031633036109c0576001600160a01b03168082526008602052604082205460ff161561096857808252600860205260408220805461ff0019166101001790557f22ab73af03f04a21e91c7923327f99279b7f5d07d9551762c39bccdf051f1fe98280a280f35b9050346128145760c0366003190112612814576126ca612855565b6126d261286b565b916126db6128ab565b6084356001600160401b0381168091036128145760a4356001600160401b0381116128145761270e903690600401612915565b9190943033036128465760115463a9059cbb60e01b86526001600160a01b03602091821c811660048801526001600160801b0386166024880152919091169490816044815f895af1908115612809575f91612827575b501561281857601154602081901c6001600160a01b031693843b15612814575f9663ffffffff9488946001600160801b036127e9946040519c8d9b8c9a8b996311da526160e01b8b521660048a01526024890152606435604489015260018060a01b0316606488015216608486015260a485015260e060c485015260e4840191612a3c565b03925af18015612809576127fb575080f35b61280791505f906129df565b005b6040513d5f823e3d90fd5b5f80fd5b6312171d8360e31b5f5260045ffd5b612840915060203d60201161099f5761099181836129df565b5f612764565b6314e1dbf760e11b5f5260045ffd5b600435906001600160a01b038216820361281457565b602435906001600160a01b038216820361281457565b608435906001600160a01b038216820361281457565b35906001600160a01b038216820361281457565b604435906001600160801b038216820361281457565b35906001600160801b038216820361281457565b600435906001600160401b038216820361281457565b60e435906001600160401b038216820361281457565b35906001600160401b038216820361281457565b9181601f84011215612814578235916001600160401b038311612814576020838186019501011161281457565b90600182811c92168015612970575b602083101461295c57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612951565b60c081019081106001600160401b0382111761299557604052565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b0382111761299557604052565b604081019081106001600160401b0382111761299557604052565b90601f801991011681019081106001600160401b0382111761299557604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90816020910312612814575180151581036128145790565b908060209392818452848401375f828201840152601f01601f1916010190565b60c4356001600160401b03811681036128145790565b60e4356001600160401b03811681036128145790565b356001600160401b03811681036128145790565b6001600160401b03166001600160401b038114612ab95760010190565b634e487b7160e01b5f52601160045260245ffd5b600754811015612ae95760075f5260205f209060011b01905f90565b634e487b7160e01b5f52603260045260245ffd5b60065460c01c48810290808204481490151715612ab95764e8d4a51000810190818111612ab95764e8d4a50fff01908111612ab95764e8d4a510006001600160801b0391041690565b6001600160801b0360035416620186a0026001600160801b038116908103612ab95790565b60075480821015612bd8575f198101908111612ab9578114612bd05760018101809111612ab9576001600160401b036001612ba96201518093612acd565b50015460081c1601906001600160401b038211612ab9576001600160401b03821643109190565b506001905f90565b50505f905f90565b9081602091031261281457516001600160401b03811681036128145790565b3560ff811681036128145790565b903590601e198136030182121561281457018035906001600160401b0382116128145760200191813603831361281457565b6001600160401b03811161299557601f01601f191660200190565b600954811015612ae95760095f5260205f2001905f90565b356001600160a01b03811681036128145790565b356001600160801b03811681036128145790565b600b545f91600c5482146132cd576064820693845f52600d60205260405f2054905f19821480156132c3575b6132ae5780159384156132bc575f19935b60408051602081019182529590926001600160a01b03612cf684612897565b1660608801526020830135608088018190529960408401956001600160a01b03612d1f88612897565b1660a08a01526060850199612d338b6128c1565b6001600160801b031660c08b01526080860198612d4f8a6128c1565b6001600160801b031660e08c015260a08701356101008c015260c0870197612d7689612901565b6001600160401b03166101208d015260e088019b612d938d612901565b6001600160401b0316610140820152806101008a0198612db38a8c6139be565b610160840161014090526101a0840190612dcc92612a3c565b612dda6101208d018d6139be565b848303605f1901610180860152612df19291612a3c565b90604083015203601f1981018252612e0990826129df565b519020036132ae571561329b57505f52600d6020525f1960405f205560018101809111612ab957600b555b612e3d82612c72565b946001600160401b03612e4f88612a88565b16156130eb576001600160801b03612e6682612c86565b166130c6575b50629896806001600160401b03612e8285612a88565b1611613059576001600160401b03612e9984612a88565b16612f3e57505050916001600160801b035f516020613acb5f395f51905f529492612ef3612eed86612edf612ecf606099612c72565b612ed889612c86565b908661394b565b159586612f1b575b50612c72565b94612c86565b90604051975060018060a01b0316875216602086015215604085015260018060a01b031692a3565b612f3890612f31612f2b8a612c86565b91612a88565b90866139ef565b5f612ee7565b612f63612f4a85612c72565b91612f5d612f578a612c86565b95612a88565b93612c0d565b9092303b1561281457604051625d97ef60e01b81526001600160a01b03808916600483015290931660248401526001600160801b03949094166044830152606482018a90526001600160401b0393909316608482015260c060a4820152915f9183918291612fd59160c4840191612a3c565b038183305af1908161301a575b505f516020613acb5f395f51905f529492612ef3612eed606096946001600160801b0394155f146130135789612edf565b6001612edf565b6001600160801b0391965092612ef3612eed606096946130495f5f516020613acb5f395f51905f529a986129df565b5f99945094965050509294612fe2565b505050839194506130a161309b6001600160801b039261309660609661308f612f2b5f516020613acb5f395f51905f529a612c86565b908a6139ef565b612c72565b92612c86565b604080516001600160a01b0397881681529290911660208301525f90820152931692a3565b6130e4906130dd60018060a01b035f541691612c86565b908761394b565b505f612e6c565b50509350509350506130fe919350612c72565b613106612afd565b926001600160801b0361311882612c86565b166001600160801b03851611613280575b8361313661313b92612c86565b613347565b906001600160801b038416613263575b61315e8261315885612c72565b8361394b565b156131d4577f0f7ef08806234f85aaee43d3ba4589c3bc6d5ac3fc8edd56fc3d91cc7553bdcb926131cf906001600160a01b039061319b90612c72565b1694604051938493849160409194936001600160801b038092606086019760018060a01b0316865216602085015216910152565b0390a2565b6001600160a01b0381165f908152600a602052604090207f5fea28d0adb7d877ae3259768f41ad6741aa1784c4475746dd931364f62e68a1936131cf9161321a82612c72565b60018060a01b03165f5260205260405f206001600160801b036132408682845416613327565b82546001600160801b03191691161790556001600160a01b039061319b90612c72565b5f5461327a9085906001600160a01b03168361394b565b5061314b565b61313693508061329261313b92612c86565b94509050613129565b91505f52600d60205260405f2055612e34565b626aee3160e51b5f5260045ffd5b8193612cd7565b505f198114612cc6565b63d7fa91d160e01b5f5260045ffd5b6001600160a01b03165f9081526008602052604090205460ff8116156133185760081c60ff161561330957565b63dec712ef60e01b5f5260045ffd5b631fcf8c4760e11b5f5260045ffd5b906001600160801b03809116911601906001600160801b038211612ab957565b906001600160801b03809116911603906001600160801b038211612ab957565b9091613371612b46565b926001600160801b0361338b613385612afd565b86613327565b16906001600160801b03811691821061348157846133a891613347565b6040516323b872dd60e01b81523360048201523060248201526044810192909252926001600160a01b03166020826064815f855af1918215612809578592613464575b506001600160801b0382166133fe575050565b5f805460405163a9059cbb60e01b81526001600160a01b0390911660048201526001600160801b0393909316602484015260209183916044918391905af18015612809576134495750565b6134619060203d60201161099f5761099181836129df565b50565b61347c9060203d60201161099f5761099181836129df565b6133eb565b636ba4a1c760e01b5f5260045ffd5b6040519060a060208301935f8552600180831b038151166040850152600180831b036020820151166060850152600180831b0360408201511660808501526001600160801b0360608201511682850152600180831b0360808201511660c0850152015160e0830152610100820152610100815261350f610120826129df565b51902090565b60055560065461352d6001600160401b038216612a9c565b67ffffffffffffffff199091166001600160401b0382161760065590565b3d15613575573d9061355c82612c3f565b9161356a60405193846129df565b82523d5f602084013e565b606090565b905f80809360405160208101918252602081526135986040826129df565b519071f90827f1c53a10cb7a02335b1753200029355afa6135b761354b565b90806135dc575b6135c55750565b909150602081805181010312612814576020015190565b506020815110156135be565b801561363657600c5490600b548203828111612ab9576064111561362757606482065f52600d60205260405f205560018101808211612ab957600c5590565b632764e20b60e01b5f5260045ffd5b505f1990565b602081830312612814578051906001600160401b038211612814570181601f820112156128145780519061366f82612c3f565b9261367d60405194856129df565b8284526020838301011161281457815f9260208093018386015e8301015290565b6040516136aa816129c4565b600180825260208083019182526001600160a01b039093165f8181526008948590526040902092518354925161ffff1990931660ff911515919091161791151590931b61ff0016179055600954600160401b811015612995578060016137139201600955612c5a565b81546001600160a01b0360039290921b91821b19169083901b1790556040516306fdde0360e01b81525f81600481855afa908115612809575f9161383c575b506040516395d89b4160e01b81525f81600481865afa908115612809575f91613822575b5060405163e5a6b10f60e01b8152915f83600481875afa928315612809577f4ac4dcc08b0c26c3fb6b58c64c1392b7934b1ce6b0382a5986ea5c3de795e053936137de936131cf925f926137fa575b506137ec90604051958695606087526060870190612a00565b908582036020870152612a00565b908382036040850152612a00565b6137ec91925061381b903d805f833e61381381836129df565b81019061363c565b91906137c5565b61383691503d805f833e61381381836129df565b5f613776565b61385091503d805f833e61381381836129df565b5f613752565b60ff1660028114908115613868575090565b600391501490565b602081519101519060208110613884575090565b5f199060200360031b1b1690565b8015801561393b575b61392a575f9081906401000003d0199060079082908181800909086040516020810191602083526020604083015260206060830152608082015263800001e9600160ff1b0360a08201526401000003d01960c082015260c0815261390060e0826129df565b519060055afa61390e61354b565b9015801561392f575b61392a57613926600191613870565b1490565b505f90565b50602081511415613917565b506401000003d01981101561389b565b60405163a9059cbb60e01b81526001600160a01b0392831660048201526001600160801b03939093166024840152602091839160449183915f91165af15f918161399d575b5061399a57505f90565b90565b6139b791925060203d60201161099f5761099181836129df565b905f613990565b9035601e19823603018112156128145701602081359101916001600160401b03821161281457813603831361281457565b909160606001600160401b037fadf6f2901dd7af2f28a594f47a925894a08d4de10609dff591a80642648775c5921693613a6a6001600160801b0360405195613a378761297a565b60018060a01b031692838752306020880152876040880152169485848201525f60808201525f60a0820152600554613490565b9384600555600654906001600160401b03613a86818416612a9c565b1680926001600160401b0319161760065560405192835260208301526040820152a356febb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd65042ea6dad60c26f055e80ec401b3437c854ed586a0704d305bb4e9ea4518cfa26469706673582212200d1d9cdc2a22dec315788ec45fde1a3fb24f9de00620586048e6932aa1f3424364736f6c63430008230033a26469706673582212200e343618458bd48c93c94672f94ead1fdcba35eb5174779cd030df148009e60064736f6c6343000823003360808060405234601557610188908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63b628c33414610025575f80fd5b346100db576101e03660031901126100db5760043563ffffffff8116036100db5761004e6100df565b506100576100f6565b5061006061010d565b5060a4356001600160a01b038116036100db5760403660c31901126100db576080366101031901126100db576101a43567ffffffffffffffff81116100db576100ad903690600401610124565b50506101c4359067ffffffffffffffff82116100db576100d36020923690600401610124565b505060018152f35b5f80fd5b6024359067ffffffffffffffff821682036100db57565b6044359067ffffffffffffffff821682036100db57565b6084359067ffffffffffffffff821682036100db57565b9181601f840112156100db5782359167ffffffffffffffff83116100db57602083818601950101116100db5756fea26469706673582212205ddab8535399d9aa9aa9f26c3a78a7a22d61a341d88ba8d664c1a5bec1e5f1a464736f6c6343000823003360a034606e57601f6105a238819003918201601f19168301916001600160401b03831184841017607257808492602094604052833981010312606e57516001600160a01b03811690819003606e5760805260405161051b908161008782396080518181816047015261015e0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f3560e01c90816311da52611461007a5750637b9a730114610032575f80fd5b34610076575f366003190112610076576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5f80fd5b346100765760e0366003190112610076576004359063ffffffff8216809203610076576024356001600160a01b03811690819003610076576064356001600160a01b038116939084900361007657608435916fffffffffffffffffffffffffffffffff83168093036100765760a4359167ffffffffffffffff83168093036100765760c4359567ffffffffffffffff871161007657366023880112156100765786600401359567ffffffffffffffff871161007657366024888a010111610076575f546104a05760015f9081556390b7f6fd60e01b825260048201849052816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156102bf575f91610323575b50602001516001600160a01b031633036103145760405163a9059cbb60e01b81528160048201528560248201526020816044815f885af19081156102bf575f916102d9575b50156102ca5760e45f928760209860246040519b8c9a8b998a96630a137ff560e01b88526004880152338488015260443560448801526064870152608486015260c060a48601528260c486015201848401378181018301859052601f01601f19168101030193f19081156102bf575f9161027c575b506001600160e01b03191663f5ec800b60e01b0161026d575f8055005b63be51ced360e01b5f5260045ffd5b90506020813d6020116102b7575b81610297602093836104af565b8101031261007657516001600160e01b0319811681036100765781610250565b3d915061028a565b6040513d5f823e3d90fd5b6312171d8360e31b5f5260045ffd5b90506020813d60201161030c575b816102f4602093836104af565b810103126100765751801515810361007657886101db565b3d91506102e7565b63efc26b5760e01b5f5260045ffd5b90503d805f833e61033481836104af565b8101906020818303126100765780519067ffffffffffffffff8211610076570190610140828203126100765760405191610140830183811067ffffffffffffffff82111761048c57604052805163ffffffff8116810361007657835261039c602082016104d1565b60208401526103ad604082016104d1565b60408401526103be606082016104d1565b60608401526103cf608082016104d1565b60808401526103e060a082016104d1565b60a084015260c081015160c084015260e081015160e084015261010081015167ffffffffffffffff81168103610076576101008401526101208101519067ffffffffffffffff8211610076570181601f820112156100765780519067ffffffffffffffff821161048c5760405192610462601f8401601f1916602001856104af565b8284526020838301011161007657815f9260208093018386015e8301015261012082015288610196565b634e487b7160e01b5f52604160045260245ffd5b6336a4d21b60e11b5f5260045ffd5b90601f8019910116810190811067ffffffffffffffff82111761048c57604052565b51906001600160a01b03821682036100765756fea2646970667358221220da86638e3c15856c8c0aeecb2ec313f9e0d1c7f258a0a340fc904a96f58e55a064736f6c63430008230033" + "zoneFactoryBytecode": "0x60808060405234610138575f805463ffffffff191660011781556003600755600680546001600160a01b03191633908117909155907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a36101a28181016001600160401b03811183821017610124578291614e9f833903905ff08015610119576001600160a01b03165f8181526003602052604090819020805460ff19166001179055600480546001600160a01b031916909217909155516105a2808201906001600160401b0382118383101761012457602091839161504183393081520301905ff0801561011957600580546001600160a01b0319166001600160a01b0392909216919091179055604051614d62908161013d8239f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fdfe6080806040526004361015610012575f80fd5b5f905f3560e01c90816301b290d314610edc575080632b7ac3f314610eb45780632d45796a146104815780633cb747bf1461045857806374134d371461043a57806376628f83146103f85780638da5cb5b146103cf57806390b7f6fd14610174578063eee83499146101275763f2fde38b1461008c575f80fd5b34610124576020366003190112610124576004356001600160a01b03811690819003610122576006546001600160a01b0381169033829003610113578215610104576001600160a01b03191682176006557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6349e27cff60e01b8452600484fd5b6330cd747160e01b8452600484fd5b505b80fd5b50346101245780600319360112610124575f1963ffffffff825416019063ffffffff82116101605760208263ffffffff60405191168152f35b634e487b7160e01b81526011600452602490fd5b50346101245760203660031901126101245760043563ffffffff81168091036101225760606101206040516101a881610f2e565b84815284602082015284604082015284838201528460808201528460a08201528460c08201528460e0820152846101008201520152815260016020526040812090604051906101f682610f2e565b825463ffffffff811683526001600160a01b03602091821c811691840191825260018501548116604080860191825260028701548316606087019081526003880154841660808801908152600489015490941660a08801908152600589015460c0890190815260068a015460e08a0190815260078b01546001600160401b03166101008b0190815294516008909b018054989b989597919692959394919391929189918b91906102a582610fb1565b80855291600181169081156103b15750600114610377575b5050036102ca9089610f4a565b6101208a81019889526040805160208082529c5163ffffffff168d8201529c516001600160a01b03908116918e01919091529151821660608d01529151811660808c01529151821660a08b015291511660c0890152905160e0880152905161010087015290516001600160401b031690850152516101408085015280516101608501819052849390929183910161018085015e8183016101800152601f1990601f01168101036101800190f35b8c5260208c208c92505b81831061039657505081016020015f806102bd565b80602092948385600194549201015201910190918a92610381565b9150506020925060ff191682840152151560051b8201015f806102bd565b50346101245780600319360112610124576006546040516001600160a01b039091168152602090f35b5034610124576020366003190112610124576004356001600160a01b038116908190036101225760408260ff9260209452600384522054166040519015158152f35b5034610124578060031936011261012457602060405162e4e1c08152f35b50346101245780600319360112610124576005546040516001600160a01b039091168152602090f35b5034610c3c576020366003190112610c3c576001600160401b0360043511610c3c5761010060043536036003190112610c3c576006546001600160a01b03163303610ea5576104d4600435600401610f1a565b6040516335ec42c960e01b81526001600160a01b03909116600482015260208160248161083f60921b5afa908115610c31575f91610e6a575b5015610e5b576001600160a01b03610529600435602401610f1a565b1615610e4c576001600160a01b03610545600435604401610f1a565b1615610e3d576001600160a01b03610561600435606401610f1a565b165f52600360205260ff60405f20541615610e2e5762e4e1c05a10610e1f575f5463ffffffff80821614610e1057600163ffffffff82160163ffffffff8111610dfc5763ffffffff1663ffffffff198216175f5560075460018101808211610dfc57600755606081610c54575050604051606b60f91b6020820152602560fa1b60218201523060601b6022820152600160ff1b603682015260178152610608603782610f4a565b60018060a01b03906020815191012016604051613d438082018281106001600160401b03821117610c40578291610fea833903905ff0908115610c3157610653600435600401610f1a565b6005546001600160a01b031661066d600435602401610f1a565b61067b604460043501610f1a565b610689606460043501610f1a565b9061069860c460043501610f6b565b926106ad60e460043501600435600401610f7f565b91906001600160a01b038a163b15610c3c576001600160401b0363ffffffff96610144968d9686955f9b6040519d8e9c8d9b633fd2490760e01b8d521660048c015260018060a01b031660248b015260448a015260018060a01b0316606489015260018060a01b0316608488015260018060a01b031660a48701526084600435013560c48701521660e485015261012061010485015281610124850152848401378181018301859052601f01601f19168101030181836001600160a01b0388165af18015610c3157610c1c575b506001600160a01b03821603610bc057610798600435600401610f1a565b6107a6602460043501610f1a565b6107b4604460043501610f1a565b906001600160401b036107cb606460043501610f1a565b6107d960c460043501610f6b565b906107ee60e460043501600435600401610f7f565b959094604051976107fe89610f2e565b63ffffffff8b1689526001600160a01b038a811660208b015290811660408a0152908116606089015290811660808801521660a0860152600435608481013560c087015260a4013560e0860152166101008401526001600160401b038211610ba85785906040519261087a6020601f19601f8401160185610f4a565b8084523681830111610bbc57806020928386013783010152610120820190815263ffffffff848116865260016020818152604080892086518154848901516001600160c01b031990911691909616179490921b640100000000600160c01b03169390931781559184015190820180546001600160a01b03199081166001600160a01b0393841617909155606085015160028401805483169184169190911790556080850151600384018054831691841691909117905560a0850151600484018054909216921691909117905560c0830151600582015560e083015160068201556101009092015160078301805467ffffffffffffffff19166001600160401b0392831617905590518051918211610ba8576109986008840154610fb1565b601f8111610b53575b50602090601f8311600114610ae257828793604098936008938a979592610ad7575b50508160011b915f199060031b1c1916179101555b6001600160a01b0383168152600260205220805460ff19166001179055610a026004803501610f1a565b6001600160401b03610a18602460043501610f1a565b610a26604460043501610f1a565b610a34606460043501610f1a565b90610a4360c460043501610f6b565b88516001600160a01b03968716815293861660208501529085168389015290841660608301526004356084810135608084015260a4013560a08301529190911660c08201529082169063ffffffff8416907fa0d8b561b80ba334b8b367f1fd53ab8d8719fa1c348a8de4bbfd8bbffa90b3379060e090a3825163ffffffff9290921682526001600160a01b03166020820152f35b015190505f806109c3565b9060088401875280872091875b601f1985168110610b3b57508360409894936008936001938b9896601f19811610610b23575b505050811b019101556109d8565b01515f1960f88460031b161c191690555f8080610b15565b91926020600181928685015181550194019201610aef565b828111156109a15760088401875260208720601f840160051c9060208510610ba0575b81601f9101920160051c0390875b828110610b925750506109a1565b808960019284015501610b84565b889150610b76565b634e487b7160e01b86526041600452602486fd5b8280fd5b60405162461bcd60e51b815260206004820152602e60248201527f506f7274616c2061646472657373206d69736d61746368202d206e6f6e63652060448201526d3a3930b1b5b4b7339032b93937b960911b6064820152608490fd5b610c299194505f90610f4a565b5f925f61077a565b6040513d5f823e3d90fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b607f8211610ca65750604051606b60f91b6020820152602560fa1b60218201523060601b602282015260f89190911b6001600160f81b031916603682015260178152610ca1603782610f4a565b610608565b60ff8211610cfd575060405160d760f81b6020820152602560fa1b60218201523060601b6022820152608160f81b603682015260f89190911b6001600160f81b031916603782015260188152610ca1603882610f4a565b61ffff8211610d555750604051601b60fb1b6020820152602560fa1b60218201523060601b6022820152604160f91b603682015260f09190911b6001600160f01b031916603782015260198152610ca1603982610f4a565b62ffffff8211610dae575060405160d960f81b6020820152602560fa1b60218201523060601b6022820152608360f81b603682015260e89190911b6001600160e81b0319166037820152601a8152610ca1603a82610f4a565b604051606d60f91b6020820152602560fa1b60218201523090911b6022820152602160fa1b603682015260e09190911b6001600160e01b0319166037820152601b8152610ca1603b82610f4a565b634e487b7160e01b5f52601160045260245ffd5b63e558bfa360e01b5f5260045ffd5b6307099c5360e21b5f5260045ffd5b63baa3de5f60e01b5f5260045ffd5b633c9f858360e21b5f5260045ffd5b630b5eba9f60e41b5f5260045ffd5b63c1ab6dc160e01b5f5260045ffd5b90506020813d602011610e9d575b81610e8560209383610f4a565b81010312610c3c57518015158103610c3c575f61050d565b3d9150610e78565b6330cd747160e01b5f5260045ffd5b34610c3c575f366003190112610c3c576004546040516001600160a01b039091168152602090f35b34610c3c576020366003190112610c3c576004356001600160a01b0381169190829003610c3c576020915f526002825260ff60405f20541615158152f35b356001600160a01b0381168103610c3c5790565b61014081019081106001600160401b03821117610c4057604052565b90601f801991011681019081106001600160401b03821117610c4057604052565b356001600160401b0381168103610c3c5790565b903590601e1981360301821215610c3c57018035906001600160401b038211610c3c57602001918136038313610c3c57565b90600182811c92168015610fdf575b6020831014610fcb57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610fc056fe60808060405234601557613d29908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f905f3560e01c9081625d97ef146128c157508062ebfdb41461283757806309a0a234146125695780630d0c743e146121355780630e18b681146120a65780630e86bbdc146120115780632678224714611fe857806327c71b5014611f625780632b7ac3f314611f395780632c37826e14611f0f5780632dfdf0b514611ee85780633488ce0d14611e7d57806337f981d814611e2857806339dc015d14611cbf5780633cb747bf14611c965780633fd24907146117d35780634256ce38146117b557806345c5d2fb14611797578063580bbcfc1461176d5780635c1bba3814611746578063652ef10b1461156b578063748538d91461152c57806375829def146114a75780638081a8b21461148957806383a146b31461146e578063857e85f81461141257806386f47e55146113f45780638976248d146113c857806394ce88b0146113aa578063959f47c3146112d65780639a97784b146112335780639ecd4cc0146111fb578063b01f22e414610bf0578063b02690f214610bb8578063be2a63ee14610b8e578063bf8e244014610b0a578063bffa55d514610a4b578063c13a2d4b146109d2578063c690908a146108de578063cfaabb4d146108b4578063d179978a14610890578063e09eae10146107fe578063e202d995146107d5578063e4643e8514610748578063e47178491461072a578063e84abe6914610700578063ecf79a4e146106d9578063ef10b1871461038e578063f22a195e14610370578063f490ca961461034d578063f706cfbf1461032f578063f851a44014610306578063fb2f1206146102e55763fe136c4e1461026e575f80fd5b346102e25760203660031901126102e2576040809161028b612a67565b816020845161029981612bd6565b82815201526001600160a01b031681526008602052208151906102bb82612bd6565b5460ff6020818316151593848152019160081c161515815282519182525115156020820152f35b80fd5b50346102e257806003193601126102e257602060065460c01c604051908152f35b50346102e257806003193601126102e2576001546040516001600160a01b039091168152602090f35b50346102e257806003193601126102e2576020604051620186a08152f35b50346102e257806003193601126102e2576020604051670de0b6b3a76400008152f35b50346102e257806003193601126102e2576020600454604051908152f35b50346102e25760a03660031901126102e25760043560243560ff8116908181036106d5576044359060ff82168092036106d15784546001600160a01b031633036106c2576103db90613a5c565b156106b3576103e983613a98565b156106b35783608060209260405184810190308252876040820152866060820152606081526104188482612bf1565b51902090604051918252848201526064356040820152608435606082015282805260015afa156106a857825183806401000003d019600781878181800909086040805160208082018181529282018190526060820152608081019290925263400000f4600160fe1b0360a08301526401000003d01960c0808401919091528252906104a460e082612bf1565b519060055afa6104b2613754565b908061069d575b15610668576104c790613a76565b6002831460018216150361063e575b6040519060208201908582526040830152604082526104f6606083612bf1565b905190206001600160a01b03918216801592909116908215610633575b5050610624576001600160401b0343169060405161053081612bbb565b83815260208101908282526040810190848252600754600160401b811015610610578060016105629201600755612cdf565b9190916105fc5760ff916001915181550192511668ffffffffffffffff008354925160081b169168ffffffffffffffffff191617179055600754905f1982019182116105e857917f82b5f4090f18a082bc8156b956154bfe0319307f5e5a7e903ef33f14ad2cb17e9391608093604051938452602084015260408301526060820152a180f35b634e487b7160e01b85526011600452602485fd5b634e487b7160e01b88526004889052602488fd5b634e487b7160e01b88526041600452602488fd5b6392536faf60e01b8352600483fd5b141590505f80610513565b6401000003d019036401000003d0198111156104d657634e487b7160e01b85526011600452602485fd5b60405162461bcd60e51b815260206004820152600d60248201526c1b5bd9195e1c0819985a5b1959609a1b6044820152606490fd5b5060208151146104b9565b6040513d84823e3d90fd5b632f0be49b60e11b8452600484fd5b6314f8a09f60e21b8552600485fd5b8480fd5b8380fd5b50346102e257806003193601126102e25760206001600160801b0360035416604051908152f35b50346102e257806003193601126102e25760206001600160401b0360065460401c16604051908152f35b50346102e257806003193601126102e2576020600954604051908152f35b50346102e257806003193601126102e2576002546001600160a01b03811690811580156107cb575b6107bc5782546001600160a01b0319808216841785559091166002556001600160a01b03167f9945af900637995146537e5ea7a7be455dfd08bbb46fdd79e9c3a2bad0c4eef88380a380f35b63d71402d960e01b8352600483fd5b5081331415610770565b50346102e257806003193601126102e2576002546040516001600160a01b039091168152602090f35b50346102e25760203660031901126102e257610818612ae7565b81546001600160a01b0316330361088157600680546001600160c01b031660c083901b6001600160c01b0319161790556040516001600160401b0390911681527f66bcd750662bb66118e25a8e421ae73974634d9af2d44fb9e600d250917fe69090602090a180f35b6314f8a09f60e21b8252600482fd5b50346102e257806003193601126102e257602063ffffffff60115416604051908152f35b50346102e25760203660031901126102e25760406020916004358152600d83522054604051908152f35b50346102e25760203660031901126102e2576108f8612a67565b6001546001600160a01b031633036109c3576001600160a01b0381168083526008602052604083205460ff166109b4576040516335ec42c960e01b8152600481019190915260208160248161083f60921b5afa9081156109a957839161097a575b501561096b57610968906138a7565b80f35b631fcf8c4760e11b8252600482fd5b61099c915060203d6020116109a2575b6109948183612bf1565b810190612c36565b5f610959565b503d61098a565b6040513d85823e3d90fd5b632909298f60e11b8352600483fd5b637bfa4b9f60e01b8252600482fd5b50346102e25760403660031901126102e2576004356001600160401b038111610a47576101406003198236030112610a475781546001600160a01b0316330361088157601054610a3857610a3190600160105560243590600401612eac565b8060105580f35b630b5c738160e01b8252600482fd5b5080fd5b50346102e25760203660031901126102e257610a65612a67565b6001600160a01b038116808352600a60208181526040808620335f818152918452828220548689529484528288208183529093522080546001600160801b03191690556001600160801b0390911692610abf918491613b54565b15610afb57602092506040518281527fffd3bbab073ab4b2d0792c270104924c14c285a153b9acddabae166395d2eb5c843392a3604051908152f35b63be51ced360e01b8352600483fd5b50346102e25760203660031901126102e257610b24612a67565b81546001600160a01b03169033829003610b7f57600280546001600160a01b0319166001600160a01b03929092169182179055907f12cf9e4d760566fb6a3e0193406f2295fc4b53f2b60817145a61f29e470cf7d68380a380f35b6314f8a09f60e21b8352600483fd5b50346102e257806003193601126102e25760206001600160401b0360065460801c16604051908152f35b50346102e25760203660031901126102e2576020610bd7600435612e6c565b905460405160039290921b1c6001600160a01b03168152f35b50346102e25760a03660031901126102e257610c0a612a67565b906024356001600160801b0381168103610a47576001600160401b0360643511610a475760a060643536036003190112610a4757610c46612a93565b906001600160a01b038216156111ec57610c5f846134e5565b604051639c4bad2960e01b81526020816004816001600160a01b0389165afa9081156111b45783610cce9260209287916111bf575b506040516337de09eb60e11b81526001600160401b0390911660048201526001600160a01b03909116602482015291829081906044820190565b038161100f60921b5afa9081156111b4578491611195575b501561118657610d02610cfd602460643501612e11565b613a5c565b1561117757610d1660643560040135613a98565b15611177576040610d31604460643501606435600401612e1f565b90500361114c57610d43604435612d7d565b50156110ba57610d539084613570565b9060405192610d6184612b8c565b6001600160a01b0386811685523360208601526001600160801b03841660408087019190915290821660608601526044356080860152519360a085016001600160401b038111868210176110a6576040526064356004013585526024606435013560ff811681036110a25760208601526001600160401b03604460643501351161109e5736606435604481013501602301121561109e57610e0d60643560448101350160040135612e51565b610e1a6040519182612bf1565b60643560448101350160048101358083523660249190920101116110a25760643560448101350160048101359060240160208301376064803560448101358101600401358301602001899052604088019290925201356001600160a01b03198116900361109e57606480350135606086015260846064350135946001600160801b031986169586810361109a5792611057886080610fce6001600160401b03968660209e99847f4debdbd2891da07eb466d2025028a25aab636034cf3c941afd1025a0c590a07a9c99015260a082019081526005549060405194602093869485019750600188526060604086015260018060a01b038151168286015260018060a01b0360208201511660a08601526001600160801b0360408201511660c086015260018060a01b0360608201511660e08601520151610100840152519060c061012084015281516101408401528f60ff9083015116610160840152610f90604083015160a06101808601526101e0850190612c12565b6060808401516001600160a01b0319166101a08601526080909301516001600160801b0319166101c08501529183015203601f198101835282612bf1565b51902098899760ff610fdf8a61371e565b956001600160801b03610ff6602460643501612e11565b918161100c604460643501606435600401612e1f565b969097506040519c60018060a01b03168d521660208c01521660408a015260443560608a01526064356004013560808a01521660a088015261016060c0880152610160870191612c4e565b6064803501356001600160a01b03191660e08601526101008501979097526001600160a01b031661012084015216610140820152339381900390a3604051908152f35b8780fd5b8580fd5b8680fd5b634e487b7160e01b87526041600452602487fd5b826007546044351015611137576110d2604435612cdf565b5090600160443501918260443511611123576001600160401b03600181816110fb606497612cdf565b5094015460081c1692015460081c169063034dff0f60e01b8352604435600452602452604452fd5b634e487b7160e01b82526011600452602482fd5b637f455d3760e11b8152604435600452602490fd5b6044836111628260643501606435600401612e1f565b632331a96360e11b8352600452506040602452fd5b632f0be49b60e11b8352600483fd5b6354cfe65960e01b8352600483fd5b6111ae915060203d6020116109a2576109948183612bf1565b5f610ce6565b6040513d86823e3d90fd5b6111df9150833d85116111e5575b6111d78183612bf1565b810190612df2565b5f610c94565b503d6111cd565b638dc30b3960e01b8352600483fd5b50346102e25760203660031901126102e25760406001600160401b03611222600435612d7d565b835191151582529091166020820152f35b50346102e25760203660031901126102e25760043590806040805161125781612bbb565b82815282602082015201526007548210156112c457606061127783612cdf565b506001600160401b0360405161128c81612bbb565b60ff600184549485845201549183604060208301928486168452019360081c1683526040519485525116602084015251166040820152f35b602491637f455d3760e11b8252600452fd5b50346102e257806003193601126102e2576040519080600e54906112f982612b54565b80855291600181169081156113835750600114611339575b6113358461132181860382612bf1565b604051918291602083526020830190612c12565b0390f35b600e81525f516020613cb45f395f51905f52939250905b8082106113695750909150810160200161132182611311565b919260018160209254838588010152019101909291611350565b60ff191660208087019190915292151560051b850190920192506113219150839050611311565b50346102e257806003193601126102e2576020600b54604051908152f35b50346102e257806003193601126102e25760206113e3612d58565b6001600160801b0360405191168152f35b50346102e257806003193601126102e2576020604051629896808152f35b50346102e25760403660031901126102e257604061142e612a67565b91611437612a7d565b9260018060a01b03168152600a602052209060018060a01b03165f5260205260206001600160801b0360405f205416604051908152f35b50346102e257806003193601126102e25760206113e3612d0f565b50346102e257806003193601126102e2576020600c54604051908152f35b50346102e25760203660031901126102e2576114c1612a67565b6001546001600160a01b0316903382900361151d57600f80546001600160a01b0319166001600160a01b03929092169182179055907fe5cd1c804f1c9cc6d7009e4c0fb532f0e2d8863524c3323a6b3790c3f80bf25c8380a380f35b637bfa4b9f60e01b8352600483fd5b50346102e25760203660031901126102e25760209060ff906040906001600160a01b03611557612a67565b168152600884522054166040519015158152f35b50346102e25760203660031901126102e2576004356001600160401b038111610a475761159c903690600401612b27565b8254909291906001600160a01b03163303610881576001600160401b038311611732576115ca600e54612b54565b601f81116116d6575b508192601f8111600114611648578083947ff4e00967b25e707df96d88676243b33be84847ef27615af8ef91290b52294fc6949161163d575b508160011b905f198360031b1c191617600e555b611637604051928392602084526020840191612c4e565b0390a180f35b90508201355f61160c565b600e8352601f198116935f516020613cb45f395f51905f5290845b8681106116be5750827ff4e00967b25e707df96d88676243b33be84847ef27615af8ef91290b52294fc69596106116a5575b5050600181811b01600e55611620565b8301355f19600384901b60f8161c191690555f80611695565b90916020600181928588013581550193019101611663565b838111156115d357600e8352601f840160051c906020851061172a575b601f82910160051c0390835b82811061170d5750506115d3565b80855f516020613cb45f395f51905f5260019385010155016116ff565b8391506116f3565b634e487b7160e01b82526041600452602482fd5b50346102e257806003193601126102e257546040516001600160a01b039091168152602090f35b50346102e257806003193601126102e25760206001600160401b0360125460a01c16604051908152f35b50346102e257806003193601126102e2576020600554604051908152f35b50346102e257806003193601126102e2576020600754604051908152f35b50346102e2576101203660031901126102e2576004359063ffffffff82168092036102e257611800612a7d565b604435926001600160a01b0384168403611c92576064356001600160a01b03811691908290036106d557611832612a93565b60a4356001600160a01b038116919082900361109e57611850612afd565b90610104356001600160401b03811161109a57611871903690600401612b27565b959098612d7960911b3303611c83576012549560ff8760e01c16611c7557601180546001600160c01b0319169190911760209290921b640100000000600160c01b0316919091179055600180546001600160a01b03199081169290921790558754166001600160a01b039190911617865560c4356004556001600160e81b03199092161760a09190911b67ffffffffffffffff60a01b1617600160e01b176012556001600160401b038111611b8c5761192b600e54612b54565b601f8111611c19575b5082601f8211600114611bab57839482939492611ba0575b50508160011b915f199060031b1c191617600e555b6119b360405161197081612bd6565b60018082526020808301918252608360961b86526008908190526040862092518354925161ffff1990931660ff9115159190911617911515901b61ff0016179055565b600954600160401b811015611b8c578060016119d29201600955612e6c565b81546001600160a01b0360039290921b91821b1916608360961b9182901b179091556040516306fdde0360e01b8152908390829060049082905afa9081156109a9578391611b72575b506040516395d89b4160e01b8152908382600481608360961b5afa9182156111b4578492611b56575b5060405163e5a6b10f60e01b8152918483600481608360961b5afa928315611b4b578593611af7575b507f4ac4dcc08b0c26c3fb6b58c64c1392b7934b1ce6b0382a5986ea5c3de795e05391611acf611abc611aae93604051948594606086526060860190612c12565b908482036020860152612c12565b8281036040840152608360961b95612c12565b0390a26001608360961b03196001600160a01b03821601611aee575080f35b610968906138a7565b611aae91935091611acf611abc611b407f4ac4dcc08b0c26c3fb6b58c64c1392b7934b1ce6b0382a5986ea5c3de795e053953d808b833e611b388183612bf1565b810190613845565b959350505091611a6d565b6040513d87823e3d90fd5b611b6b9192503d8086833e611b388183612bf1565b905f611a44565b611b8691503d8085833e611b388183612bf1565b5f611a1b565b634e487b7160e01b83526041600452602483fd5b013590505f8061194c565b600e8452601f198216945f516020613cb45f395f51905f5291855b878110611c01575083600195969710611be8575b505050811b01600e55611961565b01355f19600384901b60f8161c191690555f8080611bda565b90926020600181928686013581550194019101611bc6565b8181111561193457600e8452601f820160051c9060208310611c6d575b601f82910160051c0390845b828110611c50575050611934565b80865f516020613cb45f395f51905f526001938501015501611c42565b849150611c36565b62dc149f60e41b8a5260048afd5b631966391b60e11b8952600489fd5b8280fd5b50346102e257806003193601126102e257601154604051602091821c6001600160a01b03168152f35b50346102e25760203660031901126102e257611cd9612ae7565b906007549081158015611dce575b611db25780915f19810190811161112357836001600160401b0384929116925b818310611d3857606083611d1a81612cdf565b509060ff600183549301541660405192835260208301526040820152f35b9091828101808211611d9e5760018101809111611d9e5760011c90846001600160401b036001611d6785612cdf565b50015460081c1611611d7c5750915b90611d07565b92505f19810190811115611d7657634e487b7160e01b82526011600452602482fd5b634e487b7160e01b83526011600452602483fd5b63302c66ab60e21b81526001600160401b038316600452602490fd5b5060075415611e1457600781527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6895460081c6001600160401b0390811690841610611ce7565b634e487b7160e01b81526032600452602490fd5b50346102e25760203660031901126102e2576020906040906001600160a01b03611e50612a67565b16815260088352205460ff81169081611e6f575b506040519015158152f35b60ff915060081c165f611e64565b50346102e257806003193601126102e257600754908115611ed9575f198201918211611ec5576040611eae83612cdf565b5060ff600182549201541682519182526020820152f35b634e487b7160e01b81526011600452602490fd5b630c322fb560e31b8152600490fd5b50346102e257806003193601126102e25760206001600160401b0360065416604051908152f35b50346102e257806003193601126102e25760206001600160401b0360035460801c16604051908152f35b50346102e257806003193601126102e2576012546040516001600160a01b039091168152602090f35b50346102e25760203660031901126102e257611f7c612a67565b6001546001600160a01b031633036109c3576001600160a01b03168082526008602052604082205460ff161561096b57808252600860205260408220805461ff00191690557feb225a736fbfee3f85ccb72bdf84ff0396ab358b7970e2cc351ab3e3fd92358d8280a280f35b50346102e257806003193601126102e257600f546040516001600160a01b039091168152602090f35b50346102e25760203660031901126102e2576004356001600160801b038116809103610a475781546001600160a01b0316330361088157670de0b6b3a76400008111612097576020817fc62141e607d6fcbf7d11fd2b6d8e18e5ebef6d3fff8136ca98822801abbaea38926001600160801b03196003541617600355604051908152a180f35b630d62f21160e11b8252600482fd5b50346102e257806003193601126102e257600f546001600160a01b038116908115801561212b575b61211c57600180546001600160a01b031980821685179092559116600f556001600160a01b03167ff8ccb027dfcd135e000e9d45e6cc2d662578a8825d4c45b5e32e0adf67e79ec68380a380f35b63058d9a1b60e01b8352600483fd5b50813314156120ce565b50346102e2576101603660031901126102e257612150612ae7565b6024356001600160401b038116918282036106d55760403660431901126106d55760803660831901126106d5576101043591610124356001600160401b03811161109e576121a2903690600401612b27565b9094610144356001600160401b03811161109a576121c4903690600401612b27565b88546001600160a01b031691903383900361255a5760443593600454850361254b57601254906001600160401b038916976001600160401b038360a01c16891061250c578161252a575050879443881161251b5761222188613783565b945b851561250c57612231612c6e565b98600654996001600160401b03808c60401c169116148015906124e9575b80156124cd575b6124be57908d96959493929163ffffffff6011541696600354996001600160401b038b60801c16995060018a01926001600160401b0384116124a7576001600160401b0393929184916040519b632d8a30cd60e21b8d5260048d015260248c01521660448a0152606489015216608487015260a486015260c4850152606435968760e486015260843561010486015260a4359b8c61012487015260c435916001600160401b0383168093036124a357602095879561234f8f946123629489979688976101448901526001600160401b0361232e612afd565b166101648901526101848801526101e06101a48801526101e4870191612c4e565b848103600319016101c486015291612c4e565b03916001600160a01b03165afa908115612498578991612479575b501561246a5767ffffffffffffffff60801b9061239990612cae565b60801b16906001600160401b0360801b1916176003556004556fffffffffffffffff00000000000000006123cb612c84565b60401b16916001600160401b0360801b9060801b169077ffffffffffffffffffffffffffffffff0000000000000000191617176006557f5a66941dc92cb865480c966eff640c02b1d00d544b74332fd67c6f1cbfccdf39608061242d836137f1565b936001600160401b03600354831c1693600454906001600160401b0360065460401c1691604051938452602084015260408301526060820152a380f35b6309bde33960e01b8852600488fd5b612492915060203d6020116109a2576109948183612bf1565b5f61237d565b6040513d8b823e3d90fd5b8e80fd5b50505060248f634e487b7160e01b81526011600452fd5b633d9dffbd60e01b8e5260048efd5b506124d6612c84565b6001600160401b03808c16911611612256565b506124f2612c84565b6001600160401b0380612503612c6e565b1691161061224f565b632705871360e01b8d5260048dfd5b632705871360e01b8c5260048cfd5b88829792111561250c5743871161250c576125459096613783565b94612223565b6309bde33960e01b8b5260048bfd5b6314f8a09f60e21b8a5260048afd5b50346102e25760a03660031901126102e257612583612a67565b61258b612a7d565b90612594612abd565b606435906125a0612a93565b6001600160a01b038116959093908615612828576125bd826134e5565b604051639c4bad2960e01b81526001600160a01b03831695906020816004818a5afa9081156109a9578391612809575b506040516337de09eb60e11b81526001600160401b03821660048201526001600160a01b038916602482015260208160448161100f60921b5afa9081156111b45784916127ea575b50156111865760405163b389e30560e01b81526001600160401b03821660048201526001600160a01b038916602482015260208160448161100f60921b5afa9081156111b45784916127cb575b5015611186576040516337de09eb60e11b81526001600160401b039190911660048201526001600160a01b0391909116602482015260208160448161100f60921b5afa9081156106a85782916127ac575b501561279d5750946001600160401b03926126f86001600160801b0393602098613570565b939061273e826040519961270b8b612b8c565b898b52338c8c015260018060a01b0316968760408c015216988960608201528560808201528460a0820152600554613699565b976127488961371e565b956040519889528a8901526040880152166060860152608085015260a08401521660c0820152817ffee50869847a4d073bfd83ec655472febff62beb46febf4ec48752f0686affd160e03393a3604051908152f35b6354cfe65960e01b8152600490fd5b6127c5915060203d6020116109a2576109948183612bf1565b5f6126d3565b6127e4915060203d6020116109a2576109948183612bf1565b5f612682565b612803915060203d6020116109a2576109948183612bf1565b5f612635565b612822915060203d6020116111e5576111d78183612bf1565b5f6125ed565b638dc30b3960e01b8152600490fd5b50346102e25760203660031901126102e257612851612a67565b6001546001600160a01b031633036109c3576001600160a01b03168082526008602052604082205460ff161561096b57808252600860205260408220805461ff0019166101001790557f22ab73af03f04a21e91c7923327f99279b7f5d07d9551762c39bccdf051f1fe98280a280f35b905034612a265760c0366003190112612a26576128dc612a67565b6128e4612a7d565b916128ed612abd565b6084356001600160401b038116809103612a265760a4356001600160401b038111612a2657612920903690600401612b27565b919094303303612a585760115463a9059cbb60e01b86526001600160a01b03602091821c811660048801526001600160801b0386166024880152919091169490816044815f895af1908115612a1b575f91612a39575b5015612a2a57601154602081901c6001600160a01b031693843b15612a26575f9663ffffffff9488946001600160801b036129fb946040519c8d9b8c9a8b996311da526160e01b8b521660048a01526024890152606435604489015260018060a01b0316606488015216608486015260a485015260e060c485015260e4840191612c4e565b03925af18015612a1b57612a0d575080f35b612a1991505f90612bf1565b005b6040513d5f823e3d90fd5b5f80fd5b6312171d8360e31b5f5260045ffd5b612a52915060203d6020116109a2576109948183612bf1565b5f612976565b6314e1dbf760e11b5f5260045ffd5b600435906001600160a01b0382168203612a2657565b602435906001600160a01b0382168203612a2657565b608435906001600160a01b0382168203612a2657565b35906001600160a01b0382168203612a2657565b604435906001600160801b0382168203612a2657565b35906001600160801b0382168203612a2657565b600435906001600160401b0382168203612a2657565b60e435906001600160401b0382168203612a2657565b35906001600160401b0382168203612a2657565b9181601f84011215612a26578235916001600160401b038311612a265760208381860195010111612a2657565b90600182811c92168015612b82575b6020831014612b6e57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612b63565b60c081019081106001600160401b03821117612ba757604052565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b03821117612ba757604052565b604081019081106001600160401b03821117612ba757604052565b90601f801991011681019081106001600160401b03821117612ba757604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90816020910312612a2657518015158103612a265790565b908060209392818452848401375f828201840152601f01601f1916010190565b60c4356001600160401b0381168103612a265790565b60e4356001600160401b0381168103612a265790565b356001600160401b0381168103612a265790565b6001600160401b03166001600160401b038114612ccb5760010190565b634e487b7160e01b5f52601160045260245ffd5b600754811015612cfb5760075f5260205f209060011b01905f90565b634e487b7160e01b5f52603260045260245ffd5b60065460c01c48810290808204481490151715612ccb5764e8d4a51000810190818111612ccb5764e8d4a50fff01908111612ccb5764e8d4a510006001600160801b0391041690565b6001600160801b0360035416620186a0026001600160801b038116908103612ccb5790565b60075480821015612dea575f198101908111612ccb578114612de25760018101809111612ccb576001600160401b036001612dbb6201518093612cdf565b50015460081c1601906001600160401b038211612ccb576001600160401b03821643109190565b506001905f90565b50505f905f90565b90816020910312612a2657516001600160401b0381168103612a265790565b3560ff81168103612a265790565b903590601e1981360301821215612a2657018035906001600160401b038211612a2657602001918136038313612a2657565b6001600160401b038111612ba757601f01601f191660200190565b600954811015612cfb5760095f5260205f2001905f90565b356001600160a01b0381168103612a265790565b356001600160801b0381168103612a265790565b600b545f90600c5481146134d6576064810690815f52600d60205260405f2054915f19831480156134cc575b6134b657851580156134c457855f19935b604080516020810191825290949091906001600160a01b03612f0a85612aa9565b1660608401526020840135608084018190529a60408501956001600160a01b03612f3388612aa9565b1660a08601526060860199612f478b612ad3565b6001600160801b031660c08701526080870198612f638a612ad3565b6001600160801b031660e088015260a088013561010088015260c0880196612f8a88612b13565b6001600160401b03166101208201528060e08a019c612fa88e612b13565b6001600160401b03166101408301526101008b019a612fc78c82613bc7565b610160850161014090526101a0850190612fe092612c4e565b906101208101612fef91613bc7565b848303605f19016101808601526130069291612c4e565b90604083015203601f198101825261301e9082612bf1565b519020036134b657156134a357505f52600d6020525f1960405f205560018101809111612ccb57600b555b61305288612e84565b936001600160401b0361306487612c9a565b16156132f3576001600160801b0361307b82612e98565b166132d5575b50629896806001600160401b0361309783612c9a565b1611613267576001600160401b036130ae82612c9a565b166131565750505f516020613cd45f395f51905f5294955090613106613100836130f26130e26001600160801b0396612e84565b6130eb89612e98565b9086613b54565b159586613133575b50612e84565b94612e98565b604080516001600160a01b03938416815293909116602084015292159282019290925291169250606090a3565b613150906131496131438a612e98565b91612c9a565b9086613bf8565b5f6130fa565b61317b61316284612e84565b9261317561316f89612e98565b93612c9a565b99612e1f565b929098303b15612a2657604051625d97ef60e01b81526001600160a01b03808816600483015290921660248301526001600160801b03929092166044820152606481018a90526001600160401b0391909116608482015260c060a4820152965f91889182916131ef9160c484019190612c4e565b038183305af19586613231575b50906131066131006001600160801b03935f516020613cd45f395f51905f529798155f1461322a57886130f2565b60016130f2565b5f516020613cd45f395f51905f5295506131006001600160801b03939261325b5f61310694612bf1565b5f9750929350506131fc565b50506001600160801b039195505f516020613cd45f395f51905f5294506132aa6132b0916132a5869561329e613143606099612e98565b908a613bf8565b612e84565b92612e98565b604080516001600160a01b0397881681529290911660208301525f90820152931692a3565b6132ec906130eb60018060a01b035f541691612e98565b505f613081565b5050509492955050506133069150612e84565b61330e612d0f565b926001600160801b0361332082612e98565b166001600160801b03851611613488575b8361333e61334392612e98565b613550565b906001600160801b03841661346b575b6133668261336085612e84565b83613b54565b156133dc577f0f7ef08806234f85aaee43d3ba4589c3bc6d5ac3fc8edd56fc3d91cc7553bdcb926133d7906001600160a01b03906133a390612e84565b1694604051938493849160409194936001600160801b038092606086019760018060a01b0316865216602085015216910152565b0390a2565b6001600160a01b0381165f908152600a602052604090207f5fea28d0adb7d877ae3259768f41ad6741aa1784c4475746dd931364f62e68a1936133d79161342282612e84565b60018060a01b03165f5260205260405f206001600160801b036134488682845416613530565b82546001600160801b03191691161790556001600160a01b03906133a390612e84565b5f546134829085906001600160a01b031683613b54565b50613353565b61333e93508061349a61334392612e98565b94509050613331565b91505f52600d60205260405f2055613049565b626aee3160e51b5f5260045ffd5b858793612ee9565b505f198614612ed8565b63d7fa91d160e01b5f5260045ffd5b6001600160a01b03165f9081526008602052604090205460ff8116156135215760081c60ff161561351257565b63dec712ef60e01b5f5260045ffd5b631fcf8c4760e11b5f5260045ffd5b906001600160801b03809116911601906001600160801b038211612ccb57565b906001600160801b03809116911603906001600160801b038211612ccb57565b909161357a612d58565b926001600160801b0361359461358e612d0f565b86613530565b16906001600160801b03811691821061368a57846135b191613550565b6040516323b872dd60e01b81523360048201523060248201526044810192909252926001600160a01b03166020826064815f855af1918215612a1b57859261366d575b506001600160801b038216613607575050565b5f805460405163a9059cbb60e01b81526001600160a01b0390911660048201526001600160801b0393909316602484015260209183916044918391905af18015612a1b576136525750565b61366a9060203d6020116109a2576109948183612bf1565b50565b6136859060203d6020116109a2576109948183612bf1565b6135f4565b636ba4a1c760e01b5f5260045ffd5b6040519060a060208301935f8552600180831b038151166040850152600180831b036020820151166060850152600180831b0360408201511660808501526001600160801b0360608201511682850152600180831b0360808201511660c0850152015160e0830152610100820152610100815261371861012082612bf1565b51902090565b6005556006546137366001600160401b038216612cae565b67ffffffffffffffff199091166001600160401b0382161760065590565b3d1561377e573d9061376582612e51565b916137736040519384612bf1565b82523d5f602084013e565b606090565b905f80809360405160208101918252602081526137a1604082612bf1565b519071f90827f1c53a10cb7a02335b1753200029355afa6137c0613754565b90806137e5575b6137ce5750565b909150602081805181010312612a26576020015190565b506020815110156137c7565b801561383f57600c5490600b548203828111612ccb576064111561383057606482065f52600d60205260405f205560018101808211612ccb57600c5590565b632764e20b60e01b5f5260045ffd5b505f1990565b602081830312612a26578051906001600160401b038211612a26570181601f82011215612a265780519061387882612e51565b926138866040519485612bf1565b82845260208383010111612a2657815f9260208093018386015e8301015290565b6139026040516138b681612bd6565b600180825260208083019182526001600160a01b039094165f8181526008958690526040902092518354925161ffff1990931660ff911515919091161791151590941b61ff0016179055565b600954600160401b811015612ba7578060016139219201600955612e6c565b81546001600160a01b0360039290921b91821b19169083901b1790556040516306fdde0360e01b81525f81600481855afa908115612a1b575f91613a42575b506040516395d89b4160e01b81525f81600481865afa908115612a1b575f91613a28575b5060405163e5a6b10f60e01b8152915f83600481875afa928315612a1b577f4ac4dcc08b0c26c3fb6b58c64c1392b7934b1ce6b0382a5986ea5c3de795e053936139ec936133d7925f92613a08575b506139fa90604051958695606087526060870190612c12565b908582036020870152612c12565b908382036040850152612c12565b6139fa919250613a21903d805f833e611b388183612bf1565b91906139d3565b613a3c91503d805f833e611b388183612bf1565b5f613984565b613a5691503d805f833e611b388183612bf1565b5f613960565b60ff1660028114908115613a6e575090565b600391501490565b602081519101519060208110613a8a575090565b5f199060200360031b1b1690565b80158015613b44575b613b33575f9081906401000003d0199060079082908181800909086040805160208082018181529282018190526060820152608081019290925263800001e9600160ff1b0360a08301526401000003d01960c080840191909152825290613b0960e082612bf1565b519060055afa613b17613754565b90158015613b38575b613b3357613b2f600191613a76565b1490565b505f90565b50602081511415613b20565b506401000003d019811015613aa1565b60405163a9059cbb60e01b81526001600160a01b0392831660048201526001600160801b03939093166024840152602091839160449183915f91165af15f9181613ba6575b50613ba357505f90565b90565b613bc091925060203d6020116109a2576109948183612bf1565b905f613b99565b9035601e1982360301811215612a265701602081359101916001600160401b038211612a26578136038313612a2657565b909160606001600160401b037fadf6f2901dd7af2f28a594f47a925894a08d4de10609dff591a80642648775c5921693613c736001600160801b0360405195613c4087612b8c565b60018060a01b031692838752306020880152876040880152169485848201525f60808201525f60a0820152600554613699565b9384600555600654906001600160401b03613c8f818416612cae565b1680926001600160401b0319161760065560405192835260208301526040820152a356febb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd65042ea6dad60c26f055e80ec401b3437c854ed586a0704d305bb4e9ea4518cfa264697066735822122041431fb5b8be8b24a76fc9c6e092a85750ea346b7f6f2c04112cb6cb3a8e45ff64736f6c63430008230033a2646970667358221220eef9acf83327d8b68585640ac516ccbdb7fa119e538682eebad845f9d7437ad364736f6c6343000823003360808060405234601557610188908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63b628c33414610025575f80fd5b346100db576101e03660031901126100db5760043563ffffffff8116036100db5761004e6100df565b506100576100f6565b5061006061010d565b5060a4356001600160a01b038116036100db5760403660c31901126100db576080366101031901126100db576101a43567ffffffffffffffff81116100db576100ad903690600401610124565b50506101c4359067ffffffffffffffff82116100db576100d36020923690600401610124565b505060018152f35b5f80fd5b6024359067ffffffffffffffff821682036100db57565b6044359067ffffffffffffffff821682036100db57565b6084359067ffffffffffffffff821682036100db57565b9181601f840112156100db5782359167ffffffffffffffff83116100db57602083818601950101116100db5756fea26469706673582212205ddab8535399d9aa9aa9f26c3a78a7a22d61a341d88ba8d664c1a5bec1e5f1a464736f6c6343000823003360a034606e57601f6105a238819003918201601f19168301916001600160401b03831184841017607257808492602094604052833981010312606e57516001600160a01b03811690819003606e5760805260405161051b908161008782396080518181816047015261015e0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f3560e01c90816311da52611461007a5750637b9a730114610032575f80fd5b34610076575f366003190112610076576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5f80fd5b346100765760e0366003190112610076576004359063ffffffff8216809203610076576024356001600160a01b03811690819003610076576064356001600160a01b038116939084900361007657608435916fffffffffffffffffffffffffffffffff83168093036100765760a4359167ffffffffffffffff83168093036100765760c4359567ffffffffffffffff871161007657366023880112156100765786600401359567ffffffffffffffff871161007657366024888a010111610076575f546104a05760015f9081556390b7f6fd60e01b825260048201849052816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156102bf575f91610323575b50602001516001600160a01b031633036103145760405163a9059cbb60e01b81528160048201528560248201526020816044815f885af19081156102bf575f916102d9575b50156102ca5760e45f928760209860246040519b8c9a8b998a96630a137ff560e01b88526004880152338488015260443560448801526064870152608486015260c060a48601528260c486015201848401378181018301859052601f01601f19168101030193f19081156102bf575f9161027c575b506001600160e01b03191663f5ec800b60e01b0161026d575f8055005b63be51ced360e01b5f5260045ffd5b90506020813d6020116102b7575b81610297602093836104af565b8101031261007657516001600160e01b0319811681036100765781610250565b3d915061028a565b6040513d5f823e3d90fd5b6312171d8360e31b5f5260045ffd5b90506020813d60201161030c575b816102f4602093836104af565b810103126100765751801515810361007657886101db565b3d91506102e7565b63efc26b5760e01b5f5260045ffd5b90503d805f833e61033481836104af565b8101906020818303126100765780519067ffffffffffffffff8211610076570190610140828203126100765760405191610140830183811067ffffffffffffffff82111761048c57604052805163ffffffff8116810361007657835261039c602082016104d1565b60208401526103ad604082016104d1565b60408401526103be606082016104d1565b60608401526103cf608082016104d1565b60808401526103e060a082016104d1565b60a084015260c081015160c084015260e081015160e084015261010081015167ffffffffffffffff81168103610076576101008401526101208101519067ffffffffffffffff8211610076570181601f820112156100765780519067ffffffffffffffff821161048c5760405192610462601f8401601f1916602001856104af565b8284526020838301011161007657815f9260208093018386015e8301015261012082015288610196565b634e487b7160e01b5f52604160045260245ffd5b6336a4d21b60e11b5f5260045ffd5b90601f8019910116810190811067ffffffffffffffff82111761048c57604052565b51906001600160a01b03821682036100765756fea2646970667358221220da86638e3c15856c8c0aeecb2ec313f9e0d1c7f258a0a340fc904a96f58e55a064736f6c63430008230033" } diff --git a/crates/node/src/genesis.rs b/crates/node/src/genesis.rs index 9258f3398..7c047a6c1 100644 --- a/crates/node/src/genesis.rs +++ b/crates/node/src/genesis.rs @@ -20,9 +20,6 @@ const TEMPO_STATE_ADDRESS: Address = address!("0x1c00000000000000000000000000000 const ZONE_INBOX_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000001"); /// ZoneConfig predeploy address. const ZONE_CONFIG_ADDRESS: Address = address!("0x1c00000000000000000000000000000000000003"); -/// ZoneFeeManager precompile address. -const ZONE_FEE_MANAGER_ADDRESS: Address = address!("0xfeEC000000000000000000000000000000000000"); - /// `tempoPortal` immutable occurrences in ZoneInbox deployed bytecode. const ZONE_INBOX_PORTAL_IMMUTABLES: usize = 4; /// `tempoPortal` immutable occurrences in ZoneConfig deployed bytecode. @@ -139,6 +136,7 @@ fn patch_bytes(buf: &mut [u8], needle: &[u8], replacement: &[u8]) -> usize { #[cfg(test)] mod tests { use super::*; + use tempo_zone_contracts::ZONE_FEE_MANAGER_ADDRESS; #[test] fn patch_bytes_replaces_non_overlapping_occurrences() { diff --git a/crates/precompiles/src/zone_fee_manager.rs b/crates/precompiles/src/zone_fee_manager.rs index e1b05dea0..783925498 100644 --- a/crates/precompiles/src/zone_fee_manager.rs +++ b/crates/precompiles/src/zone_fee_manager.rs @@ -9,18 +9,23 @@ use alloy_evm::precompiles::DynPrecompile; use alloy_primitives::{Address, U256, keccak256}; use alloy_sol_types::{SolError, SolValue}; use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult}; +use tempo_contracts::precompiles::TIP20Error; use tempo_precompiles::{ DelegateCallNotAllowed, charge_input_cost, dispatch, error::{Result, TempoPrecompileError}, mutate_void, - storage::{Handler, Mapping, StorageCtx, evm::EvmPrecompileStorageProvider}, + storage::{ContractStorage, Handler, Mapping, StorageCtx, evm::EvmPrecompileStorageProvider}, tip20::{ITIP20, TIP20Token, validate_usd_currency}, + tip403_registry::AuthRole as TempoAuthRole, view, }; use tempo_precompiles_macros::contract; use tempo_zone_contracts::{IZoneFeeManager, PORTAL_TOKEN_CONFIGS_SLOT, ZONE_FEE_MANAGER_ADDRESS}; +use zone_primitives::policy::AuthRole; -use crate::{L1StorageReader, TempoState}; +use crate::{ + L1StorageReader, TempoState, policy::PolicyCheck, tip403_proxy::ZoneTip403ProxyRegistry, +}; /// L1 state access required to resolve [`ZoneConfig`](https://github.com/tempoxyz/tempo-zones) /// token enablement at the zone's finalized Tempo checkpoint. @@ -42,10 +47,12 @@ pub trait ZoneConfigReader: L1StorageReader { /// Zone fee manager storage. /// -/// This layout is owned by the zone implementation. In particular, no Tempo -/// `TipFeeManager` storage slots are read or overwritten. +/// The first three slots retain Tempo's fee-manager shape so shared tooling can +/// resolve user preferences without maintaining a second storage decoder. Zones +/// do not use validator preferences, so slot zero remains empty. #[contract(addr = ZONE_FEE_MANAGER_ADDRESS)] pub struct ZoneFeeManager { + _validator_tokens: Mapping, user_tokens: Mapping, collected_fees: Mapping>, } @@ -57,7 +64,10 @@ impl ZoneFeeManager { } fn map_reader_error(error: PrecompileError) -> TempoPrecompileError { - TempoPrecompileError::Fatal(error.to_string()) + match error { + PrecompileError::Fatal(message) => TempoPrecompileError::Fatal(message), + error => TempoPrecompileError::Fatal(error.to_string()), + } } fn is_enabled(&self, provider: &P, token: Address) -> Result { @@ -107,9 +117,10 @@ impl ZoneFeeManager { } /// Collects the maximum fee before execution without consulting FeeAMM state. - pub fn collect_fee_pre_tx( + pub fn collect_fee_pre_tx( &mut self, provider: &P, + registry: Option<&ZoneTip403ProxyRegistry>, fee_payer: Address, fee_token: Address, max_amount: U256, @@ -117,7 +128,7 @@ impl ZoneFeeManager { self.ensure_enabled(provider, fee_token)?; let mut token = TIP20Token::from_address(fee_token)?; - token.ensure_transfer_authorized(fee_payer, self.address)?; + self.ensure_fee_transfer_authorized(registry, &token, fee_payer)?; token.transfer_fee_pre_tx(fee_payer, max_amount)?; Ok(fee_token) } @@ -141,7 +152,12 @@ impl ZoneFeeManager { } /// Transfers a sequencer's accrued fees out of protocol custody. - pub fn distribute_fees(&mut self, sequencer: Address, token: Address) -> Result<()> { + pub fn distribute_fees( + &mut self, + registry: Option<&ZoneTip403ProxyRegistry>, + sequencer: Address, + token: Address, + ) -> Result<()> { StorageCtx.set_tip1060_storage_credit_minting(false); let amount = self.collected_fees[sequencer][token].read()?; @@ -151,6 +167,7 @@ impl ZoneFeeManager { self.collected_fees[sequencer][token].write(U256::ZERO)?; let mut tip20 = TIP20Token::from_address(token)?; + self.ensure_transfer_authorized(registry, token, self.address, sequencer)?; tip20.transfer( self.address, ITIP20::transferCall { @@ -165,9 +182,64 @@ impl ZoneFeeManager { }) } + fn ensure_fee_transfer_authorized( + &self, + registry: Option<&ZoneTip403ProxyRegistry>, + token: &TIP20Token, + fee_payer: Address, + ) -> Result<()> { + let Some(registry) = registry else { + return if self.storage.spec().is_t8() { + token.ensure_authorized_as(&[(fee_payer, TempoAuthRole::sender())]) + } else { + token.ensure_transfer_authorized(fee_payer, self.address) + }; + }; + + let policy_id = registry + .resolve_transfer_policy_id(token.address()) + .map_err(Self::map_reader_error)?; + let authorized = if self.storage.spec().is_t8() { + registry + .is_authorized(policy_id, fee_payer, AuthRole::Sender) + .map_err(Self::map_reader_error)? + } else { + registry + .is_transfer_authorized(policy_id, fee_payer, self.address) + .map_err(Self::map_reader_error)? + }; + if !authorized { + return Err(TIP20Error::policy_forbids().into()); + } + Ok(()) + } + + fn ensure_transfer_authorized( + &self, + registry: Option<&ZoneTip403ProxyRegistry>, + token: Address, + from: Address, + to: Address, + ) -> Result<()> { + let Some(registry) = registry else { + return TIP20Token::from_address(token)?.ensure_transfer_authorized(from, to); + }; + let policy_id = registry + .resolve_transfer_policy_id(token) + .map_err(Self::map_reader_error)?; + if !registry + .is_transfer_authorized(policy_id, from, to) + .map_err(Self::map_reader_error)? + { + return Err(TIP20Error::policy_forbids().into()); + } + Ok(()) + } + /// Wraps the public ZoneFeeManager ABI for EVM registration. - pub fn create( + pub fn create( provider: P, + registry: Option>, cfg: &revm::context::CfgEnv, ) -> DynPrecompile { let spec = cfg.spec; @@ -196,15 +268,21 @@ impl ZoneFeeManager { ); StorageCtx::enter(&mut storage, || { - Self::new().call_with_provider(&provider, input.data, input.caller) + Self::new().call_with_provider( + &provider, + registry.as_ref(), + input.data, + input.caller, + ) }) }, ) } - fn call_with_provider( + fn call_with_provider( &mut self, provider: &P, + registry: Option<&ZoneTip403ProxyRegistry>, calldata: &[u8], msg_sender: Address, ) -> PrecompileResult { @@ -223,7 +301,7 @@ impl ZoneFeeManager { self.set_user_token(provider, sender, call.token) }), distributeFees(call) => mutate_void(call, msg_sender, |_, call| { - self.distribute_fees(call.sequencer, call.token) + self.distribute_fees(registry, call.sequencer, call.token) }), } }) @@ -251,6 +329,7 @@ mod tests { struct MockZoneConfig { portal: Address, enabled: Vec
, + authorized: bool, } impl L1StorageReader for MockZoneConfig { @@ -277,6 +356,49 @@ mod tests { } } + impl PolicyCheck for MockZoneConfig { + fn is_authorized( + &self, + _policy_id: u64, + _user: Address, + _role: AuthRole, + ) -> core::result::Result { + Ok(self.authorized) + } + + fn resolve_transfer_policy_id( + &self, + _token: Address, + ) -> core::result::Result { + Ok(1) + } + + fn policy_type_sync( + &self, + _policy_id: u64, + ) -> core::result::Result< + tempo_contracts::precompiles::ITIP403Registry::PolicyType, + PrecompileError, + > { + unreachable!("not used by fee-manager tests") + } + + fn compound_policy_data( + &self, + _policy_id: u64, + ) -> core::result::Result<(u64, u64, u64), PrecompileError> { + unreachable!("not used by fee-manager tests") + } + + fn policy_exists(&self, _policy_id: u64) -> core::result::Result { + unreachable!("not used by fee-manager tests") + } + + fn policy_id_counter(&self) -> u64 { + 1 + } + } + #[test] fn collects_enabled_tokens_without_touching_fee_amm() -> TestResult { let mut storage = HashMapStorageProvider::new(1); @@ -296,14 +418,16 @@ mod tests { let provider = MockZoneConfig { portal: address!("0x0000000000000000000000000000000000001234"), enabled: vec![alpha.address(), beta.address()], + authorized: true, }; + let registry = ZoneTip403ProxyRegistry::new(provider.clone()); let mut manager = ZoneFeeManager::new(); for (token, max, used) in [ (alpha.address(), U256::from(2_000), U256::from(1_250)), (beta.address(), U256::from(3_000), U256::from(2_500)), ] { - manager.collect_fee_pre_tx(&provider, user, token, max)?; + manager.collect_fee_pre_tx(&provider, Some(®istry), user, token, max)?; manager.collect_fee_post_tx(user, used, max - used, token, sequencer)?; assert_eq!(manager.collected_fees(sequencer, token)?, used); @@ -319,7 +443,7 @@ mod tests { used ); - manager.distribute_fees(sequencer, token)?; + manager.distribute_fees(Some(®istry), sequencer, token)?; assert_eq!(manager.collected_fees(sequencer, token)?, U256::ZERO); assert_eq!( TIP20Token::from_address(token)? @@ -351,10 +475,18 @@ mod tests { let provider = MockZoneConfig { portal: Address::random(), enabled: Vec::new(), + authorized: true, }; + let registry = ZoneTip403ProxyRegistry::new(provider.clone()); let error = ZoneFeeManager::new() - .collect_fee_pre_tx(&provider, user, token.address(), U256::from(1_000)) + .collect_fee_pre_tx( + &provider, + Some(®istry), + user, + token.address(), + U256::from(1_000), + ) .unwrap_err(); assert!(matches!(error, TempoPrecompileError::FeeManagerError(_))); assert_eq!( @@ -365,4 +497,46 @@ mod tests { Ok(()) }) } + + #[test] + fn rejects_fee_collection_forbidden_by_l1_policy() -> TestResult { + let mut storage = HashMapStorageProvider::new(1); + let admin = Address::random(); + let user = Address::random(); + + StorageCtx::enter(&mut storage, || { + let token = TIP20Setup::create("Restricted USD", "rUSD", admin) + .with_issuer(admin) + .with_mint(user, U256::from(10_000u64)) + .apply()?; + let provider = MockZoneConfig { + portal: Address::random(), + enabled: vec![token.address()], + authorized: false, + }; + let registry = ZoneTip403ProxyRegistry::new(provider.clone()); + + let error = ZoneFeeManager::new() + .collect_fee_pre_tx( + &provider, + Some(®istry), + user, + token.address(), + U256::from(1_000), + ) + .unwrap_err(); + + assert_eq!(error, TIP20Error::policy_forbids().into()); + Ok(()) + }) + } + + #[test] + fn preserves_zone_rpc_error_marker() { + let error = ZoneFeeManager::map_reader_error(crate::zone_rpc_error("unavailable")); + assert_eq!( + error, + TempoPrecompileError::Fatal("[zone rpc] unavailable".into()) + ); + } } diff --git a/specs/ref-impls/src/tempo/ZonePortal.sol b/specs/ref-impls/src/tempo/ZonePortal.sol index e19fa8714..c016eabf0 100644 --- a/specs/ref-impls/src/tempo/ZonePortal.sol +++ b/specs/ref-impls/src/tempo/ZonePortal.sol @@ -25,6 +25,7 @@ import { ENCRYPTED_PAYLOAD_PLAINTEXT_SIZE } from "../libraries/EncryptedDeposit. import { Secp256k1Lib } from "../libraries/Secp256k1Lib.sol"; import { WithdrawalQueue, WithdrawalQueueLib } from "../libraries/WithdrawalQueueLib.sol"; import { StdPrecompiles } from "tempo-std/StdPrecompiles.sol"; +import { StdTokens } from "tempo-std/StdTokens.sol"; import { ITIP20 } from "tempo-std/interfaces/ITIP20.sol"; import { ITIP20Factory } from "tempo-std/interfaces/ITIP20Factory.sol"; import { ITIP403Registry } from "tempo-std/interfaces/ITIP403Registry.sol"; @@ -166,8 +167,11 @@ contract ZonePortal is IZonePortal { genesisTempoBlockNumber = _genesisTempoBlockNumber; rpcUrl = _rpcUrl; - // Enable the initial token - _enableTokenInternal(_initialToken); + // Zones fall back to pathUSD when a user has not selected a fee token. + _enableTokenInternal(StdTokens.PATH_USD_ADDRESS); + if (_initialToken != StdTokens.PATH_USD_ADDRESS) { + _enableTokenInternal(_initialToken); + } } /*////////////////////////////////////////////////////////////// diff --git a/specs/ref-impls/test/tempo/ZonePortal.t.sol b/specs/ref-impls/test/tempo/ZonePortal.t.sol index f99b969b5..3418bf339 100644 --- a/specs/ref-impls/test/tempo/ZonePortal.t.sol +++ b/specs/ref-impls/test/tempo/ZonePortal.t.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; +import { StdTokens } from "tempo-std/StdTokens.sol"; import { ITIP20 } from "tempo-std/interfaces/ITIP20.sol"; import { ITIP403Registry } from "tempo-std/interfaces/ITIP403Registry.sol"; @@ -188,6 +189,21 @@ contract ZonePortalProxyStorageTest is Test { function test_proxyMetadataIsReadFromPortalStorage() public { address initialToken = makeAddr("initial token"); + vm.mockCall( + StdTokens.PATH_USD_ADDRESS, + abi.encodeWithSelector(ITIP20.name.selector), + abi.encode("pathUSD") + ); + vm.mockCall( + StdTokens.PATH_USD_ADDRESS, + abi.encodeWithSelector(ITIP20.symbol.selector), + abi.encode("pathUSD") + ); + vm.mockCall( + StdTokens.PATH_USD_ADDRESS, + abi.encodeWithSelector(ITIP20.currency.selector), + abi.encode("USD") + ); vm.mockCall( initialToken, abi.encodeWithSelector(ITIP20.name.selector), abi.encode("Initial Token") ); From 158d488b4c3a38e4859e64224c279f13bedd5c12 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xKitsune@protonmail.com> Date: Fri, 17 Jul 2026 18:42:08 -0400 Subject: [PATCH 5/6] fix: align zone fee token pool validation --- Cargo.lock | 28 +- Cargo.toml | 24 +- crates/evm/src/lib.rs | 10 + crates/node/Cargo.toml | 6 +- crates/node/src/fee_token_validator.rs | 252 ++++++++++++++++++ crates/node/src/lib.rs | 1 + crates/node/src/node.rs | 13 +- crates/precompiles/src/zone_fee_manager.rs | 51 +++- .../ref-impls/storage-layouts/ZonePortal.json | 90 ++++--- specs/ref-impls/test/tempo/ZonePortal.t.sol | 3 + 10 files changed, 407 insertions(+), 71 deletions(-) create mode 100644 crates/node/src/fee_token_validator.rs diff --git a/Cargo.lock b/Cargo.lock index 4ea6f7cbc..dc41478c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11136,7 +11136,7 @@ dependencies = [ [[package]] name = "tempo-alloy" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy-consensus", "alloy-contract", @@ -11175,7 +11175,7 @@ dependencies = [ [[package]] name = "tempo-chainspec" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy-eips", "alloy-evm", @@ -11199,7 +11199,7 @@ dependencies = [ [[package]] name = "tempo-contracts" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy-contract", "alloy-primitives", @@ -11211,7 +11211,7 @@ dependencies = [ [[package]] name = "tempo-dkg-onchain-artifacts" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "bytes", "commonware-codec", @@ -11223,7 +11223,7 @@ dependencies = [ [[package]] name = "tempo-evm" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy-consensus", "alloy-evm", @@ -11258,7 +11258,7 @@ dependencies = [ [[package]] name = "tempo-hardfork" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy-eips", "alloy-evm", @@ -11270,7 +11270,7 @@ dependencies = [ [[package]] name = "tempo-node" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy", "alloy-eips", @@ -11328,7 +11328,7 @@ dependencies = [ [[package]] name = "tempo-payload-builder" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy-consensus", "alloy-eip7928", @@ -11366,7 +11366,7 @@ dependencies = [ [[package]] name = "tempo-payload-types" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy-eips", "alloy-primitives", @@ -11386,7 +11386,7 @@ dependencies = [ [[package]] name = "tempo-precompiles" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy", "alloy-evm", @@ -11409,7 +11409,7 @@ dependencies = [ [[package]] name = "tempo-precompiles-macros" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy", "proc-macro2", @@ -11420,7 +11420,7 @@ dependencies = [ [[package]] name = "tempo-primitives" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy-consensus", "alloy-eips", @@ -11452,7 +11452,7 @@ dependencies = [ [[package]] name = "tempo-revm" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy-consensus", "alloy-evm", @@ -11475,7 +11475,7 @@ dependencies = [ [[package]] name = "tempo-transaction-pool" version = "1.10.1" -source = "git+https://github.com/tempoxyz/tempo?rev=436f6cf069a0c8aafa9a3a197ffa17488b5f1e81#436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" +source = "git+https://github.com/tempoxyz/tempo?rev=f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd#f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" dependencies = [ "alloy-consensus", "alloy-eips", diff --git a/Cargo.toml b/Cargo.toml index eff5c52ff..d073b3cba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,25 +94,25 @@ incremental = false [workspace.dependencies] # tempo (from upstream) -tempo-alloy = { git = "https://github.com/tempoxyz/tempo", rev = "436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" } -tempo-chainspec = { git = "https://github.com/tempoxyz/tempo", rev = "436f6cf069a0c8aafa9a3a197ffa17488b5f1e81", default-features = false } -tempo-consensus = { git = "https://github.com/tempoxyz/tempo", rev = "436f6cf069a0c8aafa9a3a197ffa17488b5f1e81", default-features = false } -tempo-contracts = { git = "https://github.com/tempoxyz/tempo", rev = "436f6cf069a0c8aafa9a3a197ffa17488b5f1e81", default-features = false, features = [ +tempo-alloy = { git = "https://github.com/tempoxyz/tempo", rev = "f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" } +tempo-chainspec = { git = "https://github.com/tempoxyz/tempo", rev = "f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd", default-features = false } +tempo-consensus = { git = "https://github.com/tempoxyz/tempo", rev = "f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd", default-features = false } +tempo-contracts = { git = "https://github.com/tempoxyz/tempo", rev = "f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd", default-features = false, features = [ "serde", ] } -tempo-evm = { git = "https://github.com/tempoxyz/tempo", rev = "436f6cf069a0c8aafa9a3a197ffa17488b5f1e81", default-features = false } -tempo-node = { git = "https://github.com/tempoxyz/tempo", rev = "436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" } -tempo-payload-types = { git = "https://github.com/tempoxyz/tempo", rev = "436f6cf069a0c8aafa9a3a197ffa17488b5f1e81", default-features = false } -tempo-precompiles = { git = "https://github.com/tempoxyz/tempo", rev = "436f6cf069a0c8aafa9a3a197ffa17488b5f1e81", default-features = false } -tempo-precompiles-macros = { git = "https://github.com/tempoxyz/tempo", rev = "436f6cf069a0c8aafa9a3a197ffa17488b5f1e81" } -tempo-primitives = { git = "https://github.com/tempoxyz/tempo", rev = "436f6cf069a0c8aafa9a3a197ffa17488b5f1e81", default-features = false, features = [ +tempo-evm = { git = "https://github.com/tempoxyz/tempo", rev = "f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd", default-features = false } +tempo-node = { git = "https://github.com/tempoxyz/tempo", rev = "f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" } +tempo-payload-types = { git = "https://github.com/tempoxyz/tempo", rev = "f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd", default-features = false } +tempo-precompiles = { git = "https://github.com/tempoxyz/tempo", rev = "f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd", default-features = false } +tempo-precompiles-macros = { git = "https://github.com/tempoxyz/tempo", rev = "f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd" } +tempo-primitives = { git = "https://github.com/tempoxyz/tempo", rev = "f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd", default-features = false, features = [ "reth", "serde", "serde-bincode-compat", "reth-codec", ] } -tempo-revm = { git = "https://github.com/tempoxyz/tempo", rev = "436f6cf069a0c8aafa9a3a197ffa17488b5f1e81", default-features = false } -tempo-transaction-pool = { git = "https://github.com/tempoxyz/tempo", rev = "436f6cf069a0c8aafa9a3a197ffa17488b5f1e81", default-features = false } +tempo-revm = { git = "https://github.com/tempoxyz/tempo", rev = "f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd", default-features = false } +tempo-transaction-pool = { git = "https://github.com/tempoxyz/tempo", rev = "f9e3bbe6e06ec31ce3bb2adaf04e7b04d565b5fd", default-features = false } # zones tempo-zone-contracts = { path = "crates/contracts", default-features = false } diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index d44be03ed..87af327a6 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -330,6 +330,16 @@ impl ZoneEvmConfig { pub fn tempo_chain_spec(&self) -> &Arc { self.inner.chain_spec() } + + /// Returns the L1 provider used for portal-backed configuration reads. + pub fn l1_provider(&self) -> &L1StateProvider { + &self.zone_factory.l1_reader + } + + /// Returns the L1-backed TIP-403 policy provider, when configured. + pub fn policy_provider(&self) -> Option<&PolicyProvider> { + self.zone_factory.policy_provider.as_ref() + } } impl BlockExecutorFactory for ZoneEvmConfig { diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 605e97f93..6c3536fa1 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -16,6 +16,7 @@ tempo-chainspec.workspace = true tempo-evm = { workspace = true, features = ["rpc", "engine"] } tempo-node.workspace = true tempo-primitives.workspace = true +tempo-revm.workspace = true tempo-transaction-pool.workspace = true tempo-alloy.workspace = true tempo-contracts.workspace = true @@ -54,6 +55,9 @@ reth-tasks.workspace = true reth-tracing = { workspace = true, optional = true } reth-transaction-pool.workspace = true +# revm +revm.workspace = true + # alloy alloy-consensus.workspace = true alloy-genesis.workspace = true @@ -101,12 +105,10 @@ reth-ethereum = { workspace = true, features = ["node", "test-utils"] } reth-node-core.workspace = true reth-tracing.workspace = true reqwest.workspace = true -revm.workspace = true serde = { workspace = true, features = ["derive"] } sha2.workspace = true tempo-payload-types.workspace = true tempo-precompiles = { workspace = true, features = ["test-utils"] } -tempo-revm.workspace = true thiserror.workspace = true tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros"] } tokio-tungstenite.workspace = true diff --git a/crates/node/src/fee_token_validator.rs b/crates/node/src/fee_token_validator.rs new file mode 100644 index 000000000..595dabc4c --- /dev/null +++ b/crates/node/src/fee_token_validator.rs @@ -0,0 +1,252 @@ +//! Zone-specific fee-token admission for the shared Tempo transaction pool. + +use alloy_primitives::Address; +use reth_storage_api::StateProviderFactory; +use revm::precompile::PrecompileError; +use tempo_chainspec::hardfork::TempoHardfork; +use tempo_revm::{TempoInvalidTransaction, error::FeePaymentError}; +use tempo_transaction_pool::validator::{ + FeeTokenSettlement, FeeTokenValidationError, FeeTokenValidator, +}; +use tempo_zone_contracts::ZONE_FEE_MANAGER_ADDRESS; +use zone_l1::TempoStateExt; +use zone_precompiles::{ZoneConfigReader, ZoneTip403ProxyRegistry, policy::PolicyCheck}; +use zone_primitives::policy::AuthRole; + +/// Validates resolved fee tokens against the portal and L1 TIP-403 policy. +#[derive(Clone)] +pub(crate) struct ZoneFeeTokenValidator { + client: Client, + l1_reader: L1, + registry: ZoneTip403ProxyRegistry, +} + +impl core::fmt::Debug for ZoneFeeTokenValidator { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("ZoneFeeTokenValidator") + .finish_non_exhaustive() + } +} + +impl ZoneFeeTokenValidator { + pub(crate) const fn new( + client: Client, + l1_reader: L1, + registry: ZoneTip403ProxyRegistry, + ) -> Self { + Self { + client, + l1_reader, + registry, + } + } +} + +impl ZoneFeeTokenValidator +where + L1: ZoneConfigReader, + Policy: PolicyCheck, +{ + fn validate_at_block( + &self, + fee_payer: Address, + fee_token: Address, + spec: TempoHardfork, + block_number: u64, + ) -> Result { + let enabled = self + .l1_reader + .is_enabled_token(fee_token, block_number) + .map_err(Self::provider_error)?; + if !enabled { + return Err(FeeTokenValidationError::Invalid( + TempoInvalidTransaction::InvalidFeeToken(fee_token), + )); + } + + let policy_id = self + .registry + .resolve_transfer_policy_id(fee_token) + .map_err(Self::provider_error)?; + let authorized = if spec.is_t8() { + self.registry + .is_authorized(policy_id, fee_payer, AuthRole::Sender) + .map_err(Self::provider_error)? + } else { + self.registry + .is_transfer_authorized(policy_id, fee_payer, ZONE_FEE_MANAGER_ADDRESS) + .map_err(Self::provider_error)? + }; + if !authorized { + return Err(FeeTokenValidationError::Invalid( + TempoInvalidTransaction::CollectFeePreTx(FeePaymentError::Other( + "TIP-403 policy forbids fee transfer".into(), + )), + )); + } + + Ok(FeeTokenSettlement::Direct) + } + + fn provider_error(error: PrecompileError) -> FeeTokenValidationError { + FeeTokenValidationError::Other(Box::new(error)) + } +} + +impl FeeTokenValidator for ZoneFeeTokenValidator +where + Client: StateProviderFactory + Send + Sync, + L1: ZoneConfigReader, + Policy: PolicyCheck + Send + Sync, +{ + fn validate_fee_token( + &self, + fee_payer: Address, + fee_token: Address, + spec: TempoHardfork, + ) -> Result { + let state = self + .client + .latest() + .map_err(|error| FeeTokenValidationError::Other(Box::new(error)))?; + let block_number = state + .tempo_block_number() + .map_err(|error| FeeTokenValidationError::Other(Box::new(error)))?; + self.validate_at_block(fee_payer, fee_token, spec, block_number) + } + + fn uses_fee_amm(&self) -> bool { + false + } +} + +#[cfg(test)] +mod tests { + use alloy_primitives::B256; + use tempo_contracts::precompiles::ITIP403Registry::PolicyType; + use zone_precompiles::L1StorageReader; + + use super::*; + + #[derive(Clone)] + struct MockL1 { + enabled: bool, + } + + impl L1StorageReader for MockL1 { + fn read_l1_storage( + &self, + _account: Address, + _slot: B256, + _block_number: u64, + ) -> Result { + Ok(B256::with_last_byte(u8::from(self.enabled))) + } + } + + impl ZoneConfigReader for MockL1 { + fn zone_portal_address(&self) -> Address { + Address::random() + } + } + + #[derive(Clone)] + struct MockPolicy { + sender: bool, + recipient: bool, + } + + impl PolicyCheck for MockPolicy { + fn is_authorized( + &self, + _policy_id: u64, + _user: Address, + role: AuthRole, + ) -> Result { + Ok(match role { + AuthRole::Transfer => self.sender && self.recipient, + AuthRole::Sender => self.sender, + AuthRole::Recipient => self.recipient, + AuthRole::MintRecipient => false, + }) + } + + fn resolve_transfer_policy_id(&self, _token: Address) -> Result { + Ok(1) + } + + fn policy_type_sync(&self, _policy_id: u64) -> Result { + unreachable!("not used by fee-token admission") + } + + fn compound_policy_data( + &self, + _policy_id: u64, + ) -> Result<(u64, u64, u64), PrecompileError> { + unreachable!("not used by fee-token admission") + } + + fn policy_exists(&self, _policy_id: u64) -> Result { + unreachable!("not used by fee-token admission") + } + + fn policy_id_counter(&self) -> u64 { + 1 + } + } + + fn validator( + enabled: bool, + sender: bool, + recipient: bool, + ) -> ZoneFeeTokenValidator<(), MockL1, MockPolicy> { + let policy = MockPolicy { sender, recipient }; + ZoneFeeTokenValidator::new((), MockL1 { enabled }, ZoneTip403ProxyRegistry::new(policy)) + } + + #[test] + fn enabled_authorized_tokens_settle_directly() { + let result = validator(true, true, true).validate_at_block( + Address::random(), + Address::random(), + TempoHardfork::T7, + 42, + ); + assert!(matches!(result, Ok(FeeTokenSettlement::Direct))); + } + + #[test] + fn disabled_tokens_are_rejected() { + let token = Address::random(); + let result = validator(false, true, true).validate_at_block( + Address::random(), + token, + TempoHardfork::T7, + 42, + ); + assert!(matches!( + result, + Err(FeeTokenValidationError::Invalid( + TempoInvalidTransaction::InvalidFeeToken(rejected) + )) if rejected == token + )); + } + + #[test] + fn t8_exempts_fee_manager_recipient_authorization() { + let fee_payer = Address::random(); + let token = Address::random(); + let validator = validator(true, true, false); + + assert!(matches!( + validator.validate_at_block(fee_payer, token, TempoHardfork::T7, 42), + Err(FeeTokenValidationError::Invalid( + TempoInvalidTransaction::CollectFeePreTx(_) + )) + )); + assert!(matches!( + validator.validate_at_block(fee_payer, token, TempoHardfork::T8, 42), + Ok(FeeTokenSettlement::Direct) + )); + } +} diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 711ae41bb..f14775bed 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -10,6 +10,7 @@ use eyre as _; pub mod cli; pub mod dev; pub mod engine; +mod fee_token_validator; pub mod genesis; pub mod node; mod replication; diff --git a/crates/node/src/node.rs b/crates/node/src/node.rs index 0f1038e9e..2f8376b7a 100644 --- a/crates/node/src/node.rs +++ b/crates/node/src/node.rs @@ -5,6 +5,7 @@ use crate::{ ZoneEngine, + fee_token_validator::ZoneFeeTokenValidator, replication::{broadcast_persisted_blocks, import_leader_blocks}, rpc::{ZoneRpc, ZoneRpcApi, rpc_connection_config, start_private_rpc}, }; @@ -1064,6 +1065,15 @@ where // this store is effectively a noop let blob_store = InMemoryBlobStore::default(); + let policy_provider = evm_config + .policy_provider() + .cloned() + .ok_or_else(|| eyre::eyre!("zone fee-token validation requires a policy provider"))?; + let fee_token_validator = ZoneFeeTokenValidator::new( + ctx.provider().clone(), + evm_config.l1_provider().clone(), + zone_precompiles::ZoneTip403ProxyRegistry::new(policy_provider), + ); let tempo_evm_config = TempoEvmConfig::new(evm_config.tempo_chain_spec().clone()); let additional_tasks = ctx.config().txpool.additional_validation_tasks; let task_executor = ctx.task_executor().clone(); @@ -1102,13 +1112,14 @@ where let aa_2d_pool = AA2dPool::new(aa_2d_config); let amm_liquidity_cache = AmmLiquidityCache::new(ctx.provider())?; - let validator = validator.map(|v| { + let validator = validator.map(move |v| { TempoTransactionValidator::new( v, DEFAULT_AA_VALID_AFTER_MAX_SECS, DEFAULT_MAX_TEMPO_AUTHORIZATIONS, amm_liquidity_cache.clone(), ) + .with_fee_token_validator(fee_token_validator.clone()) }); let protocol_pool = Pool::new( validator, diff --git a/crates/precompiles/src/zone_fee_manager.rs b/crates/precompiles/src/zone_fee_manager.rs index 783925498..96db9438c 100644 --- a/crates/precompiles/src/zone_fee_manager.rs +++ b/crates/precompiles/src/zone_fee_manager.rs @@ -164,10 +164,10 @@ impl ZoneFeeManager { if amount.is_zero() { return Ok(()); } - self.collected_fees[sequencer][token].write(U256::ZERO)?; let mut tip20 = TIP20Token::from_address(token)?; self.ensure_transfer_authorized(registry, token, self.address, sequencer)?; + self.collected_fees[sequencer][token].write(U256::ZERO)?; tip20.transfer( self.address, ITIP20::transferCall { @@ -531,6 +531,55 @@ mod tests { }) } + #[test] + fn rejects_fee_distribution_forbidden_by_l1_policy() -> TestResult { + let mut storage = HashMapStorageProvider::new(1); + let admin = Address::random(); + let user = Address::random(); + let sequencer = Address::random(); + let amount = U256::from(1_000); + + StorageCtx::enter(&mut storage, || { + let token = TIP20Setup::create("Restricted USD", "rUSD", admin) + .with_issuer(admin) + .with_mint(user, U256::from(10_000u64)) + .apply()?; + let allowed = MockZoneConfig { + portal: Address::random(), + enabled: vec![token.address()], + authorized: true, + }; + let allowed_registry = ZoneTip403ProxyRegistry::new(allowed.clone()); + let mut manager = ZoneFeeManager::new(); + manager.collect_fee_pre_tx( + &allowed, + Some(&allowed_registry), + user, + token.address(), + amount, + )?; + manager.collect_fee_post_tx(user, amount, U256::ZERO, token.address(), sequencer)?; + + let denied = MockZoneConfig { + authorized: false, + ..allowed + }; + let denied_registry = ZoneTip403ProxyRegistry::new(denied); + let error = manager + .distribute_fees(Some(&denied_registry), sequencer, token.address()) + .unwrap_err(); + + assert_eq!(error, TIP20Error::policy_forbids().into()); + assert_eq!(manager.collected_fees(sequencer, token.address())?, amount); + assert_eq!( + TIP20Token::from_address(token.address())? + .balance_of(ITIP20::balanceOfCall { account: sequencer })?, + U256::ZERO + ); + Ok(()) + }) + } + #[test] fn preserves_zone_rpc_error_marker() { let error = ZoneFeeManager::map_reader_error(crate::zone_rpc_error("unavailable")); diff --git a/specs/ref-impls/storage-layouts/ZonePortal.json b/specs/ref-impls/storage-layouts/ZonePortal.json index 871e25788..e90fe6262 100644 --- a/specs/ref-impls/storage-layouts/ZonePortal.json +++ b/specs/ref-impls/storage-layouts/ZonePortal.json @@ -1,7 +1,7 @@ { "storage": [ { - "astId": 4983, + "astId": 48496, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "sequencer", "offset": 0, @@ -9,7 +9,7 @@ "type": "t_address" }, { - "astId": 4986, + "astId": 48499, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "admin", "offset": 0, @@ -17,7 +17,7 @@ "type": "t_address" }, { - "astId": 4989, + "astId": 48502, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "pendingSequencer", "offset": 0, @@ -25,7 +25,7 @@ "type": "t_address" }, { - "astId": 4992, + "astId": 48505, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "zoneGasRate", "offset": 0, @@ -33,7 +33,7 @@ "type": "t_uint128" }, { - "astId": 4994, + "astId": 48507, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "withdrawalBatchIndex", "offset": 16, @@ -41,7 +41,7 @@ "type": "t_uint64" }, { - "astId": 4996, + "astId": 48509, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "blockHash", "offset": 0, @@ -49,7 +49,7 @@ "type": "t_bytes32" }, { - "astId": 4999, + "astId": 48512, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "currentDepositQueueHash", "offset": 0, @@ -57,7 +57,7 @@ "type": "t_bytes32" }, { - "astId": 5002, + "astId": 48515, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "depositCount", "offset": 0, @@ -65,7 +65,7 @@ "type": "t_uint64" }, { - "astId": 5005, + "astId": 48518, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "lastProcessedDepositNumber", "offset": 8, @@ -73,7 +73,7 @@ "type": "t_uint64" }, { - "astId": 5008, + "astId": 48521, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "lastSyncedTempoBlockNumber", "offset": 16, @@ -81,23 +81,31 @@ "type": "t_uint64" }, { - "astId": 5013, + "astId": 48524, + "contract": "src/tempo/ZonePortal.sol:ZonePortal", + "label": "bouncebackGas", + "offset": 24, + "slot": "6", + "type": "t_uint64" + }, + { + "astId": 48529, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "_encryptionKeys", "offset": 0, "slot": "7", - "type": "t_array(t_struct(EncryptionKeyEntry)2600_storage)dyn_storage" + "type": "t_array(t_struct(EncryptionKeyEntry)43576_storage)dyn_storage" }, { - "astId": 5019, + "astId": 48535, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "_tokenConfigs", "offset": 0, "slot": "8", - "type": "t_mapping(t_address,t_struct(TokenConfig)3009_storage)" + "type": "t_mapping(t_address,t_struct(TokenConfig)43985_storage)" }, { - "astId": 5023, + "astId": 48539, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "_enabledTokens", "offset": 0, @@ -105,7 +113,7 @@ "type": "t_array(t_address)dyn_storage" }, { - "astId": 5030, + "astId": 48546, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "refunds", "offset": 0, @@ -113,15 +121,15 @@ "type": "t_mapping(t_address,t_mapping(t_address,t_uint128))" }, { - "astId": 5034, + "astId": 48550, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "_withdrawalQueue", "offset": 0, "slot": "11", - "type": "t_struct(WithdrawalQueue)4680_storage" + "type": "t_struct(WithdrawalQueue)45715_storage" }, { - "astId": 5037, + "astId": 48553, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "rpcUrl", "offset": 0, @@ -129,7 +137,7 @@ "type": "t_string_storage" }, { - "astId": 5040, + "astId": 48556, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "pendingAdmin", "offset": 0, @@ -137,7 +145,7 @@ "type": "t_address" }, { - "astId": 5043, + "astId": 48559, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "_withdrawalReentrancyStatus", "offset": 0, @@ -145,7 +153,7 @@ "type": "t_uint256" }, { - "astId": 5046, + "astId": 48562, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "zoneId", "offset": 0, @@ -153,7 +161,7 @@ "type": "t_uint32" }, { - "astId": 5048, + "astId": 48564, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "messenger", "offset": 4, @@ -161,7 +169,7 @@ "type": "t_address" }, { - "astId": 5050, + "astId": 48566, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "verifier", "offset": 0, @@ -169,7 +177,7 @@ "type": "t_address" }, { - "astId": 5052, + "astId": 48568, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "genesisTempoBlockNumber", "offset": 20, @@ -177,7 +185,7 @@ "type": "t_uint64" }, { - "astId": 5054, + "astId": 48570, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "_initialized", "offset": 28, @@ -197,11 +205,11 @@ "numberOfBytes": "32", "base": "t_address" }, - "t_array(t_struct(EncryptionKeyEntry)2600_storage)dyn_storage": { + "t_array(t_struct(EncryptionKeyEntry)43576_storage)dyn_storage": { "encoding": "dynamic_array", "label": "struct EncryptionKeyEntry[]", "numberOfBytes": "32", - "base": "t_struct(EncryptionKeyEntry)2600_storage" + "base": "t_struct(EncryptionKeyEntry)43576_storage" }, "t_bool": { "encoding": "inplace", @@ -220,12 +228,12 @@ "numberOfBytes": "32", "value": "t_mapping(t_address,t_uint128)" }, - "t_mapping(t_address,t_struct(TokenConfig)3009_storage)": { + "t_mapping(t_address,t_struct(TokenConfig)43985_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct TokenConfig)", "numberOfBytes": "32", - "value": "t_struct(TokenConfig)3009_storage" + "value": "t_struct(TokenConfig)43985_storage" }, "t_mapping(t_address,t_uint128)": { "encoding": "mapping", @@ -246,13 +254,13 @@ "label": "string", "numberOfBytes": "32" }, - "t_struct(EncryptionKeyEntry)2600_storage": { + "t_struct(EncryptionKeyEntry)43576_storage": { "encoding": "inplace", "label": "struct EncryptionKeyEntry", "numberOfBytes": "64", "members": [ { - "astId": 2595, + "astId": 43571, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "x", "offset": 0, @@ -260,7 +268,7 @@ "type": "t_bytes32" }, { - "astId": 2597, + "astId": 43573, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "yParity", "offset": 0, @@ -268,7 +276,7 @@ "type": "t_uint8" }, { - "astId": 2599, + "astId": 43575, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "activationBlock", "offset": 1, @@ -277,13 +285,13 @@ } ] }, - "t_struct(TokenConfig)3009_storage": { + "t_struct(TokenConfig)43985_storage": { "encoding": "inplace", "label": "struct TokenConfig", "numberOfBytes": "32", "members": [ { - "astId": 3006, + "astId": 43982, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "enabled", "offset": 0, @@ -291,7 +299,7 @@ "type": "t_bool" }, { - "astId": 3008, + "astId": 43984, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "depositsActive", "offset": 1, @@ -300,13 +308,13 @@ } ] }, - "t_struct(WithdrawalQueue)4680_storage": { + "t_struct(WithdrawalQueue)45715_storage": { "encoding": "inplace", "label": "struct WithdrawalQueue", "numberOfBytes": "96", "members": [ { - "astId": 4673, + "astId": 45708, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "head", "offset": 0, @@ -314,7 +322,7 @@ "type": "t_uint256" }, { - "astId": 4675, + "astId": 45710, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "tail", "offset": 0, @@ -322,7 +330,7 @@ "type": "t_uint256" }, { - "astId": 4679, + "astId": 45714, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "slots", "offset": 0, diff --git a/specs/ref-impls/test/tempo/ZonePortal.t.sol b/specs/ref-impls/test/tempo/ZonePortal.t.sol index 3418bf339..98c0aed9d 100644 --- a/specs/ref-impls/test/tempo/ZonePortal.t.sol +++ b/specs/ref-impls/test/tempo/ZonePortal.t.sol @@ -256,6 +256,9 @@ contract ZonePortalProxyStorageTest is Test { assertEq(ZonePortal(proxyA).messenger(), messengerA); assertEq(ZonePortal(proxyA).verifier(), verifierA); assertEq(ZonePortal(proxyA).genesisTempoBlockNumber(), 100); + assertTrue(ZonePortal(proxyA).isTokenEnabled(StdTokens.PATH_USD_ADDRESS)); + assertTrue(ZonePortal(proxyA).isTokenEnabled(initialToken)); + assertEq(ZonePortal(proxyA).enabledTokenCount(), 2); assertEq(ZonePortal(proxyB).zoneId(), 2); assertEq(ZonePortal(proxyB).messenger(), messengerB); From 50ef816f09636d23f2e50bf45b6332748ddd2153 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xKitsune@protonmail.com> Date: Fri, 17 Jul 2026 18:49:49 -0400 Subject: [PATCH 6/6] chore: refresh portal storage layout --- .../ref-impls/storage-layouts/ZonePortal.json | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/specs/ref-impls/storage-layouts/ZonePortal.json b/specs/ref-impls/storage-layouts/ZonePortal.json index e90fe6262..55fb06fdd 100644 --- a/specs/ref-impls/storage-layouts/ZonePortal.json +++ b/specs/ref-impls/storage-layouts/ZonePortal.json @@ -1,7 +1,7 @@ { "storage": [ { - "astId": 48496, + "astId": 5026, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "sequencer", "offset": 0, @@ -9,7 +9,7 @@ "type": "t_address" }, { - "astId": 48499, + "astId": 5029, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "admin", "offset": 0, @@ -17,7 +17,7 @@ "type": "t_address" }, { - "astId": 48502, + "astId": 5032, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "pendingSequencer", "offset": 0, @@ -25,7 +25,7 @@ "type": "t_address" }, { - "astId": 48505, + "astId": 5035, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "zoneGasRate", "offset": 0, @@ -33,7 +33,7 @@ "type": "t_uint128" }, { - "astId": 48507, + "astId": 5037, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "withdrawalBatchIndex", "offset": 16, @@ -41,7 +41,7 @@ "type": "t_uint64" }, { - "astId": 48509, + "astId": 5039, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "blockHash", "offset": 0, @@ -49,7 +49,7 @@ "type": "t_bytes32" }, { - "astId": 48512, + "astId": 5042, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "currentDepositQueueHash", "offset": 0, @@ -57,7 +57,7 @@ "type": "t_bytes32" }, { - "astId": 48515, + "astId": 5045, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "depositCount", "offset": 0, @@ -65,7 +65,7 @@ "type": "t_uint64" }, { - "astId": 48518, + "astId": 5048, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "lastProcessedDepositNumber", "offset": 8, @@ -73,7 +73,7 @@ "type": "t_uint64" }, { - "astId": 48521, + "astId": 5051, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "lastSyncedTempoBlockNumber", "offset": 16, @@ -81,7 +81,7 @@ "type": "t_uint64" }, { - "astId": 48524, + "astId": 5054, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "bouncebackGas", "offset": 24, @@ -89,23 +89,23 @@ "type": "t_uint64" }, { - "astId": 48529, + "astId": 5059, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "_encryptionKeys", "offset": 0, "slot": "7", - "type": "t_array(t_struct(EncryptionKeyEntry)43576_storage)dyn_storage" + "type": "t_array(t_struct(EncryptionKeyEntry)2642_storage)dyn_storage" }, { - "astId": 48535, + "astId": 5065, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "_tokenConfigs", "offset": 0, "slot": "8", - "type": "t_mapping(t_address,t_struct(TokenConfig)43985_storage)" + "type": "t_mapping(t_address,t_struct(TokenConfig)3051_storage)" }, { - "astId": 48539, + "astId": 5069, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "_enabledTokens", "offset": 0, @@ -113,7 +113,7 @@ "type": "t_array(t_address)dyn_storage" }, { - "astId": 48546, + "astId": 5076, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "refunds", "offset": 0, @@ -121,15 +121,15 @@ "type": "t_mapping(t_address,t_mapping(t_address,t_uint128))" }, { - "astId": 48550, + "astId": 5080, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "_withdrawalQueue", "offset": 0, "slot": "11", - "type": "t_struct(WithdrawalQueue)45715_storage" + "type": "t_struct(WithdrawalQueue)4725_storage" }, { - "astId": 48553, + "astId": 5083, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "rpcUrl", "offset": 0, @@ -137,7 +137,7 @@ "type": "t_string_storage" }, { - "astId": 48556, + "astId": 5086, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "pendingAdmin", "offset": 0, @@ -145,7 +145,7 @@ "type": "t_address" }, { - "astId": 48559, + "astId": 5089, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "_withdrawalReentrancyStatus", "offset": 0, @@ -153,7 +153,7 @@ "type": "t_uint256" }, { - "astId": 48562, + "astId": 5092, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "zoneId", "offset": 0, @@ -161,7 +161,7 @@ "type": "t_uint32" }, { - "astId": 48564, + "astId": 5094, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "messenger", "offset": 4, @@ -169,7 +169,7 @@ "type": "t_address" }, { - "astId": 48566, + "astId": 5096, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "verifier", "offset": 0, @@ -177,7 +177,7 @@ "type": "t_address" }, { - "astId": 48568, + "astId": 5098, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "genesisTempoBlockNumber", "offset": 20, @@ -185,7 +185,7 @@ "type": "t_uint64" }, { - "astId": 48570, + "astId": 5100, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "_initialized", "offset": 28, @@ -205,11 +205,11 @@ "numberOfBytes": "32", "base": "t_address" }, - "t_array(t_struct(EncryptionKeyEntry)43576_storage)dyn_storage": { + "t_array(t_struct(EncryptionKeyEntry)2642_storage)dyn_storage": { "encoding": "dynamic_array", "label": "struct EncryptionKeyEntry[]", "numberOfBytes": "32", - "base": "t_struct(EncryptionKeyEntry)43576_storage" + "base": "t_struct(EncryptionKeyEntry)2642_storage" }, "t_bool": { "encoding": "inplace", @@ -228,12 +228,12 @@ "numberOfBytes": "32", "value": "t_mapping(t_address,t_uint128)" }, - "t_mapping(t_address,t_struct(TokenConfig)43985_storage)": { + "t_mapping(t_address,t_struct(TokenConfig)3051_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct TokenConfig)", "numberOfBytes": "32", - "value": "t_struct(TokenConfig)43985_storage" + "value": "t_struct(TokenConfig)3051_storage" }, "t_mapping(t_address,t_uint128)": { "encoding": "mapping", @@ -254,13 +254,13 @@ "label": "string", "numberOfBytes": "32" }, - "t_struct(EncryptionKeyEntry)43576_storage": { + "t_struct(EncryptionKeyEntry)2642_storage": { "encoding": "inplace", "label": "struct EncryptionKeyEntry", "numberOfBytes": "64", "members": [ { - "astId": 43571, + "astId": 2637, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "x", "offset": 0, @@ -268,7 +268,7 @@ "type": "t_bytes32" }, { - "astId": 43573, + "astId": 2639, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "yParity", "offset": 0, @@ -276,7 +276,7 @@ "type": "t_uint8" }, { - "astId": 43575, + "astId": 2641, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "activationBlock", "offset": 1, @@ -285,13 +285,13 @@ } ] }, - "t_struct(TokenConfig)43985_storage": { + "t_struct(TokenConfig)3051_storage": { "encoding": "inplace", "label": "struct TokenConfig", "numberOfBytes": "32", "members": [ { - "astId": 43982, + "astId": 3048, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "enabled", "offset": 0, @@ -299,7 +299,7 @@ "type": "t_bool" }, { - "astId": 43984, + "astId": 3050, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "depositsActive", "offset": 1, @@ -308,13 +308,13 @@ } ] }, - "t_struct(WithdrawalQueue)45715_storage": { + "t_struct(WithdrawalQueue)4725_storage": { "encoding": "inplace", "label": "struct WithdrawalQueue", "numberOfBytes": "96", "members": [ { - "astId": 45708, + "astId": 4718, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "head", "offset": 0, @@ -322,7 +322,7 @@ "type": "t_uint256" }, { - "astId": 45710, + "astId": 4720, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "tail", "offset": 0, @@ -330,7 +330,7 @@ "type": "t_uint256" }, { - "astId": 45714, + "astId": 4724, "contract": "src/tempo/ZonePortal.sol:ZonePortal", "label": "slots", "offset": 0,