# method Pinned simulator 6b6921f8d463314aa7c96234e1be25a25245f704; true current-step price -> WAD slot 31 before trades. NP2 overrides pHat after its estimator update at every afterSwap; sigma recursion and fee equations untouched. No tuning. Seeds 2540000..2541999, NumPy default_rng(seed): size, rate, sigma; price seed=seed, retail seed+1. Separate candidate/Vanilla and NP2/Vanilla run_batch calls, 8 workers, 16 seeds/call; subtract submission edges. # code The NP2 baseline is reconstructed from [TWO](https://api.imdnotdead.fun/artifacts/528a6da78583c6142644a552c86670f9d25c20e39e92f4de44deae05ddc8cecd) with the requested interior-routing block from [NP](https://api.imdnotdead.fun/artifacts/da313379a2e69e9286668865e06b18dfcae25732c82b934b0fd9c2ef0d8c2ff5). The full candidate below differs from NP2 only by the three-line oracle override (comment, assignment, blank line). Remove that exact block to recover NP2. | Artifact | SHA-256 (UTF-8 source, including final newline) | |---|---| | NP2 | `51f1c1a9d4999ee460eb8bbc66880da8074b173399a4f5d71f96671482e18ac0` | | Oracle strategy | `83e7942ed44fdbf8b7d3258d24b35f11bc56810e8cf2f5319e4315bbef5fdf8d` | | Engine patch | `ee1fd1db94ea2c0ee91fba1bb31341bfa52f8acbcacbb910dfe5f195bdaf3ba2` | | CH calibration source | `6dd68f77a70900319460215374143dbd5d924198450a46d9f5f91f6b1e3d35c3` | Slot 31 is inside the base contract's [fixed 32-slot array](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/contracts/src/AMMStrategyBase.sol) and is unused by NP2/Vanilla. The engine writes the current step's fair price after the price draw, before arbitrage and retail. That value is constant throughout the step, so every hook sees the current price. The hook reads it only after the trade; posted fees for the first trade are unchanged by the injection. `afterInitialize` is unchanged. Prices use the engine's existing `Wad::from_f64` conversion. `sigmaHat` retains its original initial value, update, cap and decay; it is not replaced by the simulator's true sigma. Its numeric path can change because its return calculation reads the previous persisted `pHat` and trades change. The existing pHat estimator still executes before the override. All toxicity, flow, skew, stale-price and tail-fee equations remain intact. Apply this unified diff at the pinned checkout root. The added edge maps are attribution only. The hook-error counter addresses the original engine's [silent keep-current-fees fallback](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_sim_rs/src/amm/cfmm.rs). ```diff diff --git a/amm_sim_rs/src/amm/cfmm.rs b/amm_sim_rs/src/amm/cfmm.rs index 02e6645..633f7e0 100644 --- a/amm_sim_rs/src/amm/cfmm.rs +++ b/amm_sim_rs/src/amm/cfmm.rs @@ -40,6 +40,8 @@ pub struct TradeResult { pub struct CFMM { /// Strategy name pub name: String, + /// Diagnostic: failed afterSwap calls (must be zero). + pub hook_errors: u64, /// EVM strategy for fee decisions strategy: EVMStrategy, /// Current X reserves @@ -62,6 +64,7 @@ impl CFMM { let name = strategy.name().to_string(); Self { name, + hook_errors: 0, strategy, reserve_x, reserve_y, @@ -289,16 +292,24 @@ impl CFMM { }) } + /// Install this step's fair price before any trade; all prints share it. + pub fn set_oracle_price(&mut self, price: f64) -> Result<(), crate::evm::strategy::EVMError> { + self.strategy.set_oracle_price(price) + } + /// Update fees from strategy after a trade. fn update_fees(&mut self, trade_info: &TradeInfo) { if let Ok((bid_fee, ask_fee)) = self.strategy.after_swap(trade_info) { self.current_fees = FeeQuote::new(bid_fee.clamp_fee(), ask_fee.clamp_fee()); + } else { + self.hook_errors += 1; } // On error, keep current fees } /// Reset the AMM for a new simulation. pub fn reset(&mut self, reserve_x: f64, reserve_y: f64) -> Result<(), crate::evm::strategy::EVMError> { + self.hook_errors = 0; self.reserve_x = reserve_x; self.reserve_y = reserve_y; self.accumulated_fees_x = 0.0; diff --git a/amm_sim_rs/src/evm/strategy.rs b/amm_sim_rs/src/evm/strategy.rs index 1d06af1..69c1332 100644 --- a/amm_sim_rs/src/evm/strategy.rs +++ b/amm_sim_rs/src/evm/strategy.rs @@ -172,6 +172,13 @@ impl EVMStrategy { .ok_or_else(|| EVMError::InvalidReturnData("Failed to decode fee pair".into())) } + /// Research-only oracle channel: slots[31] is unused by NP2 and Vanilla. + pub fn set_oracle_price(&mut self, price: f64) -> Result<(), EVMError> { + let value = U256::from(Wad::from_f64(price).0 as u128); + self.db.insert_account_storage(STRATEGY_ADDRESS, U256::from(31), value) + .map_err(|e| EVMError::ExecutionFailed(format!("oracle storage: {:?}", e))) + } + /// Handle a trade event and return updated fees. /// /// Returns (bid_fee, ask_fee) in WAD. diff --git a/amm_sim_rs/src/simulation/engine.rs b/amm_sim_rs/src/simulation/engine.rs index 6834c43..4452002 100644 --- a/amm_sim_rs/src/simulation/engine.rs +++ b/amm_sim_rs/src/simulation/engine.rs @@ -113,6 +113,9 @@ impl SimulationEngine { edges.insert(submission_name.clone(), 0.0); edges.insert(baseline_name.clone(), 0.0); + let mut arb_edges = edges.clone(); + let mut retail_edges = edges.clone(); + // Run simulation steps let mut steps = Vec::with_capacity(self.config.n_steps as usize); @@ -136,11 +139,18 @@ impl SimulationEngine { for t in 0..self.config.n_steps { // 1. Generate new fair price let fair_price = price_process.step(); + // Research-only channel. This cannot change the fees charged to the + // first trade: the contract reads it only in the afterSwap hook. + for amm in amms.iter_mut() { + amm.set_oracle_price(fair_price) + .map_err(|e| SimulationError::EVMError(e.to_string()))?; + } // 2. Arbitrageur extracts profit from each AMM for amm in amms.iter_mut() { if let Some(arb_result) = arbitrageur.execute_arb(amm, fair_price, t as u64) { *arb_volume_y.get_mut(&arb_result.amm_name).unwrap() += arb_result.amount_y; + *arb_edges.get_mut(&arb_result.amm_name).unwrap() -= arb_result.profit; let entry = edges.entry(arb_result.amm_name).or_insert(0.0); // AMM edge is the negative of arbitrageur profit at true price *entry += -arb_result.profit; @@ -157,6 +167,7 @@ impl SimulationEngine { } else { trade.amount_y - trade.amount_x * fair_price }; + *retail_edges.get_mut(&trade.amm_name).unwrap() += trade_edge; let entry = edges.entry(trade.amm_name).or_insert(0.0); *entry += trade_edge; } @@ -204,11 +215,15 @@ impl SimulationEngine { pnl.insert(name.clone(), final_value - init_value); } + let hook_errors = amms.iter().map(|a| (a.name.clone(), a.hook_errors)).collect(); Ok(LightweightSimResult { seed, strategies: vec![submission_name, baseline_name], pnl, edges, + arb_edges, + retail_edges, + hook_errors, initial_fair_price, initial_reserves, steps, diff --git a/amm_sim_rs/src/types/result.rs b/amm_sim_rs/src/types/result.rs index 0257335..ed5ab31 100644 --- a/amm_sim_rs/src/types/result.rs +++ b/amm_sim_rs/src/types/result.rs @@ -58,6 +58,14 @@ pub struct LightweightSimResult { #[pyo3(get)] pub edges: HashMap, + /// Attribution only; no effect on routing or fees. + #[pyo3(get)] + pub arb_edges: HashMap, + #[pyo3(get)] + pub retail_edges: HashMap, + #[pyo3(get)] + pub hook_errors: HashMap, + /// Initial fair price #[pyo3(get)] pub initial_fair_price: f64, ``` Full `scratch/Oracle.sol`: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {AMMStrategyBase} from "./AMMStrategyBase.sol"; import {TradeInfo} from "./IAMMStrategy.sol"; contract Strategy is AMMStrategyBase { uint256 constant ELAPSED_CAP = 8; uint256 constant SIGNAL_THRESHOLD = WAD / 650; uint256 constant DIR_DECAY = 750000000000000000; uint256 constant ACT_DECAY = 530000000000000000; uint256 constant SIZE_DECAY = 530000000000000000; uint256 constant TOX_DECAY = 910000000000000000; uint256 constant SIGMA_DECAY = 690000000000000000; uint256 constant LAMBDA_DECAY = 990000000000000000; uint256 constant SIZE_BLEND_DECAY = 828000000000000000; uint256 constant TOX_BLEND_DECAY = 51000000000000000; uint256 constant ACT_BLEND_DECAY = 985000000000000000; uint256 constant PHAT_ALPHA = 270000000000000000; uint256 constant PHAT_ALPHA_RETAIL = 60000000000000000; uint256 constant PHAT_ALPHA_SAME = 120000000000000000; uint256 constant REVERT_U = 0; uint256 constant DIR_IMPACT_MULT = 4; uint256 constant GATE_SIGMA_MULT = 7200000000000000000; uint256 constant MIN_GATE = 30000000000000000; uint256 constant TRADE_TOX_BOOST = 3500 * BPS; uint256 constant STALE_ATTRACT_FRAC = 1050000000000000000; uint256 constant RET_CAP = WAD / 35; uint256 constant TOX_CAP = WAD / 5; uint256 constant TRADE_RATIO_CAP = WAD / 5; uint256 constant LAMBDA_CAP = 1820000000000000000; uint256 constant STEP_COUNT_CAP = 64; uint256 constant BASE_FEE = 4 * BPS; uint256 constant SIGMA_COEF = 200000000000000000; uint256 constant LAMBDA_COEF = 12 * BPS; uint256 constant FLOW_SIZE_COEF = 4842 * BPS; uint256 constant TOX_COEF = 300 * BPS; uint256 constant TOX_QUAD_COEF = 15000 * BPS; uint256 constant ACT_COEF = 91843 * BPS; uint256 constant DIR_COEF = 20 * BPS; uint256 constant DIR_TOX_COEF = 100 * BPS; uint256 constant STALE_DIR_COEF = 6850 * BPS; uint256 constant SIGMA_TOX_COEF = 500 * BPS; uint256 constant TAIL_KNEE = 600 * BPS; uint256 constant TAIL_SLOPE_PROTECT = 930000000000000000; uint256 constant TAIL_SLOPE_ATTRACT = 850000000000000000; function afterInitialize(uint256 initialX, uint256 initialY) external override returns (uint256, uint256) { slots[0] = BASE_FEE; slots[1] = BASE_FEE; slots[2] = 0; slots[3] = WAD; slots[4] = 0; slots[5] = initialX > 0 ? wdiv(initialY, initialX) : 100 * WAD; slots[6] = 950000000000000; slots[7] = 800000000000000000; slots[8] = 2000000000000000; slots[9] = 0; slots[10] = 0; slots[11] = 0; slots[12] = 0; return (BASE_FEE, BASE_FEE); } function afterSwap(TradeInfo calldata trade) external override returns (uint256, uint256) { uint256 prevBidFee = slots[0]; uint256 prevAskFee = slots[1]; uint256 lastTs = slots[2]; uint256 dirState = slots[3]; uint256 actEma = slots[4]; uint256 pHat = slots[5]; uint256 sigmaHat = slots[6]; uint256 lambdaHat = slots[7]; uint256 sizeHat = slots[8]; uint256 toxEma = slots[9]; uint256 stepTradeCount = slots[10]; bool isNewStep = trade.timestamp > lastTs; if (isNewStep) { uint256 elapsedRaw = trade.timestamp - lastTs; uint256 elapsed = elapsedRaw > ELAPSED_CAP ? ELAPSED_CAP : elapsedRaw; dirState = _decayCentered(dirState, DIR_DECAY, elapsed); actEma = wmul(actEma, _powWad(ACT_DECAY, elapsed)); sizeHat = wmul(sizeHat, _powWad(SIZE_DECAY, elapsed)); toxEma = wmul(toxEma, _powWad(TOX_DECAY, elapsed)); if (stepTradeCount > 0 && elapsedRaw > 0) { uint256 lambdaInst = (stepTradeCount * WAD) / elapsedRaw; if (lambdaInst > LAMBDA_CAP) lambdaInst = LAMBDA_CAP; lambdaHat = wmul(lambdaHat, LAMBDA_DECAY) + wmul(lambdaInst, WAD - LAMBDA_DECAY); } stepTradeCount = 0; } bool firstInStep = stepTradeCount == 0; uint256 spot = trade.reserveX > 0 ? wdiv(trade.reserveY, trade.reserveX) : pHat; if (pHat == 0) pHat = spot; uint256 feeUsed = trade.isBuy ? prevBidFee : prevAskFee; uint256 gamma = feeUsed < WAD ? WAD - feeUsed : 0; uint256 pImplied; if (gamma == 0) { pImplied = spot; } else { pImplied = trade.isBuy ? wmul(spot, gamma) : wdiv(spot, gamma); } { uint256 ret = pHat > 0 ? wdiv(absDiff(pImplied, pHat), pHat) : 0; uint256 alpha = firstInStep ? PHAT_ALPHA : PHAT_ALPHA_RETAIL; uint256 prePhat = pHat; bool secondPrint = !firstInStep && stepTradeCount == 1; bool sameDir = false; bool oppDir = false; if (secondPrint) { bool up2 = pImplied >= pHat; uint256 d1 = slots[12]; if (d1 != 0) { sameDir = (d1 == 1) == up2; oppDir = !sameDir; } if (sameDir) alpha = PHAT_ALPHA_SAME; } if (firstInStep) { slots[11] = pHat; slots[12] = pImplied >= pHat ? 1 : 2; } uint256 adaptiveGate = wmul(sigmaHat, GATE_SIGMA_MULT); if (adaptiveGate < MIN_GATE) adaptiveGate = MIN_GATE; if (ret <= adaptiveGate) { pHat = wmul(pHat, WAD - alpha) + wmul(pImplied, alpha); } if (oppDir && REVERT_U > 0) { uint256 pre = slots[11]; if (pre > pHat) pHat = pHat + wmul(pre - pHat, REVERT_U); else pHat = pHat - wmul(pHat - pre, REVERT_U); } if (firstInStep) { if (ret > RET_CAP) ret = RET_CAP; sigmaHat = wmul(sigmaHat, SIGMA_DECAY) + wmul(ret, WAD - SIGMA_DECAY); } } // Interior retail routing equalizes fee-adjusted marginal prices. // Subsequent trades in a step cannot be the initial arbitrage trade. if (!firstInStep && feeUsed > 30 * BPS && gamma > 0) { uint256 normalSpot = trade.isBuy ? wdiv(wmul(spot, gamma), WAD - 30 * BPS) : wdiv(wmul(spot, WAD - 30 * BPS), gamma); pHat = wmul(pHat, 90 * WAD / 100) + wmul(normalSpot, 10 * WAD / 100); } // Research-only oracle override, after the unchanged estimator/sigma update. pHat = slots[31]; uint256 tradeRatio = trade.reserveY > 0 ? wdiv(trade.amountY, trade.reserveY) : 0; if (tradeRatio > TRADE_RATIO_CAP) tradeRatio = TRADE_RATIO_CAP; if (tradeRatio > SIGNAL_THRESHOLD) { uint256 push = tradeRatio * DIR_IMPACT_MULT; if (push > WAD / 4) push = WAD / 4; if (trade.isBuy) { dirState = dirState + push; if (dirState > 2 * WAD) dirState = 2 * WAD; } else { dirState = dirState > push ? dirState - push : 0; } actEma = wmul(actEma, ACT_BLEND_DECAY) + wmul(tradeRatio, WAD - ACT_BLEND_DECAY); sizeHat = wmul(sizeHat, SIZE_BLEND_DECAY) + wmul(tradeRatio, WAD - SIZE_BLEND_DECAY); if (sizeHat > WAD) sizeHat = WAD; } uint256 tox = pHat > 0 ? wdiv(absDiff(spot, pHat), pHat) : 0; if (tox > TOX_CAP) tox = TOX_CAP; toxEma = wmul(toxEma, TOX_BLEND_DECAY) + wmul(tox, WAD - TOX_BLEND_DECAY); uint256 toxSignal = toxEma; stepTradeCount = stepTradeCount + 1; if (stepTradeCount > STEP_COUNT_CAP) stepTradeCount = STEP_COUNT_CAP; uint256 flowSize = wmul(lambdaHat, sizeHat); uint256 fBase = BASE_FEE + wmul(SIGMA_COEF, sigmaHat) + wmul(LAMBDA_COEF, lambdaHat) + wmul(FLOW_SIZE_COEF, flowSize); uint256 fMid = fBase + wmul(TOX_COEF, toxSignal) + wmul(TOX_QUAD_COEF, wmul(toxSignal, toxSignal)) + wmul(ACT_COEF, actEma); fMid = fMid + wmul(SIGMA_TOX_COEF, wmul(sigmaHat, toxSignal)); uint256 dirDev; bool sellPressure; if (dirState >= WAD) { dirDev = dirState - WAD; sellPressure = true; } else { dirDev = WAD - dirState; sellPressure = false; } uint256 skew = wmul(DIR_COEF, dirDev) + wmul(DIR_TOX_COEF, wmul(dirDev, toxSignal)); uint256 bidFee; uint256 askFee; if (sellPressure) { bidFee = fMid + skew; askFee = fMid > skew ? fMid - skew : 0; } else { askFee = fMid + skew; bidFee = fMid > skew ? fMid - skew : 0; } { uint256 staleShift = wmul(STALE_DIR_COEF, toxSignal); uint256 attractShift = wmul(staleShift, STALE_ATTRACT_FRAC); if (spot >= pHat) { bidFee = bidFee + staleShift; askFee = askFee > attractShift ? askFee - attractShift : 0; } else { askFee = askFee + staleShift; bidFee = bidFee > attractShift ? bidFee - attractShift : 0; } } { bool tradeAligned = (trade.isBuy && spot >= pHat) || (!trade.isBuy && spot < pHat); if (tradeAligned) { uint256 tradeBoost = wmul(TRADE_TOX_BOOST, tradeRatio); if (trade.isBuy) { bidFee = bidFee + tradeBoost; } else { askFee = askFee + tradeBoost; } } } if (sellPressure) { bidFee = clampFee(_compressTailWithSlope(bidFee, TAIL_SLOPE_PROTECT)); askFee = clampFee(_compressTailWithSlope(askFee, TAIL_SLOPE_ATTRACT)); } else { askFee = clampFee(_compressTailWithSlope(askFee, TAIL_SLOPE_PROTECT)); bidFee = clampFee(_compressTailWithSlope(bidFee, TAIL_SLOPE_ATTRACT)); } slots[0] = bidFee; slots[1] = askFee; slots[2] = trade.timestamp; slots[3] = dirState; slots[4] = actEma; slots[5] = pHat; slots[6] = sigmaHat; slots[7] = lambdaHat; slots[8] = sizeHat; slots[9] = toxEma; slots[10] = stepTradeCount; return (bidFee, askFee); } function _compressTailWithSlope(uint256 fee, uint256 slope) internal pure returns (uint256) { if (fee <= TAIL_KNEE) return fee; return TAIL_KNEE + wmul(fee - TAIL_KNEE, slope); } function _powWad(uint256 factor, uint256 exp) internal pure returns (uint256 result) { result = WAD; while (exp > 0) { if (exp & 1 == 1) result = wmul(result, factor); factor = wmul(factor, factor); exp >>= 1; } } function _decayCentered(uint256 centered, uint256 decayFactor, uint256 elapsed) internal pure returns (uint256) { uint256 mul = _powWad(decayFactor, elapsed); if (centered >= WAD) { return WAD + wmul(centered - WAD, mul); } uint256 below = wmul(WAD - centered, mul); return below < WAD ? WAD - below : 0; } function getName() external pure override returns (string memory) { return "no_cubic"; } } ``` Measurement driver used (`scratch/measure.py`); NP2 is the candidate with the override removed, and CH is the [pinned CH source](https://raw.githubusercontent.com/ChengHua926/amm-challenge/d34a5199c59a4c44d5336a25f1436b4ae1f21104/contracts/src/Strategy.sol). Run with the supplied environment activated. `calibrate` ran against the original engine before patching (16-seed chunks, final 8 seeds); `parity` and `confirm` ran after rebuilding the patch. Every confirmation call contains exactly 16 seeds. ```python import json, sys, time, hashlib from pathlib import Path import numpy as np import amm_sim_rs from amm_competition.evm.compiler import SolidityCompiler from amm_competition.evm.baseline import get_vanilla_bytecode_and_abi from amm_competition.competition.config import build_config def configs(seeds): out=[] for s in seeds: rng=np.random.default_rng(s) size=rng.uniform(19,21);rate=rng.uniform(.6,1);sigma=rng.uniform(.000882,.001008) out.append(build_config(seed=s,gbm_sigma=sigma,retail_arrival_rate=rate,retail_mean_size=size)) return out def compile_file(name): source=Path('scratch/'+name+'.sol').read_text();r=SolidityCompiler().compile(source) assert r.success,r.errors print('compiled',name,hashlib.sha256(source.encode()).hexdigest(),flush=True) return r.bytecode def extract(r): d=dict(seed=r.seed,edge=r.edges['submission'],fees=r.average_fees['submission']) for key in ['arb_edges','retail_edges','hook_errors']: if hasattr(r,key):d[key]=getattr(r,key)['submission'] if 'arb_edges' in d: assert abs(d['edge']-d['arb_edges']-d['retail_edges'])<1e-8 assert d['hook_errors']==0 return d def main(): mode=sys.argv[1];vanilla=get_vanilla_bytecode_and_abi()[0];t0=time.time() if mode in ['calibrate','parity']: end=1000 if mode=='calibrate' else 16 for name in ['CH','NP2']: bytecode=compile_file(name); rows=[] with open('scratch/'+mode+'_'+name+'.jsonl','w') as f: for start in range(0,end,16): batch=amm_sim_rs.run_batch(bytecode,vanilla,configs(range(start,min(start+16,end))),8) for r in batch.results: d=extract(r);rows.append(d);f.write(json.dumps(d)+'\n') f.flush() if (start//16)%8==0:print(mode,name,len(rows),'mean',np.mean([d['edge'] for d in rows]),'seconds',time.time()-t0,flush=True) del batch print('DONE',name,'n',len(rows),'mean',np.mean([r['edge'] for r in rows]),flush=True) elif mode=='confirm': cand=compile_file('Oracle');base=compile_file('NP2');rows=[] with open('scratch/confirm.jsonl','w') as f: for start in range(2540000,2542000,16): cfgs=configs(range(start,start+16)) c=amm_sim_rs.run_batch(cand,vanilla,cfgs,8) cr=[extract(r) for r in c.results]; del c b=amm_sim_rs.run_batch(base,vanilla,cfgs,8) br=[extract(r) for r in b.results]; del b for x,y in zip(cr,br): assert x['seed']==y['seed'] d=dict(seed=x['seed'],candidate=x,np2=y,diff=x['edge']-y['edge']);rows.append(d);f.write(json.dumps(d)+'\n') f.flush() if len(rows)%128==0 or len(rows)==2000: ds=np.array([r['diff'] for r in rows]);print('confirm',len(rows),'mean',ds.mean(),'se',ds.std(ddof=1)/np.sqrt(len(ds)),'seconds',time.time()-t0,flush=True) ds=np.array([r['diff'] for r in rows]);summary=dict(n=len(ds),mean_diff=float(ds.mean()),se=float(ds.std(ddof=1)/np.sqrt(len(ds))),worst=float(ds.min())) print('PAIRED',json.dumps(summary),flush=True) Path('scratch/paired.json').write_text(json.dumps(summary,indent=2)) if __name__=='__main__':main() ``` Additional storage/gas and engine plumbing checks used (seeds 10000..10015 are diagnostic only; no parameters were selected): ```python import json, math from pathlib import Path import numpy as np from pyrevm import EVM from amm_competition.evm.compiler import SolidityCompiler source=Path('scratch/Oracle.sol').read_text() r=SolidityCompiler().compile(source); assert r.success,r.errors vm=EVM();caller='0x2000000000000000000000000000000000000002' addr=vm.deploy(deployer=caller,code=r.bytecode,value=0,gas=10000000) def call(selector,values): data=bytes.fromhex(selector)+b''.join(int(x).to_bytes(32,'big') for x in values) out=vm.message_call(caller=caller,to=addr,calldata=data,value=0,gas=250000) assert len(out)==64 fees=[int.from_bytes(out[i:i+32],'big') for i in [0,32]] assert all(0<=f<=10**17 for f in fees) return fees W=10**18 call('837aef47',[100*W,10000*W]) rng=np.random.default_rng(10000);x=100.;y=10000.;ts=0;max_gas=0 for i in range(512): if i%4==0:ts+=int(rng.integers(1,16)) p=100*math.exp(.001*math.sin(i/13));pwad=int(p*W) # Apply alternating constant-product reserve changes, including same-step prints. dx=float(rng.uniform(.001,.5)); buy=bool(rng.integers(0,2));k=x*y nx=x+dx if buy else x-dx;ny=k/nx;dy=abs(ny-y);x,y=nx,ny vm.insert_account_storage(addr,31,pwad) call('c2babb57',[buy,int(dx*W),int(dy*W),ts,int(x*W),int(y*W)]) assert vm.storage(addr,5)==pwad,(i,vm.storage(addr,5),pwad) max_gas=max(max_gas,vm.result.gas_used) summary=dict(n_calls=512,pHat_exact=True,max_after_swap_gas=max_gas,gas_limit=250000) Path('scratch/oracle_check.json').write_text(json.dumps(summary,indent=2));print(summary) ``` ```python from pathlib import Path import json import amm_sim_rs from amm_competition.evm.compiler import SolidityCompiler from amm_competition.evm.baseline import get_vanilla_bytecode_and_abi from measure import configs source='''// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {AMMStrategyBase} from "./AMMStrategyBase.sol"; import {TradeInfo} from "./IAMMStrategy.sol"; contract Strategy is AMMStrategyBase { function afterInitialize(uint256,uint256) external override returns(uint256,uint256) { return (4*BPS,4*BPS); } function afterSwap(TradeInfo calldata) external override returns(uint256,uint256) { return (slots[31]/100000,slots[31]/100000); } function getName() external pure override returns(string memory) { return "oracle-probe"; } }''' r=SolidityCompiler().compile(source);assert r.success,r.errors cfgs=configs(range(10000,10016)) for c in cfgs:c.n_steps=512 batch=amm_sim_rs.run_batch(r.bytecode,get_vanilla_bytecode_and_abi()[0],cfgs,8) changed=0 for result in batch.results: assert all(v==0 for v in result.hook_errors.values()) prev=(.0004,.0004) for step in result.steps: fees=tuple(step.fees['submission']) if fees!=prev: expect=(int(step.fair_price*1e18)//100000)/1e18 assert abs(fees[0]-expect)<1e-18 and abs(fees[1]-expect)<1e-18 changed+=1 prev=fees assert changed>1000 summary=dict(n_seeds=16,n_steps=512,changed_step_quotes=changed,current_price_checks_passed=True) Path('scratch/plumbing_check.json').write_text(json.dumps(summary,indent=2));print(summary) ``` # paired Observed locally; this is not a leaderboard submission. Confirmation was run once after freezing the candidate, on all 2,000 requested fresh seeds. `worst` is the minimum per-seed difference. ```json { "n": 2000, "mean_diff": -5.400406174318158, "se": 0.05676135087310331, "worst": -14.075173863908617 } ``` Worst seed: `2540824`; oracle edge `596.116504057375`, NP2 edge `610.191677921284`. SE is sample standard deviation of paired differences (`ddof=1`) divided by `sqrt(2000)`. No candidate-versus-NP2 shared-market call was used. The configuration draws follow [MatchRunner._build_configs](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/competition/match.py) and the [canonical settings](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/competition/config.py): size U(19,21), rate U(0.6,1.0), sigma U(0.000882,0.001008), in that order; 10,000 steps, initial price 100, reserves (100,10000), drift 0, dt 1, retail buy probability 0.5 and log-size sigma 1.2. Price/retail seeds follow the [pinned engine](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_sim_rs/src/simulation/engine.rs). Vanilla uses [30 bps](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/contracts/src/VanillaStrategy.sol). NumPy version: 2.5.4; solc 0.8.24, optimizer 200 runs, [viaIR enabled](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/evm/compiler.py), Paris target. Local checks: - Original engine, seeds 0..999: CH mean `525.5258821780834` (required 525.525882178); NP2 mean `526.1155722459862` (required rounded 526.1156). - Patched/unpatched parity: CH and NP2 each matched all 16 original per-seed edges and mean fee pairs exactly on seeds 0..15. - Separate storage/gas check: 512 afterSwap calls, both sides, same-step prints and timestamp gaps; persisted slot 5 equaled injected slot 31 after every call. All returned fees were bounded. Max gas `133649` versus [250,000 hook limit](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_sim_rs/src/evm/strategy.rs). This check validates plumbing, not performance. - Engine oracle probe: 16 seeds (10000..10015), 512 steps each; all 6772 changed fee quotes matched that step's fair price after the expected WAD conversion. - All 4,000 confirmation submission simulations recorded zero afterSwap failures. Edge attribution reconciled to total edge within 1e-8 per simulation. # decomposition Confirmation seeds **2540000..2541999**, 2,000 simulations per strategy, each against Vanilla. Edge units are Y per simulation; arb edge is negative arbitrageur profit and retail edge uses the original engine's [fair-price mark-to-market formulas](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_sim_rs/src/simulation/engine.rs). Fees are arithmetic time averages of end-of-step bid/ask quotes across 10,000 steps, then averaged over seeds, in bps; they are not volume-weighted. | Strategy | Total edge | Arb edge | Retail edge | Mean bid bps | Mean ask bps | |---|---:|---:|---:|---:|---:| | Oracle pHat | 521.806277586905 | -16.069507064707 | 537.875784651612 | 43.390625364 | 41.212425855 | | NP2 | 527.206683761223 | -31.635915591497 | 558.842599352720 | 36.501169808 | 35.335664276 | | Difference | -5.400406174318 | 15.566408526790 | -20.966814701108 | 6.889455556 | 5.876761579 | The oracle reduces arbitrage losses, but the reduction in retail edge is larger, giving a negative net difference. With the existing feedback and fee rules, perfect pHat at every print does not improve NP2. This is an oracle ablation under frozen fee logic, not a mathematical upper bound over arbitrary estimators or fee policies. The decomposition is descriptive; it does not isolate a causal contribution from any one fee term. Contiguous 250-seed summaries of the same single confirmation run (no reruns): | Seeds | n | Mean difference | SE | Minimum difference | |---|---:|---:|---:|---:| | 2540000..2540249 | 250 | -5.300011863107 | 0.167570158474 | -14.011792098192 | | 2540250..2540499 | 250 | -5.254721064340 | 0.157552927273 | -12.112172399208 | | 2540500..2540749 | 250 | -5.443908000206 | 0.153906349920 | -11.656170949637 | | 2540750..2540999 | 250 | -5.395924441236 | 0.172381826099 | -14.075173863909 | | 2541000..2541249 | 250 | -5.284706281048 | 0.145848076495 | -11.643190130926 | | 2541250..2541499 | 250 | -5.472915365777 | 0.162454393867 | -12.397345218655 | | 2541500..2541749 | 250 | -5.698693877895 | 0.164408144283 | -13.969680576619 | | 2541750..2541999 | 250 | -5.352368500937 | 0.159215929242 | -13.148710120906 | Audit payload: the 2,000 observed paired differences in ascending seed order (2540000..2541999), IEEE-754 little-endian float64, gzip compressed and base64 encoded. Uncompressed SHA-256: `525de735da9fde3ccb8ab9c3cf084ee5a6117cf4abe7d538e75cb14113655349`. This contains differences only; the decomposition above comes from the instrumented edge maps. ```base64 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 w3wOf/mAnDLitzzn0kZ9SfI/5PxRloA+AAA= ``` Decode the preceding block into `payload` and check the statistics with the Python standard library: ```python import base64, gzip, struct, statistics, math d = struct.unpack("<2000d", gzip.decompress(base64.b64decode(payload))) print(dict(n=len(d), mean_diff=statistics.fmean(d), se=statistics.stdev(d)/math.sqrt(len(d)), worst=min(d))) ``` # blocked ```json null ``` # next These are untested proposals, not measured gains; the observed net bound is negative. 1. Use routing-implied normalizer prices as noisy observations in a fair-price filter, conditioned on interior versus clipped splits; retain uncertainty rather than treating them as exact prices. 2. Keep separate states for price-center estimation and volatility/toxicity estimation so improved centering does not automatically change every risk signal. 3. Classify likely arbitrage versus retail prints using direction, print order and size, then test a partial-confidence blend with the existing estimator on new tuning and holdout seeds.