|
| 1 | +// SPDX-License-Identifier: CC0-1.0 |
| 2 | + |
| 3 | +pragma solidity 0.8.15; |
| 4 | + |
| 5 | +import "forge-std/Test.sol"; |
| 6 | +import "../../shared/Env.t.sol"; |
| 7 | +import "../../../src/bridge/polygon/PolygonBobToken.sol"; |
| 8 | +import "../../../src/proxy/EIP1967Proxy.sol"; |
| 9 | + |
| 10 | +interface IRootChainManager { |
| 11 | + function registerPredicate(bytes32 tokenType, address predicate) external; |
| 12 | + function mapToken(address rootToken, address childToken, bytes32 tokenType) external; |
| 13 | + function depositFor(address user, address rootToken, bytes memory depositData) external; |
| 14 | +} |
| 15 | + |
| 16 | +interface IChildChainManager { |
| 17 | + function onStateReceive(uint256, bytes calldata data) external; |
| 18 | +} |
| 19 | + |
| 20 | +interface IBobPredicate { |
| 21 | + function exitTokens(address, address rootToken, bytes memory log) external; |
| 22 | +} |
| 23 | + |
| 24 | +contract PolygonPoSBridge is Test { |
| 25 | + event LockedERC20( |
| 26 | + address indexed depositor, address indexed depositReceiver, address indexed rootToken, uint256 amount |
| 27 | + ); |
| 28 | + event Withdrawn(address indexed account, uint256 value); |
| 29 | + |
| 30 | + BobToken bobMainnet; |
| 31 | + PolygonBobToken bobPolygon; |
| 32 | + |
| 33 | + uint256 mainnetFork; |
| 34 | + uint256 polygonFork; |
| 35 | + |
| 36 | + address bobPredicate; |
| 37 | + address rootChainManager = 0xA0c68C638235ee32657e8f720a23ceC1bFc77C77; |
| 38 | + address rootChainManagerOwner = 0xFa7D2a996aC6350f4b56C043112Da0366a59b74c; |
| 39 | + address childChainManager = 0xA6FA4fB5f76172d178d61B04b0ecd319C5d1C0aa; |
| 40 | + address stateSyncer = 0x0000000000000000000000000000000000001001; |
| 41 | + |
| 42 | + function setUp() public { |
| 43 | + mainnetFork = vm.createFork(forkRpcUrlMainnet); |
| 44 | + polygonFork = vm.createFork(forkRpcUrlPolygon); |
| 45 | + |
| 46 | + vm.selectFork(mainnetFork); |
| 47 | + |
| 48 | + EIP1967Proxy bobProxy = new EIP1967Proxy(address(this), mockImpl, ""); |
| 49 | + BobToken bobImpl = new BobToken(address(bobProxy)); |
| 50 | + bobProxy.upgradeTo(address(bobImpl)); |
| 51 | + bobMainnet = BobToken(address(bobProxy)); |
| 52 | + |
| 53 | + bobMainnet.updateMinter(address(this), true, false); |
| 54 | + |
| 55 | + vm.selectFork(polygonFork); |
| 56 | + |
| 57 | + bobProxy = new EIP1967Proxy(address(this), mockImpl, ""); |
| 58 | + PolygonBobToken bobImpl2 = new PolygonBobToken(address(bobProxy)); |
| 59 | + bobProxy.upgradeTo(address(bobImpl2)); |
| 60 | + bobPolygon = PolygonBobToken(address(bobProxy)); |
| 61 | + |
| 62 | + bobPolygon.updateMinter(address(this), true, false); |
| 63 | + bobPolygon.updateMinter(childChainManager, true, true); |
| 64 | + |
| 65 | + vm.selectFork(mainnetFork); |
| 66 | + |
| 67 | + vm.etch(rootChainManagerOwner, ""); |
| 68 | + vm.startPrank(rootChainManagerOwner); |
| 69 | + bytes memory predicateCode = bytes.concat( |
| 70 | + vm.getCode("out/PolygonERC20MintBurnPredicate.sol/PolygonERC20MintBurnPredicate.json"), |
| 71 | + abi.encode(rootChainManager) |
| 72 | + ); |
| 73 | + assembly { |
| 74 | + sstore(bobPredicate.slot, create(0, add(predicateCode, 32), mload(predicateCode))) |
| 75 | + } |
| 76 | + IRootChainManager(rootChainManager).registerPredicate(keccak256("BOB"), bobPredicate); |
| 77 | + vm.recordLogs(); |
| 78 | + IRootChainManager(rootChainManager).mapToken(address(bobMainnet), address(bobPolygon), keccak256("BOB")); |
| 79 | + vm.stopPrank(); |
| 80 | + _syncState(); |
| 81 | + |
| 82 | + bobMainnet.updateMinter(bobPredicate, true, true); |
| 83 | + |
| 84 | + vm.label(address(bobMainnet), "BOB"); |
| 85 | + vm.label(address(bobPolygon), "BOB"); |
| 86 | + } |
| 87 | + |
| 88 | + function _syncState() internal { |
| 89 | + uint256 curFork = vm.activeFork(); |
| 90 | + Vm.Log[] memory logs = vm.getRecordedLogs(); |
| 91 | + vm.selectFork(polygonFork); |
| 92 | + for (uint256 i = 0; i < logs.length; i++) { |
| 93 | + if (logs[i].topics[0] == bytes32(0x103fed9db65eac19c4d870f49ab7520fe03b99f1838e5996caf47e9e43308392)) { |
| 94 | + vm.prank(stateSyncer); |
| 95 | + IChildChainManager(childChainManager).onStateReceive(0, abi.decode(logs[i].data, (bytes))); |
| 96 | + } |
| 97 | + } |
| 98 | + vm.selectFork(curFork); |
| 99 | + } |
| 100 | + |
| 101 | + function testBridgeToPolygon() public { |
| 102 | + vm.selectFork(mainnetFork); |
| 103 | + |
| 104 | + bobMainnet.mint(user1, 100 ether); |
| 105 | + |
| 106 | + vm.startPrank(user1); |
| 107 | + bobMainnet.approve(bobPredicate, 10 ether); |
| 108 | + vm.expectEmit(true, true, true, true, bobPredicate); |
| 109 | + emit LockedERC20(user1, user2, address(bobMainnet), 10 ether); |
| 110 | + vm.recordLogs(); |
| 111 | + IRootChainManager(rootChainManager).depositFor(user2, address(bobMainnet), abi.encode(10 ether)); |
| 112 | + vm.stopPrank(); |
| 113 | + |
| 114 | + _syncState(); |
| 115 | + |
| 116 | + assertEq(bobMainnet.totalSupply(), 90 ether); |
| 117 | + assertEq(bobMainnet.balanceOf(user1), 90 ether); |
| 118 | + |
| 119 | + vm.selectFork(polygonFork); |
| 120 | + |
| 121 | + assertEq(bobPolygon.totalSupply(), 10 ether); |
| 122 | + assertEq(bobPolygon.balanceOf(user2), 10 ether); |
| 123 | + } |
| 124 | + |
| 125 | + function testBridgeFromPolygon() public { |
| 126 | + vm.selectFork(polygonFork); |
| 127 | + |
| 128 | + bobPolygon.mint(user1, 100 ether); |
| 129 | + |
| 130 | + vm.prank(user1); |
| 131 | + vm.expectEmit(true, false, false, true, address(bobPolygon)); |
| 132 | + emit Withdrawn(user1, 10 ether); |
| 133 | + bobPolygon.withdraw(10 ether); |
| 134 | + |
| 135 | + vm.selectFork(mainnetFork); |
| 136 | + |
| 137 | + // cast --to-rlp '["<token>", ["<topic0>", "<topic1>"], "<data>"]' |
| 138 | + bytes memory logRLP = bytes.concat( |
| 139 | + hex"f87a94", |
| 140 | + abi.encodePacked(address(bobPolygon)), |
| 141 | + hex"f842a0", |
| 142 | + keccak256("Withdrawn(address,uint256)"), |
| 143 | + hex"a0", |
| 144 | + abi.encode(user1), |
| 145 | + hex"a0", |
| 146 | + abi.encode(10 ether) |
| 147 | + ); |
| 148 | + |
| 149 | + vm.etch(rootChainManager, ""); |
| 150 | + vm.prank(rootChainManager); |
| 151 | + IBobPredicate(bobPredicate).exitTokens(user1, address(bobMainnet), logRLP); |
| 152 | + |
| 153 | + assertEq(bobMainnet.totalSupply(), 10 ether); |
| 154 | + assertEq(bobMainnet.balanceOf(user1), 10 ether); |
| 155 | + |
| 156 | + vm.selectFork(polygonFork); |
| 157 | + |
| 158 | + assertEq(bobPolygon.totalSupply(), 90 ether); |
| 159 | + assertEq(bobPolygon.balanceOf(user1), 90 ether); |
| 160 | + } |
| 161 | +} |
0 commit comments