Exemple #1
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})

    #####################################################################################
    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)
Exemple #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})

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

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

    # whitelist lp in spell
    curve_spell.setWhitelistLPTokens([lp, lp_eurs], [True, True],
                                     {'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 (failed tx desired)')

    eurs_amt = 1 * 10**3
    seur_amt = 1 * 10**3
    lp_amt = 0
    borrow_eurs_amt = 0
    borrow_seur_amt = 0
    borrow_lp_amt = 0
    minLPMint = 0

    pid = 9
    gid = 0

    try:
        tx = homora.execute(
            1,
            curve_spell,
            curve_spell.addLiquidity2.encode_input(
                lp_eurs,  # lp_eurs
                [eurs_amt, seur_amt],  # supply tokens
                lp_amt,  # supply LP
                [borrow_eurs_amt, borrow_seur_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)')

    eurs_amt = 1 * 10**3
    seur_amt = 1 * 10**3
    lp_amt = 0
    borrow_eurs_amt = 0
    borrow_seur_amt = 0
    borrow_lp_amt = 0
    minLPMint = 0

    pid = 23
    gid = 0

    try:
        tx = homora.execute(
            1,
            curve_spell,
            curve_spell.addLiquidity2.encode_input(
                lp_eurs,  # lp_eurs
                [eurs_amt, seur_amt],  # supply tokens
                lp_amt,  # supply LP
                [borrow_eurs_amt, borrow_seur_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
    eurs_repay = 2**256 - 1  # max
    seur_repay = 2**256 - 1  # max
    lp_repay = 0

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

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

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

    # remove liquidity from the same position
    prevABal = renbtc.balanceOf(alice)
    prevBBal = wbtc.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**12
    renbtc_repay = 2**256 - 1  # max
    wbtc_repay = 2**256 - 1  # max
    lp_repay = 0

    tx = homora.execute(
        1,
        curve_spell,
        curve_spell.removeLiquidity2.encode_input(
            lp,  # LP token
            lp_take_amt,  # LP amount to take out
            lp_want,  # LP amount to withdraw to wallet
            [renbtc_repay, wbtc_repay],  # repay amounts
            lp_repay,  # repay LP amount
            [0, 0]  # min amounts
        ),
        {'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('spell renbtc balance', renbtc.balanceOf(curve_spell))
    print('spell wbtc balance', wbtc.balanceOf(curve_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)

    _, _, _, renbtcDebt, renbtcDebtShare = homora.getBankInfo(renbtc)
    _, _, _, wbtcDebt, wbtcDebtShare = homora.getBankInfo(wbtc)
    print('bank renbtc totalDebt', renbtcDebt)
    print('bank renbtc totalDebt', renbtcDebt)

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

    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 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 wbtcDebt == 0, 'wbtcDebt should be 0'
    assert renbtcDebt == 0, 'renbtcDebt 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 = 9, 0

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

    tx = homora.execute(
        2,
        curve_spell,
        curve_spell.removeLiquidity2.encode_input(
            lp,  # LP token
            2**256 - 1,  # LP amount to take out
            lp_amt,  # LP amount to withdraw to wallet
            [0, 0],  # repay amounts
            0,  # repay LP amount
            [0, 0]  # min amounts
        ),
        {'from': alice})

    curLPBal = lp.balanceOf(alice)

    assert prevLPBal == curLPBal, 'incorrect LP Balance'

    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')

    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
Exemple #4
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})

    #####################################################################################
    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)
Exemple #5
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 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 = 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 (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 = 0
    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 + 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'

    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)
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})

    #####################################################################################
    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)

    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')
    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})
    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})

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

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

    # whitelist lp in spell
    curve_spell.setWhitelistLPTokens([lp], [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 = 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 = 2000 * 10**18  # 2000 DAI
    borrow_usdc_amt = 200 * 10**6  # 200 USDC
    borrow_usdt_amt = 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
Exemple #8
0
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})

    # 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.')

    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'

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

    lp_amt = 10 * 10**18
    prevLPBal = lp.balanceOf(alice)

    tx = homora.execute(
        0,
        sushiswap_spell,
        sushiswap_spell.addLiquidityWERC20.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
        ),
        {'from': alice})

    tx = homora.execute(
        2,
        sushiswap_spell,
        sushiswap_spell.removeLiquidityWERC20.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'

    return tx
Exemple #9
0
def test_operation(pm, chain):
    dai_liquidity = accounts.at("0xbebc44782c7db0a1a60cb6fe97d0b483032ff1c7",
                                force=True)  # using curve pool (lots of dai)

    crv3_liquidity = accounts.at("0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490",
                                 force=True)  # yearn treasury (lots of crv3)

    altCRV_liquidity = accounts.at(
        "0xe6e6e25efda5f69687aa9914f8d750c523a1d261",
        force=True)  # giant amount

    rewards = accounts[2]
    gov = accounts[3]
    guardian = accounts[4]
    bob = accounts[5]
    alice = accounts[6]
    strategist = accounts[7]
    tinytim = accounts[8]

    dai = Contract("0x6b175474e89094c44da98b954eedeac495271d0f",
                   owner=gov)  # DAI token

    dai.approve(dai_liquidity, Wei("1000000 ether"), {"from": dai_liquidity})
    dai.transferFrom(dai_liquidity, gov, Wei("300000 ether"),
                     {"from": dai_liquidity})

    crv3 = Contract("0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490",
                    owner=gov)  # crv3 token address (threePool)

    altCRV = Contract("0x1AEf73d49Dedc4b1778d0706583995958Dc862e6",
                      owner=gov)  # altCRV token (mUSDCrv)

    crv3.approve(crv3_liquidity, Wei("1000000 ether"),
                 {"from": crv3_liquidity})
    crv3.transferFrom(crv3_liquidity, gov, Wei("100 ether"),
                      {"from": crv3_liquidity})

    altCRV.approve(altCRV_liquidity, Wei("1000000 ether"),
                   {"from": altCRV_liquidity})
    altCRV.transferFrom(altCRV_liquidity, gov, Wei("1000 ether"),
                        {"from": altCRV_liquidity})

    # config dai vault.
    Vault = pm(config["dependencies"][0]).Vault
    yDAI = Vault.deploy({"from": gov})
    yDAI.initialize(dai, gov, rewards, "", "", {"from": gov})
    yDAI.setDepositLimit(Wei("1000000 ether"))

    threePool = Contract("0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7",
                         owner=gov)  # crv3 pool address (threePool)
    altCrvPool = Contract("0x8474DdbE98F5aA3179B3B3F5942D724aFcdec9f6",
                          owner=gov)  # atlCrvPool pool address (musdCrv)
    targetVault = Contract("0x0FCDAeDFb8A7DfDa2e9838564c5A1665d856AFDF",
                           owner=gov)  # target vault (musdCrv)
    targetVaultStrat = Contract("0xBA0c07BBE9C22a1ee33FE988Ea3763f21D0909a0",
                                owner=gov)  # targetVault strat (threePool)
    targetVaultStratOwner = Contract(
        "0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52",
        owner=gov)  # targetVault stratOwner (threePool)

    strategy = guardian.deploy(StrategyDAImUSDv2, yDAI, dai, threePool,
                               targetVault, crv3, altCRV, altCrvPool)
    strategy.setStrategist(strategist)

    yDAI.addStrategy(strategy, 10_000, 0, 0, {"from": gov})

    dai.approve(gov, Wei("1000000 ether"), {"from": gov})
    dai.transferFrom(gov, bob, Wei("1000 ether"), {"from": gov})
    dai.transferFrom(gov, alice, Wei("4000 ether"), {"from": gov})
    dai.transferFrom(gov, tinytim, Wei("10 ether"), {"from": gov})
    dai.approve(yDAI, Wei("1000000 ether"), {"from": bob})
    dai.approve(yDAI, Wei("1000000 ether"), {"from": alice})
    dai.approve(yDAI, Wei("1000000 ether"), {"from": tinytim})
    crv3.approve(gov, Wei("1000000 ether"), {"from": gov})
    yDAI.approve(gov, Wei("1000000 ether"), {"from": gov})
    targetVault.approve(gov, Wei("1000000 ether"), {"from": gov})
    altCRV.approve(gov, Wei("1000000 ether"), {"from": gov})
    dai.approve(threePool, Wei("1000000 ether"), {"from": gov})
    crv3.approve(altCrvPool, Wei("1000000 ether"), {"from": gov})

    targetVaultStrat.setStrategistReward(0, {"from": targetVaultStratOwner})
    targetVaultStrat.setTreasuryFee(0, {"from": targetVaultStratOwner})
    targetVaultStrat.setWithdrawalFee(0, {"from": targetVaultStratOwner})

    # users deposit to vault
    yDAI.deposit(Wei("1000 ether"), {"from": bob})
    yDAI.deposit(Wei("4000 ether"), {"from": alice})
    yDAI.deposit(Wei("10 ether"), {"from": tinytim})

    a = yDAI.pricePerShare()

    chain.mine(1)

    strategy.harvest({"from": gov})

    assert targetVault.balanceOf(strategy) > 0
    chain.sleep(3600 * 24 * 7 * 10)
    chain.mine(1)
    a = yDAI.pricePerShare()

    # small profit
    t = targetVault.getPricePerFullShare()
    c = strategy.estimatedTotalAssets()
    targetVaultStrat.harvest({"from": targetVaultStratOwner})
    s = targetVault.getPricePerFullShare()
    d = strategy.estimatedTotalAssets()
    assert t < s
    assert d > c

    assert yDAI.strategies(strategy).dict()['totalDebt'] < d

    strategy.harvest({"from": gov})
    chain.mine(1)

    b = yDAI.pricePerShare()

    assert b > a

    #withdrawals have a slippage protection parameter, defaults to 1 = 0.01%.
    #overwriting here to be 1.5%, to account for slippage from multiple hops.
    #slippage also counts "beneficial" slippage, such as DAI being overweight in these pools
    #d = yUSDT3.balanceOf(alice)

    c = yDAI.balanceOf(alice)

    yDAI.withdraw(c, alice, 150, {"from": alice})

    assert dai.balanceOf(alice) > 0
    assert dai.balanceOf(strategy) == 0
    assert dai.balanceOf(bob) == 0
    assert targetVault.balanceOf(strategy) > 0

    d = yDAI.balanceOf(bob)
    yDAI.withdraw(d, bob, 150, {"from": bob})

    assert dai.balanceOf(bob) > 0
    assert dai.balanceOf(strategy) == 0

    e = yDAI.balanceOf(tinytim)
    yDAI.withdraw(e, tinytim, 150, {"from": tinytim})

    assert dai.balanceOf(tinytim) > 0
    assert dai.balanceOf(strategy) == 0

    # We should have made profit
    assert yDAI.pricePerShare() > 1

    #print("\ntinytim", dai.balanceOf(tinytim)/1e18)
    #print("\nbob", dai.balanceOf(bob)/1e18)
    #print("\nalice", dai.balanceOf(alice)/1e18)
    #print("\nvault", targetVault.balanceOf(strategy)/1e18)

    pass
def main():
    deployer = accounts.at('0xB593d82d53e2c187dc49673709a6E9f806cdC835',
                           force=True)
    # deployer = accounts.load('gh')

    werc20 = WERC20.at('0xe28D9dF7718b0b5Ba69E01073fE82254a9eD2F98')
    wmas = WMasterChef.at('0x373ae78a14577682591E088F2E78EF1417612c68')
    wliq = WLiquidityGauge.at('0xfdB4f97953150e47C8606758C13e70b5a789a7ec')
    wsindex = WStakingRewards.at('0x713df2DDDA9C7d7bDa98A9f8fCd82c06c50fbd90')
    wsperp = WStakingRewards.at('0xC4635854480ffF80F742645da0310e9e59795c63')

    uni_kp3r_oracle = ERC20KP3ROracle.at(
        '0x8E2A3777AB22e1c5f6d1fF2BcC6C4aA6aB1DeA14')
    sushi_kp3r_oracle = ERC20KP3ROracle.at(
        '0xC331687FD71bb1D7f2e237091F8888dDcaD50C9a')
    core_oracle = CoreOracle.at('0x1E5BDdd0cDF8839d6b27b34927869eF0AD7bf692')
    uni_oracle = UniswapV2Oracle.at(
        '0x910aD02D355c2966e8dD8E32C890cD50DB29C3B9')
    bal_oracle = BalancerPairOracle.at(
        '0x8F93B0AA2643bf74ab38d6a5910fA82D2da02134')
    crv_oracle = CurveOracle.at('0x04DE0E42B3b0483248deafE86C4F5428613b76Ff')
    proxy_oracle = ProxyOracle.at('0x43c425fB2b00a991A51b18c217D749E393bF1AB2')

    # pair token with oracle
    from .tokens import Tokens

    # re-set oracles
    oracle_params = [
        # base tokens
        [Tokens.weth, [12616, 7927, 10250]],
        [Tokens.dai, [10525, 9502, 10250]],
        [Tokens.usdc, [10525, 9502, 10250]],
        [Tokens.usdt, [10525, 9502, 10250]],
        [Tokens.dpi, [14886, 6718, 10250]],
        [Tokens.yfi, [13681, 7309, 10250]],
        [Tokens.perp, [14886, 6718, 10250]],
        [Tokens.snx, [13681, 7309, 10250]],
        [Tokens.susd, [11660, 8576, 10250]],
        [Tokens.uni, [14886, 6718, 10250]],
        [Tokens.sushi, [14886, 6718, 10250]],
        # lp tokens
        [Tokens.uni_eth_dpi, [14886, 6718, 10250]],
        [Tokens.uni_eth_yfi, [13681, 7309, 10250]],
        [Tokens.uni_eth_snx, [13681, 7309, 10250]],
        [Tokens.uni_eth_susd, [12616, 7927, 10250]],
        [Tokens.uni_eth_uni, [14886, 6718, 10250]],
        [Tokens.sushi_eth_dpi, [14886, 6718, 10250]],
        [Tokens.sushi_eth_yfi, [13681, 7309, 10250]],
        [Tokens.sushi_eth_snx, [13681, 7309, 10250]],
        [Tokens.sushi_eth_susd, [12616, 7927, 10250]],
        [Tokens.sushi_eth_sushi, [14886, 6718, 10250]],
        [Tokens.bal_perp_usdc, [14886, 6718, 10250]],
        [Tokens.crv_3pool, [10525, 9502, 10250]],
        [Tokens.crv_susd, [11660, 8576, 10250]],
    ]

    token_list_2, param_list = zip(*oracle_params)

    proxy_oracle.setOracles(token_list_2, param_list, {'from': deployer})

    print('DONE')
Exemple #11
0
def atomic_enter():
    """Use a flash loan to deposit into leveraged cyy3crv position."""
    # TODO: we need an account with private keys
    account = accounts.at(os.environ["LEVERAGE_ACCOUNT"])
    print(f"Hello, {account}")

    min_3crv_to_claim = os.environ.get("MIN_3CRV_TO_CLAIM", 50)

    # deploy our contracts if necessary
    argobytes_factory = get_or_create(account, ArgobytesFactory)
    argobytes_flash_borrower = get_or_create(account, ArgobytesFlashBorrower)
    enter_cyy3crv_action = get_or_create(account, EnterCYY3CRVAction)

    # get the clone for the account
    argobytes_clone = get_or_clone(account, argobytes_factory,
                                   argobytes_flash_borrower)

    print(f"clone: {argobytes_clone}")

    assert account == argobytes_clone.owner(), "Wrong owner detected!"

    print("Preparing contracts...")
    # TODO: use multicall to get all the addresses?
    dai = lazy_contract(enter_cyy3crv_action.DAI())
    usdc = lazy_contract(enter_cyy3crv_action.USDC())
    usdt = lazy_contract(enter_cyy3crv_action.USDT())
    threecrv = lazy_contract(enter_cyy3crv_action.THREE_CRV())
    threecrv_pool = lazy_contract(enter_cyy3crv_action.THREE_CRV_POOL())
    y3crv = lazy_contract(enter_cyy3crv_action.Y_THREE_CRV())
    cyy3crv = lazy_contract(enter_cyy3crv_action.CY_Y_THREE_CRV())
    fee_distribution = lazy_contract(
        enter_cyy3crv_action.THREE_CRV_FEE_DISTRIBUTION())
    lender = DyDxFlashLender

    # use multiple workers to fetch the contracts
    # there will still be some to fetch, but this speeds things up some
    # this can take some time since solc/vyper may have to download
    # TODO: i think doing this in parallel might be confusiing things
    # poke_contracts([dai, usdc, usdt, threecrv, threecrv_pool, y3crv, cyy3crv, lender])

    tokens = [dai, usdc, usdt, threecrv, y3crv, cyy3crv]

    balances = get_balances(account, tokens)
    print(f"{account} balances")
    print_token_balances(balances)

    claimable_3crv = get_claimable_3crv(account, fee_distribution,
                                        min_3crv_to_claim)

    # TODO: calculate/prompt for these
    min_3crv_mint_amount = 1
    tip_3crv = 0
    tip_address = _resolve_address(
        "tip.satoshiandkin.eth"
    )  # TODO: put this on a subdomain and uses an immutable
    min_cream_liquidity = 1000

    enter_data = EnterData(
        dai=balances[dai],
        dai_flash_fee=None,
        usdc=balances[usdc],
        usdt=balances[usdt],
        min_3crv_mint_amount=min_3crv_mint_amount,
        threecrv=balances[threecrv],
        tip_3crv=tip_3crv,
        y3crv=balances[y3crv],
        min_cream_liquidity=min_cream_liquidity,
        sender=account,
        tip_address=tip_address,
        claim_3crv=claimable_3crv > min_3crv_to_claim,
    )

    # TODO: do this properly. use virtualprice and yearn's price calculation
    print("warning! summed_balances is not actually priced in USD")
    summed_balances = (enter_data.dai + enter_data.usdc + enter_data.usdt +
                       enter_data.threecrv + enter_data.y3crv + claimable_3crv)

    assert summed_balances > 100, "no coins"

    # TODO: figure out the actual max leverage, then prompt the user for it (though i dont see much reason not to go the full amount here)
    flash_loan_amount = int(summed_balances * 7.4)

    print(f"flash_loan_amount: {flash_loan_amount}")

    assert flash_loan_amount > 0, "no flash loan calculated"

    enter_data = enter_data._replace(
        dai_flash_fee=lender.flashFee(dai, flash_loan_amount))

    pprint(enter_data)

    extra_balances = {}

    if enter_data.claim_3crv:
        extra_balances[threecrv.address] = claimable_3crv

    approve(account, balances, extra_balances, argobytes_clone)

    # flashloan through the clone
    enter_tx = argobytes_clone.flashBorrow(
        lender,
        dai,
        flash_loan_amount,
        Action(
            enter_cyy3crv_action,
            CallType.DELEGATE,
            False,
            "enter",
            enter_data,
        ).tuple,
    )

    print(f"enter success! {enter_tx.return_value}")

    enter_tx.info()

    num_events = len(enter_tx.events)
    print(f"num events: {num_events}")

    enter_return = to_int(enter_tx.return_value)

    print(f"return value: {enter_return}")

    assert enter_return > 0, "no cyy3ccrv returned!"

    print(f"clone ({argobytes_clone.address}) balances")
    balances = get_balances(argobytes_clone, tokens)
    print_token_balances(balances)

    # TODO: why is this not working? we return cyy3crv.balanceOf!
    # assert balances[cyy3crv] == enter_tx.return_value

    # TODO: make sure the clone has cyy3crv?

    print(f"account ({account}) balances")
    print_token_balances(get_balances(account, tokens))
Exemple #12
0
    def testTransactions(self):
        rewardsEscrow = self.badger.rewardsEscrow
        multi = GnosisSafe(self.badger.devMultisig)
        opsMulti = GnosisSafe(self.badger.opsMultisig)

        # Setup
        accounts[7].transfer(multi.get_first_owner(), Wei("2 ether"))
        print(
            "Supplied ETH",
            accounts.at(multi.get_first_owner(), force=True).balance(),
        )

        badger = self.badger
        tree = self.badger.badgerTree

        before = badger.token.balanceOf(tree)
        top_up = Wei("100000 ether")
        top_up_digg = Wei("40 gwei")
        harvest_badger = Wei("30000 ether")
        harvest_digg = Wei("40 gwei")

        # Top up Tree
        # TODO: Make the amount based on what we'll require for the next week
        id = multi.addTx(
            MultisigTxMetadata(description="Top up badger tree with Badger"),
            {
                "to":
                rewardsEscrow.address,
                "data":
                rewardsEscrow.transfer.encode_input(badger.token, tree,
                                                    top_up),
            },
        )

        tx = multi.executeTx(id)

        after = badger.token.balanceOf(tree)
        assert after == before + top_up

        before = badger.digg.token.balanceOf(tree)

        tx = multi.execute(
            MultisigTxMetadata(description="Top up badger tree with DIGG"),
            {
                "to":
                rewardsEscrow.address,
                "data":
                rewardsEscrow.transfer.encode_input(badger.digg.token, tree,
                                                    top_up_digg),
            },
        )

        print(tx.call_trace(), before, after)

        after = badger.digg.token.balanceOf(tree)
        assert after == before + top_up_digg

        # multi.execute(
        #     MultisigTxMetadata(description="Top up rewards manager with Badger"),
        #     {
        #         "to": rewardsEscrow.address,
        #         "data": rewardsEscrow.transfer.encode_input(
        #             badger.token, badger.badgerRewardsManager, harvest_badger
        #         ),
        #     },
        # )

        multi.execute(
            MultisigTxMetadata(description="Top up rewards manager with DIGG"),
            {
                "to":
                rewardsEscrow.address,
                "data":
                rewardsEscrow.transfer.encode_input(
                    badger.digg.token, badger.badgerRewardsManager,
                    harvest_digg),
            },
        )

        # grant_token_locking_permission(self.badger, self.badger.unlockScheduler)

        geyserDists = []

        for key, distribution in self.distributions.items():
            if distribution.hasGeyserDistribution() == True:
                print("has geyser distribution", key)
                dist = GeyserDistributor()

                dists = dist.generate(
                    badger,
                    multi,
                    key,
                    distributions=distribution.getGeyserDistributions(),
                    start=self.start,
                    duration=self.duration,
                    end=self.end,
                )

                geyserDists.extend(dists)
                console.log("after " + key, geyserDists)

        # Add unlock schedeules inbulk
        console.log(geyserDists)
        tx = opsMulti.execute(
            MultisigTxMetadata(description="Signal unlock schedules"),
            {
                "to":
                badger.unlockScheduler.address,
                "data":
                badger.unlockScheduler.signalTokenLocks.encode_input(
                    geyserDists),
            },
        )
        print(tx.call_trace())

        tokens = [self.badger.token.address, self.badger.digg.token.address]

        for key in geyser_keys:
            print(key)
            geyser = self.badger.getGeyser(key)
            for token in tokens:
                print(token)
                console.log(
                    "{} schedules for {}".format(token, key),
                    geyser.getUnlockSchedulesFor(token),
                )
Exemple #13
0
def test_operation(pm, chain):
    wbtc_liquidity = accounts.at("0x93054188d876f558f4a66b2ef1d97d16edf0895b",
                                 force=True)  # using curve pool (lots of wbtc)

    sbtc_liquidity = accounts.at("0x13c1542a468319688b89e323fe9a3be3a90ebb27",
                                 force=True)  # synthetix (lots of sbtc)

    rewards = accounts[2]
    gov = accounts[3]
    guardian = accounts[4]
    bob = accounts[5]
    alice = accounts[6]
    strategist = accounts[7]
    tinytim = accounts[8]

    wbtc = Contract("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
                    owner=gov)  # wbtc token

    wbtc.approve(wbtc_liquidity, Wei("1000000 ether"),
                 {"from": wbtc_liquidity})
    wbtc.transferFrom(wbtc_liquidity, gov, 3000000000,
                      {"from": wbtc_liquidity})

    sbtc = Contract("0x075b1bb99792c9E1041bA13afEf80C91a1e70fB3",
                    owner=gov)  # sbtcCRV token address (threePool)

    sbtc.approve(sbtc_liquidity, Wei("1000000 ether"),
                 {"from": sbtc_liquidity})
    sbtc.transferFrom(sbtc_liquidity, gov, Wei("100 ether"),
                      {"from": sbtc_liquidity})

    # config vault.
    Vault = pm(config["dependencies"][0]).Vault
    yUSDT3 = Vault.deploy({"from": gov})
    yUSDT3.initialize(wbtc, gov, rewards, "", "")
    yUSDT3.setDepositLimit(Wei("1000000 ether"))

    sbtcPool = Contract("0x7fC77b5c7614E1533320Ea6DDc2Eb61fa00A9714",
                        owner=gov)  # sbtcCRV pool address (threePool)
    ysBTC = Contract("0x7Ff566E1d69DEfF32a7b244aE7276b9f90e9D0f6",
                     owner=gov)  # sbtc vault (threePool)
    #crv3Strat = Contract(
    #    "0xC59601F0CC49baa266891b7fc63d2D5FE097A79D", owner=gov
    #)  # crv3 strat (threePool)
    #crv3StratOwner = Contract(
    #    "0xd0aC37E3524F295D141d3839d5ed5F26A40b589D", owner=gov
    #)  # crv3 stratOwner (threePool)
    # uni = Contract.from_explorer(
    #  "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", owner=gov
    # )  # UNI router v2

    strategy = guardian.deploy(StrategyWBTCsBTCv2, yUSDT3, wbtc, sbtcPool,
                               ysBTC, sbtc)
    strategy.setStrategist(strategist)

    yUSDT3.addStrategy(strategy, 10_000, 0, 0, {"from": gov})

    wbtc.approve(gov, Wei("1000000 ether"), {"from": gov})
    wbtc.transferFrom(gov, bob, 100000000, {"from": gov})
    wbtc.transferFrom(gov, alice, 1000000000, {"from": gov})
    wbtc.transferFrom(gov, tinytim, 1000000, {"from": gov})
    wbtc.approve(yUSDT3, Wei("1000000 ether"), {"from": bob})
    wbtc.approve(yUSDT3, Wei("1000000 ether"), {"from": alice})
    wbtc.approve(yUSDT3, Wei("1000000 ether"), {"from": tinytim})
    sbtc.approve(gov, Wei("1000000 ether"), {"from": gov})
    yUSDT3.approve(gov, Wei("1000000 ether"), {"from": gov})
    sbtc.approve(ysBTC, Wei("1000000 ether"), {"from": gov})
    wbtc.approve(sbtcPool, Wei("1000000 ether"), {"from": gov})

    wbtc.approve(sbtcPool, Wei("1000000 ether"), {"from": gov})
    # depositing DAI to generate crv3 tokens.
    #crv3.approve(crv3_liquidity, Wei("1000000 ether"), {"from": crv3_liquidity})
    #threePool.add_liquidity([0, 200000000000, 0], 0, {"from": gov})
    #giving Gov some shares to mimic profit
    #yCRV3.depositAll({"from": gov})

    # users deposit to vault
    yUSDT3.deposit(100000000, {"from": bob})
    yUSDT3.deposit(1000000000, {"from": alice})
    yUSDT3.deposit(1000000, {"from": tinytim})

    chain.mine(1)

    strategy.harvest({"from": gov})
    chain.mine(1)

    newstrategy = guardian.deploy(StrategyWBTCsBTCv2, yUSDT3, wbtc, sbtcPool,
                                  ysBTC, sbtc)
    newstrategy.setStrategist(strategist)

    yUSDT3.migrateStrategy(strategy, newstrategy, {"from": gov})

    assert ysBTC.balanceOf(strategy) == 0
    assert ysBTC.balanceOf(newstrategy) > 0

    pass
Exemple #14
0
def main():
    admin = accounts[0]
    alice = accounts[1]
    usdt = interface.IERC20Ex('0xdAC17F958D2ee523a2206206994597C13D831ec7')
    lpusdt = interface.IERC20Ex('0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852')
    crusdt = interface.ICErc20('0x797AAB1ce7c01eB727ab980762bA88e7133d2157')
    router = interface.IUniswapV2Router02('0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D')
    erc20_oracle = ERC20KP3ROracle.deploy(KP3R_ADDRESS, {'from': admin})
    lp_oracle = UniswapV2LPKP3ROracle.deploy(KP3R_ADDRESS, {'from': admin})
    oracle = ProxyOracle.deploy({'from': admin})
    oracle.setOracles(
        [
            '0xdAC17F958D2ee523a2206206994597C13D831ec7',  # USDT
            '0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852',  # USDT-ETH
        ],
        [
            [erc20_oracle, 10000, 10000, 10000],
            [lp_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})
    # lpusdt.approve(homora, 2**256-1, {'from': alice})
    # Steal some LP from the staking pool
    lpusdt.transfer(alice, 1*10**17, {'from': accounts.at('0x6C3e4cb2E96B01F4b866965A91ed4437839A121a', force=True)})
    household_spell = HouseHoldSpell.deploy(homora, WETH_ADDRESS, {'from': admin})
    tx = homora.execute(
        0,  # position id
        household_spell,
        household_spell.putCollateral.encode_input(
            '0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852',  # USDT
            '0.00001 ether',  # valued at approximately $600
        ),
        {'from': alice},

    )
    print('put collateral gas', tx.gas_used)
    position_id = tx.return_value
    print(homora.getCollateralETHValue(position_id), homora.getBorrowETHValue(position_id))
    tx = homora.execute(
        position_id,  # position id
        household_spell,
        household_spell.borrow.encode_input(
            '0xdAC17F958D2ee523a2206206994597C13D831ec7',  # USDT
            '500000000',  # $500
        ),
        {'from': alice},
    )
    print('bal', usdt.balanceOf(alice))
    print('put collateral gas', tx.gas_used)

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

    usdt.approve(homora, 2**256-1, {'from': alice})
    usdt.approve(crusdt, 2**256-1, {'from': accounts.at(homora, force=True)})

    tx = homora.execute(
        position_id,  # position id
        household_spell,
        household_spell.repay.encode_input(
            '0xdAC17F958D2ee523a2206206994597C13D831ec7',  # USDT
            '300000000',  # $300
        ),
        {'from': alice},
    )
    print('bal', usdt.balanceOf(alice))
    print('repay gas', tx.gas_used)
def check_vault(vault_proxy_addr, vault_impl_addr, beth_token_addr,
                bridge_connector_addr, rewards_liquidator_addr,
                insurance_connector_addr, terra_rewards_distributor_addr,
                vault_admin, vault_liquidations_admin,
                rew_liq_max_steth_eth_price_difference_percent,
                rew_liq_max_eth_usdc_price_difference_percent,
                rew_liq_max_usdc_ust_price_difference_percent,
                rew_liq_max_steth_ust_price_difference_percent):
    # Needed to properly decode LogMessagePublished events of Wormhole bridge
    wormhole = interface.Wormhole(c.wormhole_addr)

    vault_proxy = AnchorVaultProxy.at(vault_proxy_addr)
    vault_impl = AnchorVault.at(vault_impl_addr)
    vault = Contract.from_abi('AnchorVault', vault_proxy.address,
                              AnchorVault.abi)
    beth_token = bEth.at(beth_token_addr)
    ust_token = interface.WormholeWrappedToken(c.ust_token_addr)
    steth_token = interface.Lido(c.steth_token_addr)

    bridge_connector = BridgeConnectorWormhole.at(bridge_connector_addr)
    rewards_liquidator = RewardsLiquidator.at(rewards_liquidator_addr)
    insurance_connector = InsuranceConnector.at(insurance_connector_addr)

    terra_rewards_distributor_wh_encoded_addr = wh.encode_addr(
        terra_rewards_distributor_addr)

    log.ok('vault admin', vault_admin)

    print()

    log.ok('stETH', steth_token.address)
    log.ok('UST', ust_token.address)

    print()

    log.ok('bETH', beth_token.address)
    assert_equals('  admin', beth_token.admin(), vault_admin)
    assert_equals('  minter', beth_token.minter(), vault_proxy)
    # assert_equals('  totalSupply', beth_token.totalSupply(), 0)
    assert_equals('  name', beth_token.name(), 'bETH')
    assert_equals('  symbol', beth_token.symbol(), 'bETH')
    assert_equals('  decimals', beth_token.decimals(), 18)

    print()

    log.ok('AnchorVault impl', vault_impl.address)

    assert_equals('  admin', vault_impl.admin(), ZERO_ADDRESS)
    assert_equals('  bridge_connector', vault_impl.bridge_connector(),
                  ZERO_ADDRESS)
    assert_equals('  rewards_liquidator', vault_impl.rewards_liquidator(),
                  ZERO_ADDRESS)
    assert_equals('  insurance_connector', vault_impl.insurance_connector(),
                  ZERO_ADDRESS)

    print()

    no_liquidation_interval = 0
    restricted_liquidation_interval = 26 * 60 * 60

    log.ok('AnchorVaultProxy', vault_proxy.address)
    assert_equals('  proxy_getAdmin', vault_proxy.proxy_getAdmin(),
                  vault_admin)
    assert_equals('  proxy_getIsOssified', vault_proxy.proxy_getIsOssified(),
                  False)
    assert_equals('  implementation', vault_proxy.implementation(), vault_impl)
    assert_equals('  admin', vault.admin(), vault_admin)
    assert_equals('  beth_token', vault.beth_token(), beth_token.address)
    assert_equals('  steth_token', vault.steth_token(), steth_token.address)
    assert_equals('  bridge_connector', vault.bridge_connector(),
                  bridge_connector.address)
    assert_equals('  rewards_liquidator', vault.rewards_liquidator(),
                  rewards_liquidator.address)
    assert_equals('  insurance_connector', vault.insurance_connector(),
                  insurance_connector.address)
    assert_equals('  vault_liquidations_admin', vault.liquidations_admin(),
                  vault_liquidations_admin)
    assert_equals('  no_liquidation_interval', vault.no_liquidation_interval(),
                  no_liquidation_interval)
    assert_equals('  restricted_liquidation_interval',
                  vault.restricted_liquidation_interval(),
                  restricted_liquidation_interval)
    assert_equals('  anchor_rewards_distributor',
                  vault.anchor_rewards_distributor(),
                  terra_rewards_distributor_wh_encoded_addr)
    assert_equals('  get_rate', vault.get_rate(), 10**18)

    print()

    log.ok('BridgeConnectorWormhole', bridge_connector.address)
    assert_equals('  wormhole_token_bridge',
                  bridge_connector.wormhole_token_bridge(),
                  c.wormhole_token_bridge_addr)

    print()

    log.ok('RewardsLiquidator', rewards_liquidator.address)
    assert_equals('  admin', rewards_liquidator.admin(), vault_admin)
    assert_equals('  vault', rewards_liquidator.vault(), vault_proxy.address)
    assert_equals(
        '  max_steth_eth_price_difference_percent',
        rewards_liquidator.max_steth_eth_price_difference_percent(),
        int(rew_liq_max_steth_eth_price_difference_percent * 10**18 / 100))
    assert_equals(
        '  max_eth_usdc_price_difference_percent',
        rewards_liquidator.max_eth_usdc_price_difference_percent(),
        int(rew_liq_max_eth_usdc_price_difference_percent * 10**18 / 100))
    assert_equals(
        '  max_usdc_ust_price_difference_percent',
        rewards_liquidator.max_usdc_ust_price_difference_percent(),
        int(rew_liq_max_usdc_ust_price_difference_percent * 10**18 / 100))
    assert_equals(
        '  max_steth_ust_price_difference_percent',
        rewards_liquidator.max_steth_ust_price_difference_percent(),
        int(rew_liq_max_steth_ust_price_difference_percent * 10**18 / 100))

    print()

    log.ok('InsuranceConnector', insurance_connector.address)

    print()

    log.ok('liquidations admin', vault_liquidations_admin)
    log.ok(
        'no liquidation interval',
        f'{no_liquidation_interval} sec ({no_liquidation_interval / 3600} hrs)'
    )
    log.ok(
        'restricted liquidation interval',
        f'{restricted_liquidation_interval} sec ({restricted_liquidation_interval / 3600} hrs)'
    )
    log.ok('terra rewards distributor',
           terra_rewards_distributor_wh_encoded_addr)

    print()

    if c.get_is_live():
        print('Running on a live network, cannot run further checks.')
        print('Run on a mainnet fork to do this.')
        return

    log.h('Performing AnchorVault fork checks')

    with chain_snapshot():
        if not vault.can_deposit_or_withdraw():
            log.h('Selling rewards...')
            tx = vault.collect_rewards(
                {'from': accounts.at(vault_liquidations_admin, force=True)})
            # tx.info()
            assert_equals('AnchorVault.can_deposit_or_withdraw',
                          vault.can_deposit_or_withdraw(), True)

        beth_supply_before = beth_token.totalSupply()
        bridge_balance_before = beth_token.balanceOf(
            c.wormhole_token_bridge_addr)

        log.ok('bETH total supply', beth_supply_before)
        log.ok('bridge bETH balance', bridge_balance_before)

        log.h('Obtaining stETH...')

        holder_1 = accounts[0]
        holder_2 = accounts[1]

        steth_token.submit(ZERO_ADDRESS, {
            'from': holder_1,
            'value': 3 * 10**18
        })
        assert steth_token.balanceOf(holder_1) > 0

        log.ok('holder_1 balance', steth_token.balanceOf(holder_1))
        assert_equals('holder_2 balance', steth_token.balanceOf(holder_2), 0)

        log.h('Submitting stETH...')

        terra_recipient_1 = '0x0000000000000000000000008badf00d8badf00d8badf00d8badf00d8badf00d'
        steth_token.approve(vault, 2 * 10**18, {'from': holder_1})
        tx = vault.submit(2 * 10**18, terra_recipient_1, b'', vault.version(),
                          {'from': holder_1})
        # tx.info()

        assert 'Deposited' in tx.events
        assert tx.events['Deposited'][0].address == vault.address
        log.ok('Deposited event emitted')

        deposited_amount = tx.events['Deposited']['amount']
        assert deposited_amount >= 1.999 * 10**18 and deposited_amount <= 2 * 10**18
        log.ok('Deposited.amount', deposited_amount)

        assert_equals('holder_1 balance', beth_token.balanceOf(holder_1), 0)

        beth_supply = beth_token.totalSupply()
        assert beth_supply == beth_supply_before + deposited_amount
        assert_equals('bETH total supply', beth_supply,
                      beth_supply_before + deposited_amount)

        bridge_balance = beth_token.balanceOf(c.wormhole_token_bridge_addr)
        assert bridge_balance == bridge_balance_before + deposited_amount
        assert_equals('bridge bETH balance', bridge_balance,
                      bridge_balance_before + deposited_amount)

        assert 'LogMessagePublished' in tx.events
        assert tx.events['LogMessagePublished'][0].address == c.wormhole_addr
        log.ok('LogMessagePublished event emitted')

        expected_payload = wh.assemble_transfer_payload(
            token_address=beth_token.address,
            normalized_amount=wh.normalize_amount(deposited_amount, 18),
            to_address=terra_recipient_1,
            token_chain_id=wh.CHAIN_ID_ETHERUM,
            to_chain_id=wh.CHAIN_ID_TERRA,
            fee=0)

        assert tx.events['LogMessagePublished'][0][
            'payload'] == expected_payload
        log.ok('LogMessagePublished.payload', expected_payload)

        log.h('Submitting ETH...')

        bridge_balance_before = bridge_balance
        beth_supply_before = beth_supply

        terra_recipient_2 = '0x000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef'
        tx = vault.submit(1 * 10**18, terra_recipient_2, b'', vault.version(),
                          {
                              'from': holder_2,
                              'value': 1 * 10**18
                          })
        # tx.info()

        assert 'Deposited' in tx.events
        assert tx.events['Deposited'][0].address == vault.address
        log.ok('Deposited event emitted')

        deposited_amount = tx.events['Deposited']['amount']
        assert deposited_amount >= 0.999 * 10**18 and deposited_amount <= 1 * 10**18
        log.ok('Deposited.amount', deposited_amount)

        assert_equals('holder_2 balance', beth_token.balanceOf(holder_2), 0)

        beth_supply = beth_token.totalSupply()
        assert beth_supply == beth_supply_before + deposited_amount
        assert_equals('bETH total supply', beth_supply,
                      beth_supply_before + deposited_amount)

        bridge_balance = beth_token.balanceOf(c.wormhole_token_bridge_addr)
        assert bridge_balance == bridge_balance_before + deposited_amount
        assert_equals('bridge bETH balance', bridge_balance,
                      bridge_balance_before + deposited_amount)

        assert 'LogMessagePublished' in tx.events
        assert tx.events['LogMessagePublished'][0].address == c.wormhole_addr
        log.ok('LogMessagePublished event emitted')

        expected_payload = wh.assemble_transfer_payload(
            token_address=beth_token.address,
            normalized_amount=wh.normalize_amount(deposited_amount, 18),
            to_address=terra_recipient_2,
            token_chain_id=wh.CHAIN_ID_ETHERUM,
            to_chain_id=wh.CHAIN_ID_TERRA,
            fee=0)

        assert tx.events['LogMessagePublished'][0][
            'payload'] == expected_payload
        log.ok('LogMessagePublished.payload', expected_payload)

        log.h('Rebasing stETH by 0.02%...')

        lido_oracle_report(steth_rebase_percent=0.02)
        assert_equals('AnchorVault.can_deposit_or_withdraw',
                      vault.can_deposit_or_withdraw(), False)

        log.h('Selling rewards...')

        liquidations_admin = accounts.at(vault.liquidations_admin(),
                                         force=True)
        tx = vault.collect_rewards({'from': liquidations_admin})
        # tx.info()

        assert 'RewardsCollected' in tx.events
        assert tx.events['RewardsCollected'][0].address == vault.address
        log.ok('RewardsCollected event emitted')

        ust_amount = tx.events['RewardsCollected'][0]['ust_amount']
        assert ust_amount > 0
        log.ok('RewardsCollected.ust_amount', ust_amount)

        assert 'LogMessagePublished' in tx.events
        assert tx.events['LogMessagePublished'][0].address == c.wormhole_addr
        log.ok('LogMessagePublished event emitted')

        expected_payload = wh.assemble_transfer_payload(
            # See https://github.com/certusone/wormhole/blob/aff369f/ethereum/contracts/bridge/Bridge.sol#L97-L103
            # For wrapped tokens, the "token address" field is set to the address of the token on its native chain
            token_address=str(ust_token.nativeContract()),
            normalized_amount=wh.normalize_amount(ust_amount,
                                                  ust_token.decimals()),
            to_address=terra_rewards_distributor_addr,
            token_chain_id=ust_token.chainId(),
            to_chain_id=wh.CHAIN_ID_TERRA,
            fee=0)

        assert tx.events['LogMessagePublished'][0][
            'payload'] == expected_payload
        log.ok('LogMessagePublished.payload', expected_payload)
Exemple #16
0
def test_loss(pm, chain):
    # probably not needed
    rhegic_liquidity = accounts.at(
        "0x8ac7de95ff8d0ee72e0b54f781ab2e18f27108fb", force=True)

    # weth needed to test strategy
    weth_liquidity = accounts.at("0x2f0b23f53734252bda2277357e97e1517d6b042a",
                                 force=True)  # Maker vault (~2.6mil)

    rewards = accounts[2]
    gov = accounts[3]
    guardian = accounts[4]
    bob = accounts[5]
    alice = accounts[6]
    strategist = accounts[7]

    rHegic = Contract.from_explorer(
        "0x47C0aD2aE6c0Ed4bcf7bc5b380D7205E89436e84", owner=gov)
    rHegic.approve(rhegic_liquidity, Wei("1000000 ether"),
                   {"from": rhegic_liquidity})
    rHegic.transferFrom(rhegic_liquidity, gov, Wei("888000 ether"),
                        {"from": rhegic_liquidity})

    # vault deals with weth, so addresses need it.
    weth = Contract.from_explorer("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
                                  owner=gov)  # weth token
    weth.approve(weth_liquidity, Wei("100 ether"), {"from": weth_liquidity})
    weth.transferFrom(weth_liquidity, gov, Wei("88 ether"),
                      {"from": weth_liquidity})

    # declaring yEthLP as the vault
    Vault = pm(config["dependencies"][0]).Vault
    yEthLP = gov.deploy(Vault, weth, gov, rewards, "", "")

    ethPool = Contract.from_explorer(
        "0x878F15ffC8b894A1BA7647c7176E4C01f74e140b",
        owner=gov)  # ETH LP pool (writeETH)
    ethPoolStaking = Contract.from_explorer(
        "0x9b18975e64763bDA591618cdF02D2f14a9875981",
        owner=gov)  # ETH LP pool staking for rHegic
    uni = Contract.from_explorer("0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
                                 owner=gov)  # UNI router v2

    strategy = guardian.deploy(StrategyEthHegicLP, weth, yEthLP, rHegic,
                               ethPoolStaking, ethPool, uni)
    strategy.setStrategist(strategist)

    # adding strategy to the vault
    yEthLP.addStrategy(strategy, Wei("88 ether"), 0, 50, {"from": gov})

    # folks need weth for the contract
    weth.approve(gov, Wei("100 ether"), {"from": gov})
    weth.transferFrom(gov, bob, Wei("10 ether"), {"from": gov})
    weth.transferFrom(gov, alice, Wei("78 ether"), {"from": gov})
    weth.approve(yEthLP, Wei("100 ether"), {"from": bob})
    weth.approve(yEthLP, Wei("100 ether"), {"from": alice})

    yEthLP.deposit(Wei("10 ether"), {"from": bob})
    yEthLP.deposit(Wei("78 ether"), {"from": alice})
    strategy.harvest()

    rHegic.approve(gov, Wei("1000 ether"), {"from": gov})

    # TODO: Instead of sending rhegic to the strategy directly,
    # we should push the clock forward to grab rhegic from Hegic protocol
    rHegic.transferFrom(gov, strategy, Wei("100 ether"), {"from": gov})

    strategy.harvest({"from": gov})

    assert rHegic.balanceOf(strategy) == 0
    assert ethPool.balanceOf(strategy) == 0
    assert ethPoolStaking.balanceOf(strategy) > 0

    # We should have made profit
    assert yEthLP.pricePerShare() / 1e18 > 1

    chain.sleep(3600 * 24 * 15)  # 15 days
    chain.mine(1)

    # This should work by doing it by hand since exitPosition is not working
    # strategy.setEmergencyExit({"from": gov})
    # strategy.harvest({"from": gov})

    # Withdraw funds
    pool = Contract.from_explorer(strategy.ethPool())
    staking = Contract.from_explorer(strategy.ethPoolStaking())
    staking.withdraw(staking.balanceOf(strategy), {"from": strategy})
    pool.withdraw(pool.shareOf(strategy), pool.balanceOf(strategy),
                  {"from": strategy})

    # revoke, steal weth and let's see losses
    yEthLP.revokeStrategy(strategy, {"from": gov})
    weth.transfer(gov, weth.balanceOf(strategy), {"from": strategy})
    strategy.harvest({"from": gov})

    assert yEthLP.strategies(strategy).dict()["totalLoss"] > 0
Exemple #17
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)

    mint_tokens(dpi, crdpi)

    # 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})

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

    # whitelist token in bank
    homora.setWhitelistTokens([dpi], [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 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 = 10**10
    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)
def test_hegic_strategy_infura(pm):
    hegic_liquidity = accounts.at(
        "0x736f85bf359e3e2db736d395ed8a4277123eeee1", force=True
    )  # Hegic: Liquidity M&U (lot's of hegic)

    rewards = accounts[2]
    gov = accounts[3]
    guardian = accounts[4]
    bob = accounts[5]
    alice = accounts[6]
    strategist = accounts[7]

    hegic = Contract.from_explorer(
        "0x584bC13c7D411c00c01A62e8019472dE68768430", owner=gov
    )  # Hegic token
    hegic.approve(hegic_liquidity, Wei("1000000 ether"), {"from": hegic_liquidity})
    hegic.transferFrom(
        hegic_liquidity, gov, Wei("888000 ether"), {"from": hegic_liquidity}
    )

    Vault = pm(config["dependencies"][0]).Vault
    yHegic = gov.deploy(Vault, hegic, gov, rewards, "", "")

    hegicStaking = Contract.from_explorer(
        "0x840a1AE46B7364855206Eb5b7286Ab7E207e515b", owner=gov
    )  # HEGIC WBTC Staking lot (hlWBTC)
    uni = Contract.from_explorer(
        "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", owner=gov
    )  # UNI router v2

    wbtcLiquidity = accounts.at(
        "0x9939d72411c6af3c97beff0ac81e3b780e4712ee", force=True
    )
    wbtc = Contract.from_explorer("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599")
    wbtc.approve(hegicStaking, Wei("100 ether"), {"from": wbtcLiquidity})

    strategy = guardian.deploy(
        StrategyHegicWBTC, yHegic, hegic, hegicStaking, uni, wbtc
    )
    strategy.setStrategist(strategist)

    yHegic.addStrategy(strategy, Wei("888000 ether"), 0, 50, {"from": gov})

    hegic.approve(gov, Wei("1000000 ether"), {"from": gov})
    hegic.transferFrom(gov, bob, Wei("100000 ether"), {"from": gov})
    hegic.transferFrom(gov, alice, Wei("788000 ether"), {"from": gov})
    hegic.approve(yHegic, Wei("1000000 ether"), {"from": bob})
    hegic.approve(yHegic, Wei("1000000 ether"), {"from": alice})

    yHegic.deposit(Wei("100000 ether"), {"from": bob})
    yHegic.deposit(Wei("788000 ether"), {"from": alice})

    strategy.harvest()

    assert hegic.balanceOf(strategy) == 0
    assert hegicStaking.balanceOf(strategy) == 1

    hegicStaking.sendProfit(1 * 1e8, {"from": wbtcLiquidity})
    strategy.harvest({"from": gov})

    # We should have made profit
    assert yHegic.pricePerShare() > 1
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)

    mint_tokens(susd, crsusd)

    # 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})

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

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

    # whitelist lp in spell
    curve_spell.setWhitelistLPTokens([lp, lp_busd], [True, True], {'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 = 10**6
    borrow_susd_amt = 10**18
    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 liquidity (failed tx desired)')

    ydai_amt = 200 * 10**6
    yusdc_amt = 500 * 10**6
    yusdt_amt = 400 * 10**6
    ybusd_amt = 100 * 10**6
    lp_amt = 0
    borrow_ydai_amt = 0
    borrow_yusdc_amt = 0
    borrow_yusdt_amt = 0
    borrow_ybusd_amt = 0
    borrow_lp_amt = 0
    minLPMint = 0

    pid = 12
    gid = 0

    try:
        tx = homora.execute(
            1,
            curve_spell,
            curve_spell.addLiquidity4.encode_input(
                lp_busd,  # LP_busd
                [ydai_amt, yusdc_amt, yusdt_amt, ybusd_amt],  # supply tokens
                lp_amt,  # supply LP
                [borrow_ydai_amt, borrow_yusdc_amt, borrow_yusdt_amt, borrow_ybusd_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)')

    ydai_amt = 0  # 2 * 10**6
    yusdc_amt = 0  # 5 * 10**6
    yusdt_amt = 0  # 4 * 10**6
    ybusd_amt = 1 * 10**6
    lp_amt = 0
    borrow_ydai_amt = 0
    borrow_yusdc_amt = 0
    borrow_yusdt_amt = 0
    borrow_ybusd_amt = 0
    borrow_lp_amt = 0
    minLPMint = 0

    pid = 1
    gid = 0

    try:
        tx = homora.execute(
            1,
            curve_spell,
            curve_spell.addLiquidity4.encode_input(
                lp_busd,  # LP_busd
                [ydai_amt, yusdc_amt, yusdt_amt, ybusd_amt],  # supply tokens
                lp_amt,  # supply LP
                [borrow_ydai_amt, borrow_yusdc_amt, borrow_yusdt_amt, borrow_ybusd_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
    ydai_repay = 2**256-1  # max
    yusdc_repay = 2**256-1  # max
    yusdt_repay = 2**256-1  # max
    ybusd_repay = 2**256-1  # max
    lp_repay = 0

    try:
        tx = homora.execute(
            1,
            curve_spell,
            curve_spell.removeLiquidity4.encode_input(
                lp_busd,  # LP_busd token
                lp_take_amt,  # LP amount to take out
                lp_want,  # LP amount to withdraw to wallet
                [ydai_repay, yusdc_repay, yusdt_repay, ybusd_repay],  # repay amounts
                lp_repay,  # repay LP amount
                [0, 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)
    prevDBal = susd.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 = 0
    dai_repay = 2**256-1  # max
    usdc_repay = 2**256-1  # max
    usdt_repay = 2**256-1  # max
    susd_repay = 2**256-1  # max
    lp_repay = 0

    tx = homora.execute(
        1,
        curve_spell,
        curve_spell.removeLiquidity4.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, susd_repay],  # repay amounts
            lp_repay,  # repay LP amount
            [0, 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'

    # 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 = 12, 0

    tx = homora.execute(
        0,
        curve_spell,
        curve_spell.addLiquidity4.encode_input(
            lp,  # LP
            [0, 0, 0, 0],  # supply tokens
            lp_amt,  # supply LP
            [0, 0, 0, 0],  # borrow tokens
            0,  # borrow LP
            0,  # min LP mint
            pid,
            gid
        ),
        {'from': alice}
    )

    tx = homora.execute(
        2,
        curve_spell,
        curve_spell.removeLiquidity4.encode_input(
            lp,  # LP token
            2**256-1,  # LP amount to take out
            lp_amt,  # LP amount to withdraw to wallet
            [0, 0, 0, 0],  # repay amounts
            0,  # repay LP amount
            [0, 0, 0, 0]  # min amounts
        ),
        {'from': alice}
    )

    curLPBal = lp.balanceOf(alice)

    assert prevLPBal == curLPBal, 'incorrect LP Balance'

    return tx
Exemple #20
0
def live():
    admin = accounts.at("0xC447FcAF1dEf19A583F97b3620627BF69c05b5fB")
    with open(config.DEPLOYMENTS_JSON) as fp:
        pool_proxy = json.load(fp)['PoolProxy']

    transfer_ownership(admin, pool_proxy, config.REQUIRED_CONFIRMATIONS)
def main():
    if USE_STRATEGIES:
        web3.eth.setGasPriceStrategy(gas_strategy)
        web3.middleware_onion.add(middleware.time_based_cache_middleware)
        web3.middleware_onion.add(
            middleware.latest_block_based_cache_middleware)
        web3.middleware_onion.add(middleware.simple_cache_middleware)
        if POA:
            web3.middleware_onion.inject(middleware.geth_poa_middleware,
                                         layer=0)

    deployer = accounts.at(DEPLOYER)

    lp_token = deploy_erc20s_and_pool(deployer)

    token = repeat(ERC20CRV.deploy, "Curve DAO Token", "CRV", 18, 10**9, {
        'from': deployer,
        'required_confs': CONFS
    })
    save_abi(token, 'token_crv')

    escrow = repeat(VotingEscrow.deploy, token, "Vote-escrowed CRV", "veCRV",
                    "veCRV_0.99", {
                        'from': deployer,
                        'required_confs': CONFS
                    })
    save_abi(escrow, 'voting_escrow')

    repeat(escrow.changeController, TOKEN_MANAGER, {
        'from': deployer,
        'required_confs': CONFS
    })

    for account in DISTRIBUTION_ADDRESSES:
        repeat(token.transfer, account, DISTRIBUTION_AMOUNT, {
            'from': deployer,
            'required_confs': CONFS
        })

    gauge_controller = repeat(GaugeController.deploy, token, escrow, {
        'from': deployer,
        'required_confs': CONFS
    })
    save_abi(gauge_controller, 'gauge_controller')

    minter = repeat(Minter.deploy, token, gauge_controller, {
        'from': deployer,
        'required_confs': CONFS
    })
    save_abi(minter, 'minter')

    liquidity_gauge = repeat(LiquidityGauge.deploy, lp_token, minter, {
        'from': deployer,
        'required_confs': CONFS
    })
    save_abi(liquidity_gauge, 'liquidity_gauge')

    repeat(token.set_minter, minter, {
        'from': deployer,
        'required_confs': CONFS
    })
    repeat(gauge_controller.add_type, b'Liquidity', {
        'from': deployer,
        'required_confs': CONFS
    })
    repeat(gauge_controller.change_type_weight, 0, 10**18, {
        'from': deployer,
        'required_confs': CONFS
    })
    repeat(gauge_controller.add_gauge, liquidity_gauge, 0, 10**18, {
        'from': deployer,
        'required_confs': CONFS
    })

    repeat(gauge_controller.transfer_ownership, ARAGON_AGENT, {
        'from': deployer,
        'required_confs': CONFS
    })
    repeat(escrow.transfer_ownership, ARAGON_AGENT, {
        'from': deployer,
        'required_confs': CONFS
    })

    repeat(PoolProxy.deploy, {'from': deployer, 'required_confs': CONFS})
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
def main():
    admin = accounts[0]

    alice = accounts[1]
    bob = accounts[2]

    dai = interface.IERC20Ex('0x6B175474E89094C44Da98b954EedeAC495271d0F')
    usdc = interface.IERC20Ex('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48')
    usdt = interface.IERC20Ex('0xdAC17F958D2ee523a2206206994597C13D831ec7')
    wbtc = interface.IERC20Ex('0x2260fac5e5542a773aa44fbcfedf7c193bc2c599')
    renbtc = interface.IERC20Ex('0xeb4c2781e4eba804ce9a9803c67d0893436bb27d')
    crv = interface.IERC20Ex('0xD533a949740bb3306d119CC777fa900bA034cd52')

    lp_3pool = interface.IERC20Ex('0x6c3f90f043a72fa612cbac8115ee7e52bde6e490')
    lp_btc = interface.IERC20Ex('0x49849c98ae39fff122806c06791fa73784fb3675')
    pool_3pool = interface.ICurvePool(
        '0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7')
    pool_btc = interface.ICurvePool(
        '0x93054188d876f558f4a66B2EF1d97d16eDf0895B')
    registry = interface.ICurveRegistry(
        '0x7d86446ddb609ed0f5f8684acf30380a356b2b4c')

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

    # set approval
    dai.approve(wgauge, 2**256 - 1, {'from': alice})
    usdc.approve(wgauge, 2**256 - 1, {'from': alice})
    usdt.approve(wgauge, 2**256 - 1, {'from': alice})
    renbtc.approve(wgauge, 2**256 - 1, {'from': alice})
    wbtc.approve(wgauge, 2**256 - 1, {'from': alice})
    lp_3pool.approve(wgauge, 2**256 - 1, {'from': alice})
    lp_3pool.approve(gauge, 2**256 - 1, {'from': alice})
    lp_btc.approve(wgauge, 2**256 - 1, {'from': alice})
    lp_btc.approve(gauge, 2**256 - 1, {'from': alice})

    dai.approve(wgauge, 2**256 - 1, {'from': bob})
    usdc.approve(wgauge, 2**256 - 1, {'from': bob})
    usdt.approve(wgauge, 2**256 - 1, {'from': bob})
    renbtc.approve(wgauge, 2**256 - 1, {'from': bob})
    wbtc.approve(wgauge, 2**256 - 1, {'from': bob})
    lp_3pool.approve(wgauge, 2**256 - 1, {'from': bob})
    lp_3pool.approve(gauge, 2**256 - 1, {'from': bob})
    lp_btc.approve(wgauge, 2**256 - 1, {'from': bob})
    lp_btc.approve(gauge, 2**256 - 1, {'from': bob})

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

    mint_tokens(dai, bob)
    mint_tokens(usdc, bob)
    mint_tokens(usdt, bob)
    mint_tokens(renbtc, bob)
    mint_tokens(wbtc, bob)

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

    mint_tokens(lp_3pool, bob)
    mint_tokens(lp_btc, bob)

    # register gauges
    wgauge.registerGauge(0, 0, {'from': admin})
    wgauge.registerGauge(9, 0, {'from': admin})

    ######################################################################
    # Check encoding and decoding ids
    print(
        '######################################################################'
    )
    print('Case 1.')

    # check 3pool
    pid = 0
    gid = 0
    crvPerShare = 210
    encoded_id = wgauge.encodeId(pid, gid, crvPerShare)
    print('encoded id', encoded_id)
    assert (encoded_id >> 248) == pid
    assert (encoded_id >> 240) & ((1 << 8) - 1) == gid
    assert (encoded_id & ((1 << 240) - 1)) == crvPerShare

    d_pid, d_gid, d_crvPerShare = wgauge.decodeId(encoded_id)
    print('decoded pid', d_pid)
    print('decoded gid', d_gid)
    print('decoded crvPerShare', d_crvPerShare)
    assert d_pid == pid
    assert d_gid == gid
    assert d_crvPerShare == crvPerShare

    # check renbtc pool
    pid = 9
    gid = 0
    crvPerShare = 100
    encoded_id = wgauge.encodeId(pid, gid, crvPerShare)
    print('encoded id', encoded_id)
    assert (encoded_id >> 248) == pid
    assert (encoded_id >> 240) & ((1 << 8) - 1) == gid
    assert (encoded_id & ((1 << 240) - 1)) == crvPerShare

    d_pid, d_gid, d_crvPerShare = wgauge.decodeId(encoded_id)
    print('decoded pid', d_pid)
    print('decoded gid', d_gid)
    print('decoded crvPerShare', d_crvPerShare)
    assert d_pid == pid
    assert d_gid == gid
    assert d_crvPerShare == crvPerShare

    ######################################################################
    # check getUnderlyingToken

    pid = 0
    gid = 0
    crvPerShare = 200
    id_num = wgauge.encodeId(pid, gid, crvPerShare)
    lpToken = wgauge.getUnderlyingToken(id_num)
    print('lpToken', lpToken)
    assert lpToken == lp_3pool

    pid = 9
    gid = 0
    crvPerShare = 100
    id_num = wgauge.encodeId(pid, gid, crvPerShare)
    lpToken = wgauge.getUnderlyingToken(id_num)
    print('lpToken', lpToken)
    assert lpToken == lp_btc

    ######################################################################
    # check mint & burn
    print(
        '######################################################################'
    )
    print('Case 2.')

    pid = 0
    gid = 0
    amt = 10**18

    print('alice lp 3pool balance', lp_3pool.balanceOf(alice))

    # mint
    tx = wgauge.mint(pid, gid, amt, {'from': alice})
    encoded_id = tx.return_value
    print('tx status', tx.status)
    print('encoded id', encoded_id)
    gauge, prevAccCrvPerShare = wgauge.gauges(pid, gid)
    print('gauge', gauge)
    print('prevAccCrvPerShare', prevAccCrvPerShare)
    print('alice wlp_3pool balance', wgauge.balanceOf(alice, encoded_id))
    assert tx.status == 1
    assert wgauge.balanceOf(alice, encoded_id) == amt

    chain.sleep(20000)

    # burn exact
    prevCrvBalance = crv.balanceOf(alice)
    tx = wgauge.burn(encoded_id, amt, {'from': alice})

    print('tx status', tx.status)
    print('alice wlp_3pool balance', wgauge.balanceOf(alice, encoded_id))

    print('alice crv balance', crv.balanceOf(alice))
    receivedCrv = crv.balanceOf(alice) - prevCrvBalance

    assert tx.status == 1
    assert tx.return_value == pid
    assert wgauge.balanceOf(alice, encoded_id) == 0  # remove all

    # check reward same as staking directly
    prevCrv = crv.balanceOf(alice)
    print('alice lp_3pool balance',
          interface.IERC20Ex(lp_3pool).balanceOf(alice))
    gauge, _ = wgauge.gauges(pid, gid)
    tx = interface.ILiquidityGauge(gauge).deposit(amt, {'from': alice})
    chain.sleep(20000)
    minter = interface.ILiquidityGaugeMinter(
        interface.ILiquidityGauge(gauge).minter())
    print('minter', minter)
    tx = minter.mint(gauge, {'from': alice})
    print('tx status', tx.status)
    tx = interface.ILiquidityGauge(gauge).withdraw(amt, {'from': alice})
    receivedCrvFromGauge = crv.balanceOf(alice) - prevCrv
    print('receivedCrvFromGauge', receivedCrvFromGauge)
    assert almostEqual(receivedCrv, receivedCrvFromGauge)

    ######################################################################
    # check mint & burn max_int

    print(
        '######################################################################'
    )
    print('Case 3.')

    pid = 0
    gid = 0
    amt = 10**18

    print('alice lp 3pool balance', lp_3pool.balanceOf(alice))

    # mint alice
    tx = wgauge.mint(pid, gid, amt, {'from': alice})
    encoded_id = tx.return_value
    print('tx status', tx.status)
    print('encoded id', encoded_id)
    gauge, prevAccCrvPerShare = wgauge.gauges(pid, gid)
    print('gauge', gauge)
    print('prevAccCrvPerShare', prevAccCrvPerShare)
    print('alice wlp_3pool balance', wgauge.balanceOf(alice, encoded_id))
    assert tx.status == 1
    assert wgauge.balanceOf(alice, encoded_id) == amt

    # mint bob
    prevCrvBob = crv.balanceOf(bob)
    print('bob lp_3pool balance', interface.IERC20Ex(lp_3pool).balanceOf(bob))
    gauge, _ = wgauge.gauges(pid, gid)
    tx = interface.ILiquidityGauge(gauge).deposit(amt, {'from': bob})

    chain.sleep(10000)

    # burn max_int alice
    prevCrvBalance = crv.balanceOf(alice)
    tx = wgauge.burn(encoded_id, 2**256 - 1, {'from': alice})

    print('tx status', tx.status)
    print('alice wlp_3pool balance', wgauge.balanceOf(alice, encoded_id))

    print('alice crv balance', crv.balanceOf(alice))
    receivedCrv = crv.balanceOf(alice) - prevCrvBalance

    assert tx.status == 1
    assert tx.return_value == pid
    assert wgauge.balanceOf(alice, encoded_id) == 0  # remove all

    # burn all bob
    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(amt, {'from': bob})
    receivedCrvFromGauge = crv.balanceOf(bob) - prevCrvBob

    print('receivedCrv', receivedCrv)
    print('receivedCrvFromGauge', receivedCrvFromGauge)
    assert almostEqual(receivedCrv, receivedCrvFromGauge)

    ######################################################################
    # check mint & burn (try more than available--revert, half, then remaining)

    print(
        '######################################################################'
    )
    print('Case 4.')

    pid = 0
    gid = 0
    amt = 10**18

    print('alice lp 3pool balance', lp_3pool.balanceOf(alice))

    # mint alice
    tx = wgauge.mint(pid, gid, amt, {'from': alice})
    encoded_id = tx.return_value
    print('tx status', tx.status)
    print('encoded id', encoded_id)
    gauge, prevAccCrvPerShare = wgauge.gauges(pid, gid)
    print('gauge', gauge)
    print('prevAccCrvPerShare', prevAccCrvPerShare)
    print('alice wlp_3pool balance', wgauge.balanceOf(alice, encoded_id))
    assert tx.status == 1
    assert wgauge.balanceOf(alice, encoded_id) == amt

    # mint bob
    prevCrvBob = crv.balanceOf(bob)
    print('bob lp_3pool balance', interface.IERC20Ex(lp_3pool).balanceOf(bob))
    gauge, _ = wgauge.gauges(pid, gid)
    tx = interface.ILiquidityGauge(gauge).deposit(amt, {'from': bob})

    chain.sleep(10000)

    # burn too much (expected failed)
    prevCrvBalance = crv.balanceOf(alice)
    try:
        tx = wgauge.burn(encoded_id, amt + 1, {'from': alice})
        assert tx.status == 0
    except:
        pass

    print('alice wlpusdt balance', wgauge.balanceOf(alice, encoded_id))
    print('alice crv balance', crv.balanceOf(alice))
    assert prevCrvBalance == crv.balanceOf(alice)
    assert wgauge.balanceOf(alice, encoded_id) == amt

    # burn half alice
    prevCrvBalance = crv.balanceOf(alice)
    tx = wgauge.burn(encoded_id, amt // 2, {'from': alice})

    print('tx status', tx.status)
    print('alice wlp_3pool balance', wgauge.balanceOf(alice, encoded_id))
    print('amt - amt//2', amt - amt // 2)

    print('alice crv balance', crv.balanceOf(alice))

    assert tx.status == 1
    assert tx.return_value == pid
    assert wgauge.balanceOf(alice, encoded_id) == amt - amt // 2  # remove half

    # burn half bob
    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(amt // 2, {'from': bob})

    chain.sleep(10000)

    # burn remaining alice
    tx = wgauge.burn(encoded_id, 2**256 - 1, {'from': alice})

    print('tx status', tx.status)
    print('alice wlp_3pool balance', wgauge.balanceOf(alice, encoded_id))

    print('alice crv balance', crv.balanceOf(alice))
    receivedCrv = crv.balanceOf(alice) - prevCrvBalance

    assert tx.status == 1
    assert tx.return_value == pid

    # burn remaining bob
    tx = minter.mint(gauge, {'from': bob})
    print('tx status', tx.status)
    tx = interface.ILiquidityGauge(gauge).withdraw(amt - amt // 2,
                                                   {'from': bob})
    receivedCrvFromGauge = crv.balanceOf(bob) - prevCrvBob

    print('receivedCrv alice', receivedCrv)
    print('receivedCrvFromGauge bob', receivedCrvFromGauge)

    assert wgauge.balanceOf(alice, encoded_id) == 0  # remove all

    # check reward same as staking directly
    assert almostEqual(receivedCrv, receivedCrvFromGauge)
Exemple #24
0
def test_single_user_harvest_flow(settConfig):
    badger = badger_single_sett(settConfig)

    sett = badger.getSett(settConfig["id"])
    strategy = badger.getStrategy(settConfig["id"])
    want = badger.getStrategyWant(settConfig["id"])

    settKeeper = accounts.at(sett.keeper(), force=True)
    strategyKeeper = accounts.at(strategy.keeper(), force=True)

    snap = DiggSnapshotManager(badger, settConfig["id"])

    deployer = badger.deployer
    randomUser = accounts[6]

    tendable = strategy.isTendable()

    startingBalance = want.balanceOf(deployer)

    depositAmount = startingBalance // 2
    assert startingBalance >= depositAmount
    assert startingBalance >= 0

    # Deposit
    want.approve(sett, MaxUint256, {"from": deployer})
    snap.settDeposit(depositAmount, {"from": deployer})

    # Push/rebase on an exchange rate of 1.2 (DIGG trading at 1.2x BTC)
    snap.rebase(1.2 * 10**18, {"from": deployer})

    # Earn
    snap.settEarn({"from": settKeeper})

    if tendable:
        with reverts("onlyAuthorizedActors"):
            strategy.tend({"from": randomUser})

        snap.settTend({"from": strategyKeeper})

    chain.sleep(days(0.5))
    chain.mine()

    # Push/rebase on an exchange rate of 0.6 (DIGG trading at 0.8x BTC)
    snap.rebase(0.6 * 10**18, {"from": deployer})

    if tendable:
        snap.settTend({"from": strategyKeeper})

    chain.sleep(days(1))
    chain.mine()

    with reverts("onlyAuthorizedActors"):
        strategy.harvest({"from": randomUser})

    snap.settHarvest({"from": strategyKeeper})

    chain.sleep(days(1))
    chain.mine()

    # Push/rebase on an exchange rate of 1.6 (DIGG trading at 1.6x BTC)
    snap.rebase(1.6 * 10**18, {"from": deployer})

    if tendable:
        snap.settTend({"from": strategyKeeper})

    snap.settWithdraw(depositAmount // 2, {"from": deployer})

    chain.sleep(days(3))
    chain.mine()

    # Push/rebase on an exchange rate of 0.7 (DIGG trading at 0.7x BTC)
    snap.rebase(0.7 * 10**18, {"from": deployer})

    snap.settHarvest({"from": strategyKeeper})
    snap.settWithdraw(depositAmount // 2 - 1, {"from": deployer})

    assert False
Exemple #25
0
def fx_ballot_maker(accounts):
  return accounts.at('0xAD4f7415407B83a081A0Bee22D05A8FDC18B42da', force=True)
from brownie import ProxyAdmin, accounts

DEPLOYER = accounts.at("0x7EeAC6CDdbd1D0B8aF061742D41877D7F707289a", True)
ADMINS = [
    "0x7EeAC6CDdbd1D0B8aF061742D41877D7F707289a",
    "0x7EeAC6CDdbd1D0B8aF061742D41877D7F707289a",
]


def main():
    ProxyAdmin.deploy(ADMINS, {"from": DEPLOYER})
Exemple #27
0
def test_operation(pm, chain):
    dai_liquidity = accounts.at(
        "0xbebc44782c7db0a1a60cb6fe97d0b483032ff1c7", force=True
    )  # using curve pool (lots of dai)

    crv3_liquidity =  accounts.at(
        "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490", force=True
    )  # yearn treasury (lots of crv3)

    rewards = accounts[2]
    gov = accounts[3]
    guardian = accounts[4]
    bob = accounts[5]
    alice = accounts[6]
    strategist = accounts[7]
    tinytim = accounts[8]

    dai = Contract("0x6b175474e89094c44da98b954eedeac495271d0f", owner=gov)  # DAI token

    dai.approve(dai_liquidity, Wei("1000000 ether"), {"from": dai_liquidity})
    dai.transferFrom(dai_liquidity, gov, Wei("10000 ether"), {"from": dai_liquidity})

    crv3 = Contract(
        "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490", owner=gov
    )  # crv3 token address (threePool)

    crv3.approve(crv3_liquidity, Wei("1000000 ether"), {"from": crv3_liquidity})
    crv3.transferFrom(crv3_liquidity, gov, Wei("100 ether"), {"from": crv3_liquidity})

    # config yvDAI vault.
    Vault = pm(config["dependencies"][0]).Vault
    yUSDT3 = gov.deploy(Vault, dai, gov, rewards, "", "")

    threePool = Contract(
        "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7", owner=gov
    )  # crv3 pool address (threePool)
    yCRV3 = Contract(
        "0x9cA85572E6A3EbF24dEDd195623F188735A5179f", owner=gov
    )  # crv3 vault (threePool)
    # uni = Contract.from_explorer(
    #  "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", owner=gov
    # )  # UNI router v2

    strategy = guardian.deploy(StrategyDAI3Pool, yUSDT3, dai, threePool, yCRV3, crv3)
    strategy.setStrategist(strategist)

    yUSDT3.addStrategy(
        strategy, Wei("1000000000 ether"), 2 ** 256 - 1, 50, {"from": gov}
    )

    dai.approve(gov, Wei("1000000 ether"), {"from": gov})
    dai.transferFrom(gov, bob, Wei("1000 ether"), {"from": gov})
    dai.transferFrom(gov, alice, Wei("4000 ether"), {"from": gov})
    dai.transferFrom(gov, tinytim, Wei("10 ether"), {"from":gov})
    dai.approve(yUSDT3, Wei("1000000 ether"), {"from": bob})
    dai.approve(yUSDT3, Wei("1000000 ether"), {"from": alice})
    dai.approve(yUSDT3, Wei("1000000 ether"), {"from": tinytim})
    crv3.approve(gov, Wei("1000000 ether"), {"from": gov})
    yUSDT3.approve(gov, Wei("1000000 ether"), {"from": gov})
    crv3.approve(yCRV3, Wei("1000000 ether"), {"from": gov})
    dai.approve(threePool, Wei("1000000 ether"), {"from": gov})

    # users deposit to vault
    yUSDT3.deposit(Wei("1000 ether"), {"from": bob})
    yUSDT3.deposit(Wei("4000 ether"), {"from": alice})
    yUSDT3.deposit(Wei("10 ether"), {"from": tinytim})

    # a = dai.balanceOf(address(bob))
    # b = dai.balanceOf(address(alice))

    # print(a)
    # print(b)

    chain.mine(1)

    strategy.harvest({"from": gov})
    chain.mine(10)

    crv3.transferFrom(gov, yCRV3, Wei("100 ether"), {"from": gov})
    strategy.harvest({"from": gov})
    chain.mine(10)

    assert 1 == 2

    yUSDT3.withdraw({"from": alice})

    assert dai.balanceOf(alice) > 0
    assert dai.balanceOf(strategy) == 0
    assert dai.balanceOf(bob) == 0

    yUSDT3.withdraw({"from": bob})

    assert dai.balanceOf(bob) > 0
    assert dai.balanceOf(strategy) == 0

    assert 1 == 2
    def __init__(self,
                 badger,
                 multi,
                 key,
                 distributions,
                 start=0,
                 duration=0,
                 end=0):
        # == Distribute to Geyser ==
        geyser = badger.getGeyser(key)
        rewardsEscrow = badger.rewardsEscrow

        self.start = start
        self.duration = duration
        self.end = end

        multi = GnosisSafe(badger.devMultisig)
        for asset, dist in distributions.items():
            token = asset_to_address(asset)
            self.validate_staking_rewards_emission(key, asset)

            stakingRewards = badger.getSettRewards(key)
            console.print(
                "Staking Rewards Distribution for asset {} on {}: {}".format(
                    asset, key, val(dist)),
                style="yellow",
            )

            # Approve if not approved
            if not rewardsEscrow.isApproved(stakingRewards):
                id = multi.addTx(
                    MultisigTxMetadata(
                        description="Approve StakingRewards " + key,
                        operation="transfer",
                    ),
                    {
                        "to":
                        rewardsEscrow.address,
                        "data":
                        rewardsEscrow.approveRecipient.encode_input(
                            stakingRewards),
                    },
                )

                multi.executeTx(id)

                assert rewardsEscrow.isApproved(stakingRewards) == True

            # Add tokens if insufficent tokens
            preBal = badger.token.balanceOf(stakingRewards)
            print("PreBalance", val(preBal))

            if preBal < dist:
                required = dist - preBal
                console.print(
                    "⊁ We need to add {} to the {} Badger supply of {} to reach the goal of {} Badger"
                    .format(
                        val(required),
                        key,
                        val(preBal),
                        val(dist),
                    ),
                    style="blue",
                )

                id = multi.addTx(
                    MultisigTxMetadata(
                        description="Top up tokens for staking rewards " + key,
                        operation="transfer",
                    ),
                    {
                        "to":
                        rewardsEscrow.address,
                        "data":
                        rewardsEscrow.transfer.encode_input(
                            badger.token, stakingRewards, required),
                    },
                )

                multi.executeTx(id)

            assert badger.token.balanceOf(stakingRewards) >= dist

            # Modify the rewards duration, if necessary
            if stakingRewards.rewardsDuration() != self.duration:
                id = multi.addTx(
                    MultisigTxMetadata(
                        description="Modify Staking Rewards duration for " +
                        key,
                        operation="call.notifyRewardAmount",
                    ),
                    {
                        "to":
                        stakingRewards.address,
                        "data":
                        stakingRewards.setRewardsDuration.encode_input(
                            self.duration),
                    },
                )
                tx = multi.executeTx(id)

            # assert stakingRewards.rewardsDuration() == self.duration

            # Notify Rewards Amount
            id = multi.addTx(
                MultisigTxMetadata(
                    description="Distribute Staking Rewards For " + key,
                    operation="call.notifyRewardAmount",
                ),
                {
                    "to":
                    stakingRewards.address,
                    "data":
                    stakingRewards.notifyRewardAmount.encode_input(
                        self.start,
                        dist,
                    ),
                },
            )

            tx = multi.executeTx(id)
            console.print(tx.call_trace())
            console.print("notify rewards events", tx.events)

            # Verify Results

            rewardsDuration = stakingRewards.rewardsDuration()
            rewardRate = stakingRewards.rewardRate()
            periodFinish = stakingRewards.periodFinish()
            lastUpdate = stakingRewards.lastUpdateTime()

            oldRewardsRate = Wei("50000 ether") // rewardsDuration

            console.log({
                "start":
                to_utc_date(self.start),
                "end":
                to_utc_date(self.end),
                "finish":
                to_utc_date(periodFinish),
                "rewardRate":
                rewardRate,
                "expectedRewardRate":
                dist // rewardsDuration,
                "rewardsRateDiff":
                rewardRate - dist // rewardsDuration,
                "oldRewardsRate":
                oldRewardsRate,
                "howTheRateChanged":
                (dist // rewardsDuration) / oldRewardsRate,
                "howWeExpectedItToChange":
                Wei("35000 ether") / Wei("50000 ether"),
                "lastUpdate":
                to_utc_date(lastUpdate),
            })

            assert lastUpdate == self.start
            assert rewardsDuration == self.duration
            assert rewardRate == dist // rewardsDuration
            assert periodFinish == self.start + self.duration

            bal = badger.token.balanceOf(stakingRewards)
            assert bal >= dist

            if bal > dist * 2:
                console.print(
                    "[red] Warning: Staking rewards for {} has excessive coins [/red]"
                    .format(key))

            # Harvest the rewards and ensure the amount updated is appropriate
            strategy = badger.getStrategy(key)
            keeper = accounts.at(strategy.keeper(), force=True)

            before = strategy.balance()
            chain.sleep(self.start - chain.time() + 2)
            strategy.harvest({"from": keeper})
            after = strategy.balance()

            print({"before": before, "after": after})
}


EMERGENCY_DAO = {
    'forwarder': "0xf409Ce40B5bb1e4Ef8e97b1979629859c6d5481f",
    'agent': "0x00669DF67E4827FCc0E48A1838a8d5AB79281909",
    'voting': "0x1115c9b3168563354137cdc60efb66552dd50678",
    'token': "0x4c0947B16FB1f755A2D32EC21A0c4181f711C500",
    'quorum': 51,
}

# the intended target of the vote, should be one of the above constant dicts
TARGET = CURVE_DAO

# address to create the vote from - you will need to modify this prior to mainnet use
SENDER = accounts.at("0x9B44473E223f8a3c047AD86f387B80402536B029", force=True)

# a list of calls to perform in the vote, formatted as a lsit of tuples
# in the format (target, function name, *input args).
#
# for example, to call:
# `GaugeController("0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB").add_gauge("0xFA712EE4788C042e2B7BB55E6cb8ec569C4530c1", 0, 0)
#
# use the following:
# [("0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", "add_gauge", "0xFA712EE4788C042e2B7BB55E6cb8ec569C4530c1", 0, 0),]

ACTIONS = [
    # ("target", "fn_name", *args),
]

# metadata description to include with vote
def governance_timelock():
    return accounts.at("0x21CF9b77F88Adf8F8C98d7E33Fe601DC57bC0893",
                       force=True)