def __repr__(self): return "Schedule(sett={},token={},initalTokensLocked={},startTime={},duration={} days,endTime={}".format( self.sett, self.token, self.initialTokensLocked, to_utc_date(self.startTime), to_days(self.duration), to_utc_date(self.endTime), )
def printState(self, title): console.print( "\n[yellow]=== 🦡 Rewards Schedule: {} 🦡 ===[/yellow]".format( title)) table = [] rewardsEscrow = self.badger.rewardsEscrow for key, amount in self.amounts.items(): geyser = self.badger.getGeyser(key) assert rewardsEscrow.isApproved(geyser) """ function signalTokenLock( address geyser, address token, uint256 amount, uint256 durationSec, uint256 startTime ) """ encoded = rewardsEscrow.signalTokenLock.encode_input( geyser, self.badger.token, amount, self.duration, self.start) table.append([ key, geyser, self.badger.token, val(amount), to_utc_date(self.start), to_utc_date(self.end), to_days(self.duration), val(self.tokensPerDay(amount)), self.badger.rewardsEscrow, encoded, ]) print( tabulate( table, headers=[ "key", "geyser", "token", "total amount", "start time", "end time", "duration", "rate per day", "destination", "encoded call", ], tablefmt="rst", )) print("total distributed: ", val(self.total))
def print_logger_unlock_schedules(self, beneficiary, name=None): logger = self.rewardsLogger schedules = logger.getAllUnlockSchedulesFor(beneficiary) if not name: name = "" console.print(f"[cyan]=== Latest Unlock Schedules {name}===[/cyan]") table = [] if len(schedules) == 0: return for schedule in schedules: print(schedule) s = LoggerUnlockSchedule(schedule) digg_shares = s.token == self.digg.token if digg_shares: scaled = shares_to_fragments(s.amount) else: scaled = val(amount=s.amount, token=s.token) table.append([ name, s.beneficiary, s.token, scaled, to_days(s.duration), to_utc_date(s.start), to_utc_date(s.end), "{:.0f}".format(s.start), "{:.0f}".format(s.end), ]) print( tabulate( table, headers=[ "name", "beneficiary", "token", "amount", "duration", "start", "end", "start", "end", ], )) print("\n")
def print_latest_unlock_schedules(self, geyser, name=None): if not name: name = "" console.print(f"[cyan]=== Latest Unlock Schedules {name}===[/cyan]") table = [] tokens = geyser.getDistributionTokens() for token in tokens: schedules = geyser.getUnlockSchedulesFor(token) num_schedules = geyser.unlockScheduleCount(token) if num_schedules == 0: continue last_schedule = num_schedules - 1 s = UnlockSchedule(token, schedules[last_schedule]) digg_shares = token == self.digg.token if digg_shares: scaled = shares_to_fragments(s.amount) else: scaled = val(amount=s.amount, token=token) table.append([ name, token, scaled, to_days(s.duration), to_utc_date(s.start), to_utc_date(s.end), "{:.0f}".format(s.start), "{:.0f}".format(s.end), ]) print( tabulate( table, headers=[ "geyser", "token", "amount", "duration", "start", "end", "start", "end", ], )) print("\n")
def get_distributed_for_token_at(token, endTime, schedules, name): totalToDistribute = 0 for index, schedule in enumerate(schedules): if endTime < schedule.startTime: toDistribute = 0 console.log("\nSchedule {} for {} completed\n".format(index, name)) else: rangeDuration = endTime - schedule.startTime toDistribute = min( schedule.initialTokensLocked, int(schedule.initialTokensLocked * rangeDuration // schedule.duration), ) if schedule.startTime <= endTime and schedule.endTime >= endTime: console.log( "Tokens distributed by schedule {} at {} are {}% of total\n".format( index, to_utc_date(schedule.startTime), (int(toDistribute) / int(schedule.initialTokensLocked) * 100), ) ) console.log( "Total duration of schedule elapsed is {} hours out of {} hours, or {}% of total duration.\n".format( to_hours(rangeDuration), to_hours(schedule.duration), rangeDuration / schedule.duration * 100, ) ) totalToDistribute += toDistribute return totalToDistribute
def main(): badger = connect_badger(badger_config.prod_json) table = [] tree = badger.badgerTree lastPublish = tree.lastPublishTimestamp() timeSinceLastPublish = chain.time() - lastPublish table.append(["now", to_utc_date(chain.time())]) table.append(["---------------", "--------------------"]) table.append(["lastPublishTimestamp", lastPublish]) table.append(["lastPublishDate", to_utc_date(lastPublish)]) table.append(["---------------", "--------------------"]) table.append(["secondsSinceLastPublish", timeSinceLastPublish]) table.append(["hoursSinceLastPublish", timeSinceLastPublish / 3600]) print(tabulate(table, headers=["metric", "value"]))
def get_distributed_for_token_at(self, token, endTime, read=False): """ Get total distribution for token within range, across unlock schedules """ totalToDistribute = 0 unlockSchedules = self.unlockSchedules[token] index = 0 for schedule in unlockSchedules: if endTime < schedule.startTime: toDistribute = 0 rangeDuration = endTime - schedule.startTime if read: console.print( "\n[cyan]Schedule {} for {}: Complete[/cyan]".format( index, self.key)) else: rangeDuration = endTime - schedule.startTime toDistribute = min( schedule.initialTokensLocked, int(schedule.initialTokensLocked * rangeDuration // schedule.duration), ) # Output if in rewards range, or read flag is on if read and (schedule.startTime <= endTime and schedule.endTime >= endTime): console.print( "\n[blue] == Schedule {} for {} == [/blue]".format( index, self.key)) console.log( "Total tokens distributed by schedule starting at {} by the end of rewards cycle are {} out of {} total." .format( to_utc_date(schedule.startTime), val(toDistribute), val(schedule.initialTokensLocked), )) console.log( "Total duration of schedule elapsed is {} hours out of {} hours, or {}% of total duration." .format(to_hours(rangeDuration), to_hours(schedule.duration), rangeDuration / schedule.duration * 100)) console.log("\n") totalToDistribute += toDistribute index += 1 return totalToDistribute
def printUniTrade(method, params): path = params[2] input_token = path[0] output_token = path[-1] table = [] table.append(["input token", input_token]) table.append(["output token", output_token]) table.append(["expected output", params[0]]) table.append(["max input", params[1]]) table.append(["path", params[2]]) table.append(["recipient", params[3]]) table.append(["expiration time", to_utc_date(params[4])]) table.append(["time until expiration", days(params[4] - chain.time())]) console.print(tabulate(table, headers=["metric", "value"]))
def main(): badger = connect_badger("deploy-final.json") bBadger = badger.getSett("native.badger") escrows = [ "0x1fc3C85456322C8514c0ff7694Ea4Ef5bC7F9f37", "0xaeDb773C226e6d74f2cd3542372076779Ff6fA6E" ] timelocks = [ "0x2Bc1A5E26ad0316375E68942fe0B387adE6b9254", "0x7C651D13DfB87748b0F05914dFb40E5B15a78D35", "0xB6c9e9Ba41291044Cf5dadFB22D72d3fe9312880", "0xdbd185c59f64d2d39c6ababf5d701669417a002d" # "0x1fc3C85456322C8514c0ff7694Ea4Ef5bC7F9f37", # "0xaeDb773C226e6d74f2cd3542372076779Ff6fA6E" ] for address in timelocks: vesting = interface.ITokenTimelock(address) console.print( { "token": vesting.token(), "beneficiary": vesting.beneficiary(), "releaseTime": vesting.releaseTime(), "releaseDate": to_utc_date(vesting.releaseTime()), } ) chain.sleep(days(182)) chain.mine() for address in timelocks: vesting = interface.ITokenTimelock(address) beneficiary = accounts.at(vesting.beneficiary(), force=True) pre = get_token_balances([bBadger], [vesting, beneficiary]) vesting.release({"from": badger.deployer}) post = get_token_balances([bBadger], [vesting, beneficiary]) diff_token_balances(pre, post)
def main(): badger = connect_badger("deploy-final.json") test_user = accounts.at(decouple.config("TEST_ACCOUNT"), force=True) distribute_test_ether(test_user, Wei("20 ether")) distribute_from_whales(test_user, assets=["bBadger", "badger", "usdc"]) rest = get_active_rewards_schedule(badger) usdc = interface.IERC20(registry.tokens.usdc) usdc_per_badger = 40.37 * 0.75 usdc_total = 13386240 multi = GnosisSafe(badger.devMultisig) badger_total_scaled = usdc_total / usdc_per_badger badger_total = Wei(str(badger_total_scaled) + " ether") bBadger = badger.getSett("native.badger") ppfs = bBadger.getPricePerFullShare() bBadger_total = int(badger_total / ppfs * 10**18) badger_total = Wei(str(badger_total_scaled) + " ether") console.print({ "TRADE": "BASED", "usdc_per_badger": usdc_per_badger, "usdc_total": usdc_total, "badger_total_scaled": badger_total_scaled, "badger_total": badger_total, "ppfs": ppfs, "bBadger_total": str(bBadger_total), }) params = { "beneficiary": "0x3159b46a7829a0dbfa856888af768fe7146e7418", "duration": days(182), "usdcAmount": usdc_total * 10**6, "bBadgerAmount": bBadger_total, # "usdcAmount": 0, # "bBadgerAmount": 0, } console.print(params) # # Oxb1 Test beneficiary = accounts.at(params["beneficiary"], force=True) escrow = OtcEscrow.at("0x7163fB2fA38Ea3BBc1F8525F3d8D0417C0c9d903") # bBadger.transfer(badger.devMultisig, Wei("100000 ether"), {"from": test_user}) pre = get_token_balances( [usdc, bBadger], [test_user, escrow, badger.devMultisig, beneficiary]) pre.print() # assert usdc.balanceOf(params["beneficiary"]) >= params["usdcAmount"] # multi.execute(MultisigTxMetadata(description="Transfer to 0xb1"), { # "to": bBadger.address, # "data": bBadger.transfer.encode_input(escrow, bBadger_total + Wei("1000 ether")) # }) # assert usdc.allowance(beneficiary, escrow) >= params["usdcAmount"] # usdc.approve(escrow, params["usdcAmount"], {"from": beneficiary}) # tx = escrow.swap({"from": beneficiary}) tx = multi.execute(MultisigTxMetadata(description="Swap"), { "to": escrow.address, "data": escrow.swap.encode_input() }, print_output=False) chain.mine() print(tx.call_trace()) vesting = interface.ITokenTimelock( tx.events["VestingDeployed"][0]["vesting"]) console.print({ "token": vesting.token(), "beneficiary": vesting.beneficiary(), "releaseTime": to_utc_date(vesting.releaseTime()), }) post = get_token_balances( [usdc, bBadger], [test_user, escrow, badger.devMultisig, beneficiary]) diff_token_balances(pre, post) try: vesting.release({"from": test_user}) except: print("early vest failed!") chain.sleep(days(182)) chain.mine() # End vesting.release({"from": test_user}) post = get_token_balances( [usdc, bBadger], [test_user, escrow, badger.devMultisig, beneficiary]) diff_token_balances(pre, post) return escrow = OtcEscrow.deploy( params["beneficiary"], params["duration"], params["usdcAmount"], params["bBadgerAmount"], {"from": badger.deployer}, ) beneficiary = accounts.at(params["beneficiary"], force=True) usdc.transfer(beneficiary, params["usdcAmount"], {"from": test_user}) usdc.transfer(beneficiary, 1500000000000, {"from": test_user}) badger.token.transfer(badger.devMultisig, badger_total, {"from": test_user}) multi.execute( MultisigTxMetadata(description="Whitelist Multi"), { "to": bBadger.address, "data": bBadger.approveContractAccess.encode_input( badger.devMultisig), }, ) assert badger.token.balanceOf(badger.devMultisig) > Wei("100 ether") multi.execute( MultisigTxMetadata(description="Approve bBadger Contract"), { "to": badger.token.address, "data": badger.token.approve.encode_input(bBadger, badger_total), }, ) multi.execute( MultisigTxMetadata(description="Deposit"), { "to": bBadger.address, "data": bBadger.deposit.encode_input(badger_total) }, ) console.print( "bBadger.balanceOf(badger.devMultisig)", bBadger.balanceOf(badger.devMultisig), params["bBadgerAmount"], params["bBadgerAmount"] - bBadger.balanceOf(badger.devMultisig)) assert bBadger.balanceOf(badger.devMultisig) >= params["bBadgerAmount"] chain.mine() chain.sleep(14) chain.mine() multi.execute( MultisigTxMetadata(description="Transfer"), { "to": bBadger.address, "data": bBadger.transfer.encode_input(escrow, params["bBadgerAmount"]), }, ) assert bBadger.balanceOf(escrow) == params["bBadgerAmount"] multi.execute( MultisigTxMetadata(description="Revoke"), { "to": escrow.address, "data": escrow.revoke.encode_input() }, ) assert bBadger.balanceOf(escrow) == 0 assert bBadger.balanceOf(badger.devMultisig) >= params["bBadgerAmount"] print(bBadger.balanceOf(badger.devMultisig)) bBadger.transfer(escrow, params["bBadgerAmount"], {"from": test_user}) pre = get_token_balances( [usdc, bBadger], [test_user, escrow, badger.devMultisig, beneficiary]) console.print(pre) assert usdc.balanceOf(beneficiary) >= params["usdcAmount"] assert bBadger.balanceOf(escrow) == params["bBadgerAmount"] usdc.approve(escrow, params["usdcAmount"], {"from": beneficiary}) tx = escrow.swap({"from": beneficiary}) post = get_token_balances( [usdc, bBadger], [test_user, escrow, badger.devMultisig, beneficiary]) console.print(tx.events) post.print() diff_token_balances(pre, post) vesting = interface.ITokenTimelock( tx.events["VestingDeployed"][0]["vesting"]) console.print({ "token": vesting.token(), "beneficiary": vesting.beneficiary(), "releaseTime": to_utc_date(vesting.releaseTime()), }) chain.sleep(days(365)) chain.mine() vesting.release({"from": test_user})
def printState(self, title): console.print( "\n[yellow]=== 🦡 Rewards Schedule: {} 🦡 ===[/yellow]".format( title)) table = [] rewardsEscrow = self.badger.rewardsEscrow for key, dist in self.distributions.items(): if key == "native.digg": continue print(key, dist) geyser = self.badger.getGeyser(key) print(geyser) assert rewardsEscrow.isApproved(geyser) for asset, value in dist.toGeyser.items(): """ function signalTokenLock( address geyser, address token, uint256 amount, uint256 durationSec, uint256 startTime ) """ encoded = rewardsEscrow.signalTokenLock.encode_input( geyser, asset_to_address(asset), value, self.duration, self.start) asset_contract = interface.IERC20(asset_to_address(asset)) scaled = val(value, decimals=18) if asset == "digg": scaled = val(shares_to_fragments(value), decimals=9) table.append([ key, # geyser, asset, value, scaled, to_utc_date(self.start), to_utc_date(self.end), to_days(self.duration), # geyser.address, # encoded, ]) print( tabulate( table, headers=[ "key", # "geyser", "token", "total amount", "scaled amount", "start time", "end time", "duration", # "rate per day", # "destination", # "encoded call", ], tablefmt="rst", )) print("total distributed for {}: ".format(asset), val(self.totals[asset]))
def testTransactions(self): rewardsEscrow = self.badger.rewardsEscrow multi = GnosisSafe(self.badger.devMultisig) # 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("200000 ether") # 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", operation="Top Up Badger Tree", ), { "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 for key, distribution in self.distributions.items(): console.print("===== Distributions for {} =====".format(key), style="bold yellow") # == Distribute to Geyser == geyser = self.badger.getGeyser(key) # Approve Geyser as recipient if required if not rewardsEscrow.isApproved(geyser): id = multi.addTx( MultisigTxMetadata( description="Approve StakingRewards " + key, operation="transfer", ), { "to": rewardsEscrow.address, "data": rewardsEscrow.approveRecipient.encode_input(geyser), }, ) multi.executeTx(id) numSchedules = geyser.unlockScheduleCount(self.badger.token) console.print( "Geyser Distribution for {}: {}".format( key, val(distribution.toGeyser)), style="yellow", ) id = multi.addTx( MultisigTxMetadata( description="Signal unlock schedule for " + key, operation="signalTokenLock", ), { "to": rewardsEscrow.address, "data": rewardsEscrow.signalTokenLock.encode_input( geyser, self.badger.token, distribution.toGeyser, self.duration, self.start, ), }, ) multi.executeTx(id) # Verify Results numSchedulesAfter = geyser.unlockScheduleCount(self.badger.token) console.print( "Schedule Addition", { "numSchedules": numSchedules, "numSchedulesAfter": numSchedulesAfter }, ) assert numSchedulesAfter == numSchedules + 1 unlockSchedules = geyser.getUnlockSchedulesFor(self.badger.token) schedule = unlockSchedules[-1] print(schedule) assert schedule[0] == distribution.toGeyser assert schedule[1] == self.end assert schedule[2] == self.duration assert schedule[3] == self.start # == Distribute to StakingRewards, if relevant == if distribution.toStakingRewards > 0: stakingRewards = self.badger.getSettRewards(key) console.print( "Staking Rewards Distribution for {}: {}".format( key, val(distribution.toStakingRewards)), 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 = self.badger.token.balanceOf(stakingRewards) if preBal < distribution.toStakingRewards: required = distribution.toStakingRewards - 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(distribution.toStakingRewards), ), style="blue", ) id = multi.addTx( MultisigTxMetadata( description="Top up tokens for staking rewards " + key, operation="transfer", ), { "to": rewardsEscrow.address, "data": rewardsEscrow.transfer.encode_input( self.badger.token, stakingRewards, required), }, ) multi.executeTx(id) assert (self.badger.token.balanceOf(stakingRewards) >= distribution.toStakingRewards) # 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, distribution.toStakingRewards, ), }, ) 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": distribution.toStakingRewards // rewardsDuration, "rewardsRateDiff": rewardRate - distribution.toStakingRewards // rewardsDuration, "oldRewardsRate": oldRewardsRate, "howTheRateChanged": (distribution.toStakingRewards // rewardsDuration) / oldRewardsRate, "howWeExpectedItToChange": Wei("35000 ether") / Wei("50000 ether"), "lastUpdate": to_utc_date(lastUpdate), }) assert lastUpdate == self.start assert rewardsDuration == self.duration assert rewardRate == distribution.toStakingRewards // rewardsDuration assert periodFinish == self.start + self.duration bal = self.badger.token.balanceOf(stakingRewards) assert bal >= distribution.toStakingRewards if bal > distribution.toStakingRewards * 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 = self.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})
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})