// SPDX-License-Identifier: MIT pragma solidity 0.8.26; import "forge-std/Test.sol"; import "../src/Probe.sol"; interface IPM { function unlock(bytes calldata) external returns (bytes memory); function swap(PoolKey memory, SwapParams memory, bytes calldata) external returns (int256); function settle() external payable returns (uint256); function sync(address) external; function take(address, address, uint256) external; } // Swapper sungguhan: swap lalu benar-benar melunasi & mengambil token (bukan simulasi) contract Swapper { IPM constant PM = IPM(0x8366a39CC670B4001A1121B8F6A443A643e40951); function run(PoolKey calldata key, bool z, uint256 amountIn) external payable returns (uint256 out) { bytes memory r = PM.unlock(abi.encode(key, z, amountIn)); out = abi.decode(r, (uint256)); } function unlockCallback(bytes calldata data) external returns (bytes memory) { (PoolKey memory key, bool z, uint256 amt) = abi.decode(data, (PoolKey, bool, uint256)); uint160 lim = z ? 4295128740 : 1461446703485210103287273052203988822378723970341; int256 d = PM.swap(key, SwapParams(z, -int256(amt), lim), ""); int128 a0 = int128(d >> 128); int128 a1 = int128(d); (address cin, address cout, int128 pay, int128 get) = z ? (key.currency0, key.currency1, a0, a1) : (key.currency1, key.currency0, a1, a0); uint256 owe = uint256(int256(-pay)); if (cin == address(0)) PM.settle{value: owe}(); else { PM.sync(cin); IERC20(cin).transfer(address(PM), owe); PM.settle(); } PM.take(cout, address(this), uint256(int256(get))); return abi.encode(uint256(int256(get))); } receive() external payable {} } contract MatchTest is Test { Probe probe; Swapper swapper; address constant ETH = address(0); address constant USDG = 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168; function setUp() public { probe = new Probe(); swapper = new Swapper(); vm.deal(address(swapper), 100 ether); } function _check(PoolKey memory key, uint256 amt) internal { (bool ok, int128 a0, int128 a1,) = probe.quote(key, true, -int256(amt)); assertTrue(ok, "probe failed"); uint256 quoted = uint256(int256(a1)); uint256 got = swapper.run(key, true, amt); emit log_named_uint("quoted", quoted); emit log_named_uint("executed", got); assertEq(got, quoted, "simulation must equal execution to the wei"); a0; } // bolak-balik dengan pelunasan sungguhan (tokenTrip) vs beli + jual yang benar-benar dieksekusi function _checkTrip(PoolKey memory key, uint256 amt) internal { vm.deal(address(probe), 10 ether); (uint8 st, , uint256 rec, uint256 paid, uint256 ret,) = probe.tokenTrip(key, true, amt); assertEq(st, 4, "tokenTrip did not finish"); uint256 bought = swapper.run(key, true, amt); uint256 back = swapper.run(key, false, bought); emit log_named_uint("trip received (sim)", rec); emit log_named_uint("bought (real)", bought); emit log_named_uint("trip returned (sim)", ret); emit log_named_uint("sold back for (real)", back); assertEq(rec, bought, "received must equal real buy"); assertEq(paid, rec, "no transfer tax expected on this token"); assertEq(ret, back, "returned must equal real sell"); } function test_tokenTrip_ETH_USDG() public { _checkTrip(PoolKey(ETH, USDG, 100, 1, address(0)), 0.01 ether); } function test_tokenTrip_PONS() public { address token = vm.envOr("PONS_TOKEN", address(0)); if (token == address(0)) return; _checkTrip(PoolKey(ETH, token, 0, 200, 0xE5e702641Ea86F4ae6cC3cDaeD2B886f976Be044), 0.01 ether); } function test_plainPool_ETH_USDG() public { _check(PoolKey(ETH, USDG, 100, 1, address(0)), 0.05 ether); } function test_hookPool_PONS() public { // pool Pons V2 (hook mengambil fee lewat afterSwap); alamat token diisi lewat env address token = vm.envOr("PONS_TOKEN", address(0)); if (token == address(0)) return; _check(PoolKey(ETH, token, 0, 200, 0xE5e702641Ea86F4ae6cC3cDaeD2B886f976Be044), 0.01 ether); } }