Skip to content

Commit

Permalink
add test for claiming Morpho rewards
Browse files Browse the repository at this point in the history
  • Loading branch information
jankjr committed Jun 28, 2023
1 parent 31b5841 commit 4a97ead
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 10 deletions.
4 changes: 2 additions & 2 deletions common/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
ONDO: '0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3',
sDAI: '0x83f20f44975d03b1b09e64809b757c47f942beea',
cbETH: '0xBe9895146f7AF43049ca1c1AE358B0541Ea49704',
MORPHO: '0x9994E35Db50125E0DF82e4c2dde62496CE330999',
MORPHO: '0x9994e35db50125e0df82e4c2dde62496ce330999',
astETH: "0x1982b2F5814301d4e9a8b0201555376e62F82428"
},
chainlinkFeeds: {
Expand Down Expand Up @@ -252,7 +252,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
sDAI: '0x83f20f44975d03b1b09e64809b757c47f942beea',
cbETH: '0xBe9895146f7AF43049ca1c1AE358B0541Ea49704',
astETH: "0x1982b2F5814301d4e9a8b0201555376e62F82428",
MORPHO: '0x9994E35Db50125E0DF82e4c2dde62496CE330999',
MORPHO: '0x9994e35db50125e0df82e4c2dde62496ce330999',
},
chainlinkFeeds: {
RSR: '0x759bBC1be8F90eE6457C44abc7d443842a976d02',
Expand Down
2 changes: 1 addition & 1 deletion contracts/plugins/assets/erc20/RewardableERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ abstract contract RewardableERC20 is IRewardable, ERC20 {
accumulatedRewards[account] += newRewards;
}

function _claimAndSyncRewards() internal {
function _claimAndSyncRewards() internal virtual {
uint256 delta;
uint256 _totalSupply = totalSupply();
if (_totalSupply > 0) {
Expand Down
18 changes: 17 additions & 1 deletion contracts/plugins/assets/morpho-aave/IMorpho.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import { RewardableERC4626Vault } from "../erc20/RewardableERC4626Vault.sol";
import { IERC20Metadata, RewardableERC4626Vault } from "../erc20/RewardableERC4626Vault.sol";

interface IMorpho {
function supply(address _poolToken, uint256 _amount) external;
Expand Down Expand Up @@ -31,3 +31,19 @@ interface IUsersLens {
abstract contract IMorphoTokenisedDeposit is RewardableERC4626Vault {
function getExchangeRate() external view virtual returns (uint192);
}

interface IMorphoToken is IERC20Metadata {
function setPublicCapability(bytes4 functionSig, bool enabled) external;

function setUserRole(
address user,
uint8 role,
bool enabled
) external;

function setRoleCapability(
uint8 role,
bytes4 functionSig,
bool enabled
) external;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import "hardhat/console.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
Expand Down Expand Up @@ -49,20 +49,28 @@ contract MorphoAaveV2TokenisedDeposit is IMorphoTokenisedDeposit {
uint256 _totalSupply = totalSupply();
if (_totalSupply > 0) {
uint256 initialBal = rewardToken.balanceOf(address(this));

rewardsDistributor.claim(address(this), _claimable, _proof);

uint256 endingBal = rewardToken.balanceOf(address(this));
delta = endingBal - initialBal;

// {qRewards/share} += {qRewards} * {qShare/share} / {qShare}
rewardsPerShare += ((delta) * one) / _totalSupply;
}
_syncAccount(msg.sender);
_claimAccountRewards(msg.sender);
}

function rewardTokenBalance(address account) external returns (uint256 claimableRewards) {
_syncAccount(account);
claimableRewards = accumulatedRewards[account] - claimedRewards[account];
}

// solhint-disable-next-line no-empty-blocks
function _claimAssetRewards() internal virtual override {}

// solhint-disable-next-line no-empty-blocks
function _claimAndSyncRewards() internal virtual override {}

function totalAssets() public view virtual override returns (uint256) {
(, , uint256 currentBalance) = morphoLens.getCurrentSupplyBalanceInOf(
poolToken,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { MockV3Aggregator } from '@typechain/MockV3Aggregator'
import { TestICollateral } from '@typechain/TestICollateral'
import { ERC20Mock, MockV3Aggregator__factory } from '@typechain/index'
import { expect } from 'chai'
import { BigNumber, BigNumberish, ContractFactory } from 'ethers'
import { BigNumber, BigNumberish, ContractFactory, utils } from 'ethers'
import { ethers } from 'hardhat'
import collateralTests from '../collateralTests'
import { getResetFork } from '../helpers'
Expand All @@ -18,7 +18,12 @@ import {
ORACLE_TIMEOUT,
PRICE_TIMEOUT,
} from './constants'
import hre from 'hardhat'
import { MorphoAaveCollateralFixtureContext, mintCollateralTo } from './mintCollateralTo'
import { setCode } from '@nomicfoundation/hardhat-network-helpers'
import { whileImpersonating } from '#/utils/impersonation'
import { whales } from '#/tasks/testing/upgrade-checker-utils/constants'
import { formatEther } from 'ethers/lib/utils'

interface MAFiatCollateralOpts extends CollateralOpts {
underlyingToken?: string
Expand Down Expand Up @@ -173,7 +178,106 @@ const getExpectedPrice = async (ctx: MorphoAaveCollateralFixtureContext): Promis
*/

// eslint-disable-next-line @typescript-eslint/no-empty-function
const collateralSpecificConstructorTests = () => {}
const collateralSpecificConstructorTests = () => {
it('tokenised deposits can correctly claim rewards', async () => {
const morphoTokenOwner = '0xcBa28b38103307Ec8dA98377ffF9816C164f9AFa'
const forkBlock = 17574117
const claimer = '0x05e818959c2Aa4CD05EDAe9A099c38e7Bdc377C6'
const reset = getResetFork(forkBlock)
await reset()
const MorphoTokenisedDepositFactory = await ethers.getContractFactory(
'MorphoAaveV2TokenisedDeposit'
)
const usdtVault = await MorphoTokenisedDepositFactory.deploy({
morphoController: networkConfig[1].MORPHO_AAVE_CONTROLLER!,
morphoLens: networkConfig[1].MORPHO_AAVE_LENS!,
underlyingERC20: networkConfig[1].tokens.USDT!,
poolToken: networkConfig[1].tokens.aUSDT!,
rewardsDistributor: networkConfig[1].MORPHO_REWARDS_DISTRIBUTOR!,
rewardToken: networkConfig[1].tokens.MORPHO!,
})
const vaultCode = await ethers.provider.getCode(usdtVault.address)
await setCode(claimer, vaultCode)

const vaultWithClaimableRewards = usdtVault.attach(claimer)
const erc20Factory = await ethers.getContractFactory('ERC20Mock')
const underlyingERC20 = await erc20Factory.attach(networkConfig[1].tokens.USDT!)
const depositAmount = utils.parseUnits('1000', 6)

const user = hre.ethers.provider.getSigner(0)
const userAddress = await user.getAddress()

expect(
formatEther(await vaultWithClaimableRewards.callStatic.rewardTokenBalance(userAddress))
).to.be.equal('0.0')

await whileImpersonating(
hre,
whales[networkConfig[1].tokens.USDT!.toLowerCase()],
async (whaleSigner) => {
await underlyingERC20.connect(whaleSigner).approve(vaultWithClaimableRewards.address, 0)
await underlyingERC20
.connect(whaleSigner)
.approve(vaultWithClaimableRewards.address, ethers.constants.MaxUint256)
await vaultWithClaimableRewards.connect(whaleSigner).mint(depositAmount, userAddress)
}
)

expect(
formatEther(await vaultWithClaimableRewards.callStatic.rewardTokenBalance(userAddress))
).to.be.equal('0.0')

await vaultWithClaimableRewards.claimOffchainRewards('14162082619942089266', [
'0x49bb35f20573d5b927c5b5c15c904839cacdf83c6119450ccb6c2ed0647aa71b',
'0xfb9f4530177774effb7af9c1723c7087f60cd135a0cb5f409ec7bbc792a79235',
'0x16dcb8d895b9520c20f476bfc23125aa8f47b800a3bea63b63f89abe158a16fe',
'0x70b3bcf266272051262da958e86efb68a3621977aab0fa0205a5e47a83f3b129',
'0xc06f6781c002b96e5860094fec5ac0692e6e39b3aafa0e02a2c9f87a993a55cb',
'0x679aafaa2e4772160288874aa86f2f1baf6ab7409109da7ad96d3b6d5cf2c3ee',
'0x5b9f1e5d9dfbdc65ec0166a6f1e2fe4a31396fa31739cce54962f1ed43638ff1',
'0xb2db22839637b4c40c7ecc800df0ed8a205c9c31d7d49c41c3d105a62d1c5526',
'0xa26071ec1b113e9033dcbccd7680617d3e75fa626b9f1c43dbc778f641f162da',
'0x53eb58db4c07b67b3bce54b530c950a4ef0c229a3ed2506c53d7c4e31ecc6bfc',
'0x14c512bd39f8b1d13d4cfaad2b4473c4022d01577249ecc97fbf0a64244378ee',
'0xea8c2ee8d43e37ceb7b0c04d59106eff88afbe3e911b656dec7caebd415ea696',
])

expect(
formatEther(await vaultWithClaimableRewards.callStatic.rewardTokenBalance(userAddress))
).to.be.equal('5.559127951025958')

// MORPHO is not a transferable token.
// POST Launch we could ask the Morpho team if our TokenVaults could get permission to transfer the MORPHO tokens.
// Otherwise owners of the TokenVault shares need to wait until the protocol enables the transfer function on the MORPHO token.

await whileImpersonating(hre, morphoTokenOwner, async (signer) => {
const morphoTokenInst = await ethers.getContractAt(
'IMorphoToken',
networkConfig[1].tokens.MORPHO!,
signer
)

await morphoTokenInst.connect(signer).setUserRole(vaultWithClaimableRewards.address, 0, true)
})

const morphoTokenInst = await ethers.getContractAt(
'IMorphoToken',
networkConfig[1].tokens.MORPHO!,
user
)
expect(formatEther(await morphoTokenInst.balanceOf(userAddress))).to.be.equal('0.0')

await vaultWithClaimableRewards.claimRewards()

expect(
formatEther(await vaultWithClaimableRewards.callStatic.rewardTokenBalance(userAddress))
).to.be.equal('0.0')

expect(formatEther(await morphoTokenInst.balanceOf(userAddress))).to.be.equal(
'5.559127951025958'
)
})
}

// eslint-disable-next-line @typescript-eslint/no-empty-function
const collateralSpecificStatusTests = () => {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { TestICollateral } from '@typechain/TestICollateral'
import {
ERC20Mock,
MockV3Aggregator__factory,
MorphoNonFiatCollateral,
MorphoNonFiatCollateral__factory,
} from '@typechain/index'
import { expect } from 'chai'
Expand Down

0 comments on commit 4a97ead

Please sign in to comment.