def test_assets_static(ironBankAdapter): cyUsdcToken = Contract(cyUsdcAddress) usdcTokenAddress = ironBankAdapter.assetUnderlyingTokenAddress( cyUsdcAddress) cySushiToken = Contract(cySushiAddress) sushiTokenAddress = ironBankAdapter.assetUnderlyingTokenAddress( cySushiAddress) _, typeId, _ = ironBankAdapter.adapterInfo() usdcTokenName = cyUsdcToken.name() usdcTokenSymbol = cyUsdcToken.symbol() usdcTokenDecimal = cyUsdcToken.decimals() sushiTokenName = cySushiToken.name() sushiTokenSymbol = cySushiToken.symbol() sushiTokenDecimal = cySushiToken.decimals() assets = ironBankAdapter.assetsStatic([cyUsdcToken, cySushiToken]) usdcAsset = assets[0] sushiAsset = assets[1] usdcAssetId = usdcAsset[0] usdcAssetTypeId = usdcAsset[1] usdcAssetTokenId = usdcAsset[2] usdcAssetName = usdcAsset[3] usdcAssetVersion = usdcAsset[4] usdcAssetSymbol = usdcAsset[5] usdcAssetDecimals = usdcAsset[6] assert usdcAssetId == cyUsdcAddress assert usdcAssetTypeId == typeId assert usdcAssetTokenId == usdcTokenAddress assert usdcAssetName == usdcTokenName assert usdcAssetVersion == "2.0.0" assert usdcAssetSymbol == usdcTokenSymbol assert usdcAssetDecimals == usdcTokenDecimal sushiAssetId = sushiAsset[0] sushiAssetTypeId = sushiAsset[1] sushiAssetTokenId = sushiAsset[2] sushiAssetName = sushiAsset[3] sushiAssetVersion = sushiAsset[4] sushiAssetSymbol = sushiAsset[5] sushiAssetDecimals = sushiAsset[6] assert sushiAssetId == cySushiAddress assert sushiAssetTypeId == typeId assert sushiAssetTokenId == sushiTokenAddress assert sushiAssetName == sushiTokenName assert sushiAssetVersion == "2.0.0" assert sushiAssetSymbol == sushiTokenSymbol assert sushiAssetDecimals == sushiTokenDecimal
def main(): multi = MultiRewards.at(MULTIREWARDS_CONTRACT_ADDRESS) reward = Contract(REWARDTOKEN_CONTRACT_ADDRESS) # sanity check on the reward amount if REWARDS_AMOUNT < 10**reward.decimals(): raise ValueError( "Reward amount is less than 1 token - are you sure this is correct?" ) # ensure the reward admin has sufficient balance of the reward token if reward.balanceOf(REWARD_ADMIN) < REWARDS_AMOUNT: raise ValueError( "Rewards admin has insufficient balance to fund the contract") # ensure the reward contract has sufficient allowance to transfer the reward token if reward.allowance(REWARD_ADMIN, multi) < REWARDS_AMOUNT: reward.approve(multi, 2**256 - 1, { "from": REWARD_ADMIN, "gas_price": gas_strategy }) # update the reward amount multi.notifyRewardAmount(reward, REWARDS_AMOUNT, { "from": REWARD_ADMIN, "gas_price": gas_strategy }) print( f"Success! {REWARDS_AMOUNT/10**reward.decimals():.2f} {reward.symbol()} has been added" )
def main(): token = Contract(REWARD_TOKEN_ADDRESS) seed_amount = 10 ** token.decimals() if token.balanceOf(REWARD_ADMIN) < seed_amount: raise ValueError("Reward admin must have at least 1 token to seed the contract for testing") # deploy the rewards contract - initially we set the reward period to one day so we # can seed a small amount for testing rewards = StakingRewards.deploy( OWNER, REWARD_ADMIN, token, STAKING_TOKEN_ADDRESS, 86400, {'from': DEPLOYER, 'gas_price': gas_strategy} ) # give infinite approval to the reward contract from the reward admin token.approve(rewards, 2**256-1, {'from': REWARD_ADMIN, 'gas_price': gas_strategy}) # seed the contract with 1 token for initial testing rewards.notifyRewardAmount(seed_amount, {'from': REWARD_ADMIN, 'gas_price': gas_strategy}) print(f"""Success! StakingRewards deployed to: {rewards} Owner: {OWNER} Reward admin: {REWARD_ADMIN} Please verify the source code on Etherscan here: https://etherscan.io/verifyContract?a={rewards} Compiler version: 0.5.17 Optimization: ON """)
def test_asset_static(ironBankAdapter): cyUsdcToken = Contract(cyUsdcAddress) usdcTokenAddress = ironBankAdapter.assetUnderlyingTokenAddress( cyUsdcAddress) _, typeId, _ = ironBankAdapter.adapterInfo() usdcTokenName = cyUsdcToken.name() usdcTokenSymbol = cyUsdcToken.symbol() usdcTokenDecimal = cyUsdcToken.decimals() assetStatic = ironBankAdapter.assetStatic(cyUsdcAddress) assetId = assetStatic[0] assetTypeId = assetStatic[1] assetTokenId = assetStatic[2] assetName = assetStatic[3] assetVersion = assetStatic[4] assetSymbol = assetStatic[5] assetDecimals = assetStatic[6] assert assetId == cyUsdcAddress assert assetTypeId == typeId assert assetTokenId == usdcTokenAddress assert assetName == usdcTokenName assert assetVersion == "2.0.0" assert assetSymbol == usdcTokenSymbol assert assetDecimals == usdcTokenDecimal
class Earn: def __init__(self, name, vault): self.name = name self.vault = Contract(vault) self.token = self.vault.token() self.scale = 10**self.vault.decimals() def __repr__(self) -> str: return f"Earn({repr(self.name)}, {repr(self.vault.address)})"
def _get_admin_balances(pool): admin_balances = [] for i in range(8): try: coin = Contract(pool.coins(i)) if hasattr(pool, "admin_balances"): balance = pool.admin_balances(i) else: balance = coin.balanceOf(pool) - pool.balances(i) balance = balance / 10**coin.decimals() * _fetch_rate(coin) admin_balances.append(balance) except Exception: return admin_balances
def main(): rewards = StakingRewards.at(REWARDS_CONTRACT_ADDRESS) token = Contract(rewards.rewardsToken()) # sanity check on the reward amount if REWARDS_AMOUNT < 10**token.decimals(): raise ValueError( "Reward amount is less than 1 token - are you sure this is correct?" ) # ensure the reward admin has sufficient balance of the reward token if token.balanceOf(REWARD_ADMIN) < REWARDS_AMOUNT: raise ValueError( "Rewards admin has insufficient balance to fund the contract") # check the reward duration and modify if needed if rewards.rewardsDuration() != REWARDS_DURATION: remaining_time = rewards.periodFinish() - chain[-1].timestamp if remaining_time > 0: raise ValueError( "Current reward period must finish before the period duration can be modified, " f"try again in {datetime.timedelta(seconds=remaining_time)}") rewards.setRewardsDuration(REWARDS_DURATION, {'from': OWNER}) # ensure the reward contract has sufficient allowance to transfer the reward token if token.allowance(REWARD_ADMIN, rewards) < REWARDS_AMOUNT: token.approve(rewards, 2**256 - 1, { 'from': REWARD_ADMIN, 'gas_price': gas_strategy }) # update the reward amount rewards.notifyRewardAmount(REWARDS_AMOUNT, { 'from': REWARD_ADMIN, 'gas_price': gas_strategy }) print( f"Success! {REWARDS_AMOUNT/10**token.decimals():.2f} {token.symbol()} has been added to " f"the rewards contract, to be distributed over {REWARDS_DURATION/86400:.1f} days." )
class VaultV1: vault: InterfaceContainer controller: InterfaceContainer token: interface.ERC20 strategy: str is_wrapped: bool is_delegated: bool # the rest is populated in post init name: Optional[str] = None decimals: Optional[int] = None def __post_init__(self): self.vault = Contract(self.vault) self.controller = Contract(self.controller) self.strategy = Contract(self.strategy) self.token = Contract(self.token) if str(self.vault) not in constants.VAULT_ALIASES: logger.warning( "no vault alias for %s, reading from vault.sybmol()", self.vault) self.name = constants.VAULT_ALIASES.get(str(self.vault), self.vault.symbol()) self.decimals = self.vault.decimals( ) # vaults inherit decimals from token self.scale = 10**self.decimals def get_price(self, block=None): if self.name == "aLINK": return magic.get_price(self.vault.underlying(), block=block) return magic.get_price(self.token, block=block) def get_strategy(self, block=None): if self.name in ["aLINK", "LINK"]: return self.strategy strategy = self.controller.strategies(self.token, block_identifier=block) return Contract(strategy) def describe(self, block=None): info = {} strategy = self.strategy if block is not None: strategy = self.get_strategy(block=block) # attrs are fetches as multicall and populate info attrs = { "vault balance": [self.vault, "balance"], "vault total": [self.vault, "totalSupply"], "strategy balance": [strategy, "balanceOf"], } # some of the oldest vaults don't implement these methods if hasattr(self.vault, "available"): attrs["available"] = [self.vault, "available"] if hasattr(self.vault, "min") and hasattr(self.vault, "max"): attrs["min"] = [self.vault, "min"] attrs["max"] = [self.vault, "max"] # new curve voter proxy vaults if hasattr(strategy, "proxy"): vote_proxy, gauge = fetch_multicall( [strategy, "voter"], # voter is static, can pin [strategy, "gauge"], # gauge is static per strategy, can cache block=block, ) vote_proxy = interface.CurveYCRVVoter(vote_proxy) gauge = Contract(gauge) info.update(curve.calculate_boost(gauge, vote_proxy, block=block)) info.update(curve.calculate_apy(gauge, self.token, block=block)) attrs["earned"] = [gauge, "claimable_tokens", vote_proxy] # / scale if hasattr(strategy, "earned"): attrs["lifetime earned"] = [strategy, "earned"] # /scale if strategy._name == "StrategyYFIGovernance": ygov = interface.YearnGovernance(strategy.gov()) attrs["earned"] = [ygov, "earned", strategy] attrs["reward rate"] = [ygov, "rewardRate"] attrs["ygov balance"] = [ygov, "balanceOf", strategy] attrs["ygov total"] = [ygov, "totalSupply"] # fetch attrs as multicall results = fetch_multicall(*attrs.values(), block=block) for name, attr in zip(attrs, results): if attr is not None: info[name] = attr / self.scale else: logger.warning("attr %s rekt %s", name, attr) # some additional post-processing if "min" in info: info["strategy buffer"] = info.pop("min") / info.pop("max") if "token price" not in info: info["token price"] = self.get_price(block=block) info["tvl"] = info["vault balance"] * info["token price"] return info
def test_clone( vault, strategy, strategist, rewards, keeper, gov, token, token_whale, borrow_token, borrow_whale, yvault, cloner, ): pd_provider = Contract("0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d") a_provider = Contract(pd_provider.ADDRESSES_PROVIDER()) lp = Contract(a_provider.getLendingPool()) vault_snx = Contract("0xF29AE508698bDeF169B89834F76704C3B205aedf") snx = Contract(vault_snx.token()) snx_whale = "0xA1d7b2d891e3A1f9ef4bBC5be20630C2FEB1c470" clone_tx = cloner.cloneAaveLenderBorrower( vault, strategist, rewards, keeper, vault_snx, True, False, "StrategyAaveLender" + token.symbol() + "BorrowerSNX", ) cloned_strategy = Contract.from_abi("Strategy", clone_tx.events["Cloned"]["clone"], strategy.abi) cloned_strategy.setStrategyParams( strategy.targetLTVMultiplier(), strategy.warningLTVMultiplier(), strategy.acceptableCostsRay(), 0, strategy.maxTotalBorrowIT(), strategy.isWantIncentivised(), False, # snx is not incentivised strategy.leaveDebtBehind(), strategy.maxLoss(), strategy.maxGasPriceToTend(), {"from": strategy.strategist()}, ) uniswap = Contract("0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D") cloned_strategy.switchDex(True, {"from": gov}) assert cloned_strategy.router() == uniswap # should fail due to already initialized with reverts(): strategy.initialize(vault, vault_snx, "NameRevert", {"from": gov}) vault.updateStrategyDebtRatio(strategy, 0, {"from": gov}) vault.addStrategy(cloned_strategy, 10_000, 0, 2**256 - 1, 0, {"from": gov}) token.approve(vault, 2**256 - 1, {"from": token_whale}) vault.deposit(10 * (10**token.decimals()), {"from": token_whale}) strategy = cloned_strategy print_debug(vault_snx, strategy, lp) tx = strategy.harvest({"from": gov}) assert vault_snx.balanceOf(strategy) > 0 print_debug(vault_snx, strategy, lp) # Sleep for 2 days chain.sleep(60 * 60 * 24 * 2) chain.mine(1) # Send some profit to yvETH snx.transfer(vault_snx, 1_000 * (10**snx.decimals()), {"from": snx_whale}) # TODO: check profits before and after strategy.harvest({"from": gov}) print_debug(vault_snx, strategy, lp) # We should have profit after getting some profit from yvETH assert vault.strategies(strategy).dict()["totalGain"] > 0 assert vault.strategies(strategy).dict()["totalLoss"] == 0 # Enough sleep for profit to be free chain.sleep(60 * 60 * 10) chain.mine(1) print_debug(vault_snx, strategy, lp) # why do we have losses? because of interests with reverts(): vault.withdraw() # so we send profits snx.transfer(vault_snx, Wei("30_000 ether"), {"from": snx_whale}) vault.withdraw({"from": token_whale})
def test_asset_positions_of(ironBankAdapter, accounts): weth = Contract(wethAddress) cyWeth = Contract(cyWethAddress) user = accounts.at(userAddress, force=True) MAX_UINT256 = 2**256 - 1 weth.approve(cyWeth, MAX_UINT256, {"from": user}) weth_bal = weth.balanceOf(userAddress) cyWeth.mint(weth_bal, {"from": user}) comptroller = Contract(comptrollerAddress) comptroller.enterMarkets([cyWeth], {"from": user}) cyWeth = Contract(cyWethAddress) cyWethTokenAddress = ironBankAdapter.assetUnderlyingTokenAddress( cyWethAddress) cyWethTokenPrice = ironBankAdapter.assetUnderlyingTokenPriceUsdc( cyWethAddress) decimal = cyWeth.decimals() userSupplyBalanceShares = cyWeth.balanceOf(userAddress) userBorrowBalanceShares = cyWeth.borrowBalanceStored(userAddress) assert userSupplyBalanceShares > 0 assert userBorrowBalanceShares > 0 exchangeRate = cyWeth.exchangeRateStored() userSupplyBalanceUnderlying = userSupplyBalanceShares * exchangeRate / 10**18 positions = ironBankAdapter.assetPositionsOf(userAddress, cyWethAddress) assert userSupplyBalanceUnderlying > 0 # print(positions) supplyPosition = positions[0] # basic info test assetId = supplyPosition[0] tokenId = supplyPosition[1] typeId = supplyPosition[2] balance = supplyPosition[3] # print(assetId, tokenId, typeId, balance) assert assetId == cyWethAddress assert tokenId == cyWethTokenAddress assert typeId == "LEND" assert balance == userSupplyBalanceShares # Test token allowances tokenAllowances = supplyPosition[5] owner = tokenAllowances[0][0] spender = tokenAllowances[0][1] allowance = tokenAllowances[0][2] assert owner == userAddress assert spender == cyWethAddress assert allowance > 0 # Test account borrow balance userBorrowedCyTokenBalance = userBorrowBalanceShares * 10**18 / exchangeRate borrowPosition = positions[1] # basic info test assetId = borrowPosition[0] tokenId = borrowPosition[1] typeId = borrowPosition[2] balance = borrowPosition[3] # print(assetId, tokenId, typeId, balance) assert assetId == cyWethAddress assert tokenId == cyWethTokenAddress assert typeId == "BORROW" assert balance == userBorrowedCyTokenBalance # Test token allowances tokenAllowances = borrowPosition[5] owner = tokenAllowances[0][0] spender = tokenAllowances[0][1] allowance = tokenAllowances[0][2] assert owner == userAddress assert spender == cyWethAddress assert allowance > 0