示例#1
0
def main():
    admin = accounts[0]

    alice = accounts[1]
    bob = accounts[2]
    usdt = interface.IERC20Ex('0xdac17f958d2ee523a2206206994597c13d831ec7')
    weth = interface.IERC20Ex('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')
    usdc = interface.IERC20Ex('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48')

    lp = interface.IERC20Ex('0x06da0fd433C1A5d7a4faa01111c044910A184553')
    lp_usdc = interface.IERC20Ex('0x3041cbd36888becc7bbcbc0045e3b1f144466f5f')
    crusdt = interface.ICErc20('0x797AAB1ce7c01eB727ab980762bA88e7133d2157')
    crusdc = interface.ICErc20('0x44fbebd2f576670a6c33f6fc0b00aa8c5753b322')

    sushi = interface.IERC20('0x6b3595068778dd592e39a122f4f5a5cf09c90fe2')

    # sushiswap router
    router = interface.IUniswapV2Router02(
        '0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f')

    chef = accounts.at(
        '0xc2edad668740f1aa35e4d8f227fb8e17dca888cd', force=True)
    wchef = WMasterChef.deploy(chef, {'from': admin})

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([usdt, weth, usdc], [2**112 // 700, 2**112, 2**112 // 700])

    uniswap_oracle = UniswapV2Oracle.deploy(simple_oracle, {'from': admin})
    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20, wchef], True, {'from': admin})
    core_oracle.setRoute(
        [usdt, weth, lp, usdc, lp_usdc],
        [simple_oracle, simple_oracle, uniswap_oracle, simple_oracle, uniswap_oracle],
        {'from': admin},
    )
    oracle.setOracles(
        [usdt, weth, lp, usdc, lp_usdc],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)
    homora.addBank(usdt, crusdt, {'from': admin})
    homora.addBank(usdc, crusdc, {'from': admin})

    # setup initial funds to alice
    mint_tokens(usdt, alice)
    mint_tokens(weth, alice)
    mint_tokens(usdc, alice)

    # check alice's funds
    print(f'Alice usdt balance {usdt.balanceOf(alice)}')
    print(f'Alice weth balance {weth.balanceOf(alice)}')

    # Steal some LP from the staking pool
    mint_tokens(lp, alice)
    mint_tokens(lp, bob)

    # set approval
    usdt.approve(homora, 2**256-1, {'from': alice})
    usdt.approve(crusdt, 2**256-1, {'from': alice})
    usdc.approve(homora, 2**256-1, {'from': alice})
    usdc.approve(crusdc, 2**256-1, {'from': alice})
    weth.approve(homora, 2**256-1, {'from': alice})
    lp.approve(homora, 2**256-1, {'from': alice})
    lp.approve(chef, 2**256-1, {'from': bob})

    sushiswap_spell = SushiswapSpellV1.deploy(
        homora, werc20, router, wchef, {'from': admin})
    # first time call to reduce gas
    sushiswap_spell.getPair(weth, usdt, {'from': admin})

    # whitelist spell in bank
    homora.setWhitelistSpells([sushiswap_spell], [True], {'from': admin})

    # whitelist token in bank
    homora.setWhitelistTokens([usdt], [True], {'from': admin})

    # whitelist lp in spell
    sushiswap_spell.setWhitelistLPTokens([lp], [True], {'from': admin})

    #####################################################################################
    print('=========================================================================')
    print('Case 1. add liquidity first time')

    prevABal = usdt.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_chef = lp.balanceOf(chef)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    usdt_amt = 10 * 10**6
    weth_amt = 2 * 10**18
    lp_amt = 0
    borrow_usdt_amt = 10 * 10**6
    borrow_weth_amt = 0

    real_borrow_usdt_amt = borrow_usdt_amt

    pid = 0

    tx = homora.execute(
        0,
        sushiswap_spell,
        sushiswap_spell.addLiquidityWMasterChef.encode_input(
            usdt,  # token 0
            weth,  # token 1
            [usdt_amt,  # supply USDT
             weth_amt,   # supply WETH
             lp_amt,  # supply LP
             borrow_usdt_amt,  # borrow USDT
             borrow_weth_amt,  # borrow WETH
             0,  # borrow LP tokens
             0,  # min USDT
             0],  # min WETH
            pid,
        ),
        {'from': alice}
    )

    curABal = usdt.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_chef = lp.balanceOf(chef)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(sushiswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, totalDebt, totalShare = homora.getBankInfo(usdt)
    print('bank usdt totalDebt', totalDebt)
    print('bank usdt totalShare', totalShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('chef prev LP balance', prevLPBal_chef)
    print('chef cur LP balance', curLPBal_chef)

    print('prev usdt res', prevARes)
    print('cur usdt res', curARes)

    print('prev weth res', prevBRes)
    print('cur weth res', curBRes)

    # alice
    assert almostEqual(curABal - prevABal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curBBal - prevBBal, -weth_amt), 'incorrect WETH amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert usdt.balanceOf(sushiswap_spell) == 0, 'non-zero spell USDT balance'
    assert weth.balanceOf(sushiswap_spell) == 0, 'non-zero spell WETH balance'
    assert lp.balanceOf(sushiswap_spell) == 0, 'non-zero spell LP balance'
    assert totalDebt == borrow_usdt_amt

    # check balance and pool reserves
    assert curABal - prevABal - borrow_usdt_amt == - \
        (curARes - prevARes), 'not all USDT tokens go to LP pool'
    assert almostEqual(curBBal - prevBBal - borrow_weth_amt, -
                       (curBRes - prevBRes)), 'not all WETH tokens go to LP pool'

    _, _, collId, collSize = homora.getPositionInfo(1)
    print('collSize', collSize)

    # staking directly
    prevSushi = sushi.balanceOf(bob)
    pid = 0
    print('bob lp balance', interface.IERC20Ex(lp).balanceOf(bob))
    tx = interface.IMasterChef(chef).deposit(pid, collSize, {'from': bob})

    chain.sleep(20000)

    prevAliceSushiBalance = sushi.balanceOf(alice)
    print('Alice sushi balance', prevAliceSushiBalance)

    #####################################################################################
    print('=========================================================================')
    print('Case 2. add liquidity (failed tx desired)')

    usdc_amt = 10 * 10**6
    weth_amt = 10**18
    lp_amt = 0
    borrow_usdc_amt = 0
    borrow_weth_amt = 0

    pid = 0

    try:
        tx = homora.execute(
            1,
            sushiswap_spell,
            sushiswap_spell.addLiquidityWMasterChef.encode_input(
                usdc,  # token 0
                weth,  # token 1
                [usdc_amt,  # supply USDC
                 weth_amt,   # supply WETH
                 lp_amt,  # supply LP
                 borrow_usdc_amt,  # borrow USDC
                 borrow_weth_amt,  # borrow WETH
                 0,  # borrow LP tokens
                 0,  # min USDC
                 0],  # min WETH
                pid,
            ),
            {'from': alice}
        )
        assert False, 'tx not fail'
    except VirtualMachineError:
        pass

    #####################################################################################
    print('=========================================================================')
    print('Case 3. remove liquidity (failed tx desired)')

    lp_take_amt = collSize
    lp_want = 0
    usdc_repay = 0
    weth_repay = 0

    try:
        tx = homora.execute(
            1,
            sushiswap_spell,
            sushiswap_spell.removeLiquidityWMasterChef.encode_input(
                usdc,  # token 0
                weth,  # token 1
                [lp_take_amt,  # take out LP tokens
                 lp_want,   # withdraw LP tokens to wallet
                 usdc_repay,  # repay USDC
                 weth_repay,  # repay WETH
                 0,  # repay LP tokens
                 0,  # min USDC
                 0],  # min WETH
            ),
            {'from': alice}
        )
        assert False, 'tx not failed'
    except VirtualMachineError:
        pass

    #####################################################################################
    print('=========================================================================')
    print('Case 4. remove liquidity')

    prevABal = usdt.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_chef = lp.balanceOf(chef)
    prevETHBal = alice.balance()

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    lp_take_amt = collSize
    lp_want = 0
    usdt_repay = 2**256-1
    weth_repay = 0

    pid = 0

    tx = homora.execute(
        1,
        sushiswap_spell,
        sushiswap_spell.removeLiquidityWMasterChef.encode_input(
            usdt,  # token 0
            weth,  # token 1
            [lp_take_amt,  # take out LP tokens
             lp_want,   # withdraw LP tokens to wallet
             usdt_repay,  # repay USDT
             weth_repay,  # repay WETH
             0,  # repay LP tokens
             0,  # min USDT
             0],  # min WETH
        ),
        {'from': alice}
    )

    curABal = usdt.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_chef = lp.balanceOf(chef)
    curETHBal = alice.balance()

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(sushiswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, totalDebt, totalShare = homora.getBankInfo(usdt)
    print('bank usdt totalDebt', totalDebt)
    print('bank usdt totalShare', totalShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('chef prev LP balance', prevLPBal_chef)
    print('chef cur LP balance', curLPBal_chef)

    print('prev usdt res', prevARes)
    print('cur usdt res', curARes)

    print('prev weth res', prevBRes)
    print('cur weth res', curBRes)

    # alice
    assert almostEqual(curBBal - prevBBal, 0), 'incorrect WETH amt'
    assert almostEqual(curLPBal - prevLPBal, lp_want), 'incorrect LP amt'

    # chef
    assert almostEqual(curLPBal_chef - prevLPBal_chef, -
                       lp_take_amt), 'incorrect chef LP amt'

    # spell
    assert usdt.balanceOf(sushiswap_spell) == 0, 'non-zero spell USDT balance'
    assert weth.balanceOf(sushiswap_spell) == 0, 'non-zero spell WETH balance'
    assert lp.balanceOf(sushiswap_spell) == 0, 'non-zero spell LP balance'

    # check balance and pool reserves
    assert almostEqual(curABal - prevABal + real_borrow_usdt_amt, -
                       (curARes - prevARes)), 'inconsistent USDT from withdraw'
    assert almostEqual(curBBal - prevBBal,
                       0), 'inconsistent WETH from withdraw'
    assert almostEqual(curETHBal - prevETHBal + weth_repay, -
                       (curBRes - prevBRes)), 'inconsistent ETH from withdraw'

    curAliceSushiBalance = sushi.balanceOf(alice)
    print('Alice sushi balance', curAliceSushiBalance)
    receivedSushi = curAliceSushiBalance - prevAliceSushiBalance
    print('received sushi', receivedSushi)

    # check with staking directly
    pid = 0
    tx = interface.IMasterChef(chef).withdraw(pid, collSize, {'from': bob})
    receivedSushiFromStaking = sushi.balanceOf(bob) - prevSushi
    print('receivedSushiFromStaking', receivedSushiFromStaking)
    assert almostEqual(receivedSushi, receivedSushiFromStaking)

    #####################################################################################
    print('=========================================================================')
    print('Case 5. add & remove all LP')

    lp_amt = 10 * 10**18
    prevLPBal = lp.balanceOf(alice)
    pid = 0
    tx = homora.execute(
        0,
        sushiswap_spell,
        sushiswap_spell.addLiquidityWMasterChef.encode_input(
            usdt,  # token 0
            weth,  # token 1
            [0,  # supply USDT
             0,   # supply WETH
             lp_amt,  # supply LP
             0,  # borrow USDT
             0,  # borrow WETH
             0,  # borrow LP tokens
             0,  # min USDT
             0],  # min WETH
            pid
        ),
        {'from': alice}
    )

    tx = homora.execute(
        2,
        sushiswap_spell,
        sushiswap_spell.removeLiquidityWMasterChef.encode_input(
            usdt,  # token 0
            weth,  # token 1
            [2**256-1,  # take out LP tokens
             lp_amt,   # withdraw LP tokens to wallet
             0,  # repay USDT
             0,   # repay WETH
             0,   # repay LP
             0,   # min USDT
             0],  # min WETH
        ),
        {'from': alice}
    )

    curLPBal = lp.balanceOf(alice)

    assert prevLPBal == curLPBal, 'incorrect LP Balance'
示例#2
0
def main():
    admin = accounts[0]

    alice = accounts[1]
    bob = accounts[2]
    renbtc = interface.IERC20Ex('0xeb4c2781e4eba804ce9a9803c67d0893436bb27d')
    wbtc = interface.IERC20Ex('0x2260fac5e5542a773aa44fbcfedf7c193bc2c599')
    eurs = interface.IERC20Ex('0xdB25f211AB05b1c97D595516F45794528a807ad8')
    seur = interface.IERC20Ex('0xD71eCFF9342A5Ced620049e616c5035F1dB98620')

    lp = interface.IERC20Ex('0x49849C98ae39Fff122806C06791Fa73784FB3675')
    pool = interface.ICurvePool('0x93054188d876f558f4a66B2EF1d97d16eDf0895B')
    lp_eurs = interface.IERC20Ex('0x194eBd173F6cDacE046C53eACcE9B953F28411d1')
    pool_eurs = interface.ICurvePool(
        '0x0Ce6a5fF5217e38315f87032CF90686C96627CAA')
    registry = interface.ICurveRegistry(
        '0x7d86446ddb609ed0f5f8684acf30380a356b2b4c')

    crrenbtc = MockCErc20.deploy(renbtc, {'from': admin})
    crwbtc = MockCErc20.deploy(wbtc, {'from': admin})
    creurs = MockCErc20.deploy(eurs, {'from': admin})
    crseur = MockCErc20.deploy(seur, {'from': admin})

    gauge = accounts.at('0xB1F2cdeC61db658F091671F5f199635aEF202CAC',
                        force=True)
    wgauge = WLiquidityGauge.deploy(
        registry, '0xD533a949740bb3306d119CC777fa900bA034cd52',
        {'from': admin})
    crv = interface.IERC20Ex(wgauge.crv())

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx(
        [renbtc, wbtc, eurs, seur],
        [2**112 * 30, 2**112 * 30, 2**112 // 700, 2**112 // 700])

    curve_oracle = CurveOracle.deploy(simple_oracle, registry, {'from': admin})
    curve_oracle.registerPool(lp)  # update pool info
    curve_oracle.registerPool(lp_eurs)  # update pool info

    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20, wgauge], True, {'from': admin})
    core_oracle.setRoute(
        [renbtc, wbtc, lp, eurs, seur, lp_eurs],
        [
            simple_oracle, simple_oracle, curve_oracle, simple_oracle,
            simple_oracle, curve_oracle
        ],
        {'from': admin},
    )
    oracle.setOracles(
        [renbtc, wbtc, lp, eurs, seur, lp_eurs],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    # initialize
    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)

    # add bank
    homora.addBank(renbtc, crrenbtc, {'from': admin})
    homora.addBank(wbtc, crwbtc, {'from': admin})
    homora.addBank(eurs, creurs, {'from': admin})
    homora.addBank(seur, crseur, {'from': admin})

    # setup initial funds to alice
    mint_tokens(renbtc, alice)
    mint_tokens(wbtc, alice)
    mint_tokens(eurs, alice)
    mint_tokens(seur, alice)

    mint_tokens(wbtc, crwbtc)

    # check alice's funds
    print(f'Alice renbtc balance {renbtc.balanceOf(alice)}')
    print(f'Alice wbtc balance {wbtc.balanceOf(alice)}')
    print(f'Alice eurs balance {eurs.balanceOf(alice)}')
    print(f'Alice seur balance {seur.balanceOf(alice)}')

    # steal some LP from the staking pool
    mint_tokens(lp, alice)
    mint_tokens(lp, bob)
    mint_tokens(lp_eurs, alice)

    # set approval
    renbtc.approve(homora, 2**256 - 1, {'from': alice})
    renbtc.approve(crrenbtc, 2**256 - 1, {'from': alice})
    wbtc.approve(homora, 2**256 - 1, {'from': alice})
    wbtc.approve(crwbtc, 2**256 - 1, {'from': alice})

    eurs.approve(homora, 2**256 - 1, {'from': alice})
    eurs.approve(creurs, 2**256 - 1, {'from': alice})
    seur.approve(homora, 2**256 - 1, {'from': alice})
    seur.approve(crseur, 2**256 - 1, {'from': alice})

    lp.approve(homora, 2**256 - 1, {'from': alice})
    lp.approve(gauge, 2**256 - 1, {'from': bob})
    lp_eurs.approve(homora, 2**256 - 1, {'from': alice})

    curve_spell = CurveSpellV1.deploy(
        homora, werc20, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', wgauge,
        {'from': admin})

    # register gauge
    wgauge.registerGauge(9, 0)
    wgauge.registerGauge(23, 0)

    # set up pools
    curve_spell.getPool(lp)
    curve_spell.getPool(lp_eurs)

    # first time call to reduce gas
    curve_spell.ensureApproveN(lp, 2, {'from': admin})
    curve_spell.ensureApproveN(lp_eurs, 2, {'from': admin})

    #####################################################################################

    print(
        '========================================================================='
    )
    print('Case 1.')

    prevABal = renbtc.balanceOf(alice)
    prevBBal = wbtc.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_gauge = lp.balanceOf(gauge)

    renbtc_amt = 1 * 10**7
    wbtc_amt = 1 * 10**7
    lp_amt = 10**13
    borrow_renbtc_amt = 0
    borrow_wbtc_amt = 20 * 10**6
    borrow_lp_amt = 0
    minLPMint = 0

    pid = 9
    gid = 0

    tx = homora.execute(
        0,
        curve_spell,
        curve_spell.addLiquidity2.encode_input(
            lp,  # LP
            [renbtc_amt, wbtc_amt],  # supply tokens
            lp_amt,  # supply LP
            [borrow_renbtc_amt, borrow_wbtc_amt],  # borrow tokens
            borrow_lp_amt,  # borrow LP
            minLPMint,  # min LP mint
            pid,
            gid),
        {'from': alice})

    curABal = renbtc.balanceOf(alice)
    curBBal = wbtc.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_gauge = lp.balanceOf(gauge)

    print('spell lp balance', lp.balanceOf(curve_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, renbtcDebt, renbtcDebtShare = homora.getBankInfo(renbtc)
    _, _, _, wbtcDebt, wbtcDebtShare = homora.getBankInfo(wbtc)

    print('bank renbtc totalDebt', renbtcDebt)
    print('bank renbtc totalShare', renbtcDebtShare)

    print('bank wbtc totalDebt', wbtcDebt)
    print('bank wbtc totalShare', wbtcDebtShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('gauge prev LP balance', prevLPBal_gauge)
    print('gauge cur LP balance', curLPBal_gauge)

    # alice
    assert almostEqual(curABal - prevABal, -renbtc_amt), 'incorrect renbtc amt'
    assert almostEqual(curBBal - prevBBal, -wbtc_amt), 'incorrect wbtc amt'
    assert almostEqual(curLPBal - prevLPBal, -lp_amt), 'incorrect LP amt'

    # spell
    assert renbtc.balanceOf(curve_spell) == 0, 'non-zero spell renbtc balance'
    assert wbtc.balanceOf(curve_spell) == 0, 'non-zero spell wbtc balance'
    assert lp.balanceOf(curve_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert renbtcDebt == borrow_renbtc_amt
    assert wbtcDebt == borrow_wbtc_amt

    _, _, collId, collSize = homora.getPositionInfo(1)
    print('collSize', collSize)

    # staking directly
    prevCrv = crv.balanceOf(bob)
    print('bob lp balance', interface.IERC20Ex(lp).balanceOf(bob))
    pid, gid = 9, 0
    gauge, _ = wgauge.gauges(pid, gid)
    print('gauge address', gauge)
    tx = interface.ILiquidityGauge(gauge).deposit(collSize, {'from': bob})

    chain.sleep(20000)

    prevAliceCrvBalance = crv.balanceOf(alice)
    print('Alice crv balance', prevAliceCrvBalance)

    # #####################################################################################

    print(
        '========================================================================='
    )
    print('Case 2. add liquidity second time')

    prevABal = renbtc.balanceOf(alice)
    prevBBal = wbtc.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_gauge = lp.balanceOf(gauge)

    renbtc_amt = 1 * 10**7
    wbtc_amt = 1 * 10**7
    lp_amt = 0
    borrow_renbtc_amt = 0
    borrow_wbtc_amt = 0
    borrow_lp_amt = 0
    minLPMint = 0

    pid = 9
    gid = 0

    tx = homora.execute(
        1,
        curve_spell,
        curve_spell.addLiquidity2.encode_input(
            lp,  # LP
            [renbtc_amt, wbtc_amt],  # supply tokens
            lp_amt,  # supply LP
            [borrow_renbtc_amt, borrow_wbtc_amt],  # borrow tokens
            borrow_lp_amt,  # borrow LP
            minLPMint,  # min LP mint
            pid,
            gid),
        {'from': alice})

    curABal = renbtc.balanceOf(alice)
    curBBal = wbtc.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_gauge = lp.balanceOf(gauge)

    print('spell lp balance', lp.balanceOf(curve_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, renbtcDebt, renbtcDebtShare = homora.getBankInfo(renbtc)
    _, _, _, wbtcDebt, wbtcDebtShare = homora.getBankInfo(wbtc)

    print('bank renbtc totalDebt', renbtcDebt)
    print('bank renbtc totalShare', renbtcDebtShare)

    print('bank wbtc totalDebt', wbtcDebt)
    print('bank wbtc totalShare', wbtcDebtShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('gauge prev LP balance', prevLPBal_gauge)
    print('gauge cur LP balance', curLPBal_gauge)

    # alice
    assert almostEqual(curABal - prevABal, -renbtc_amt), 'incorrect renbtc amt'
    assert almostEqual(curBBal - prevBBal, -wbtc_amt), 'incorrect wbtc amt'
    assert almostEqual(curLPBal - prevLPBal, -lp_amt), 'incorrect LP amt'

    # spell
    assert renbtc.balanceOf(curve_spell) == 0, 'non-zero spell renbtc balance'
    assert wbtc.balanceOf(curve_spell) == 0, 'non-zero spell wbtc balance'
    assert lp.balanceOf(curve_spell) == 0, 'non-zero spell LP balance'

    curAliceCrvBalance = crv.balanceOf(alice)
    print('Alice crv balance', curAliceCrvBalance)
    receivedCrv = curAliceCrvBalance - prevAliceCrvBalance
    print('received crv', receivedCrv)

    # check with staking directly
    minter = interface.ILiquidityGaugeMinter(
        interface.ILiquidityGauge(gauge).minter())
    print('minter', minter)
    tx = minter.mint(gauge, {'from': bob})
    print('tx status', tx.status)
    tx = interface.ILiquidityGauge(gauge).withdraw(collSize, {'from': bob})
    receivedCrvFromGauge = crv.balanceOf(bob) - prevCrv
    print('receivedCrvFromGauge', receivedCrvFromGauge)
    assert almostEqual(receivedCrv, receivedCrvFromGauge)

    return tx
def main():
    admin = accounts[0]

    alice = accounts[1]
    usdt = interface.IERC20Ex('0xdAC17F958D2ee523a2206206994597C13D831ec7')
    weth = interface.IERC20Ex('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')

    lp = interface.IERC20Ex('0x06da0fd433C1A5d7a4faa01111c044910A184553')
    crusdt = interface.ICErc20('0x797AAB1ce7c01eB727ab980762bA88e7133d2157')

    router = interface.IUniswapV2Router02(
        '0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f')

    chef = accounts.at(
        '0xc2edad668740f1aa35e4d8f227fb8e17dca888cd', force=True)
    wchef = WMasterChef.deploy(chef, {'from': admin})

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([usdt, weth], [2**112 // 700, 2**112])

    uniswap_oracle = UniswapV2Oracle.deploy(simple_oracle, {'from': admin})
    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20, wchef], True, {'from': admin})
    core_oracle.setRoute(
        [usdt, weth, lp],
        [simple_oracle, simple_oracle, uniswap_oracle],
        {'from': admin},
    )
    oracle.setOracles(
        [usdt, weth, lp],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)
    homora.addBank(usdt, crusdt, {'from': admin})

    # setup initial funds to alice
    mint_tokens(usdt, alice)
    mint_tokens(weth, alice)

    # Steal some LP from the staking pool
    mint_tokens(lp, alice)

    # check alice's funds
    print(f'Alice usdt balance {usdt.balanceOf(alice)}')
    print(f'Alice weth balance {weth.balanceOf(alice)}')
    print(f'Alice weth balance {lp.balanceOf(alice)}')

    # set approval
    usdt.approve(homora, 2**256-1, {'from': alice})
    usdt.approve(crusdt, 2**256-1, {'from': alice})
    weth.approve(homora, 2**256-1, {'from': alice})
    lp.approve(homora, 2**256-1, {'from': alice})

    sushiswap_spell = SushiswapSpellV1.deploy(
        homora, werc20, router, wchef, {'from': admin})
    # first time call to reduce gas
    sushiswap_spell.getPair(weth, usdt, {'from': admin})

    #####################################################################################
    print('=========================================================================')
    print('Case 1.')

    prevABal = usdt.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    usdt_amt = 400 * 10**6
    weth_amt = 10 ** 18
    lp_amt = 1 * 10**13
    borrow_usdt_amt = 10 * 10**6
    borrow_weth_amt = 0

    tx = homora.execute(
        0,
        sushiswap_spell,
        sushiswap_spell.addLiquidityWERC20.encode_input(
            usdt,  # token 0
            weth,  # token 1
            [usdt_amt,  # supply USDT
             weth_amt,   # supply WETH
             lp_amt,  # supply LP
             borrow_usdt_amt,  # borrow USDT
             borrow_weth_amt,  # borrow WETH
             0,  # borrow LP tokens
             0,  # min USDT
             0],  # min WETH
        ),
        {'from': alice}
    )

    curABal = usdt.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(sushiswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, totalDebt, totalShare = homora.getBankInfo(usdt)
    print('bank usdt totalDebt', totalDebt)
    print('bank usdt totalShare', totalShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('werc20 prev LP balance', prevLPBal_werc20)
    print('werc20 cur LP balance', curLPBal_werc20)

    print('prev usdt res', prevARes)
    print('cur usdt res', curARes)

    print('prev weth res', prevBRes)
    print('cur weth res', curBRes)

    # alice
    assert almostEqual(curABal - prevABal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curBBal - prevBBal, -weth_amt), 'incorrect WETH amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert usdt.balanceOf(sushiswap_spell) == 0, 'non-zero spell USDT balance'
    assert weth.balanceOf(sushiswap_spell) == 0, 'non-zero spell WETH balance'
    assert lp.balanceOf(sushiswap_spell) == 0, 'non-zero spell LP balance'
    assert totalDebt == borrow_usdt_amt

    # check balance and pool reserves
    assert curABal - prevABal - borrow_usdt_amt == - \
        (curARes - prevARes), 'not all USDT tokens go to LP pool'
    assert almostEqual(curBBal - prevBBal - borrow_weth_amt, -
                       (curBRes - prevBRes)), 'not all WETH tokens go to LP pool'

    _, _, collId, collSize = homora.getPositionInfo(1)

    #####################################################################################
    print('=========================================================================')
    print('Case 2.')

    # remove liquidity from the same position
    prevABal = usdt.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)
    prevETHBal = alice.balance()

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    lp_take_amt = collSize
    lp_want = 1 * 10**12
    usdt_repay = 2**256-1  # max
    weth_repay = 0

    real_usdt_repay = homora.borrowBalanceStored(1, usdt)

    tx = homora.execute(
        1,
        sushiswap_spell,
        sushiswap_spell.removeLiquidityWERC20.encode_input(
            usdt,  # token 0
            weth,  # token 1
            [lp_take_amt,  # take out LP tokens
             lp_want,   # withdraw LP tokens to wallet
             usdt_repay,  # repay USDT
             0,   # repay WETH
             0,   # repay LP
             0,   # min USDT
             0],  # min WETH
        ),
        {'from': alice}
    )

    curABal = usdt.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)
    curETHBal = alice.balance()

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(sushiswap_spell))
    print('spell usdt balance', usdt.balanceOf(sushiswap_spell))
    print('spell weth balance', weth.balanceOf(sushiswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta LP balance', curLPBal - prevLPBal)
    print('remove liquidity gas', tx.gas_used)
    print('bank delta lp balance', curLPBal_bank - prevLPBal_bank)
    print('bank total lp balance', curLPBal_bank)

    _, _, _, totalDebt, totalShare = homora.getBankInfo(usdt)
    print('bank usdt totalDebt', totalDebt)
    print('bank usdt totalShare', totalShare)

    print('LP want', lp_want)

    print('bank delta LP amount', curLPBal_bank - prevLPBal_bank)
    print('LP take amount', lp_take_amt)

    print('prev werc20 LP balance', prevLPBal_werc20)
    print('cur werc20 LP balance', curLPBal_werc20)

    print('real usdt repay', real_usdt_repay)

    # alice
    assert almostEqual(curBBal - prevBBal, 0), 'incorrect WETH amt'
    assert almostEqual(curLPBal - prevLPBal, lp_want), 'incorrect LP amt'

    # werc20
    assert almostEqual(curLPBal_werc20 - prevLPBal_werc20, -
                       lp_take_amt), 'incorrect werc20 LP amt'

    # spell
    assert usdt.balanceOf(sushiswap_spell) == 0, 'non-zero spell USDT balance'
    assert weth.balanceOf(sushiswap_spell) == 0, 'non-zero spell WETH balance'
    assert lp.balanceOf(sushiswap_spell) == 0, 'non-zero spell LP balance'

    # check balance and pool reserves
    assert almostEqual(curABal - prevABal + real_usdt_repay, -
                       (curARes - prevARes)), 'inconsistent USDT from withdraw'
    assert almostEqual(curBBal - prevBBal,
                       0), 'inconsistent WETH from withdraw'
    assert almostEqual(curETHBal - prevETHBal + weth_repay, -
                       (curBRes - prevBRes)), 'inconsistent ETH from withdraw'

    return tx
def main():
    admin = accounts[0]

    alice = accounts[1]
    bob = accounts[2]
    usdt = interface.IERC20Ex('0xdac17f958d2ee523a2206206994597c13d831ec7')
    weth = interface.IERC20Ex('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')

    lp = interface.IERC20Ex('0x06da0fd433C1A5d7a4faa01111c044910A184553')
    crusdt = interface.ICErc20('0x797AAB1ce7c01eB727ab980762bA88e7133d2157')

    sushi = interface.IERC20('0x6b3595068778dd592e39a122f4f5a5cf09c90fe2')

    # sushiswap router
    router = interface.IUniswapV2Router02(
        '0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f')

    chef = accounts.at('0xc2edad668740f1aa35e4d8f227fb8e17dca888cd',
                       force=True)
    wchef = WMasterChef.deploy(chef, {'from': admin})

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([usdt, weth], [2**112 // 700, 2**112])

    uniswap_oracle = UniswapV2Oracle.deploy(simple_oracle, {'from': admin})
    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20, wchef], True, {'from': admin})
    core_oracle.setRoute(
        [usdt, weth, lp],
        [simple_oracle, simple_oracle, uniswap_oracle],
        {'from': admin},
    )
    oracle.setOracles(
        [usdt, weth, lp],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)
    homora.addBank(usdt, crusdt, {'from': admin})

    # setup initial funds to alice
    mint_tokens(usdt, alice)
    mint_tokens(weth, alice)

    # check alice's funds
    print(f'Alice usdt balance {usdt.balanceOf(alice)}')
    print(f'Alice weth balance {weth.balanceOf(alice)}')

    # Steal some LP from the staking pool
    mint_tokens(lp, alice)
    mint_tokens(lp, bob)

    # set approval
    usdt.approve(homora, 2**256 - 1, {'from': alice})
    usdt.approve(crusdt, 2**256 - 1, {'from': alice})
    weth.approve(homora, 2**256 - 1, {'from': alice})
    lp.approve(homora, 2**256 - 1, {'from': alice})
    lp.approve(chef, 2**256 - 1, {'from': bob})

    sushiswap_spell = SushiswapSpellV1.deploy(homora, werc20, router, wchef,
                                              {'from': admin})
    # first time call to reduce gas
    sushiswap_spell.getPair(weth, usdt, {'from': admin})

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 1. add liquidity first time')

    prevABal = usdt.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_chef = lp.balanceOf(chef)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    usdt_amt = 10 * 10**6
    weth_amt = 10**18
    lp_amt = 0
    borrow_usdt_amt = 0
    borrow_weth_amt = 0

    pid = 0

    tx = homora.execute(
        0,
        sushiswap_spell,
        sushiswap_spell.addLiquidityWMasterChef.encode_input(
            usdt,  # token 0
            weth,  # token 1
            [
                usdt_amt,  # supply USDT
                weth_amt,  # supply WETH
                lp_amt,  # supply LP
                borrow_usdt_amt,  # borrow USDT
                borrow_weth_amt,  # borrow WETH
                0,  # borrow LP tokens
                0,  # min USDT
                0
            ],  # min WETH
            pid,
        ),
        {'from': alice})

    curABal = usdt.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_chef = lp.balanceOf(chef)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(sushiswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, totalDebt, totalShare = homora.getBankInfo(usdt)
    print('bank usdt totalDebt', totalDebt)
    print('bank usdt totalShare', totalShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('chef prev LP balance', prevLPBal_chef)
    print('chef cur LP balance', curLPBal_chef)

    print('prev usdt res', prevARes)
    print('cur usdt res', curARes)

    print('prev weth res', prevBRes)
    print('cur weth res', curBRes)

    # alice
    assert almostEqual(curABal - prevABal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curBBal - prevBBal, -weth_amt), 'incorrect WETH amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert usdt.balanceOf(sushiswap_spell) == 0, 'non-zero spell USDT balance'
    assert weth.balanceOf(sushiswap_spell) == 0, 'non-zero spell WETH balance'
    assert lp.balanceOf(sushiswap_spell) == 0, 'non-zero spell LP balance'
    assert totalDebt == borrow_usdt_amt

    # check balance and pool reserves
    assert curABal - prevABal - borrow_usdt_amt == - \
        (curARes - prevARes), 'not all USDT tokens go to LP pool'
    assert almostEqual(
        curBBal - prevBBal - borrow_weth_amt,
        -(curBRes - prevBRes)), 'not all WETH tokens go to LP pool'

    _, _, collId, collSize = homora.getPositionInfo(1)
    print('collSize', collSize)

    # staking directly
    prevSushi = sushi.balanceOf(bob)
    pid = 0
    print('bob lp balance', interface.IERC20Ex(lp).balanceOf(bob))
    tx = interface.IMasterChef(chef).deposit(pid, collSize, {'from': bob})

    chain.sleep(20000)

    prevAliceSushiBalance = sushi.balanceOf(alice)
    print('Alice sushi balance', prevAliceSushiBalance)

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 2. add liquidity the second time')

    prevABal = usdt.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_chef = lp.balanceOf(chef)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    usdt_amt = 10 * 10**6
    weth_amt = 10**18
    lp_amt = 0
    borrow_usdt_amt = 0
    borrow_weth_amt = 0

    pid = 0

    tx = homora.execute(
        1,
        sushiswap_spell,
        sushiswap_spell.addLiquidityWMasterChef.encode_input(
            usdt,  # token 0
            weth,  # token 1
            [
                usdt_amt,  # supply USDT
                weth_amt,  # supply WETH
                lp_amt,  # supply LP
                borrow_usdt_amt,  # borrow USDT
                borrow_weth_amt,  # borrow WETH
                0,  # borrow LP tokens
                0,  # min USDT
                0
            ],  # min WETH
            pid,
        ),
        {'from': alice})

    curABal = usdt.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_chef = lp.balanceOf(chef)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(sushiswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, totalDebt, totalShare = homora.getBankInfo(usdt)
    print('bank usdt totalDebt', totalDebt)
    print('bank usdt totalShare', totalShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('chef prev LP balance', prevLPBal_chef)
    print('chef cur LP balance', curLPBal_chef)

    print('prev usdt res', prevARes)
    print('cur usdt res', curARes)

    print('prev weth res', prevBRes)
    print('cur weth res', curBRes)

    # alice
    assert almostEqual(curABal - prevABal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curBBal - prevBBal, -weth_amt), 'incorrect WETH amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert usdt.balanceOf(sushiswap_spell) == 0, 'non-zero spell USDT balance'
    assert weth.balanceOf(sushiswap_spell) == 0, 'non-zero spell WETH balance'
    assert lp.balanceOf(sushiswap_spell) == 0, 'non-zero spell LP balance'
    assert totalDebt == borrow_usdt_amt

    # check balance and pool reserves
    assert curABal - prevABal - borrow_usdt_amt == - \
        (curARes - prevARes), 'not all USDT tokens go to LP pool'
    assert almostEqual(
        curBBal - prevBBal - borrow_weth_amt,
        -(curBRes - prevBRes)), 'not all WETH tokens go to LP pool'

    curAliceSushiBalance = sushi.balanceOf(alice)
    print('Alice sushi balance', curAliceSushiBalance)
    receivedSushi = curAliceSushiBalance - prevAliceSushiBalance
    print('received sushi', receivedSushi)

    # check with staking directly
    pid = 0
    tx = interface.IMasterChef(chef).withdraw(pid, collSize, {'from': bob})
    receivedSushiFromStaking = sushi.balanceOf(bob) - prevSushi
    print('receivedSushiFromStaking', receivedSushiFromStaking)
    assert almostEqual(receivedSushi, receivedSushiFromStaking)
示例#5
0
def main():
    admin = accounts[0]

    alice = accounts[1]
    bob = accounts[2]
    dfd = interface.IERC20Ex('0x20c36f062a31865bED8a5B1e512D9a1A20AA333A')
    dusd = interface.IERC20Ex('0x5BC25f649fc4e26069dDF4cF4010F9f706c23831')
    weth = interface.IERC20Ex('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')
    dai = interface.IERC20Ex('0x6B175474E89094C44Da98b954EedeAC495271d0F')

    lp = interface.IERC20Ex('0xd8e9690eff99e21a2de25e0b148ffaf47f47c972')

    # pool is lp for balancer
    pool = interface.IBalancerPool(
        '0xd8e9690eff99e21a2de25e0b148ffaf47f47c972')
    lp_dai = interface.IERC20Ex('0x8b6e6e7b5b3801fed2cafd4b22b8a16c2f2db21a')
    pool_dai = interface.IBalancerPool(
        '0x8b6e6e7b5b3801fed2cafd4b22b8a16c2f2db21a')

    crdfd = MockCErc20.deploy(dfd, {'from': admin})
    crdusd = MockCErc20.deploy(dusd, {'from': admin})
    crdai = MockCErc20.deploy(dai, {'from': admin})
    crweth = MockCErc20.deploy(weth, {'from': admin})

    werc20 = WERC20.deploy({'from': admin})

    staking = accounts.at('0xf068236ecad5fabb9883bbb26a6445d6c7c9a924',
                          force=True)

    wstaking = WStakingRewards.deploy(staking, lp, dfd, {'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([dfd, dusd],
                           [2**112 // 2 // 700, 2**112 * 2 // 700])

    balancer_oracle = BalancerPairOracle.deploy(simple_oracle, {'from': alice})

    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20, wstaking], True, {'from': admin})
    core_oracle.setRoute(
        [dfd, dusd, lp],
        [simple_oracle, simple_oracle, balancer_oracle],
        {'from': admin},
    )
    oracle.setOracles(
        [dfd, dusd, lp],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)
    homora.addBank(dfd, crdfd, {'from': admin})
    homora.addBank(dusd, crdusd, {'from': admin})

    # setup initial funds to alice
    mint_tokens(dfd, alice)
    mint_tokens(dusd, alice)

    mint_tokens(weth, alice)
    mint_tokens(dai, alice)

    mint_tokens(dfd, crdfd)

    # check alice's funds
    print(f'Alice dusd balance {dusd.balanceOf(alice)}')
    print(f'Alice dfd balance {dfd.balanceOf(alice)}')

    # Steal some LP from the staking pool
    mint_tokens(lp, alice)
    mint_tokens(lp, bob)

    # set approval
    dfd.approve(homora, 2**256 - 1, {'from': alice})
    dfd.approve(crdfd, 2**256 - 1, {'from': alice})
    dusd.approve(homora, 2**256 - 1, {'from': alice})
    dusd.approve(crdusd, 2**256 - 1, {'from': alice})
    dai.approve(homora, 2**256 - 1, {'from': alice})
    dai.approve(crdai, 2**256 - 1, {'from': alice})
    weth.approve(homora, 2**256 - 1, {'from': alice})
    weth.approve(crweth, 2**256 - 1, {'from': alice})

    lp.approve(homora, 2**256 - 1, {'from': alice})
    lp.approve(staking, 2**256 - 1, {'from': bob})

    balancer_spell = BalancerSpellV1.deploy(
        homora, werc20, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
        {'from': admin})
    # first time call to reduce gas
    balancer_spell.getPair(lp, {'from': admin})

    # whitelist spell in bank
    homora.setWhitelistSpells([balancer_spell], [True], {'from': admin})

    # whitelist token in bank
    homora.setWhitelistTokens([dfd], [True], {'from': admin})

    # whitelist lp in spell
    balancer_spell.setWhitelistLPTokens([lp, lp_dai], [True, True],
                                        {'from': admin})

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 1.')

    prevABal = dfd.balanceOf(alice)
    prevBBal = dusd.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_staking = lp.balanceOf(staking)

    prevARes = interface.IBalancerPool(lp).getBalance(dfd)
    prevBRes = interface.IBalancerPool(lp).getBalance(dusd)

    dfd_amt = 100 * 10**18
    dusd_amt = 10**18
    lp_amt = 0
    borrow_dfd_amt = 10**18
    borrow_dusd_amt = 0

    # calculate slippage control
    total_dfd_amt = dfd_amt + borrow_dfd_amt
    total_dusd_amt = dusd_amt + borrow_dusd_amt
    dfd_weight = 0.58
    dusd_weight = 0.42

    ratio = (((prevARes + total_dfd_amt) / prevARes) ** dfd_weight) * \
        (((prevBRes + total_dusd_amt) / prevBRes) ** dusd_weight) - 1
    lp_desired = lp_amt + int(
        interface.IERC20(lp).totalSupply() * ratio * 0.995)
    print('lp desired', lp_desired)

    tx = homora.execute(
        0,
        balancer_spell,
        balancer_spell.addLiquidityWStakingRewards.encode_input(
            lp,  # lp token
            [
                dfd_amt,  # supply DFD
                dusd_amt,  # supply DUSD
                lp_amt,  # supply LP
                borrow_dfd_amt,  # borrow DFD
                borrow_dusd_amt,  # borrow DUSD
                0,  # borrow LP tokens
                lp_desired
            ],  # LP desired
            wstaking),
        {'from': alice})

    curABal = dfd.balanceOf(alice)
    curBBal = dusd.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_staking = lp.balanceOf(staking)

    curARes = interface.IBalancerPool(lp).getBalance(dfd)
    curBRes = interface.IBalancerPool(lp).getBalance(dusd)

    print('spell lp balance', lp.balanceOf(balancer_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, dfdDebt, dfdShare = homora.getBankInfo(dfd)
    print('bank dfd dfdDebt', dfdDebt)
    print('bank dfd dfdShare', dfdShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('staking prev LP balance', prevLPBal_staking)
    print('staking cur LP balance', curLPBal_staking)

    print('prev dfd res', prevARes)
    print('cur dfd res', curARes)

    print('prev dusd res', prevBRes)
    print('cur dusd res', curBRes)

    # alice
    assert almostEqual(curABal - prevABal, -dfd_amt), 'incorrect DFD amt'
    assert almostEqual(curBBal - prevBBal, -dusd_amt), 'incorrect DUSD amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert dfd.balanceOf(balancer_spell) == 0, 'non-zero spell DFD balance'
    assert dusd.balanceOf(balancer_spell) == 0, 'non-zero spell DUSD balance'
    assert lp.balanceOf(balancer_spell) == 0, 'non-zero spell LP balance'
    assert dfdDebt == borrow_dfd_amt

    # check balance and pool reserves
    assert almostEqual(
        curABal - prevABal - borrow_dfd_amt,
        -(curARes - prevARes)), 'not all DFD tokens go to LP pool'
    assert almostEqual(
        curBBal - prevBBal - borrow_dusd_amt,
        -(curBRes - prevBRes)), 'not all DUSD tokens go to LP pool'

    _, _, collId, collSize = homora.getPositionInfo(1)
    print('collSize', collSize)

    # staking directly
    prevDfd = dfd.balanceOf(bob)
    print('bob lp balance', interface.IERC20Ex(lp).balanceOf(bob))
    tx = interface.IStakingRewards(staking).stake(collSize, {'from': bob})

    chain.sleep(20000)

    prevAliceDfdBalance = dfd.balanceOf(alice)
    print('Alice dfd balance', prevAliceDfdBalance)

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 2. add liquidity (failed tx desired)')

    weth_amt = 100 * 10**18
    dai_amt = 10**18
    lp_amt = 0
    borrow_weth_amt = 0
    borrow_dai_amt = 0

    # calculate slippage control
    total_weth_amt = weth_amt + borrow_weth_amt
    total_dai_amt = dai_amt + borrow_dai_amt
    dfd_weight = 0.8
    dusd_weight = 0.2

    ratio = (((prevARes + total_weth_amt) / prevARes) ** dfd_weight) * \
        (((prevBRes + total_dai_amt) / prevBRes) ** dusd_weight) - 1
    lp_desired = lp_amt + int(
        interface.IERC20(lp).totalSupply() * ratio * 0.995)
    lp_desired = 0
    print('lp desired', lp_desired)

    try:
        tx = homora.execute(
            1,
            balancer_spell,
            balancer_spell.addLiquidityWStakingRewards.encode_input(
                lp_dai,  # lp token
                [
                    weth_amt,  # supply DFD
                    dai_amt,  # supply DUSD
                    lp_amt,  # supply LP
                    borrow_weth_amt,  # borrow DFD
                    borrow_dai_amt,  # borrow DUSD
                    0,  # borrow LP tokens
                    lp_desired
                ],  # LP desired
                wstaking),
            {'from': alice})
        assert False, 'tx should fail'
    except VirtualMachineError:
        pass

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 3. remove liquidity (failed tx desired)')

    lp_take_amt = 2**256 - 1  # max
    lp_want = 0
    weth_repay = 2**256 - 1  # max
    dai_repay = 2**256 - 1  # max

    real_weth_repay = homora.borrowBalanceStored(1, dfd)
    _, _, _, real_lp_take_amt = homora.getPositionInfo(1)

    expected_withdraw_dfd = collSize * prevARes // interface.IBalancerPool(
        lp).totalSupply()
    print('expected withdraw DFD', expected_withdraw_dfd)

    try:
        tx = homora.execute(
            1,
            balancer_spell,
            balancer_spell.removeLiquidityWStakingRewards.encode_input(
                lp_dai,  # LP token
                [
                    lp_take_amt,  # take out LP tokens
                    lp_want,  # withdraw LP tokens to wallet
                    weth_repay,  # repay DFD
                    dai_repay,  # repay DUSD
                    0,  # repay LP
                    0,  # min DFD
                    0
                ],  # min DUSD
                wstaking),
            {'from': alice})
        assert False, 'tx should fail'
    except VirtualMachineError:
        pass

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 4. remove liquidity')

    # remove liquidity from the same position
    prevABal = dfd.balanceOf(alice)
    prevBBal = dusd.balanceOf(alice)
    prevETHBal = alice.balance()
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_staking = lp.balanceOf(staking)
    prevETHBal = alice.balance()
    prevCrdfdBal = lp.balanceOf(crdfd)

    prevARes = interface.IBalancerPool(lp).getBalance(dfd)
    prevBRes = interface.IBalancerPool(lp).getBalance(dusd)

    lp_take_amt = 2**256 - 1  # max
    lp_want = 0
    dfd_repay = 2**256 - 1  # max
    dusd_repay = 0

    real_dfd_repay = homora.borrowBalanceStored(1, dfd)
    _, _, _, real_lp_take_amt = homora.getPositionInfo(1)

    expected_withdraw_dfd = collSize * prevARes // interface.IBalancerPool(
        lp).totalSupply()
    print('expected withdraw DFD', expected_withdraw_dfd)

    tx = homora.execute(
        1,
        balancer_spell,
        balancer_spell.removeLiquidityWStakingRewards.encode_input(
            lp,  # LP token
            [
                lp_take_amt,  # take out LP tokens
                lp_want,  # withdraw LP tokens to wallet
                dfd_repay,  # repay DFD
                dusd_repay,  # repay DUSD
                0,  # repay LP
                0,  # min DFD
                0
            ],  # min DUSD
            wstaking),
        {'from': alice})

    curABal = dfd.balanceOf(alice)
    curBBal = dusd.balanceOf(alice)
    curETHBal = alice.balance()
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_staking = lp.balanceOf(staking)
    curETHBal = alice.balance()
    curCrdfdBal = lp.balanceOf(crdfd)

    curARes = interface.IBalancerPool(lp).getBalance(dfd)
    curBRes = interface.IBalancerPool(lp).getBalance(dusd)

    print('spell lp balance', lp.balanceOf(balancer_spell))
    print('spell dfd balance', dfd.balanceOf(balancer_spell))
    print('spell dusd balance', dusd.balanceOf(balancer_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta ETH balance', curETHBal - prevETHBal)
    print('Alice delta LP balance', curLPBal - prevLPBal)
    print('remove liquidity gas', tx.gas_used)
    print('bank delta lp balance', curLPBal_bank - prevLPBal_bank)
    print('bank total lp balance', curLPBal_bank)

    _, _, _, dfdDebt, dfdShare = homora.getBankInfo(dfd)
    print('bank dfd totalDebt', dfdDebt)
    print('bank dfd totalShare', dfdShare)

    print('LP want', lp_want)

    print('bank delta LP amount', curLPBal_bank - prevLPBal_bank)
    print('LP take amount', lp_take_amt)

    print('prev staking LP balance', prevLPBal_staking)
    print('cur staking LP balance', curLPBal_staking)

    print('real dfd repay', real_dfd_repay)

    print('curCrdfdBal', curCrdfdBal)
    print('delta crdfd', curCrdfdBal - prevCrdfdBal)

    print('A res delta', prevARes - curARes)
    print('B res delta', prevBRes - curBRes)

    # alice
    assert almostEqual(curLPBal - prevLPBal, lp_want), 'incorrect LP amt'

    # staking
    assert almostEqual(curLPBal_staking - prevLPBal_staking,
                       -real_lp_take_amt), 'incorrect staking LP amt'

    # spell
    assert dfd.balanceOf(balancer_spell) == 0, 'non-zero spell DFD balance'
    assert dusd.balanceOf(balancer_spell) == 0, 'non-zero spell DUSD balance'
    assert lp.balanceOf(balancer_spell) == 0, 'non-zero spell LP balance'

    # check balance and pool reserves
    assert almostEqual(curABal - prevABal + real_dfd_repay,
                       -(curARes - prevARes)), 'inconsistent DFD from withdraw'
    assert almostEqual(
        curBBal - prevBBal + dusd_repay,
        -(curBRes - prevBRes)), 'inconsistent DUSD from withdraw'

    curAliceDfdBalance = dfd.balanceOf(alice)
    print('Alice dfd balance', curAliceDfdBalance)
    receivedDfd = curAliceDfdBalance - prevAliceDfdBalance + real_dfd_repay - (
        prevARes - curARes)
    print('received dfd', receivedDfd)

    # check with staking directly
    tx = interface.IStakingRewards(staking).getReward({'from': bob})
    receivedDfdFromStaking = dfd.balanceOf(bob) - prevDfd
    print('receivedDfdFromStaking', receivedDfdFromStaking)
    assert almostEqual(receivedDfd, receivedDfdFromStaking)

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 5. add & remove all LP')

    lp_amt = 10 * 10**18

    prevLPBal = lp.balanceOf(alice)

    tx = homora.execute(
        0,
        balancer_spell,
        balancer_spell.addLiquidityWStakingRewards.encode_input(
            lp,  # lp token
            [
                0,  # supply DAI
                0,  # supply WETH
                lp_amt,  # supply LP
                0,  # borrow DAI
                0,  # borrow WETH
                0,  # borrow LP tokens
                0
            ],  # LP desired
            wstaking),
        {'from': alice})

    tx = homora.execute(
        2,
        balancer_spell,
        balancer_spell.removeLiquidityWStakingRewards.encode_input(
            lp,  # LP token
            [
                2**256 - 1,  # take out LP tokens
                lp_amt,  # withdraw LP tokens to wallet
                0,  # repay DAI
                0,  # repay WETH
                0,  # repay LP
                0,  # min DAI
                0
            ],  # min WETH
            wstaking),
        {'from': alice})

    curLPBal = lp.balanceOf(alice)

    assert prevLPBal == curLPBal, 'incorrect LP Balance'

    return tx
示例#6
0
def main():
    admin = accounts[0]

    alice = accounts[1]
    usdt = interface.IERC20Ex('0xdAC17F958D2ee523a2206206994597C13D831ec7')
    weth = interface.IERC20Ex('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')

    lp = interface.IERC20Ex('0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852')
    crusdt = interface.ICErc20('0x797AAB1ce7c01eB727ab980762bA88e7133d2157')
    router = interface.IUniswapV2Router02(
        '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D')

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx(
        [usdt, weth], [9011535487953795006625883219171279625142296, 2**112])

    uniswap_oracle = UniswapV2Oracle.deploy(simple_oracle, {'from': admin})
    oracle = ProxyOracle.deploy({'from': admin})
    oracle.setWhitelistERC1155([werc20], True, {'from': admin})
    oracle.setOracles(
        [
            '0xdAC17F958D2ee523a2206206994597C13D831ec7',  # USDT
            '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',  # WETH
            '0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852',  # USDT-WETH
        ],
        [
            [simple_oracle, 10000, 10000, 10000],
            [simple_oracle, 10000, 10000, 10000],
            [uniswap_oracle, 10000, 10000, 10000],
        ],
        {'from': admin},
    )

    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)
    homora.addBank(usdt, crusdt, {'from': admin})

    # setup initial funds 10^5 USDT + 10^4 WETH to alice
    setup_transfer(
        usdt,
        accounts.at('0xbe0eb53f46cd790cd13851d5eff43d12404d33e8', force=True),
        alice, 10**5 * 10**6)
    setup_transfer(
        weth,
        accounts.at('0x397ff1542f962076d0bfe58ea045ffa2d347aca0', force=True),
        alice, 10**4 * 10**18)

    # setup initial funds 10^6 USDT + 10^5 WETH to homora bank
    setup_transfer(
        usdt,
        accounts.at('0xbe0eb53f46cd790cd13851d5eff43d12404d33e8', force=True),
        homora, 10**6 * 10**6)
    setup_transfer(
        weth,
        accounts.at('0x397ff1542f962076d0bfe58ea045ffa2d347aca0', force=True),
        homora, 10**4 * 10**18)

    # check alice's funds
    print(f'Alice usdt balance {usdt.balanceOf(alice)}')
    print(f'Alice weth balance {weth.balanceOf(alice)}')

    # Steal some LP from the staking pool
    lp.transfer(
        alice, 1 * 10**17, {
            'from':
            accounts.at('0x767ecb395def19ab8d1b2fcc89b3ddfbed28fd6b',
                        force=True)
        })
    lp.transfer(
        homora, 2 * 10**17, {
            'from':
            accounts.at('0x767ecb395def19ab8d1b2fcc89b3ddfbed28fd6b',
                        force=True)
        })

    # set approval
    usdt.approve(homora, 2**256 - 1, {'from': alice})
    usdt.approve(crusdt, 2**256 - 1, {'from': alice})
    weth.approve(homora, 2**256 - 1, {'from': alice})
    lp.approve(homora, 2**256 - 1, {'from': alice})

    uniswap_spell = UniswapV2SpellV1.deploy(homora, werc20, router,
                                            {'from': admin})
    # first time call to reduce gas
    uniswap_spell.getPair(weth, usdt, {'from': admin})

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 1.')

    prevABal = usdt.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevETHBal = alice.balance()
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    usdt_amt = 400 * 10**6
    weth_amt = 10**18
    eth_amt = 10**18
    lp_amt = 1 * 10**16
    borrow_usdt_amt = 1000 * 10**6
    borrow_weth_amt = 0

    tx = homora.execute(
        0,
        uniswap_spell,
        uniswap_spell.addLiquidity.encode_input(
            usdt,  # token 0
            weth,  # token 1
            [
                usdt_amt,  # supply USDT
                weth_amt,  # supply WETH
                lp_amt,  # supply LP
                borrow_usdt_amt,  # borrow USDT
                borrow_weth_amt,  # borrow WETH
                0,  # borrow LP tokens
                0,  # min USDT
                0
            ],  # min WETH
        ),
        {
            'from': alice,
            'value': eth_amt
        })

    position_id = tx.return_value
    print('position_id', position_id)

    curABal = usdt.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curETHBal = alice.balance()
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, totalDebt, totalShare = homora.getBankInfo(usdt)
    print('bank usdt totalDebt', totalDebt)
    print('bank usdt totalShare', totalShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('werc20 prev LP balance', prevLPBal_werc20)
    print('werc20 cur LP balance', curLPBal_werc20)

    # alice
    assert almostEqual(curABal - prevABal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curBBal - prevBBal, -weth_amt), 'incorrect WETH amt'
    assert almostEqual(curETHBal - prevETHBal, -eth_amt), 'incorrect ETH amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert usdt.balanceOf(uniswap_spell) == 0, 'non-zero spell USDT balance'
    assert weth.balanceOf(uniswap_spell) == 0, 'non-zero spell WETH balance'
    assert uniswap_spell.balance() == 0, 'non-zero spell ETH balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'
    assert totalDebt == borrow_usdt_amt

    # check balance and pool reserves
    assert curABal - prevABal - borrow_usdt_amt == - \
        (curARes - prevARes), 'not all USDT tokens go to LP pool'
    assert curBBal - prevBBal - borrow_weth_amt - eth_amt == - \
        (curBRes - prevBRes), 'not all WETH tokens go to LP pool'

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 2.')

    # remove liquidity from the same position
    prevABal = usdt.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)
    prevETHBal = alice.balance()

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    lp_take_amt = 1 * 10**16
    lp_want = 1 * 10**15
    usdt_repay = 2**256 - 1  # max
    weth_repay = 0

    real_usdt_repay = homora.borrowBalanceStored(position_id, usdt)

    tx = homora.execute(
        position_id,
        uniswap_spell,
        uniswap_spell.removeLiquidity.encode_input(
            usdt,  # token 0
            weth,  # token 1
            [
                lp_take_amt,  # take out LP tokens
                lp_want,  # withdraw LP tokens to wallet
                usdt_repay,  # repay USDT
                weth_repay,  # repay WETH
                0,  # repay LP
                0,  # min USDT
                0
            ],  # min WETH
        ),
        {'from': alice})

    curABal = usdt.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)
    curETHBal = alice.balance()

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('spell usdt balance', usdt.balanceOf(uniswap_spell))
    print('spell weth balance', weth.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta LP balance', curLPBal - prevLPBal)
    print('remove liquidity gas', tx.gas_used)
    print('bank delta lp balance', curLPBal_bank - prevLPBal_bank)
    print('bank total lp balance', curLPBal_bank)

    _, _, _, totalDebt, totalShare = homora.getBankInfo(usdt)
    print('bank usdt totalDebt', totalDebt)
    print('bank usdt totalShare', totalShare)

    print('LP want', lp_want)

    print('bank delta LP amount', curLPBal_bank - prevLPBal_bank)
    print('LP take amount', lp_take_amt)

    print('prev werc20 LP balance', prevLPBal_werc20)
    print('cur werc20 LP balance', curLPBal_werc20)

    # alice
    assert almostEqual(curBBal - prevBBal, 0), 'incorrect WETH amt'
    assert almostEqual(curLPBal - prevLPBal, lp_want), 'incorrect LP amt'

    # werc20
    assert almostEqual(curLPBal_werc20 - prevLPBal_werc20,
                       -lp_take_amt), 'incorrect werc20 LP amt'

    # spell
    assert usdt.balanceOf(uniswap_spell) == 0, 'non-zero spell USDT balance'
    assert weth.balanceOf(uniswap_spell) == 0, 'non-zero spell WETH balance'
    assert uniswap_spell.balance() == 0, 'non-zero spell ETH balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'

    # check balance and pool reserves
    assert almostEqual(
        curABal - prevABal + real_usdt_repay,
        -(curARes - prevARes)), 'inconsistent USDT from withdraw'
    assert almostEqual(curBBal - prevBBal,
                       0), 'inconsistent WETH from withdraw'
    assert almostEqual(curETHBal - prevETHBal + weth_repay,
                       -(curBRes - prevBRes)), 'inconsistent ETH from withdraw'

    return tx
示例#7
0
def main():
    admin = accounts[0]

    alice = accounts[1]
    bob = accounts[2]
    dfd = interface.IERC20Ex('0x20c36f062a31865bED8a5B1e512D9a1A20AA333A')
    dusd = interface.IERC20Ex('0x5BC25f649fc4e26069dDF4cF4010F9f706c23831')

    lp = interface.IERC20Ex('0xd8e9690eff99e21a2de25e0b148ffaf47f47c972')
    # pool is lp for balancer
    pool = interface.IBalancerPool('0xd8e9690eff99e21a2de25e0b148ffaf47f47c972')

    crdfd = MockCErc20.deploy(dfd, {'from': admin})
    crdusd = MockCErc20.deploy(dusd, {'from': admin})

    werc20 = WERC20.deploy({'from': admin})

    staking = accounts.at('0xf068236ecad5fabb9883bbb26a6445d6c7c9a924', force=True)

    wstaking = WStakingRewards.deploy(staking, lp, dfd, {'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([dfd, dusd], [2**112 // 2 // 700, 2**112 * 2 // 700])

    balancer_oracle = BalancerPairOracle.deploy(simple_oracle, {'from': alice})

    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20, wstaking], True, {'from': admin})
    core_oracle.setRoute(
        [dfd, dusd, lp],
        [simple_oracle, simple_oracle, balancer_oracle],
        {'from': admin},
    )
    oracle.setOracles(
        [dfd, dusd, lp],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)
    homora.addBank(dfd, crdfd, {'from': admin})
    homora.addBank(dusd, crdusd, {'from': admin})

    # setup initial funds to alice
    mint_tokens(dfd, alice)
    mint_tokens(dusd, alice)

    # check alice's funds
    print(f'Alice dusd balance {dusd.balanceOf(alice)}')
    print(f'Alice dfd balance {dfd.balanceOf(alice)}')

    # Steal some LP from the staking pool
    mint_tokens(lp, alice)
    mint_tokens(lp, bob)

    # set approval
    dfd.approve(homora, 2**256-1, {'from': alice})
    dfd.approve(crdfd, 2**256-1, {'from': alice})
    dusd.approve(homora, 2**256-1, {'from': alice})
    dusd.approve(crdusd, 2**256-1, {'from': alice})
    lp.approve(homora, 2**256-1, {'from': alice})
    lp.approve(staking, 2**256-1, {'from': bob})

    balancer_spell = BalancerSpellV1.deploy(
        homora, werc20, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', {'from': admin})
    # first time call to reduce gas
    balancer_spell.getPair(lp, {'from': admin})

    # whitelist spell in bank
    homora.setWhitelistSpells([balancer_spell], [True], {'from': admin})

    # whitelist lp in spell
    balancer_spell.setWhitelistLPTokens([lp], [True], {'from': admin})

    #####################################################################################
    print('=========================================================================')
    print('Case 1.')

    prevABal = dfd.balanceOf(alice)
    prevBBal = dusd.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_staking = lp.balanceOf(staking)

    prevARes = interface.IBalancerPool(lp).getBalance(dfd)
    prevBRes = interface.IBalancerPool(lp).getBalance(dusd)

    dfd_amt = 100 * 10**18
    dusd_amt = 10 ** 18
    lp_amt = 0
    borrow_dfd_amt = 0
    borrow_dusd_amt = 0

    # calculate slippage control
    total_dfd_amt = dfd_amt + borrow_dfd_amt
    total_dusd_amt = dusd_amt + borrow_dusd_amt
    dfd_weight = 0.58
    dusd_weight = 0.42

    ratio = (((prevARes + total_dfd_amt) / prevARes) ** dfd_weight) * \
        (((prevBRes + total_dusd_amt) / prevBRes) ** dusd_weight) - 1
    lp_desired = lp_amt + int(interface.IERC20(lp).totalSupply() * ratio * 0.995)
    print('lp desired', lp_desired)

    tx = homora.execute(
        0,
        balancer_spell,
        balancer_spell.addLiquidityWStakingRewards.encode_input(
            lp,  # lp token
            [dfd_amt,  # supply DFD
             dusd_amt,   # supply DUSD
             lp_amt,  # supply LP
             borrow_dfd_amt,  # borrow DFD
             borrow_dusd_amt,  # borrow DUSD
             0,  # borrow LP tokens
             lp_desired],  # LP desired
            wstaking
        ),
        {'from': alice}
    )

    curABal = dfd.balanceOf(alice)
    curBBal = dusd.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_staking = lp.balanceOf(staking)

    curARes = interface.IBalancerPool(lp).getBalance(dfd)
    curBRes = interface.IBalancerPool(lp).getBalance(dusd)

    print('spell lp balance', lp.balanceOf(balancer_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, dfdDebt, dfdShare = homora.getBankInfo(dfd)
    print('bank dfd dfdDebt', dfdDebt)
    print('bank dfd dfdShare', dfdShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('staking prev LP balance', prevLPBal_staking)
    print('staking cur LP balance', curLPBal_staking)

    print('prev dfd res', prevARes)
    print('cur dfd res', curARes)

    print('prev dusd res', prevBRes)
    print('cur dusd res', curBRes)

    # alice
    assert almostEqual(curABal - prevABal, -dfd_amt), 'incorrect DFD amt'
    assert almostEqual(curBBal - prevBBal, -dusd_amt), 'incorrect DUSD amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert dfd.balanceOf(balancer_spell) == 0, 'non-zero spell DFD balance'
    assert dusd.balanceOf(balancer_spell) == 0, 'non-zero spell DUSD balance'
    assert lp.balanceOf(balancer_spell) == 0, 'non-zero spell LP balance'
    assert dfdDebt == borrow_dfd_amt

    # check balance and pool reserves
    assert almostEqual(curABal - prevABal - borrow_dfd_amt, -
                       (curARes - prevARes)), 'not all DFD tokens go to LP pool'
    assert almostEqual(curBBal - prevBBal - borrow_dusd_amt, -
                       (curBRes - prevBRes)), 'not all DUSD tokens go to LP pool'

    # #####################################################################################

    print('=========================================================================')
    print('Case 2. harvest first time')

    prevDfdBalance = dfd.balanceOf(alice)
    print('Alice DFD balance before harvest', prevDfdBalance)
    _, _, collId, collSize = homora.getPositionInfo(1)
    print('collSize', collSize)

    # staking directly
    prevDfd = dfd.balanceOf(bob)
    print('bob lp balance', interface.IERC20Ex(lp).balanceOf(bob))
    tx = interface.IStakingRewards(staking).stake(collSize, {'from': bob})

    chain.sleep(20000)

    tx = homora.execute(
        1,
        balancer_spell,
        balancer_spell.harvestWStakingRewards.encode_input(wstaking),
        {'from': alice}
    )

    print('tx gas used', tx.gas_used)

    curDfdBalance = dfd.balanceOf(alice)
    print('Alice DFD balance after harvest', curDfdBalance)
    receivedDfd = curDfdBalance - prevDfdBalance

    # check with staking directly
    tx = interface.IStakingRewards(staking).getReward({'from': bob})
    receivedDfdFromStaking = dfd.balanceOf(bob) - prevDfd
    print('receivedDfdFromStaking', receivedDfdFromStaking)
    assert almostEqual(receivedDfd, receivedDfdFromStaking)

    # #####################################################################################

    print('=========================================================================')
    print('Case 3. harvest second time')

    prevDfdBalance = dfd.balanceOf(alice)
    print('Alice DFD balance before harvest', prevDfdBalance)

    # staking directly
    prevDfd = dfd.balanceOf(bob)
    print('bob lp balance', interface.IERC20Ex(lp).balanceOf(bob))

    chain.sleep(20000)

    tx = homora.execute(
        1,
        balancer_spell,
        balancer_spell.harvestWStakingRewards.encode_input(wstaking),
        {'from': alice}
    )

    print('tx gas used', tx.gas_used)

    curDfdBalance = dfd.balanceOf(alice)
    print('Alice DFD balance after harvest', curDfdBalance)
    receivedDfd = curDfdBalance - prevDfdBalance

    # check with staking directly
    tx = interface.IStakingRewards(staking).getReward({'from': bob})
    receivedDfdFromStaking = dfd.balanceOf(bob) - prevDfd
    print('receivedDfdFromStaking', receivedDfdFromStaking)
    assert almostEqual(receivedDfd, receivedDfdFromStaking)
示例#8
0
def main():
    admin = accounts[0]

    alice = accounts[1]
    dai = interface.IERC20Ex('0x6B175474E89094C44Da98b954EedeAC495271d0F')
    usdc = interface.IERC20Ex('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48')
    usdt = interface.IERC20Ex('0xdAC17F958D2ee523a2206206994597C13D831ec7')

    lp = interface.IERC20Ex('0x6c3f90f043a72fa612cbac8115ee7e52bde6e490')
    pool = interface.ICurvePool('0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7')
    registry = interface.ICurveRegistry(
        '0x7d86446ddb609ed0f5f8684acf30380a356b2b4c')

    crdai = interface.ICErc20('0x92B767185fB3B04F881e3aC8e5B0662a027A1D9f')
    crusdc = interface.ICErc20('0x44fbebd2f576670a6c33f6fc0b00aa8c5753b322')
    crusdt = interface.ICErc20('0x797AAB1ce7c01eB727ab980762bA88e7133d2157')

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([dai, usdt, usdc, lp], [
        9060553589188986552095106856227,
        9002288773315920458132820329673073223442669,
        9011535487953795006625883219171279625142296,
        9002288773315920458132820329673
    ])

    oracle = ProxyOracle.deploy({'from': admin})
    oracle.setWhitelistERC1155([werc20], True, {'from': admin})
    oracle.setOracles(
        [
            '0x6B175474E89094C44Da98b954EedeAC495271d0F',  # DAI
            '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',  # USDC
            '0xdAC17F958D2ee523a2206206994597C13D831ec7',  # USDT
            '0x6c3f90f043a72fa612cbac8115ee7e52bde6e490',  # lp
        ],
        [
            [simple_oracle, 10000, 10000, 10000],
            [simple_oracle, 10000, 10000, 10000],
            [simple_oracle, 10000, 10000, 10000],
            [simple_oracle, 10000, 10000, 10000],
        ],
        {'from': admin},
    )

    # initialize
    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)

    # add bank
    homora.addBank(dai, crdai, {'from': admin})
    homora.addBank(usdc, crusdc, {'from': admin})
    homora.addBank(usdt, crusdt, {'from': admin})

    # setup initial funds 10^6 USDT + 10^6 USDC + 10^6 DAI to alice
    setup_transfer(
        dai,
        accounts.at('0xc3d03e4f041fd4cd388c549ee2a29a9e5075882f', force=True),
        alice, 10**6 * 10**18)
    setup_transfer(
        usdc,
        accounts.at('0xa191e578a6736167326d05c119ce0c90849e84b7', force=True),
        alice, 10**6 * 10**6)
    setup_transfer(
        usdt,
        accounts.at('0xbe0eb53f46cd790cd13851d5eff43d12404d33e8', force=True),
        alice, 10**6 * 10**6)

    # setup initial funds 10^6 USDT + 10^6 USDC + 10^6 DAI to homora bank
    setup_transfer(
        dai,
        accounts.at('0xc3d03e4f041fd4cd388c549ee2a29a9e5075882f', force=True),
        homora, 10**6 * 10**18)
    setup_transfer(
        usdc,
        accounts.at('0x397ff1542f962076d0bfe58ea045ffa2d347aca0', force=True),
        homora, 10**6 * 10**6)
    setup_transfer(
        usdt,
        accounts.at('0xbe0eb53f46cd790cd13851d5eff43d12404d33e8', force=True),
        homora, 10**6 * 10**6)

    # check alice's funds
    print(f'Alice dai balance {dai.balanceOf(alice)}')
    print(f'Alice usdc balance {usdc.balanceOf(alice)}')
    print(f'Alice usdt balance {usdt.balanceOf(alice)}')

    # steal some LP from the staking pool
    lp.transfer(
        alice, 10**6 * 10**18, {
            'from':
            accounts.at('0x8038c01a0390a8c547446a0b2c18fc9aefecc10c',
                        force=True)
        })

    # set approval
    dai.approve(homora, 2**256 - 1, {'from': alice})
    dai.approve(crdai, 2**256 - 1, {'from': alice})
    usdc.approve(homora, 2**256 - 1, {'from': alice})
    usdc.approve(crusdc, 2**256 - 1, {'from': alice})
    usdt.approve(homora, 2**256 - 1, {'from': alice})
    usdt.approve(crusdt, 2**256 - 1, {'from': alice})
    lp.approve(homora, 2**256 - 1, {'from': alice})

    curve_spell = CurveSpellV1.deploy(homora, werc20, registry,
                                      {'from': admin})

    # set up pools
    curve_spell.registerPool(lp)

    # first time call to reduce gas
    curve_spell.ensureApprove3(lp, {'from': admin})

    #####################################################################################

    # TODO: Test borrow 3 assets simultaneously
    print(
        '========================================================================='
    )
    print('Case 1.')

    prevABal = dai.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevCBal = usdt.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    dai_amt = 20000 * 10**18  # 20000 DAI
    usdc_amt = 50000 * 10**6  # 50000 USDC
    usdt_amt = 40000 * 10**6  # 40000 USDT
    lp_amt = 1 * 10**18
    borrow_dai_amt = 2000 * 10**18  # 2000 DAI
    borrow_usdc_amt = 200 * 10**6  # 200 USDC
    borrow_usdt_amt = 0  # 1000 * 10**6  # 1000 USDT
    borrow_lp_amt = 0
    minLPMint = 0

    tx = homora.execute(
        0,
        curve_spell,
        curve_spell.addLiquidity3.encode_input(
            lp,  # LP
            [dai_amt, usdc_amt, usdt_amt],  # supply tokens
            lp_amt,  # supply LP
            [borrow_dai_amt, borrow_usdc_amt, borrow_usdt_amt
             ],  # borrow tokens
            borrow_lp_amt,  # borrow LP
            minLPMint  # min LP mint
        ),
        {'from': alice})

    position_id = tx.return_value
    print('position_id', position_id)
    print('tx gas used', tx.gas_used)

    curABal = dai.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curCBal = usdt.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    print('spell lp balance', lp.balanceOf(curve_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta C balance', curCBal - prevCBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, daiDebt, daiDebtShare = homora.getBankInfo(dai)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, lpDebt, usdcDebtShare = homora.getBankInfo(usdc)

    print('bank dai totalDebt', daiDebt)
    print('bank dai totalShare', daiDebtShare)

    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('bank usdc totalDebt', usdcDebt)
    print('bank usdc totalShare', usdcDebtShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('werc20 prev LP balance', prevLPBal_werc20)
    print('werc20 cur LP balance', curLPBal_werc20)

    # alice
    assert almostEqual(curABal - prevABal, -dai_amt), 'incorrect DAI amt'
    assert almostEqual(curBBal - prevBBal, -usdc_amt), 'incorrect USDC amt'
    assert almostEqual(curCBal - prevCBal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curLPBal - prevLPBal, -lp_amt), 'incorrect LP amt'

    # spell
    assert dai.balanceOf(curve_spell) == 0, 'non-zero spell DAI balance'
    assert usdc.balanceOf(curve_spell) == 0, 'non-zero spell USDC balance'
    assert usdt.balanceOf(curve_spell) == 0, 'non-zero spell USDT balance'
    assert lp.balanceOf(curve_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert daiDebt == borrow_dai_amt
    assert usdcDebt == borrow_usdc_amt
    assert usdtDebt == borrow_usdt_amt

    # #####################################################################################

    print(
        '========================================================================='
    )
    print('Case 2.')

    # remove liquidity from the same position
    prevABal = dai.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevCBal = usdt.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    _, _, _, collSize = homora.getPositionInfo(position_id)

    lp_take_amt = 2**256 - 1  # max
    lp_want = 1 * 10**17
    dai_repay = 2**256 - 1  # max
    usdc_repay = 2**256 - 1  # max
    usdt_repay = 2**256 - 1  # max
    lp_repay = 0

    tx = homora.execute(
        position_id,
        curve_spell,
        curve_spell.removeLiquidity3.encode_input(
            lp,  # LP token
            lp_take_amt,  # LP amount to take out
            lp_want,  # LP amount to withdraw to wallet
            [dai_repay, usdc_repay, usdt_repay],  # repay amounts
            lp_repay,  # repay LP amount
            [0, 0, 0]  # min amounts
        ),
        {'from': alice})

    position_id = tx.return_value
    print('position_id', position_id)
    print('tx gas used', tx.gas_used)

    curABal = dai.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curCBal = usdt.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    print('spell lp balance', lp.balanceOf(curve_spell))
    print('spell dai balance', dai.balanceOf(curve_spell))
    print('spell usdc balance', usdc.balanceOf(curve_spell))
    print('spell usdt balance', usdt.balanceOf(curve_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta C balance', curCBal - prevCBal)
    print('Alice delta LP balance', curLPBal - prevLPBal)
    print('remove liquidity gas', tx.gas_used)
    print('bank delta lp balance', curLPBal_bank - prevLPBal_bank)
    print('bank total lp balance', curLPBal_bank)

    _, _, _, daiDebt, daiDebtShare = homora.getBankInfo(dai)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    print('bank dai totalDebt', daiDebt)
    print('bank dai totalDebt', daiDebt)

    print('bank usdc totalShare', usdcDebtShare)
    print('bank usdc totalShare', usdcDebtShare)

    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('LP want', lp_want)

    print('bank delta LP amount', curLPBal_bank - prevLPBal_bank)
    print('LP take amount', lp_take_amt)

    print('prev werc20 LP balance', prevLPBal_werc20)
    print('cur werc20 LP balance', curLPBal_werc20)

    print('coll size', collSize)

    # alice
    assert almostEqual(curLPBal - prevLPBal, lp_want), 'incorrect LP amt'

    # werc20
    assert almostEqual(curLPBal_werc20 - prevLPBal_werc20,
                       -collSize), 'incorrect werc20 LP amt'

    # spell
    assert dai.balanceOf(curve_spell) == 0, 'non-zero spell DAI balance'
    assert usdc.balanceOf(curve_spell) == 0, 'non-zero spell USDC balance'
    assert usdt.balanceOf(curve_spell) == 0, 'non-zero spell USDT balance'
    assert lp.balanceOf(curve_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert usdcDebt == 0, 'usdcDebt should be 0'
    assert daiDebt == 0, 'daiDebt should be 0'
    assert usdtDebt == 0, 'usdtDebt should be 0'

    return tx
def main():
    admin = accounts[0]

    alice = accounts[1]
    bob = accounts[2]
    dai = interface.IERC20Ex('0x6B175474E89094C44Da98b954EedeAC495271d0F')
    usdc = interface.IERC20Ex('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48')
    usdt = interface.IERC20Ex('0xdAC17F958D2ee523a2206206994597C13D831ec7')
    susd = interface.IERC20Ex('0x57ab1ec28d129707052df4df418d58a2d46d5f51')

    ydai = interface.IERC20Ex('0xC2cB1040220768554cf699b0d863A3cd4324ce32')
    yusdc = interface.IERC20Ex('0x26EA744E5B887E5205727f55dFBE8685e3b21951')
    yusdt = interface.IERC20Ex('0xE6354ed5bC4b393a5Aad09f21c46E101e692d447')
    ybusd = interface.IERC20Ex('0x04bC0Ab673d88aE9dbC9DA2380cB6B79C4BCa9aE')

    lp = interface.IERC20Ex('0xC25a3A3b969415c80451098fa907EC722572917F')
    pool = interface.ICurvePool('0xA5407eAE9Ba41422680e2e00537571bcC53efBfD')
    lp_busd = interface.IERC20Ex('0x3B3Ac5386837Dc563660FB6a0937DFAa5924333B')
    pool_busd = interface.ICurvePool('0x79a8C46DeA5aDa233ABaFFD40F3A0A2B1e5A4F27')
    registry = interface.ICurveRegistry('0x7d86446ddb609ed0f5f8684acf30380a356b2b4c')

    crdai = interface.ICErc20('0x92B767185fB3B04F881e3aC8e5B0662a027A1D9f')
    crusdc = interface.ICErc20('0x44fbebd2f576670a6c33f6fc0b00aa8c5753b322')
    crusdt = interface.ICErc20('0x797AAB1ce7c01eB727ab980762bA88e7133d2157')
    crsusd = MockCErc20.deploy(susd, {'from': admin})
    crydai = MockCErc20.deploy(ydai, {'from': admin})
    cryusdc = MockCErc20.deploy(yusdc, {'from': admin})
    cryusdt = MockCErc20.deploy(yusdt, {'from': admin})
    crybusd = MockCErc20.deploy(ybusd, {'from': admin})

    gauge = accounts.at(
        '0xA90996896660DEcC6E997655E065b23788857849', force=True)
    wgauge = WLiquidityGauge.deploy(
        registry, '0xD533a949740bb3306d119CC777fa900bA034cd52', {'from': admin})
    crv = interface.IERC20Ex(wgauge.crv())

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([dai, usdt, usdc, susd, ydai, yusdc, yusdt, ybusd],
                           [2**112 // 700, 2**112 * 10**12 // 700, 2**112 * 10**12 // 700, 2**112 // 700, 2**112 // 700, 2**112 * 10**12 // 700, 2**112 * 10**12 // 700, 2**112 // 700])

    curve_oracle = CurveOracle.deploy(simple_oracle, registry, {'from': admin})
    curve_oracle.registerPool(lp)  # update pool info
    curve_oracle.registerPool(lp_busd)  # update pool info

    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20, wgauge], True, {'from': admin})
    core_oracle.setRoute(
        [dai, usdc, usdt, susd, lp,  ydai, yusdc, yusdt, ybusd,  lp_busd, ],
        [simple_oracle, simple_oracle, simple_oracle, simple_oracle, curve_oracle,
            simple_oracle, simple_oracle, simple_oracle, simple_oracle, curve_oracle],
        {'from': admin},
    )

    oracle.setOracles(
        [dai, usdc, usdt, susd, lp,  ydai, yusdc, yusdt, ybusd,  lp_busd],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    # initialize
    homora = HomoraBank.deploy({'from': admin})
    print('donator ether', accounts[5].balance())
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)

    # add bank
    homora.addBank(dai, crdai, {'from': admin})
    homora.addBank(usdc, crusdc, {'from': admin})
    homora.addBank(usdt, crusdt, {'from': admin})
    homora.addBank(susd, crsusd, {'from': admin})
    homora.addBank(ydai, crydai, {'from': admin})
    homora.addBank(yusdc, cryusdc, {'from': admin})
    homora.addBank(yusdt, cryusdt, {'from': admin})
    homora.addBank(ybusd, crybusd, {'from': admin})

    # setup initial funds to alice
    mint_tokens(dai, alice,)
    mint_tokens(usdc, alice)
    mint_tokens(usdt, alice)
    mint_tokens(susd, alice)

    mint_tokens(ydai, alice)
    mint_tokens(yusdc, alice)
    mint_tokens(yusdt, alice)
    mint_tokens(ybusd, alice)

    # check alice's funds
    print(f'Alice dai balance {dai.balanceOf(alice)}')
    print(f'Alice usdc balance {usdc.balanceOf(alice)}')
    print(f'Alice usdt balance {usdt.balanceOf(alice)}')
    print(f'Alice susd balance {susd.balanceOf(alice)}')
    print(f'Alice ydai balance {ydai.balanceOf(alice)}')
    print(f'Alice yusdc balance {yusdc.balanceOf(alice)}')
    print(f'Alice yusdt balance {yusdt.balanceOf(alice)}')
    print(f'Alice ybusd balance {ybusd.balanceOf(alice)}')

    # steal some LP from the staking pool
    mint_tokens(lp, alice)
    mint_tokens(lp, bob)
    mint_tokens(lp_busd, alice)

    # set approval
    dai.approve(homora, 2**256-1, {'from': alice})
    dai.approve(crdai, 2**256-1, {'from': alice})
    usdc.approve(homora, 2**256-1, {'from': alice})
    usdc.approve(crusdc, 2**256-1, {'from': alice})
    usdt.approve(homora, 2**256-1, {'from': alice})
    usdt.approve(crusdt, 2**256-1, {'from': alice})
    susd.approve(homora, 2**256-1, {'from': alice})
    susd.approve(crsusd, 2**256-1, {'from': alice})

    ydai.approve(homora, 2**256-1, {'from': alice})
    ydai.approve(crydai, 2**256-1, {'from': alice})
    yusdc.approve(homora, 2**256-1, {'from': alice})
    yusdc.approve(cryusdc, 2**256-1, {'from': alice})
    yusdt.approve(homora, 2**256-1, {'from': alice})
    yusdt.approve(cryusdt, 2**256-1, {'from': alice})
    ybusd.approve(homora, 2**256-1, {'from': alice})
    ybusd.approve(crybusd, 2**256-1, {'from': alice})

    lp.approve(homora, 2**256-1, {'from': alice})
    lp.approve(gauge, 2**256-1, {'from': bob})
    lp_busd.approve(homora, 2**256-1, {'from': alice})

    curve_spell = CurveSpellV1.deploy(
        homora, werc20, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', wgauge, {'from': admin})

    # register gauge
    wgauge.registerGauge(12, 0)
    wgauge.registerGauge(1, 0)

    # set up pools
    curve_spell.getPool(lp)
    curve_spell.getPool(lp_busd)

    # first time call to reduce gas
    curve_spell.ensureApproveN(lp, 4, {'from': admin})
    curve_spell.ensureApproveN(lp_busd, 4, {'from': admin})

    #####################################################################################

    print('=========================================================================')
    print('Case 1.')

    prevABal = dai.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevCBal = usdt.balanceOf(alice)
    prevDBal = susd.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_gauge = lp.balanceOf(gauge)

    dai_amt = 200 * 10**18
    usdc_amt = 1000 * 10**6
    usdt_amt = 1000 * 10**6
    susd_amt = 1000 * 10**18
    lp_amt = 0
    borrow_dai_amt = 10 * 10**18
    borrow_usdc_amt = 10**6
    borrow_usdt_amt = 0
    borrow_susd_amt = 0
    borrow_lp_amt = 0
    minLPMint = 0

    pid = 12
    gid = 0

    tx = homora.execute(
        0,
        curve_spell,
        curve_spell.addLiquidity4.encode_input(
            lp,  # LP
            [dai_amt, usdc_amt, usdt_amt, susd_amt],  # supply tokens
            lp_amt,  # supply LP
            [borrow_dai_amt, borrow_usdc_amt, borrow_usdt_amt, borrow_susd_amt],  # borrow tokens
            borrow_lp_amt,  # borrow LP
            minLPMint,  # min LP mint
            pid,
            gid
        ),
        {'from': alice}
    )

    curABal = dai.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curCBal = usdt.balanceOf(alice)
    curDBal = susd.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_gauge = lp.balanceOf(gauge)

    print('spell lp balance', lp.balanceOf(curve_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta C balance', curCBal - prevCBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, daiDebt, daiDebtShare = homora.getBankInfo(dai)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, susdDebt, susdDebtShare = homora.getBankInfo(susd)

    print('bank dai totalDebt', daiDebt)
    print('bank dai totalShare', daiDebtShare)

    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('bank usdc totalDebt', usdcDebt)
    print('bank usdc totalShare', usdcDebtShare)

    print('bank susd totalDebt', susdDebt)
    print('bank susd totalShare', susdDebtShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('gauge prev LP balance', prevLPBal_gauge)
    print('gauge cur LP balance', curLPBal_gauge)

    # alice
    assert almostEqual(curABal - prevABal, -dai_amt), 'incorrect DAI amt'
    assert almostEqual(curBBal - prevBBal, -usdc_amt), 'incorrect USDC amt'
    assert almostEqual(curCBal - prevCBal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curDBal - prevDBal, -susd_amt), 'incorrect SUSD amt'
    assert almostEqual(curLPBal - prevLPBal, -lp_amt), 'incorrect LP amt'

    # spell
    assert dai.balanceOf(curve_spell) == 0, 'non-zero spell DAI balance'
    assert usdc.balanceOf(curve_spell) == 0, 'non-zero spell USDC balance'
    assert usdt.balanceOf(curve_spell) == 0, 'non-zero spell USDT balance'
    assert susd.balanceOf(curve_spell) == 0, 'non-zero spell SUSD balance'
    assert lp.balanceOf(curve_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert daiDebt == borrow_dai_amt
    assert usdcDebt == borrow_usdc_amt
    assert usdtDebt == borrow_usdt_amt
    assert susdDebt == borrow_susd_amt

    _, _, collId, collSize = homora.getPositionInfo(1)
    print('collSize', collSize)

    # staking directly
    prevCrv = crv.balanceOf(bob)
    print('bob lp balance', interface.IERC20Ex(lp).balanceOf(bob))
    pid, gid = 12, 0
    gauge, _ = wgauge.gauges(pid, gid)
    print('gauge', gauge)
    tx = interface.ILiquidityGauge(gauge).deposit(collSize, {'from': bob})

    chain.sleep(20000)

    prevAliceCrvBalance = crv.balanceOf(alice)
    print('Alice crv balance', prevAliceCrvBalance)

    # #####################################################################################

    print('=========================================================================')
    print('Case 2. add liqudiity second time')

    prevABal = dai.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevCBal = usdt.balanceOf(alice)
    prevDBal = susd.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_gauge = lp.balanceOf(gauge)

    dai_amt = 200 * 10**18
    usdc_amt = 1000 * 10**6
    usdt_amt = 1000 * 10**6
    susd_amt = 1000 * 10**18
    lp_amt = 0
    borrow_dai_amt = 10 * 10**18
    borrow_usdc_amt = 10**6
    borrow_usdt_amt = 0
    borrow_susd_amt = 0
    borrow_lp_amt = 0
    minLPMint = 0

    pid = 12
    gid = 0

    tx = homora.execute(
        1,
        curve_spell,
        curve_spell.addLiquidity4.encode_input(
            lp,  # LP
            [dai_amt, usdc_amt, usdt_amt, susd_amt],  # supply tokens
            lp_amt,  # supply LP
            [borrow_dai_amt, borrow_usdc_amt, borrow_usdt_amt, borrow_susd_amt],  # borrow tokens
            borrow_lp_amt,  # borrow LP
            minLPMint,  # min LP mint
            pid,
            gid
        ),
        {'from': alice}
    )

    curABal = dai.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curCBal = usdt.balanceOf(alice)
    curDBal = susd.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_gauge = lp.balanceOf(gauge)

    print('spell lp balance', lp.balanceOf(curve_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta C balance', curCBal - prevCBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, daiDebt, daiDebtShare = homora.getBankInfo(dai)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, susdDebt, susdDebtShare = homora.getBankInfo(susd)

    print('bank dai totalDebt', daiDebt)
    print('bank dai totalShare', daiDebtShare)

    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('bank usdc totalDebt', usdcDebt)
    print('bank usdc totalShare', usdcDebtShare)

    print('bank susd totalDebt', susdDebt)
    print('bank susd totalShare', susdDebtShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('gauge prev LP balance', prevLPBal_gauge)
    print('gauge cur LP balance', curLPBal_gauge)

    # alice
    assert almostEqual(curABal - prevABal, -dai_amt), 'incorrect DAI amt'
    assert almostEqual(curBBal - prevBBal, -usdc_amt), 'incorrect USDC amt'
    assert almostEqual(curCBal - prevCBal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curDBal - prevDBal, -susd_amt), 'incorrect SUSD amt'
    assert almostEqual(curLPBal - prevLPBal, -lp_amt), 'incorrect LP amt'

    # spell
    assert dai.balanceOf(curve_spell) == 0, 'non-zero spell DAI balance'
    assert usdc.balanceOf(curve_spell) == 0, 'non-zero spell USDC balance'
    assert usdt.balanceOf(curve_spell) == 0, 'non-zero spell USDT balance'
    assert susd.balanceOf(curve_spell) == 0, 'non-zero spell SUSD balance'
    assert lp.balanceOf(curve_spell) == 0, 'non-zero spell LP balance'

    curAliceCrvBalance = crv.balanceOf(alice)
    print('Alice crv balance', curAliceCrvBalance)
    receivedCrv = curAliceCrvBalance - prevAliceCrvBalance
    print('received crv', receivedCrv)

    # check with staking directly
    minter = interface.ILiquidityGaugeMinter(interface.ILiquidityGauge(gauge).minter())
    print('minter', minter)
    tx = minter.mint(gauge, {'from': bob})
    print('tx status', tx.status)
    tx = interface.ILiquidityGauge(gauge).withdraw(collSize, {'from': bob})
    receivedCrvFromGauge = crv.balanceOf(bob) - prevCrv
    print('receivedCrvFromGauge', receivedCrvFromGauge)
    assert almostEqual(receivedCrv, receivedCrvFromGauge)

    return tx
示例#10
0

lp = interface.IERC20Ex('0xd8e9690eff99e21a2de25e0b148ffaf47f47c972')
# pool is lp for balancer
pool = interface.IBalancerPool('0xd8e9690eff99e21a2de25e0b148ffaf47f47c972')

crdfd = MockCErc20.deploy(dfd, {'from': admin})
crdusd = MockCErc20.deploy(dusd, {'from': admin})

werc20 = WERC20.deploy({'from': admin})

staking = accounts.at('0xf068236ecad5fabb9883bbb26a6445d6c7c9a924', force=True)

wstaking = WStakingRewards.deploy(staking, lp, dfd, {'from': admin})

simple_oracle = SimpleOracle.deploy({'from': admin})
simple_oracle.setETHPx([dfd, dusd], [2**112 // 2 // 700, 2**112 * 2 // 700])

balancer_oracle = BalancerPairOracle.deploy(simple_oracle, {'from': alice})

core_oracle = CoreOracle.deploy({'from': admin})
oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
oracle.setWhitelistERC1155([werc20, wstaking], True, {'from': admin})
core_oracle.setRoute(
    [dfd, dusd, lp],
    [simple_oracle, simple_oracle, balancer_oracle],
    {'from': admin},
)
oracle.setOracles(
    [dfd, dusd, lp],
    [
def main():
    admin = accounts[0]

    alice = accounts[1]
    usdt = interface.IERC20Ex('0xdAC17F958D2ee523a2206206994597C13D831ec7')
    usdc = interface.IERC20Ex('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48')

    lp = interface.IERC20Ex('0x3041cbd36888becc7bbcbc0045e3b1f144466f5f')
    crusdt = interface.ICErc20('0x797AAB1ce7c01eB727ab980762bA88e7133d2157')
    crusdc = interface.ICErc20('0x44fbebd2f576670a6c33f6fc0b00aa8c5753b322')

    router = interface.IUniswapV2Router02(
        '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D')

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([usdt, usdc], [8343331721347310729683379470025550036595362,
                                          8344470555541464992529317899641128796042472])

    uniswap_oracle = UniswapV2Oracle.deploy(simple_oracle, {'from': admin})

    oracle = ProxyOracle.deploy({'from': admin})
    oracle.setWhitelistERC1155([werc20], True, {'from': admin})
    oracle.setOracles(
        [
            '0xdAC17F958D2ee523a2206206994597C13D831ec7',  # USDT
            '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',  # USDC
            '0x3041cbd36888becc7bbcbc0045e3b1f144466f5f',  # USDT-USDC
        ],
        [
            [simple_oracle, 10000, 10000, 10000],
            [simple_oracle, 10000, 10000, 10000],
            [uniswap_oracle, 10000, 10000, 10000],
        ],
        {'from': admin},
    )

    # initialize
    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)

    # add bank
    homora.addBank(usdt, crusdt, {'from': admin})
    homora.addBank(usdc, crusdc, {'from': admin})

    # setup initial funds 10^5 USDT + 10^5 USDC to alice
    setup_transfer(usdt, accounts.at('0xbe0eb53f46cd790cd13851d5eff43d12404d33e8',
                                     force=True), alice, 10**6 * 10**6)
    setup_transfer(usdc, accounts.at('0xa191e578a6736167326d05c119ce0c90849e84b7',
                                     force=True), alice, 10**6 * 10**6)

    # setup initial funds 10^6 USDT + 10^6 USDC to homora bank
    setup_transfer(usdt, accounts.at('0xbe0eb53f46cd790cd13851d5eff43d12404d33e8',
                                     force=True), homora, 10**6 * 10**6)
    setup_transfer(usdc, accounts.at('0x397ff1542f962076d0bfe58ea045ffa2d347aca0',
                                     force=True), homora, 10**6 * 10**6)

    # check alice's funds
    print(f'Alice usdt balance {usdt.balanceOf(alice)}')
    print(f'Alice usdc balance {usdc.balanceOf(alice)}')

    # steal some LP from the staking pool
    lp.transfer(alice, 1*10**8,
                {'from': accounts.at('0x85af1678527f63eb6492ab158ed5d2a94b8732c0', force=True)})

    # set approval
    usdt.approve(homora, 2**256-1, {'from': alice})
    usdt.approve(crusdt, 2**256-1, {'from': alice})
    usdc.approve(homora, 2**256-1, {'from': alice})
    usdc.approve(crusdc, 2**256-1, {'from': alice})
    lp.approve(homora, 2**256-1, {'from': alice})

    uniswap_spell = UniswapV2SpellV1.deploy(
        homora, werc20, router, {'from': admin})
    # first time call to reduce gas
    uniswap_spell.getPair(usdt, usdc, {'from': admin})

    #####################################################################################

    print('=========================================================================')
    print('Case 1.')

    prevABal = usdt.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    usdt_amt = 40000 * 10**6  # 40000 USDT
    usdc_amt = 50000 * 10**6  # 50000 USDC
    lp_amt = 1 * 10**7
    borrow_usdt_amt = 1000 * 10**6  # 1000 USDT
    borrow_usdc_amt = 200 * 10**6  # 200 USDC

    tx = homora.execute(
        0,
        uniswap_spell,
        uniswap_spell.addLiquidity.encode_input(
            usdt,  # token 0
            usdc,  # token 1
            [usdt_amt,  # supply USDT
             usdc_amt,   # supply USDC
             lp_amt,  # supply LP
             borrow_usdt_amt,  # borrow USDT
             borrow_usdc_amt,  # borrow USDC
             0,  # borrow LP tokens
             0,  # min USDT
             0],  # min USDC
        ),
        {'from': alice}
    )

    position_id = tx.return_value
    print('position_id', position_id)

    curABal = usdt.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('bank usdc totalDebt', usdcDebt)
    print('bank usdc totalShare', usdcDebtShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('werc20 prev LP balance', prevLPBal_werc20)
    print('werc20 cur LP balance', curLPBal_werc20)

    # alice
    assert almostEqual(curABal - prevABal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curBBal - prevBBal, -usdc_amt), 'incorrect USDC amt'
    assert almostEqual(curLPBal - prevLPBal, -lp_amt), 'incorrect LP amt'

    # spell
    assert usdt.balanceOf(uniswap_spell) == 0, 'non-zero spell USDT balance'
    assert usdc.balanceOf(uniswap_spell) == 0, 'non-zero spell USDC balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert usdtDebt == borrow_usdt_amt
    assert usdcDebt == borrow_usdc_amt

    #####################################################################################

    print('=========================================================================')
    print('Case 2.')

    # remove liquidity from the same position
    prevABal = usdt.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    _, _, _, collSize = homora.getPositionInfo(position_id)

    lp_take_amt = 2**256-1  # max
    lp_want = 1 * 10**5
    usdt_repay = 2**256-1  # max
    usdc_repay = 2**256-1  # max

    tx = homora.execute(
        position_id,
        uniswap_spell,
        uniswap_spell.removeLiquidity.encode_input(
            usdt,  # token 0
            usdc,  # token 1
            [lp_take_amt,  # take out LP tokens
             lp_want,   # withdraw LP tokens to wallet
             usdt_repay,  # repay USDT
             usdc_repay,   # repay USDC
             0,   # repay LP
             0,   # min USDT
             0],  # min USDC
        ),
        {'from': alice}
    )

    curABal = usdt.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('spell usdt balance', usdt.balanceOf(uniswap_spell))
    print('spell usdc balance', usdc.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta LP balance', curLPBal - prevLPBal)
    print('remove liquidity gas', tx.gas_used)
    print('bank delta lp balance', curLPBal_bank - prevLPBal_bank)
    print('bank total lp balance', curLPBal_bank)

    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('bank usdc totalDebt', usdcDebt)
    print('bank usdc totalShare', usdcDebtShare)

    print('LP want', lp_want)

    print('bank delta LP amount', curLPBal_bank - prevLPBal_bank)
    print('LP take amount', lp_take_amt)

    print('prev werc20 LP balance', prevLPBal_werc20)
    print('cur werc20 LP balance', curLPBal_werc20)

    print('coll size', collSize)

    # alice
    assert almostEqual(curLPBal - prevLPBal, lp_want), 'incorrect LP amt'

    # werc20
    assert almostEqual(curLPBal_werc20 - prevLPBal_werc20, -
                       collSize), 'incorrect werc20 LP amt'

    # spell
    assert usdt.balanceOf(uniswap_spell) == 0, 'non-zero spell USDT balance'
    assert usdc.balanceOf(uniswap_spell) == 0, 'non-zero spell USDC balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert usdtDebt == 0, 'usdtDebt should be 0'
    assert usdcDebt == 0, 'usdcDebt should be 0'

    return tx
def main():
    admin = accounts[0]

    alice = accounts[1]
    usdt = interface.IERC20Ex('0xdAC17F958D2ee523a2206206994597C13D831ec7')
    usdc = interface.IERC20Ex('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48')

    lp = interface.IERC20Ex('0x3041cbd36888becc7bbcbc0045e3b1f144466f5f')
    crusdt = interface.ICErc20('0x797AAB1ce7c01eB727ab980762bA88e7133d2157')
    crusdc = interface.ICErc20('0x44fbebd2f576670a6c33f6fc0b00aa8c5753b322')

    router = interface.IUniswapV2Router02(
        '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D')

    chef = accounts.at('0xc2edad668740f1aa35e4d8f227fb8e17dca888cd',
                       force=True)
    wchef = WMasterChef.deploy(chef, {'from': admin})

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([usdt, usdc], [
        8343331721347310729683379470025550036595362,
        8344470555541464992529317899641128796042472
    ])

    uniswap_oracle = UniswapV2Oracle.deploy(simple_oracle, {'from': admin})

    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20], True, {'from': admin})
    core_oracle.setRoute(
        [usdt, usdc, lp],
        [simple_oracle, simple_oracle, uniswap_oracle],
        {'from': admin},
    )
    oracle.setOracles(
        [usdt, usdc, lp],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    # initialize
    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)

    # add bank
    homora.addBank(usdt, crusdt, {'from': admin})
    homora.addBank(usdc, crusdc, {'from': admin})

    # setup initial funds to alice
    mint_tokens(usdt, alice)
    mint_tokens(usdc, alice)

    # check alice's funds
    print(f'Alice usdt balance {usdt.balanceOf(alice)}')
    print(f'Alice usdc balance {usdc.balanceOf(alice)}')

    # steal some LP from the staking pool
    mint_tokens(lp, alice)

    # set approval
    usdt.approve(homora, 2**256 - 1, {'from': alice})
    usdt.approve(crusdt, 2**256 - 1, {'from': alice})
    usdc.approve(homora, 2**256 - 1, {'from': alice})
    usdc.approve(crusdc, 2**256 - 1, {'from': alice})
    lp.approve(homora, 2**256 - 1, {'from': alice})

    uniswap_spell = UniswapV2SpellV1.deploy(homora, werc20, router,
                                            {'from': admin})
    # first time call to reduce gas
    uniswap_spell.getPair(usdt, usdc, {'from': admin})

    # whitelist spell in bank
    homora.setWhitelistSpells([uniswap_spell], [True], {'from': admin})

    # whitelist token in bank
    homora.setWhitelistTokens([usdt, usdc], [True, True], {'from': admin})

    # whitelist lp in spell
    uniswap_spell.setWhitelistLPTokens([lp], [True], {'from': admin})

    #####################################################################################
    # add liquidity

    print(
        '========================================================================='
    )
    print('Case 1.')

    prevABal = usdt.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    usdt_amt = 40000 * 10**6
    usdc_amt = 50000 * 10**6
    lp_amt = 1 * 10**7
    borrow_usdt_amt = 1000 * 10**6
    borrow_usdc_amt = 200 * 10**6

    tx = homora.execute(
        0,
        uniswap_spell,
        uniswap_spell.addLiquidityWERC20.encode_input(
            usdt,  # token 0
            usdc,  # token 1
            [
                usdt_amt,  # supply USDT
                usdc_amt,  # supply USDC
                lp_amt,  # supply LP
                borrow_usdt_amt,  # borrow USDT
                borrow_usdc_amt,  # borrow USDC
                0,  # borrow 0 LP tokens
                0,  # min USDT
                0
            ],  # min USDC
        ),
        {'from': alice})

    curABal = usdt.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    print('bank usdt debt', usdtDebt)
    print('bank usdt debt share', usdtDebtShare)

    print('bank usdc debt', usdcDebt)
    print('bank usdc debt share', usdcDebtShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('werc20 prev LP balance', prevLPBal_werc20)
    print('werc20 cur LP balance', curLPBal_werc20)

    print('prev usdt res', prevARes)
    print('cur usdt res', curARes)

    print('prev usdc res', prevBRes)
    print('cur usdc res', curBRes)

    # alice
    assert almostEqual(curABal - prevABal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curBBal - prevBBal, -usdc_amt), 'incorrect USDC amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert usdt.balanceOf(uniswap_spell) == 0, 'non-zero spell USDT balance'
    assert usdc.balanceOf(uniswap_spell) == 0, 'non-zero spell USDC balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'
    assert usdtDebt == borrow_usdt_amt
    assert usdcDebt == borrow_usdc_amt

    # check balance and pool reserves
    assert curABal - prevABal - borrow_usdt_amt == - \
        (curARes - prevARes), 'not all USDT tokens go to LP pool'
    assert curBBal - prevBBal - borrow_usdc_amt == - \
        (curBRes - prevBRes), 'not all USDC tokens go to LP pool'

    #####################################################################################
    # add liquidity to the same position

    print(
        '========================================================================='
    )
    print('Case 2.')

    prevABal = usdt.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    usdt_amt = 20000 * 10**6
    usdc_amt = 30000 * 10**6
    lp_amt = 1 * 10**7
    borrow_usdt_amt = 1000 * 10**6
    borrow_usdc_amt = 200 * 10**6

    tx = homora.execute(
        1,
        uniswap_spell,
        uniswap_spell.addLiquidityWERC20.encode_input(
            usdt,  # token 0
            usdc,  # token 1
            [
                usdt_amt,  # supply USDT
                usdc_amt,  # supply USDC
                lp_amt,  # supply LP
                borrow_usdt_amt,  # borrow USDT
                borrow_usdc_amt,  # borrow USDC
                0,  # borrow LP
                0,  # min USDT
                0
            ],  # min USDC
        ),
        {'from': alice})

    curABal = usdt.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    if interface.IUniswapV2Pair(lp).token0() == usdt:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    print('bank usdt debt', usdtDebt)
    print('bank usdt debt share', usdtDebtShare)

    print('bank usdc debt', usdcDebt)
    print('bank usdc debt share', usdcDebtShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('werc20 prev LP balance', prevLPBal_werc20)
    print('werc20 cur LP balance', curLPBal_werc20)

    print('prev usdt res', prevARes)
    print('cur usdt res', curARes)

    print('prev usdc res', prevBRes)
    print('cur usdc res', curBRes)

    # alice
    assert almostEqual(curABal - prevABal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curBBal - prevBBal, -usdc_amt), 'incorrect USDC amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert usdt.balanceOf(uniswap_spell) == 0, 'non-zero spell USDT balance'
    assert usdc.balanceOf(uniswap_spell) == 0, 'non-zero spell USDC balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'
    # borrow same amount twice (with small interest accrued)
    assert usdtDebt >= borrow_usdt_amt * 2
    # borrow same amount twice (with small interest accrued)
    assert usdcDebt >= borrow_usdc_amt * 2

    # check balance and pool reserves
    assert curABal - prevABal - borrow_usdt_amt == - \
        (curARes - prevARes), 'not all USDT tokens go to LP pool'
    assert curBBal - prevBBal - borrow_usdc_amt == - \
        (curBRes - prevBRes), 'not all USDC tokens go to LP pool'

    #####################################################################################
    # remove half liquidity from the same position

    print(
        '========================================================================='
    )
    print('Case 3.')

    prevABal = usdt.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    _, _, _, collSize = homora.getPositionInfo(1)

    lp_take_amt = collSize // 2
    lp_want = 1 * 10**5
    usdt_repay = MAX_INT
    usdc_repay = MAX_INT

    tx = homora.execute(
        1,
        uniswap_spell,
        uniswap_spell.removeLiquidityWERC20.encode_input(
            usdt,  # token 0
            usdc,  # token 1
            [
                lp_take_amt,  # take out LP
                lp_want,  # withdraw LP to wallet
                usdt_repay,  # repay USDT
                usdc_repay,  # repay USDC
                0,  # repay LP
                0,  # min USDT
                0
            ],  # min USDC
        ),
        {'from': alice})

    curABal = usdt.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('spell usdt balance', usdt.balanceOf(uniswap_spell))
    print('spell usdc balance', usdc.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta LP balance', curLPBal - prevLPBal)
    print('remove liquidity gas', tx.gas_used)
    print('bank delta lp balance', curLPBal_bank - prevLPBal_bank)
    print('bank total lp balance', curLPBal_bank)

    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('bank usdc totalDebt', usdcDebt)
    print('bank usdc totalShare', usdcDebtShare)

    print('LP want', lp_want)

    print('bank delta LP amount', curLPBal_bank - prevLPBal_bank)
    print('LP take amount', lp_take_amt)

    print('prev werc20 LP balance', prevLPBal_werc20)
    print('cur werc20 LP balance', curLPBal_werc20)

    print('coll size', collSize)

    # alice
    assert almostEqual(curLPBal - prevLPBal, lp_want), 'incorrect LP amt'

    # werc20
    assert almostEqual(curLPBal_werc20 - prevLPBal_werc20,
                       -lp_take_amt), 'incorrect werc20 LP amt'

    # spell
    assert usdt.balanceOf(uniswap_spell) == 0, 'non-zero spell USDT balance'
    assert usdc.balanceOf(uniswap_spell) == 0, 'non-zero spell USDC balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert usdtDebt == 0, 'usdtDebt should be 0'
    assert usdcDebt == 0, 'usdcDebt should be 0'

    #####################################################################################
    # remove remaining liquidity from the same position

    print(
        '========================================================================='
    )
    print('Case 4.')

    prevABal = usdt.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    _, _, _, collSize = homora.getPositionInfo(1)

    lp_take_amt = MAX_INT
    lp_want = 1 * 10**5
    usdt_repay = MAX_INT
    usdc_repay = MAX_INT

    tx = homora.execute(
        1,
        uniswap_spell,
        uniswap_spell.removeLiquidityWERC20.encode_input(
            usdt,  # token 0
            usdc,  # token 1
            [
                lp_take_amt,  # take out LP
                lp_want,  # withdraw LP to wallet
                usdt_repay,  # repay USDT
                usdc_repay,  # repay USDC
                0,  # repay LP
                0,  # min USDT
                0
            ],  # min USDC
        ),
        {'from': alice})

    curABal = usdt.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('spell usdt balance', usdt.balanceOf(uniswap_spell))
    print('spell usdc balance', usdc.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta LP balance', curLPBal - prevLPBal)
    print('remove liquidity gas', tx.gas_used)
    print('bank delta lp balance', curLPBal_bank - prevLPBal_bank)
    print('bank total lp balance', curLPBal_bank)

    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('bank usdc totalDebt', usdcDebt)
    print('bank usdc totalShare', usdcDebtShare)

    print('LP want', lp_want)

    print('bank delta LP amount', curLPBal_bank - prevLPBal_bank)
    print('LP take amount', lp_take_amt)

    print('prev werc20 LP balance', prevLPBal_werc20)
    print('cur werc20 LP balance', curLPBal_werc20)

    print('coll size', collSize)

    # alice
    assert almostEqual(curLPBal - prevLPBal, lp_want), 'incorrect LP amt'

    # werc20
    assert almostEqual(curLPBal_werc20 - prevLPBal_werc20,
                       -collSize), 'incorrect werc20 LP amt'

    # spell
    assert usdt.balanceOf(uniswap_spell) == 0, 'non-zero spell USDT balance'
    assert usdc.balanceOf(uniswap_spell) == 0, 'non-zero spell USDC balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert usdtDebt == 0, 'usdtDebt should be 0'
    assert usdcDebt == 0, 'usdcDebt should be 0'

    #####################################################################################
    # add liquidity without borrowing (1x)
    print(
        '========================================================================='
    )
    print('Case 5.')

    prevABal = usdt.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    usdt_amt = 2000 * 10**6  # 2000 USDT
    usdc_amt = 3000 * 10**6  # 3000 USDC
    lp_amt = 0
    borrow_usdt_amt = 0
    borrow_usdc_amt = 0
    prevBBal = usdc.balanceOf(alice)

    tx = homora.execute(
        1,
        uniswap_spell,
        uniswap_spell.addLiquidityWERC20.encode_input(
            usdt,  # token 0
            usdc,  # token 1
            [
                usdt_amt,  # supply USDT
                usdc_amt,  # supply USDC
                lp_amt,  # supply LP
                borrow_usdt_amt,  # borrow USDT
                borrow_usdc_amt,  # borrow USDC
                0,  # borrow LP
                0,  # min USDT
                0
            ],  # min USDC
        ),
        {'from': alice})

    curABal = usdt.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta lp balance', curLPBal - prevLPBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('bank usdc totalDebt', usdcDebt)
    print('bank usdc totalShare', usdcDebtShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('werc20 prev LP balance', prevLPBal_werc20)
    print('werc20 cur LP balance', curLPBal_werc20)

    # alice
    assert almostEqual(curABal - prevABal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curBBal - prevBBal, -usdc_amt), 'incorrect USDC amt'
    assert almostEqual(curLPBal - prevLPBal, -lp_amt), 'incorrect LP amt'

    # spell
    assert usdt.balanceOf(uniswap_spell) == 0, 'non-zero spell USDT balance'
    assert usdc.balanceOf(uniswap_spell) == 0, 'non-zero spell USDC balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert usdtDebt == borrow_usdt_amt
    assert usdcDebt == borrow_usdc_amt

    #####################################################################################
    # remove all liquidity from the same position
    print(
        '========================================================================='
    )
    print('Case 6.')

    prevABal = usdt.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    _, _, _, collSize = homora.getPositionInfo(1)

    lp_take_amt = MAX_INT  # max
    lp_want = 0
    usdt_repay = 0  # max
    usdc_repay = 0  # max

    tx = homora.execute(
        1,
        uniswap_spell,
        uniswap_spell.removeLiquidityWERC20.encode_input(
            usdt,
            usdc,
            [
                lp_take_amt,  # take out LP
                lp_want,  # withdraw LP to wallet
                usdt_repay,  # repay USDT
                usdc_repay,  # repay USDC
                0,  # repay LP
                0,  # min USDT
                0
            ],  # min USDC
        ),
        {'from': alice})

    curABal = usdt.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('spell usdt balance', usdt.balanceOf(uniswap_spell))
    print('spell usdc balance', usdc.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta LP balance', curLPBal - prevLPBal)
    print('remove liquidity gas', tx.gas_used)
    print('bank delta lp balance', curLPBal_bank - prevLPBal_bank)
    print('bank total lp balance', curLPBal_bank)

    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('bank usdc totalDebt', usdcDebt)
    print('bank usdc totalShare', usdcDebtShare)

    print('LP want', lp_want)

    print('bank delta LP amount', curLPBal_bank - prevLPBal_bank)
    print('LP take amount', lp_take_amt)

    print('prev werc20 LP balance', prevLPBal_werc20)
    print('cur werc20 LP balance', curLPBal_werc20)

    print('coll size', collSize)

    # alice
    assert almostEqual(curLPBal - prevLPBal, lp_want), 'incorrect LP amt'

    # werc20
    assert almostEqual(curLPBal_werc20 - prevLPBal_werc20,
                       -collSize), 'incorrect werc20 LP amt'

    # spell
    assert usdt.balanceOf(uniswap_spell) == 0, 'non-zero spell USDT balance'
    assert usdc.balanceOf(uniswap_spell) == 0, 'non-zero spell USDC balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert usdtDebt == 0, 'usdtDebt should be 0'
    assert usdcDebt == 0, 'usdcDebt should be 0'

    return tx
示例#13
0
def main():
    admin = accounts[0]

    alice = accounts[1]
    bob = accounts[2]
    dpi = interface.IERC20Ex('0x1494ca1f11d487c2bbe4543e90080aeba4ba3c2b')
    weth = interface.IERC20Ex('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')
    usdt = interface.IERC20Ex('0xdac17f958d2ee523a2206206994597c13d831ec7')

    lp = interface.IERC20Ex('0x4d5ef58aac27d99935e5b6b4a6778ff292059991')
    lp_usdt = interface.IERC20Ex('0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852')
    crdpi = MockCErc20.deploy(dpi, {'from': admin})
    crusdt = interface.ICErc20('0x797AAB1ce7c01eB727ab980762bA88e7133d2157')

    router = interface.IUniswapV2Router02(
        '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D')

    staking = accounts.at('0xB93b505Ed567982E2b6756177ddD23ab5745f309',
                          force=True)
    index = interface.IERC20Ex(
        interface.IStakingRewardsEx(staking).rewardsToken())

    wstaking = WStakingRewards.deploy(staking, lp, index, {'from': admin})

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([dpi, weth, usdt],
                           [2**112 * 100 // 700, 2**112, 2**112 // 700])

    uniswap_oracle = UniswapV2Oracle.deploy(simple_oracle, {'from': admin})
    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20, wstaking], True, {'from': admin})
    core_oracle.setRoute(
        [dpi, weth, lp, usdt, lp_usdt],
        [
            simple_oracle, simple_oracle, uniswap_oracle, simple_oracle,
            uniswap_oracle
        ],
        {'from': admin},
    )
    oracle.setOracles(
        [dpi, weth, lp, usdt, lp_usdt],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)
    homora.addBank(dpi, crdpi, {'from': admin})
    homora.addBank(usdt, crusdt, {'from': admin})

    # setup initial funds to alice
    mint_tokens(dpi, alice)
    mint_tokens(weth, alice)
    mint_tokens(usdt, alice)

    # check alice's funds
    print(f'Alice dpi balance {dpi.balanceOf(alice)}')
    print(f'Alice weth balance {weth.balanceOf(alice)}')

    # Steal some LP from the staking pool
    mint_tokens(lp, alice)
    mint_tokens(lp, bob)

    mint_tokens(lp_usdt, alice)
    mint_tokens(lp_usdt, bob)

    # set approval
    dpi.approve(homora, 2**256 - 1, {'from': alice})
    dpi.approve(crdpi, 2**256 - 1, {'from': alice})
    weth.approve(homora, 2**256 - 1, {'from': alice})
    usdt.approve(homora, 2**256 - 1, {'from': alice})
    usdt.approve(crusdt, 2**256 - 1, {'from': alice})
    lp.approve(homora, 2**256 - 1, {'from': alice})
    lp.approve(staking, 2**256 - 1, {'from': bob})

    uniswap_spell = UniswapV2SpellV1.deploy(homora, werc20, router,
                                            {'from': admin})
    # first time call to reduce gas
    uniswap_spell.getPair(weth, dpi, {'from': admin})

    # whitelist spell in bank
    homora.setWhitelistSpells([uniswap_spell], [True], {'from': admin})

    # whitelist lp in spell
    uniswap_spell.setWhitelistLPTokens([lp], [True], {'from': admin})

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 1. add liquidity first time')

    prevABal = dpi.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_staking = lp.balanceOf(staking)

    if interface.IUniswapV2Pair(lp).token0() == dpi:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    dpi_amt = 10 * 10**18
    weth_amt = 10**18
    lp_amt = 0
    borrow_dpi_amt = 0
    borrow_weth_amt = 0

    tx = homora.execute(
        0,
        uniswap_spell,
        uniswap_spell.addLiquidityWStakingRewards.encode_input(
            dpi,  # token 0
            weth,  # token 1
            [
                dpi_amt,  # supply DPI
                weth_amt,  # supply WETH
                lp_amt,  # supply LP
                borrow_dpi_amt,  # borrow DPI
                borrow_weth_amt,  # borrow WETH
                0,  # borrow LP tokens
                0,  # min DPI
                0
            ],  # min WETH
            wstaking,
        ),
        {'from': alice})

    curABal = dpi.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_staking = lp.balanceOf(staking)

    if interface.IUniswapV2Pair(lp).token0() == dpi:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, totalDebt, totalShare = homora.getBankInfo(dpi)
    print('bank dpi totalDebt', totalDebt)
    print('bank dpi totalShare', totalShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('staking prev LP balance', prevLPBal_staking)
    print('staking cur LP balance', curLPBal_staking)

    print('prev dpi res', prevARes)
    print('cur dpi res', curARes)

    print('prev weth res', prevBRes)
    print('cur weth res', curBRes)

    # alice
    assert almostEqual(curABal - prevABal, -dpi_amt), 'incorrect DPI amt'
    assert almostEqual(curBBal - prevBBal, -weth_amt), 'incorrect WETH amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert dpi.balanceOf(uniswap_spell) == 0, 'non-zero spell DPI balance'
    assert weth.balanceOf(uniswap_spell) == 0, 'non-zero spell WETH balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'
    assert totalDebt == borrow_dpi_amt

    # check balance and pool reserves
    assert curABal - prevABal - borrow_dpi_amt == - \
        (curARes - prevARes), 'not all DPI tokens go to LP pool'
    assert curBBal - prevBBal - borrow_weth_amt == - \
        (curBRes - prevBRes), 'not all WETH tokens go to LP pool'

    _, _, collId, collSize = homora.getPositionInfo(1)
    print('collSize', collSize)

    # staking directly
    prevIndex = index.balanceOf(bob)
    print('bob lp balance', interface.IERC20Ex(lp).balanceOf(bob))
    tx = interface.IStakingRewards(staking).stake(collSize, {'from': bob})

    chain.sleep(20000)

    prevAliceIndexBalance = index.balanceOf(alice)
    print('Alice index balance', prevAliceIndexBalance)

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 2. add liquidity (failed tx desired)')

    usdt_amt = 10 * 10**6
    weth_amt = 10**18
    lp_amt = 0
    borrow_usdt_amt = 0
    borrow_weth_amt = 0

    try:
        tx = homora.execute(
            1,
            uniswap_spell,
            uniswap_spell.addLiquidityWStakingRewards.encode_input(
                usdt,  # token 0
                weth,  # token 1
                [
                    usdt_amt,  # supply USDT
                    weth_amt,  # supply WETH
                    lp_amt,  # supply LP
                    borrow_usdt_amt,  # borrow USDT
                    borrow_weth_amt,  # borrow WETH
                    0,  # borrow LP tokens
                    0,  # min USDT
                    0
                ],  # min WETH
                wstaking,
            ),
            {'from': alice})
    except VirtualMachineError:
        pass

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 3. remove liquidity (failed tx desired)')

    lp_take_amt = collSize
    lp_want = 0
    usdt_repay = 0
    weth_repay = 0

    try:
        tx = homora.execute(
            1,
            uniswap_spell,
            uniswap_spell.removeLiquidityWStakingRewards.encode_input(
                usdt,  # token 0
                weth,  # token 1
                [
                    lp_take_amt,  # take out LP tokens
                    lp_want,  # withdraw LP tokens to wallet
                    usdt_repay,  # repay USDT
                    weth_repay,  # repay WETH
                    0,  # repay LP tokens
                    0,  # min USDT
                    0
                ],  # min WETH
                wstaking,
            ),
            {'from': alice})
    except VirtualMachineError:
        pass

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 4. remove liquidity')

    prevABal = dpi.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_staking = lp.balanceOf(staking)
    prevETHBal = alice.balance()

    if interface.IUniswapV2Pair(lp).token0() == dpi:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    lp_take_amt = collSize
    lp_want = 0
    dpi_repay = 0
    weth_repay = 0

    tx = homora.execute(
        1,
        uniswap_spell,
        uniswap_spell.removeLiquidityWStakingRewards.encode_input(
            dpi,  # token 0
            weth,  # token 1
            [
                lp_take_amt,  # take out LP tokens
                lp_want,  # withdraw LP tokens to wallet
                dpi_repay,  # repay DPI
                weth_repay,  # repay WETH
                0,  # repay LP tokens
                0,  # min DPI
                0
            ],  # min WETH
            wstaking,
        ),
        {'from': alice})

    curABal = dpi.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_staking = lp.balanceOf(staking)
    curETHBal = alice.balance()

    if interface.IUniswapV2Pair(lp).token0() == dpi:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, totalDebt, totalShare = homora.getBankInfo(dpi)
    print('bank dpi totalDebt', totalDebt)
    print('bank dpi totalShare', totalShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('wstaking prev LP balance', prevLPBal_staking)
    print('wstaking cur LP balance', curLPBal_staking)

    print('prev dpi res', prevARes)
    print('cur dpi res', curARes)

    print('prev weth res', prevBRes)
    print('cur weth res', curBRes)

    # alice
    assert almostEqual(curBBal - prevBBal, 0), 'incorrect WETH amt'
    assert almostEqual(curLPBal - prevLPBal, lp_want), 'incorrect LP amt'

    # staking
    assert almostEqual(curLPBal_staking - prevLPBal_staking,
                       -lp_take_amt), 'incorrect staking LP amt'

    # spell
    assert dpi.balanceOf(uniswap_spell) == 0, 'non-zero spell DPI balance'
    assert weth.balanceOf(uniswap_spell) == 0, 'non-zero spell WETH balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'

    # check balance and pool reserves
    assert almostEqual(curABal - prevABal + dpi_repay,
                       -(curARes - prevARes)), 'inconsistent DPI from withdraw'
    assert almostEqual(curBBal - prevBBal,
                       0), 'inconsistent WETH from withdraw'
    assert almostEqual(curETHBal - prevETHBal + weth_repay,
                       -(curBRes - prevBRes)), 'inconsistent ETH from withdraw'

    curAliceIndexBalance = index.balanceOf(alice)
    print('Alice index balance', curAliceIndexBalance)
    receivedIndex = curAliceIndexBalance - prevAliceIndexBalance
    print('received index', receivedIndex)

    # check with staking directly
    tx = interface.IStakingRewards(staking).getReward({'from': bob})
    receivedIndexFromStaking = index.balanceOf(bob) - prevIndex
    print('receivedIndexFromStaking', receivedIndexFromStaking)
    assert almostEqual(receivedIndex, receivedIndexFromStaking)
def main():
    admin = accounts[0]

    alice = accounts[1]
    bob = accounts[2]
    dai = interface.IERC20Ex('0x6B175474E89094C44Da98b954EedeAC495271d0F')
    usdc = interface.IERC20Ex('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48')
    usdt = interface.IERC20Ex('0xdAC17F958D2ee523a2206206994597C13D831ec7')
    adai = interface.IERC20Ex('0x028171bCA77440897B824Ca71D1c56caC55b68A3')
    ausdc = interface.IERC20Ex('0xBcca60bB61934080951369a648Fb03DF4F96263C')
    ausdt = interface.IERC20Ex('0x3Ed3B47Dd13EC9a98b44e6204A523E766B225811')

    lp = interface.IERC20Ex('0x6c3f90f043a72fa612cbac8115ee7e52bde6e490')
    pool = interface.ICurvePool('0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7')
    lp_aave = interface.IERC20Ex('0xFd2a8fA60Abd58Efe3EeE34dd494cD491dC14900')
    pool_aave = interface.ICurvePool(
        '0xDeBF20617708857ebe4F679508E7b7863a8A8EeE')
    registry = interface.ICurveRegistry(
        '0x7d86446ddb609ed0f5f8684acf30380a356b2b4c')

    crdai = interface.ICErc20('0x92B767185fB3B04F881e3aC8e5B0662a027A1D9f')
    crusdc = interface.ICErc20('0x44fbebd2f576670a6c33f6fc0b00aa8c5753b322')
    crusdt = interface.ICErc20('0x797AAB1ce7c01eB727ab980762bA88e7133d2157')
    cradai = MockCErc20.deploy(adai, {'from': admin})
    crausdc = MockCErc20.deploy(ausdc, {'from': admin})
    crausdt = MockCErc20.deploy(ausdt, {'from': admin})

    gauge = accounts.at('0xbFcF63294aD7105dEa65aA58F8AE5BE2D9d0952A',
                        force=True)
    wgauge = WLiquidityGauge.deploy(
        registry, '0xD533a949740bb3306d119CC777fa900bA034cd52',
        {'from': admin})
    crv = interface.IERC20Ex(wgauge.crv())

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([dai, usdt, usdc, adai, ausdc, ausdt], [
        2**112 // 700 // 10**12, 2**112 // 700, 2**112 // 700, 2**112 // 700,
        2**112 // 700, 2**112 // 700
    ])

    curve_oracle = CurveOracle.deploy(simple_oracle, registry, {'from': admin})
    curve_oracle.registerPool(lp)  # update pool info
    curve_oracle.registerPool(lp_aave)  # update pool info

    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20, wgauge], True, {'from': admin})
    core_oracle.setRoute(
        [dai, usdc, usdt, lp, adai, ausdc, ausdt, lp_aave],
        [
            simple_oracle, simple_oracle, simple_oracle, curve_oracle,
            simple_oracle, simple_oracle, simple_oracle, curve_oracle
        ],
        {'from': admin},
    )

    oracle.setOracles(
        [dai, usdc, usdt, lp, adai, ausdc, ausdt, lp_aave],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    # initialize
    homora = HomoraBank.deploy({'from': admin})
    print('donator ether', accounts[5].balance())
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)

    # add bank
    homora.addBank(dai, crdai, {'from': admin})
    homora.addBank(usdc, crusdc, {'from': admin})
    homora.addBank(usdt, crusdt, {'from': admin})
    homora.addBank(adai, cradai, {'from': admin})
    homora.addBank(ausdc, crausdc, {'from': admin})
    homora.addBank(ausdt, crausdt, {'from': admin})

    # setup initial funds 10^6 USDT + 10^6 USDC + 10^6 DAI to alice
    mint_tokens(
        dai,
        alice,
    )
    mint_tokens(usdc, alice)
    mint_tokens(usdt, alice)
    mint_tokens(adai, alice)
    mint_tokens(ausdc, alice)
    mint_tokens(ausdt, alice)

    # check alice's funds
    print(f'Alice dai balance {dai.balanceOf(alice)}')
    print(f'Alice usdc balance {usdc.balanceOf(alice)}')
    print(f'Alice usdt balance {usdt.balanceOf(alice)}')
    print(f'Alice adai balance {adai.balanceOf(alice)}')
    print(f'Alice ausdc balance {ausdc.balanceOf(alice)}')
    print(f'Alice ausdt balance {ausdt.balanceOf(alice)}')

    # steal some LP from the staking pool
    mint_tokens(lp, alice)
    mint_tokens(lp, bob)
    mint_tokens(lp_aave, alice)

    # set approval
    dai.approve(homora, 2**256 - 1, {'from': alice})
    dai.approve(crdai, 2**256 - 1, {'from': alice})
    usdc.approve(homora, 2**256 - 1, {'from': alice})
    usdc.approve(crusdc, 2**256 - 1, {'from': alice})
    usdt.approve(homora, 2**256 - 1, {'from': alice})
    usdt.approve(crusdt, 2**256 - 1, {'from': alice})

    adai.approve(homora, 2**256 - 1, {'from': alice})
    adai.approve(cradai, 2**256 - 1, {'from': alice})
    ausdc.approve(homora, 2**256 - 1, {'from': alice})
    ausdc.approve(crausdc, 2**256 - 1, {'from': alice})
    ausdt.approve(homora, 2**256 - 1, {'from': alice})
    ausdt.approve(crausdt, 2**256 - 1, {'from': alice})

    lp.approve(homora, 2**256 - 1, {'from': alice})
    lp.approve(gauge, 2**256 - 1, {'from': bob})
    lp_aave.approve(homora, 2**256 - 1, {'from': alice})

    curve_spell = CurveSpellV1.deploy(
        homora, werc20, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', wgauge,
        {'from': admin})

    # register gauge
    wgauge.registerGauge(0, 0)
    wgauge.registerGauge(25, 0)

    # set up pools
    curve_spell.getPool(lp)
    curve_spell.getPool(lp_aave)

    # first time call to reduce gas
    curve_spell.ensureApproveN(lp, 3, {'from': admin})
    curve_spell.ensureApproveN(lp_aave, 3, {'from': admin})

    # whitelist spell in bank
    homora.setWhitelistSpells([curve_spell], [True], {'from': admin})

    # whitelist token in bank
    homora.setWhitelistTokens([dai, usdc, usdt], [True, True, True],
                              {'from': admin})

    # whitelist lp in spell
    curve_spell.setWhitelistLPTokens([lp, lp_aave], [True, True],
                                     {'from': admin})

    #####################################################################################

    print(
        '========================================================================='
    )
    print('Case 1.')

    prevABal = dai.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevCBal = usdt.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_gauge = lp.balanceOf(gauge)

    dai_amt = 200 * 10**18
    usdc_amt = 500 * 10**6
    usdt_amt = 400 * 10**6
    lp_amt = 1 * 10**18
    borrow_dai_amt = 10 * 10**18
    borrow_usdc_amt = 20 * 10**6
    borrow_usdt_amt = 10 * 10**6
    borrow_lp_amt = 0
    minLPMint = 0

    pid = 0
    gid = 0

    tx = homora.execute(
        0,
        curve_spell,
        curve_spell.addLiquidity3.encode_input(
            lp,  # LP
            [dai_amt, usdc_amt, usdt_amt],  # supply tokens
            lp_amt,  # supply LP
            [borrow_dai_amt, borrow_usdc_amt, borrow_usdt_amt
             ],  # borrow tokens
            borrow_lp_amt,  # borrow LP
            minLPMint,  # min LP mint
            pid,
            gid),
        {'from': alice})

    curABal = dai.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curCBal = usdt.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_gauge = lp.balanceOf(gauge)

    print('spell lp balance', lp.balanceOf(curve_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta C balance', curCBal - prevCBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, daiDebt, daiDebtShare = homora.getBankInfo(dai)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, lpDebt, usdcDebtShare = homora.getBankInfo(usdc)

    print('bank dai totalDebt', daiDebt)
    print('bank dai totalShare', daiDebtShare)

    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('bank usdc totalDebt', usdcDebt)
    print('bank usdc totalShare', usdcDebtShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('gauge prev LP balance', prevLPBal_gauge)
    print('gauge cur LP balance', curLPBal_gauge)

    # alice
    assert almostEqual(curABal - prevABal, -dai_amt), 'incorrect DAI amt'
    assert almostEqual(curBBal - prevBBal, -usdc_amt), 'incorrect USDC amt'
    assert almostEqual(curCBal - prevCBal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curLPBal - prevLPBal, -lp_amt), 'incorrect LP amt'

    # spell
    assert dai.balanceOf(curve_spell) == 0, 'non-zero spell DAI balance'
    assert usdc.balanceOf(curve_spell) == 0, 'non-zero spell USDC balance'
    assert usdt.balanceOf(curve_spell) == 0, 'non-zero spell USDT balance'
    assert lp.balanceOf(curve_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert daiDebt == borrow_dai_amt
    assert usdcDebt == borrow_usdc_amt
    assert usdtDebt == borrow_usdt_amt

    _, _, collId, collSize = homora.getPositionInfo(1)
    print('collSize', collSize)

    # staking directly
    prevCrv = crv.balanceOf(bob)
    print('bob lp balance', interface.IERC20Ex(lp).balanceOf(bob))
    pid, gid = 0, 0
    gauge, _ = wgauge.gauges(pid, gid)
    tx = interface.ILiquidityGauge(gauge).deposit(collSize, {'from': bob})

    chain.sleep(20000)

    prevAliceCrvBalance = crv.balanceOf(alice)
    print('Alice crv balance', prevAliceCrvBalance)

    # #####################################################################################

    print(
        '========================================================================='
    )
    print('Case 2. add liquidity (failed tx desired)')

    adai_amt = 200 * 10**18
    ausdc_amt = 500 * 10**6
    ausdt_amt = 400 * 10**6
    lp_amt = 0
    borrow_adai_amt = 0
    borrow_ausdc_amt = 0
    borrow_ausdt_amt = 0
    borrow_lp_amt = 0
    minLPMint = 0

    pid = 0
    gid = 0

    try:
        tx = homora.execute(
            1,
            curve_spell,
            curve_spell.addLiquidity3.encode_input(
                lp_aave,  # LP_aave
                [adai_amt, ausdc_amt, ausdt_amt],  # supply tokens
                lp_amt,  # supply LP
                [borrow_adai_amt, borrow_ausdc_amt, borrow_ausdt_amt
                 ],  # borrow tokens
                borrow_lp_amt,  # borrow LP
                minLPMint,  # min LP mint
                pid,
                gid),
            {'from': alice})
        assert False, 'tx should fail'
    except VirtualMachineError:
        pass

    # #####################################################################################

    print(
        '========================================================================='
    )
    print('Case 3. add liquidity (failed tx desired)')

    adai_amt = 200 * 10**18
    ausdc_amt = 500 * 10**6
    ausdt_amt = 400 * 10**6
    lp_amt = 0
    borrow_adai_amt = 0
    borrow_ausdc_amt = 0
    borrow_ausdt_amt = 0
    borrow_lp_amt = 0
    minLPMint = 0

    pid = 25
    gid = 0

    try:
        tx = homora.execute(
            1,
            curve_spell,
            curve_spell.addLiquidity3.encode_input(
                lp_aave,  # LP_aave
                [adai_amt, ausdc_amt, ausdt_amt],  # supply tokens
                lp_amt,  # supply LP
                [borrow_adai_amt, borrow_ausdc_amt, borrow_ausdt_amt
                 ],  # borrow tokens
                borrow_lp_amt,  # borrow LP
                minLPMint,  # min LP mint
                pid,
                gid),
            {'from': alice})
        assert False, 'tx should fail'
    except VirtualMachineError:
        pass

    # #####################################################################################

    print(
        '========================================================================='
    )
    print('Case 4. remove liquidity (failed tx desired)')

    lp_take_amt = 2**256 - 1  # max
    lp_want = 1 * 10**17
    adai_repay = 2**256 - 1  # max
    ausdc_repay = 2**256 - 1  # max
    ausdt_repay = 2**256 - 1  # max
    lp_repay = 0

    try:
        tx = homora.execute(
            1,
            curve_spell,
            curve_spell.removeLiquidity3.encode_input(
                lp_aave,  # LP_aave token
                lp_take_amt,  # LP amount to take out
                lp_want,  # LP amount to withdraw to wallet
                [adai_repay, ausdc_repay, ausdt_repay],  # repay amounts
                lp_repay,  # repay LP amount
                [0, 0, 0]  # min amounts
            ),
            {'from': alice})
        assert False, 'tx should revert'
    except VirtualMachineError:
        pass

    # #####################################################################################

    print(
        '========================================================================='
    )
    print('Case 5. remove liqudiity')

    # remove liquidity from the same position
    prevABal = dai.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevCBal = usdt.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_gauge = lp.balanceOf(gauge)

    _, _, _, collSize = homora.getPositionInfo(1)

    lp_take_amt = 2**256 - 1  # max
    lp_want = 1 * 10**17
    dai_repay = 2**256 - 1  # max
    usdc_repay = 2**256 - 1  # max
    usdt_repay = 2**256 - 1  # max
    lp_repay = 0

    tx = homora.execute(
        1,
        curve_spell,
        curve_spell.removeLiquidity3.encode_input(
            lp,  # LP token
            lp_take_amt,  # LP amount to take out
            lp_want,  # LP amount to withdraw to wallet
            [dai_repay, usdc_repay, usdt_repay],  # repay amounts
            lp_repay,  # repay LP amount
            [0, 0, 0]  # min amounts
        ),
        {'from': alice})

    curABal = dai.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curCBal = usdt.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_gauge = lp.balanceOf(gauge)

    print('spell lp balance', lp.balanceOf(curve_spell))
    print('spell dai balance', dai.balanceOf(curve_spell))
    print('spell usdc balance', usdc.balanceOf(curve_spell))
    print('spell usdt balance', usdt.balanceOf(curve_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta C balance', curCBal - prevCBal)
    print('Alice delta LP balance', curLPBal - prevLPBal)
    print('remove liquidity gas', tx.gas_used)
    print('bank delta lp balance', curLPBal_bank - prevLPBal_bank)
    print('bank total lp balance', curLPBal_bank)

    _, _, _, daiDebt, daiDebtShare = homora.getBankInfo(dai)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    print('bank dai totalDebt', daiDebt)
    print('bank dai totalDebt', daiDebt)

    print('bank usdc totalShare', usdcDebtShare)
    print('bank usdc totalShare', usdcDebtShare)

    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('LP want', lp_want)

    print('bank delta LP amount', curLPBal_bank - prevLPBal_bank)
    print('LP take amount', lp_take_amt)

    print('prev gauge LP balance', prevLPBal_gauge)
    print('cur gauge LP balance', curLPBal_gauge)

    print('coll size', collSize)

    # alice
    assert almostEqual(curLPBal - prevLPBal, lp_want), 'incorrect LP amt'

    # gauge
    assert almostEqual(curLPBal_gauge - prevLPBal_gauge,
                       -collSize), 'incorrect gauge LP amt'

    # spell
    assert dai.balanceOf(curve_spell) == 0, 'non-zero spell DAI balance'
    assert usdc.balanceOf(curve_spell) == 0, 'non-zero spell USDC balance'
    assert usdt.balanceOf(curve_spell) == 0, 'non-zero spell USDT balance'
    assert lp.balanceOf(curve_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert usdcDebt == 0, 'usdcDebt should be 0'
    assert daiDebt == 0, 'daiDebt should be 0'
    assert usdtDebt == 0, 'usdtDebt should be 0'

    curAliceCrvBalance = crv.balanceOf(alice)
    print('Alice crv balance', curAliceCrvBalance)
    receivedCrv = curAliceCrvBalance - prevAliceCrvBalance
    print('received crv', receivedCrv)

    # check with staking directly
    minter = interface.ILiquidityGaugeMinter(
        interface.ILiquidityGauge(gauge).minter())
    print('minter', minter)
    tx = minter.mint(gauge, {'from': bob})
    print('tx status', tx.status)
    tx = interface.ILiquidityGauge(gauge).withdraw(collSize, {'from': bob})
    receivedCrvFromGauge = crv.balanceOf(bob) - prevCrv
    print('receivedCrvFromGauge', receivedCrvFromGauge)
    assert almostEqual(receivedCrv, receivedCrvFromGauge)

    # #####################################################################################

    print(
        '========================================================================='
    )
    print('Case 6. add & remove all LP')

    lp_amt = 10 * 10**6

    prevLPBal = lp.balanceOf(alice)

    pid, gid = 0, 0

    tx = homora.execute(
        0,
        curve_spell,
        curve_spell.addLiquidity3.encode_input(
            lp,  # LP
            [0, 0, 0],  # supply tokens
            lp_amt,  # supply LP
            [0, 0, 0],  # borrow tokens
            0,  # borrow LP
            0,  # min LP mint
            pid,
            gid),
        {'from': alice})

    tx = homora.execute(
        2,
        curve_spell,
        curve_spell.removeLiquidity3.encode_input(
            lp,  # LP token
            2**256 - 1,  # LP amount to take out
            lp_amt,  # LP amount to withdraw to wallet
            [0, 0, 0],  # repay amounts
            0,  # repay LP amount
            [0, 0, 0]  # min amounts
        ),
        {'from': alice})

    curLPBal = lp.balanceOf(alice)

    assert prevLPBal == curLPBal, 'incorrect LP Balance'

    return tx
示例#15
0
def main():
    admin = accounts[0]

    alice = accounts[1]
    bob = accounts[2]
    dpi = interface.IERC20Ex('0x1494ca1f11d487c2bbe4543e90080aeba4ba3c2b')
    weth = interface.IERC20Ex('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')

    lp = interface.IERC20Ex('0x4d5ef58aac27d99935e5b6b4a6778ff292059991')
    crdpi = MockCErc20.deploy(dpi, {'from': admin})

    router = interface.IUniswapV2Router02(
        '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D')

    staking = accounts.at('0xB93b505Ed567982E2b6756177ddD23ab5745f309',
                          force=True)
    index = interface.IERC20Ex(
        interface.IStakingRewardsEx(staking).rewardsToken())

    wstaking = WStakingRewards.deploy(staking, lp, index, {'from': admin})

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([dpi, weth], [2**112 * 100 // 700, 2**112])

    uniswap_oracle = UniswapV2Oracle.deploy(simple_oracle, {'from': admin})
    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20, wstaking], True, {'from': admin})
    core_oracle.setRoute(
        [dpi, weth, lp],
        [simple_oracle, simple_oracle, uniswap_oracle],
        {'from': admin},
    )
    oracle.setOracles(
        [dpi, weth, lp],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)
    homora.addBank(dpi, crdpi, {'from': admin})

    # setup initial funds to alice
    mint_tokens(dpi, alice)
    mint_tokens(weth, alice)

    # check alice's funds
    print(f'Alice dpi balance {dpi.balanceOf(alice)}')
    print(f'Alice weth balance {weth.balanceOf(alice)}')

    # Steal some LP from the staking pool
    mint_tokens(lp, alice)
    mint_tokens(lp, bob)

    # set approval
    dpi.approve(homora, 2**256 - 1, {'from': alice})
    dpi.approve(crdpi, 2**256 - 1, {'from': alice})
    weth.approve(homora, 2**256 - 1, {'from': alice})
    lp.approve(homora, 2**256 - 1, {'from': alice})
    lp.approve(staking, 2**256 - 1, {'from': bob})

    uniswap_spell = UniswapV2SpellV1.deploy(homora, werc20, router,
                                            {'from': admin})
    # first time call to reduce gas
    uniswap_spell.getPair(weth, dpi, {'from': admin})

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 1. add liquidity first time')

    prevABal = dpi.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_staking = lp.balanceOf(staking)

    if interface.IUniswapV2Pair(lp).token0() == dpi:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    dpi_amt = 10 * 10**18
    weth_amt = 10**18
    lp_amt = 0
    borrow_dpi_amt = 0
    borrow_weth_amt = 0

    tx = homora.execute(
        0,
        uniswap_spell,
        uniswap_spell.addLiquidityWStakingRewards.encode_input(
            dpi,  # token 0
            weth,  # token 1
            [
                dpi_amt,  # supply DPI
                weth_amt,  # supply WETH
                lp_amt,  # supply LP
                borrow_dpi_amt,  # borrow DPI
                borrow_weth_amt,  # borrow WETH
                0,  # borrow LP tokens
                0,  # min DPI
                0
            ],  # min WETH
            wstaking,
        ),
        {'from': alice})

    curABal = dpi.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_staking = lp.balanceOf(staking)

    if interface.IUniswapV2Pair(lp).token0() == dpi:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, totalDebt, totalShare = homora.getBankInfo(dpi)
    print('bank dpi totalDebt', totalDebt)
    print('bank dpi totalShare', totalShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('staking prev LP balance', prevLPBal_staking)
    print('staking cur LP balance', curLPBal_staking)

    print('prev dpi res', prevARes)
    print('cur dpi res', curARes)

    print('prev weth res', prevBRes)
    print('cur weth res', curBRes)

    # alice
    assert almostEqual(curABal - prevABal, -dpi_amt), 'incorrect DPI amt'
    assert almostEqual(curBBal - prevBBal, -weth_amt), 'incorrect WETH amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert dpi.balanceOf(uniswap_spell) == 0, 'non-zero spell DPI balance'
    assert weth.balanceOf(uniswap_spell) == 0, 'non-zero spell WETH balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'
    assert totalDebt == borrow_dpi_amt

    # check balance and pool reserves
    assert curABal - prevABal - borrow_dpi_amt == - \
        (curARes - prevARes), 'not all DPI tokens go to LP pool'
    assert curBBal - prevBBal - borrow_weth_amt == - \
        (curBRes - prevBRes), 'not all WETH tokens go to LP pool'

    _, _, collId, collSize = homora.getPositionInfo(1)
    print('collSize', collSize)

    # staking directly
    prevIndex = index.balanceOf(bob)
    print('bob lp balance', interface.IERC20Ex(lp).balanceOf(bob))
    tx = interface.IStakingRewards(staking).stake(collSize, {'from': bob})

    chain.sleep(20000)

    prevAliceIndexBalance = index.balanceOf(alice)
    print('Alice index balance', prevAliceIndexBalance)

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 2. add liquidity the second time')

    prevABal = dpi.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_staking = lp.balanceOf(staking)

    if interface.IUniswapV2Pair(lp).token0() == dpi:
        prevARes, prevBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        prevBRes, prevARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    dpi_amt = 10 * 10**18
    weth_amt = 10**18
    lp_amt = 0
    borrow_dpi_amt = 0
    borrow_weth_amt = 0

    tx = homora.execute(
        1,
        uniswap_spell,
        uniswap_spell.addLiquidityWStakingRewards.encode_input(
            dpi,  # token 0
            weth,  # token 1
            [
                dpi_amt,  # supply DPI
                weth_amt,  # supply WETH
                lp_amt,  # supply LP
                borrow_dpi_amt,  # borrow DPI
                borrow_weth_amt,  # borrow WETH
                0,  # borrow LP tokens
                0,  # min DPI
                0
            ],  # min WETH
            wstaking,
        ),
        {'from': alice})

    curABal = dpi.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_staking = lp.balanceOf(staking)

    if interface.IUniswapV2Pair(lp).token0() == dpi:
        curARes, curBRes, _ = interface.IUniswapV2Pair(lp).getReserves()
    else:
        curBRes, curARes, _ = interface.IUniswapV2Pair(lp).getReserves()

    print('spell lp balance', lp.balanceOf(uniswap_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, totalDebt, totalShare = homora.getBankInfo(dpi)
    print('bank dpi totalDebt', totalDebt)
    print('bank dpi totalShare', totalShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('staking prev LP balance', prevLPBal_staking)
    print('staking cur LP balance', curLPBal_staking)

    print('prev dpi res', prevARes)
    print('cur dpi res', curARes)

    print('prev weth res', prevBRes)
    print('cur weth res', curBRes)

    # alice
    assert almostEqual(curABal - prevABal, -dpi_amt), 'incorrect DPI amt'
    assert almostEqual(curBBal - prevBBal, -weth_amt), 'incorrect WETH amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert dpi.balanceOf(uniswap_spell) == 0, 'non-zero spell DPI balance'
    assert weth.balanceOf(uniswap_spell) == 0, 'non-zero spell WETH balance'
    assert lp.balanceOf(uniswap_spell) == 0, 'non-zero spell LP balance'
    assert totalDebt == borrow_dpi_amt

    # check balance and pool reserves
    assert curABal - prevABal - borrow_dpi_amt == - \
        (curARes - prevARes), 'not all DPI tokens go to LP pool'
    assert curBBal - prevBBal - borrow_weth_amt == - \
        (curBRes - prevBRes), 'not all WETH tokens go to LP pool'

    curAliceIndexBalance = index.balanceOf(alice)
    print('Alice index balance', curAliceIndexBalance)
    receivedIndex = curAliceIndexBalance - prevAliceIndexBalance
    print('received index', receivedIndex)

    # check with staking directly
    tx = interface.IStakingRewards(staking).getReward({'from': bob})
    receivedIndexFromStaking = index.balanceOf(bob) - prevIndex
    print('receivedIndexFromStaking', receivedIndexFromStaking)
    assert almostEqual(receivedIndex, receivedIndexFromStaking)
示例#16
0
def main():
    admin = accounts[0]

    alice = accounts[1]
    dai = interface.IERC20Ex('0x6B175474E89094C44Da98b954EedeAC495271d0F')
    weth = interface.IERC20Ex('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')

    lp = interface.IERC20Ex('0x8b6e6e7b5b3801fed2cafd4b22b8a16c2f2db21a')
    # pool is lp for balancer
    pool = interface.IBalancerPool(
        '0x8b6e6e7b5b3801fed2cafd4b22b8a16c2f2db21a')

    crdai = interface.ICErc20('0x92b767185fb3b04f881e3ac8e5b0662a027a1d9f')

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([weth, dai], [2**112, 2**112 // 700])

    balancer_oracle = BalancerPairOracle.deploy(simple_oracle, {'from': alice})

    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20], True, {'from': admin})
    core_oracle.setRoute(
        [weth, dai, lp],
        [simple_oracle, simple_oracle, balancer_oracle],
        {'from': admin},
    )
    oracle.setOracles(
        [weth, dai, lp],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)
    homora.addBank(dai, crdai, {'from': admin})

    # setup initial funds to alice
    mint_tokens(dai, alice)
    mint_tokens(weth, alice)

    # check alice's funds
    print(f'Alice weth balance {weth.balanceOf(alice)}')
    print(f'Alice dai balance {dai.balanceOf(alice)}')

    # Steal some LP from the staking pool
    mint_tokens(lp, alice)

    # set approval
    dai.approve(homora, 2**256 - 1, {'from': alice})
    dai.approve(crdai, 2**256 - 1, {'from': alice})
    weth.approve(homora, 2**256 - 1, {'from': alice})
    lp.approve(homora, 2**256 - 1, {'from': alice})

    balancer_spell = BalancerSpellV1.deploy(
        homora, werc20, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
        {'from': admin})
    # first time call to reduce gas
    balancer_spell.getPair(lp, {'from': admin})

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 1.')

    prevABal = dai.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)

    prevARes = interface.IBalancerPool(lp).getBalance(dai)
    prevBRes = interface.IBalancerPool(lp).getBalance(weth)

    dai_amt = 40000 * 10**18
    weth_amt = 10**18
    lp_amt = 1 * 10**16
    borrow_dai_amt = 1000 * 10**18
    borrow_weth_amt = 0

    # calculate slippage control
    total_dai_amt = dai_amt + borrow_dai_amt
    total_weth_amt = weth_amt + borrow_weth_amt
    dai_weight = 0.2
    weth_weight = 0.8

    ratio = (((prevARes + total_dai_amt) / prevARes) ** dai_weight) * \
        (((prevBRes + total_weth_amt) / prevBRes) ** weth_weight) - 1
    lp_desired = lp_amt + int(
        interface.IERC20(lp).totalSupply() * ratio * 0.995)
    print('lp desired', lp_desired)

    tx = homora.execute(
        0,
        balancer_spell,
        balancer_spell.addLiquidityWERC20.encode_input(
            lp,  # lp token
            [
                dai_amt,  # supply DAI
                weth_amt,  # supply WETH
                lp_amt,  # supply LP
                borrow_dai_amt,  # borrow DAI
                borrow_weth_amt,  # borrow WETH
                0,  # borrow LP tokens
                lp_desired
            ]  # LP desired
        ),
        {'from': alice})

    curABal = dai.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)

    curARes = interface.IBalancerPool(lp).getBalance(dai)
    curBRes = interface.IBalancerPool(lp).getBalance(weth)

    print('spell lp balance', lp.balanceOf(balancer_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, daiDebt, daiShare = homora.getBankInfo(dai)
    print('bank dai daiDebt', daiDebt)
    print('bank dai daiShare', daiShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('werc20 prev LP balance', prevLPBal_werc20)
    print('werc20 cur LP balance', curLPBal_werc20)

    print('prev dai res', prevARes)
    print('cur dai res', curARes)

    print('prev weth res', prevBRes)
    print('cur weth res', curBRes)

    # alice
    assert almostEqual(curABal - prevABal, -dai_amt), 'incorrect DAI amt'
    assert almostEqual(curBBal - prevBBal, -weth_amt), 'incorrect WETH amt'
    assert curLPBal - prevLPBal == -lp_amt, 'incorrect LP amt'

    # spell
    assert dai.balanceOf(balancer_spell) == 0, 'non-zero spell DAI balance'
    assert weth.balanceOf(balancer_spell) == 0, 'non-zero spell WETH balance'
    assert lp.balanceOf(balancer_spell) == 0, 'non-zero spell LP balance'
    assert daiDebt == borrow_dai_amt

    # check balance and pool reserves
    assert almostEqual(
        curABal - prevABal - borrow_dai_amt,
        -(curARes - prevARes)), 'not all DAI tokens go to LP pool'
    assert almostEqual(
        curBBal - prevBBal - borrow_weth_amt,
        -(curBRes - prevBRes)), 'not all WETH tokens go to LP pool'

    #####################################################################################
    print(
        '========================================================================='
    )
    print('Case 2.')

    # remove liquidity from the same position
    prevABal = dai.balanceOf(alice)
    prevBBal = weth.balanceOf(alice)
    prevETHBal = alice.balance()
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_werc20 = lp.balanceOf(werc20)
    prevETHBal = alice.balance()

    prevARes = interface.IBalancerPool(lp).getBalance(dai)
    prevBRes = interface.IBalancerPool(lp).getBalance(weth)

    lp_take_amt = 2**256 - 1  # max
    lp_want = 1 * 10**15
    dai_repay = 2**256 - 1  # max
    weth_repay = 0

    real_dai_repay = homora.borrowBalanceStored(1, dai)
    _, _, _, real_lp_take_amt = homora.getPositionInfo(1)

    tx = homora.execute(
        1,
        balancer_spell,
        balancer_spell.removeLiquidityWERC20.encode_input(
            lp,  # LP token
            [
                lp_take_amt,  # take out LP tokens
                lp_want,  # withdraw LP tokens to wallet
                dai_repay,  # repay DAI
                weth_repay,  # repay WETH
                0,  # repay LP
                0,  # min DAI
                0
            ],  # min WETH
        ),
        {'from': alice})
    # return tx

    curABal = dai.balanceOf(alice)
    curBBal = weth.balanceOf(alice)
    curETHBal = alice.balance()
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_werc20 = lp.balanceOf(werc20)
    curETHBal = alice.balance()

    curARes = interface.IBalancerPool(lp).getBalance(dai)
    curBRes = interface.IBalancerPool(lp).getBalance(weth)

    print('spell lp balance', lp.balanceOf(balancer_spell))
    print('spell dai balance', dai.balanceOf(balancer_spell))
    print('spell weth balance', weth.balanceOf(balancer_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta ETH balance', curETHBal - prevETHBal)
    print('Alice delta LP balance', curLPBal - prevLPBal)
    print('remove liquidity gas', tx.gas_used)
    print('bank delta lp balance', curLPBal_bank - prevLPBal_bank)
    print('bank total lp balance', curLPBal_bank)

    _, _, _, daiDebt, daiShare = homora.getBankInfo(dai)
    print('bank dai totalDebt', daiDebt)
    print('bank dai totalShare', daiShare)

    print('LP want', lp_want)

    print('bank delta LP amount', curLPBal_bank - prevLPBal_bank)
    print('LP take amount', lp_take_amt)

    print('prev werc20 LP balance', prevLPBal_werc20)
    print('cur werc20 LP balance', curLPBal_werc20)

    print('real dai repay', real_dai_repay)

    # alice
    assert almostEqual(curBBal - prevBBal, 0), 'incorrect WETH amt'
    assert almostEqual(curLPBal - prevLPBal, lp_want), 'incorrect LP amt'

    # werc20
    assert almostEqual(curLPBal_werc20 - prevLPBal_werc20,
                       -real_lp_take_amt), 'incorrect werc20 LP amt'

    # spell
    assert dai.balanceOf(balancer_spell) == 0, 'non-zero spell DAI balance'
    assert weth.balanceOf(balancer_spell) == 0, 'non-zero spell WETH balance'
    assert lp.balanceOf(balancer_spell) == 0, 'non-zero spell LP balance'

    # check balance and pool reserves
    assert almostEqual(curABal - prevABal + real_dai_repay,
                       -(curARes - prevARes)), 'inconsistent DAI from withdraw'
    assert almostEqual(curBBal - prevBBal,
                       0), 'inconsistent WETH from withdraw'
    assert almostEqual(curETHBal - prevETHBal + weth_repay,
                       -(curBRes - prevBRes)), 'inconsistent ETH from withdraw'

    return tx
示例#17
0
def main():
    admin = accounts[0]

    alice = accounts[1]
    bob = accounts[2]
    dai = interface.IERC20Ex('0x6B175474E89094C44Da98b954EedeAC495271d0F')
    usdc = interface.IERC20Ex('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48')
    usdt = interface.IERC20Ex('0xdAC17F958D2ee523a2206206994597C13D831ec7')
    adai = interface.IERC20Ex('0x028171bCA77440897B824Ca71D1c56caC55b68A3')
    ausdc = interface.IERC20Ex('0xBcca60bB61934080951369a648Fb03DF4F96263C')
    ausdt = interface.IERC20Ex('0x3Ed3B47Dd13EC9a98b44e6204A523E766B225811')

    lp = interface.IERC20Ex('0x6c3f90f043a72fa612cbac8115ee7e52bde6e490')
    pool = interface.ICurvePool('0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7')
    lp_aave = interface.IERC20Ex('0xFd2a8fA60Abd58Efe3EeE34dd494cD491dC14900')
    pool_aave = interface.ICurvePool('0xDeBF20617708857ebe4F679508E7b7863a8A8EeE')
    registry = interface.ICurveRegistry(
        '0x7d86446ddb609ed0f5f8684acf30380a356b2b4c')

    crdai = interface.ICErc20('0x92B767185fB3B04F881e3aC8e5B0662a027A1D9f')
    crusdc = interface.ICErc20('0x44fbebd2f576670a6c33f6fc0b00aa8c5753b322')
    crusdt = interface.ICErc20('0x797AAB1ce7c01eB727ab980762bA88e7133d2157')
    cradai = MockCErc20.deploy(adai, {'from': admin})
    crausdc = MockCErc20.deploy(ausdc, {'from': admin})
    crausdt = MockCErc20.deploy(ausdt, {'from': admin})

    gauge = accounts.at(
        '0xbFcF63294aD7105dEa65aA58F8AE5BE2D9d0952A', force=True)
    wgauge = WLiquidityGauge.deploy(
        registry, '0xD533a949740bb3306d119CC777fa900bA034cd52', {'from': admin})
    crv = interface.IERC20Ex(wgauge.crv())

    werc20 = WERC20.deploy({'from': admin})

    simple_oracle = SimpleOracle.deploy({'from': admin})
    simple_oracle.setETHPx([dai, usdt, usdc, adai, ausdc, ausdt], [2**112 // 700,
                                                                   2**112 // 700,
                                                                   2**112 // 700,
                                                                   2**112 // 700,
                                                                   2**112 // 700,
                                                                   2**112 // 700])

    curve_oracle = CurveOracle.deploy(simple_oracle, registry, {'from': admin})
    curve_oracle.registerPool(lp)  # update pool info
    curve_oracle.registerPool(lp_aave)  # update pool info

    core_oracle = CoreOracle.deploy({'from': admin})
    oracle = ProxyOracle.deploy(core_oracle, {'from': admin})
    oracle.setWhitelistERC1155([werc20, wgauge], True, {'from': admin})
    core_oracle.setRoute(
        [dai, usdc, usdt, lp,  adai, ausdc, ausdt, lp_aave],
        [simple_oracle, simple_oracle, simple_oracle, curve_oracle,
            simple_oracle, simple_oracle, simple_oracle, curve_oracle],
        {'from': admin},
    )

    oracle.setOracles(
        [dai, usdc, usdt, lp,  adai, ausdc, ausdt, lp_aave],
        [
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
            [10000, 10000, 10000],
        ],
        {'from': admin},
    )

    # initialize
    homora = HomoraBank.deploy({'from': admin})
    homora.initialize(oracle, 1000, {'from': admin})  # 10% fee
    setup_bank_hack(homora)

    # add bank
    homora.addBank(dai, crdai, {'from': admin})
    homora.addBank(usdc, crusdc, {'from': admin})
    homora.addBank(usdt, crusdt, {'from': admin})

    # setup initial funds to alice
    mint_tokens(dai, alice)
    mint_tokens(usdc, alice)
    mint_tokens(usdt, alice)

    # check alice's funds
    print(f'Alice dai balance {dai.balanceOf(alice)}')
    print(f'Alice usdc balance {usdc.balanceOf(alice)}')
    print(f'Alice usdt balance {usdt.balanceOf(alice)}')

    # steal some LP from the staking pool
    mint_tokens(lp, alice)
    mint_tokens(lp, bob)

    # set approval
    dai.approve(homora, 2**256-1, {'from': alice})
    dai.approve(crdai, 2**256-1, {'from': alice})
    usdc.approve(homora, 2**256-1, {'from': alice})
    usdc.approve(crusdc, 2**256-1, {'from': alice})
    usdt.approve(homora, 2**256-1, {'from': alice})
    usdt.approve(crusdt, 2**256-1, {'from': alice})
    lp.approve(homora, 2**256-1, {'from': alice})
    lp.approve(gauge, 2**256-1, {'from': bob})

    curve_spell = CurveSpellV1.deploy(
        homora, werc20, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', wgauge, {'from': admin})

    # register gauge
    wgauge.registerGauge(0, 0)

    # set up pools
    curve_spell.getPool(lp)

    # first time call to reduce gas
    curve_spell.ensureApproveN(lp, 3, {'from': admin})

    #####################################################################################

    # TODO: Test borrow 3 assets simultaneously
    print('=========================================================================')
    print('Case 1.')

    prevABal = dai.balanceOf(alice)
    prevBBal = usdc.balanceOf(alice)
    prevCBal = usdt.balanceOf(alice)
    prevLPBal = lp.balanceOf(alice)
    prevLPBal_bank = lp.balanceOf(homora)
    prevLPBal_gauge = lp.balanceOf(gauge)

    dai_amt = 20000 * 10**18  # 20000 DAI
    usdc_amt = 50000 * 10**6  # 50000 USDC
    usdt_amt = 40000 * 10**6  # 40000 USDT
    lp_amt = 0  # 1 * 10**18
    borrow_dai_amt = 0  # 2000 * 10**18  # 2000 DAI
    borrow_usdc_amt = 0  # 200 * 10**6  # 200 USDC
    borrow_usdt_amt = 0  # 1000 * 10**6  # 1000 USDT
    borrow_lp_amt = 0
    minLPMint = 0

    pid = 0
    gid = 0

    tx = homora.execute(
        0,
        curve_spell,
        curve_spell.addLiquidity3.encode_input(
            lp,  # LP
            [dai_amt, usdc_amt, usdt_amt],  # supply tokens
            lp_amt,  # supply LP
            [borrow_dai_amt, borrow_usdc_amt, borrow_usdt_amt],  # borrow tokens
            borrow_lp_amt,  # borrow LP
            minLPMint,  # min LP mint
            pid,
            gid
        ),
        {'from': alice}
    )

    curABal = dai.balanceOf(alice)
    curBBal = usdc.balanceOf(alice)
    curCBal = usdt.balanceOf(alice)
    curLPBal = lp.balanceOf(alice)
    curLPBal_bank = lp.balanceOf(homora)
    curLPBal_gauge = lp.balanceOf(gauge)

    print('spell lp balance', lp.balanceOf(curve_spell))
    print('Alice delta A balance', curABal - prevABal)
    print('Alice delta B balance', curBBal - prevBBal)
    print('Alice delta C balance', curCBal - prevCBal)
    print('add liquidity gas', tx.gas_used)
    print('bank lp balance', curLPBal_bank)

    _, _, _, daiDebt, daiDebtShare = homora.getBankInfo(dai)
    _, _, _, usdcDebt, usdcDebtShare = homora.getBankInfo(usdc)
    _, _, _, usdtDebt, usdtDebtShare = homora.getBankInfo(usdt)
    _, _, _, lpDebt, usdcDebtShare = homora.getBankInfo(usdc)

    print('bank dai totalDebt', daiDebt)
    print('bank dai totalShare', daiDebtShare)

    print('bank usdt totalDebt', usdtDebt)
    print('bank usdt totalShare', usdtDebtShare)

    print('bank usdc totalDebt', usdcDebt)
    print('bank usdc totalShare', usdcDebtShare)

    print('bank prev LP balance', prevLPBal_bank)
    print('bank cur LP balance', curLPBal_bank)

    print('gauge prev LP balance', prevLPBal_gauge)
    print('gauge cur LP balance', curLPBal_gauge)

    # alice
    assert almostEqual(curABal - prevABal, -dai_amt), 'incorrect DAI amt'
    assert almostEqual(curBBal - prevBBal, -usdc_amt), 'incorrect USDC amt'
    assert almostEqual(curCBal - prevCBal, -usdt_amt), 'incorrect USDT amt'
    assert almostEqual(curLPBal - prevLPBal, -lp_amt), 'incorrect LP amt'

    # spell
    assert dai.balanceOf(curve_spell) == 0, 'non-zero spell DAI balance'
    assert usdc.balanceOf(curve_spell) == 0, 'non-zero spell USDC balance'
    assert usdt.balanceOf(curve_spell) == 0, 'non-zero spell USDT balance'
    assert lp.balanceOf(curve_spell) == 0, 'non-zero spell LP balance'

    # debt
    assert daiDebt == borrow_dai_amt
    assert usdcDebt == borrow_usdc_amt
    assert usdtDebt == borrow_usdt_amt

    # #####################################################################################

    print('=========================================================================')
    print('Case 2. harvest first time')

    prevCRVBalance = crv.balanceOf(alice)
    print('Alice CRV balance before harvest', prevCRVBalance)
    _, _, collId, collSize = homora.getPositionInfo(1)
    print('collSize', collSize)

    # staking directly
    prevCrv = crv.balanceOf(bob)
    print('bob lp balance', interface.IERC20Ex(lp).balanceOf(bob))
    pid, gid = 0, 0
    gauge, _ = wgauge.gauges(pid, gid)
    tx = interface.ILiquidityGauge(gauge).deposit(collSize, {'from': bob})

    chain.sleep(20000)

    tx = homora.execute(
        1,
        curve_spell,
        curve_spell.harvest.encode_input(),
        {'from': alice}
    )

    print('tx gas used', tx.gas_used)

    curCRVBalance = crv.balanceOf(alice)
    print('Alice CRV balance after harvest', curCRVBalance)
    receivedCrv = curCRVBalance - prevCRVBalance

    # check with staking directly
    minter = interface.ILiquidityGaugeMinter(interface.ILiquidityGauge(gauge).minter())
    print('minter', minter)
    tx = minter.mint(gauge, {'from': bob})
    print('tx status', tx.status)
    tx = interface.ILiquidityGauge(gauge).withdraw(collSize, {'from': bob})
    receivedCrvFromGauge = crv.balanceOf(bob) - prevCrv
    print('receivedCrvFromGauge', receivedCrvFromGauge)
    assert almostEqual(receivedCrv, receivedCrvFromGauge)

    # #####################################################################################

    print('=========================================================================')
    print('Case 3. harvest second time')

    prevCRVBalance = crv.balanceOf(alice)
    print('Alice CRV balance before harvest', prevCRVBalance)
    _, _, collId, collSize = homora.getPositionInfo(1)
    print('collSize', collSize)

    # staking directly
    prevCrv = crv.balanceOf(bob)
    print('bob lp balance', interface.IERC20Ex(lp).balanceOf(bob))
    pid, gid = 0, 0
    gauge, _ = wgauge.gauges(pid, gid)
    tx = interface.ILiquidityGauge(gauge).deposit(collSize, {'from': bob})

    chain.sleep(20000)

    tx = homora.execute(
        1,
        curve_spell,
        curve_spell.harvest.encode_input(),
        {'from': alice}
    )

    print('tx gas used', tx.gas_used)

    curCRVBalance = crv.balanceOf(alice)
    print('Alice CRV balance after harvest', curCRVBalance)
    receivedCrv = curCRVBalance - prevCRVBalance

    # check with staking directly
    minter = interface.ILiquidityGaugeMinter(interface.ILiquidityGauge(gauge).minter())
    print('minter', minter)
    tx = minter.mint(gauge, {'from': bob})
    print('tx status', tx.status)
    tx = interface.ILiquidityGauge(gauge).withdraw(collSize, {'from': bob})
    receivedCrvFromGauge = crv.balanceOf(bob) - prevCrv
    print('receivedCrvFromGauge', receivedCrvFromGauge)
    assert almostEqual(receivedCrv, receivedCrvFromGauge)

    return tx