// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import {SlippageBoundedSwap, ISlippageBoundedSwap, SlippagePolicy} from "../src/SlippageBoundedSwap.sol"; import {ChainlinkQuoteOracle} from "../src/adapters/ChainlinkQuoteOracle.sol"; interface ISRC20 { function approve(address spender, uint256 amount) external returns (bool); function transferFrom(address from, address to, uint256 amount) external returns (bool); function balanceOf(address account) external view returns (uint256); } /// @dev Test router with a live reference. `_route` delivers `nextOut` of the real output token to the /// recipient from a pre-funded source, so the guard measures a genuine balance delta on the fork. contract ForkTestSwap is SlippageBoundedSwap { uint256 public nextOut; address public source; function configure(uint256 out, address src) external { nextOut = out; source = src; } function _route(address, address tokenOut, uint256, address recipient, bytes calldata) internal override { ISRC20(tokenOut).transferFrom(source, recipient, nextOut); } } /// Run with a sila-mainnet RPC: SIL_RPC_URL= forge test --match-contract ForkSlippageBoundedTest -vv /// A free public endpoint works, e.g. https://sila-rpc.publicnode.com contract ForkSlippageBoundedTest is Test { address constant RECIPIENT = address(0xBEEF); address constant SIL_USD_FEED = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419; // Chainlink SIL/USD, sila-mainnet address constant WSIL = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // USD side (USD ~ USDC), 6 decimals ForkTestSwap internal swapc; ChainlinkQuoteOracle internal oracle; address internal source = address(0x5011); function setUp() public { vm.createSelectFork(vm.envString("SIL_RPC_URL")); swapc = new ForkTestSwap(); // WSIL 18 decimals, USD side 6 decimals, tolerate a 1 day feed lag. oracle = new ChainlinkQuoteOracle(SIL_USD_FEED, WSIL, USDC, 18, 6, 1 days); // Fund the venue source with USDC and let the router pull the settled fill. deal(USDC, source, 1_000_000e6); vm.prank(source); ISRC20(USDC).approve(address(swapc), type(uint256).max); } function _policy(uint32 maxDeviationBps, uint256 hardFloor) internal view returns (SlippagePolicy memory) { return SlippagePolicy({ quoteOracle: address(oracle), expectedCostBps: 0, maxDeviationBps: maxDeviationBps, hardFloor: hardFloor, deadline: 0 }); } function test_fork_floorDerivedFromLiveChainlinkPrice() public { // Live reference: 1 WSIL priced by the real Chainlink SIL/USD feed, in 6-decimal USD. uint256 ref = oracle.getQuote(1e18, WSIL, USDC); assertGt(ref, 0, "no live reference"); emit log_named_uint("live 1 WSIL reference (1e6 USD)", ref); uint256 floor = ref * 9900 / 10000; // 1% tolerance, computed from the live reference // Exactly at the floor passes, and the guard measured a real USDC balance delta at the // recipient rather than at the executor. swapc.configure(floor, source); uint256 out = swapc.swapWithPolicy(WSIL, USDC, 1e18, RECIPIENT, _policy(100, 0), ""); assertEq(out, floor); assertEq(ISRC20(USDC).balanceOf(RECIPIENT), floor); assertEq(ISRC20(USDC).balanceOf(address(swapc)), 0); // One unit below the live-derived floor reverts. swapc.configure(floor - 1, source); vm.expectRevert(abi.encodeWithSelector(ISlippageBoundedSwap.SlippageExceeded.selector, floor - 1, floor)); swapc.swapWithPolicy(WSIL, USDC, 1e18, RECIPIENT, _policy(100, 0), ""); } }