mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Removed interest redirection, fixed tests
This commit is contained in:
parent
a3934152fe
commit
a67c56c09f
|
@ -28,13 +28,8 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
|||
|
||||
address public immutable UNDERLYING_ASSET_ADDRESS;
|
||||
|
||||
mapping(address => address) private _interestRedirectionAddresses;
|
||||
mapping(address => uint256) private _interestRedirectionIndexes;
|
||||
|
||||
mapping(address => uint256) private _scaledRedirectedBalances;
|
||||
mapping(address => uint256) private _redirectedBalanceIndexes;
|
||||
|
||||
mapping(address => address) private _interestRedirectionAllowances;
|
||||
|
||||
LendingPool private immutable _pool;
|
||||
|
||||
|
@ -69,44 +64,6 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
|||
_setDecimals(underlyingAssetDecimals);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev redirects the interest generated to a target address.
|
||||
* when the interest is redirected, the user balance is added to
|
||||
* the recepient redirected balance.
|
||||
* @param to the address to which the interest will be redirected
|
||||
**/
|
||||
function redirectInterestStream(address to) external override {
|
||||
_redirectInterestStream(msg.sender, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev redirects the interest generated by from to a target address.
|
||||
* when the interest is redirected, the user balance is added to
|
||||
* the recepient redirected balance. The caller needs to have allowance on
|
||||
* the interest redirection to be able to execute the function.
|
||||
* @param from the address of the user whom interest is being redirected
|
||||
* @param to the address to which the interest will be redirected
|
||||
**/
|
||||
function redirectInterestStreamOf(address from, address to) external override {
|
||||
require(
|
||||
msg.sender == _interestRedirectionAllowances[from],
|
||||
Errors.INTEREST_REDIRECTION_NOT_ALLOWED
|
||||
);
|
||||
_redirectInterestStream(from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev gives allowance to an address to execute the interest redirection
|
||||
* on behalf of the caller.
|
||||
* @param to the address to which the interest will be redirected. Pass address(0) to reset
|
||||
* the allowance.
|
||||
**/
|
||||
function allowInterestRedirectionTo(address to) external override {
|
||||
require(to != msg.sender, Errors.CANNOT_GIVE_ALLOWANCE_TO_HIMSELF);
|
||||
_interestRedirectionAllowances[msg.sender] = to;
|
||||
emit InterestRedirectionAllowanceChanged(msg.sender, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev burns the aTokens and sends the equivalent amount of underlying to the target.
|
||||
* only lending pools can call this function
|
||||
|
@ -128,16 +85,6 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
|||
|
||||
_burn(user, scaledAmount);
|
||||
|
||||
|
||||
if(amount == currentBalance){
|
||||
_resetDataOnZeroBalance(user);
|
||||
}
|
||||
|
||||
//if the user is redirecting his interest towards someone else,
|
||||
//we update the redirected balance of the redirection address by adding the accrued interest,
|
||||
//and removing the amount to redeem
|
||||
_updateRedirectedBalanceOfRedirectionAddress(user, user, scaledAmount, 0, index);
|
||||
|
||||
//transfers the underlying to the target
|
||||
ERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(receiverOfUnderlying, amount);
|
||||
|
||||
|
@ -160,11 +107,6 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
|||
//mint an equivalent amount of tokens to cover the new deposit
|
||||
_mint(user,scaledAmount);
|
||||
|
||||
//if the user is redirecting his interest towards someone else,
|
||||
//we update the redirected balance of the redirection address by adding the accrued interest
|
||||
//and the amount deposited
|
||||
_updateRedirectedBalanceOfRedirectionAddress(user, user, scaledAmount, 0, index);
|
||||
|
||||
emit Mint(user, amount, index);
|
||||
}
|
||||
|
||||
|
@ -187,39 +129,17 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
|||
|
||||
/**
|
||||
* @dev calculates the balance of the user, which is the
|
||||
* principal balance + interest generated by the principal balance + interest generated by the redirected balance
|
||||
* principal balance + interest generated by the principal balance
|
||||
* @param user the user for which the balance is being calculated
|
||||
* @return the total balance of the user
|
||||
**/
|
||||
function balanceOf(address user) public override(ERC20, IERC20) view returns (uint256) {
|
||||
|
||||
//current scaled balance of the user
|
||||
uint256 currentScaledBalance = super.balanceOf(user);
|
||||
|
||||
//balance redirected by other users to user for interest rate accrual
|
||||
uint256 scaledRedirectedBalance = _scaledRedirectedBalances[user];
|
||||
|
||||
if (currentScaledBalance == 0 && scaledRedirectedBalance == 0) {
|
||||
return 0;
|
||||
}
|
||||
uint256 actualBalanceRedirectedToUser = scaledRedirectedBalance > 0 ? scaledRedirectedBalance.rayMul(_redirectedBalanceIndexes[user]) : 0;
|
||||
|
||||
uint256 index = _pool.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS);
|
||||
|
||||
if(_interestRedirectionAddresses[user] == address(0)){
|
||||
//if the user is not redirecting the interest, his balance is the result of
|
||||
//the interest accrued by his current scaled balance and the interest accrued by his
|
||||
//scaled redirected balance
|
||||
return currentScaledBalance.add(scaledRedirectedBalance).rayMul(index).sub(actualBalanceRedirectedToUser);
|
||||
}
|
||||
return super.balanceOf(user).rayMul(index);
|
||||
|
||||
//if the user is redirecting, his balance only increases by the balance he is being redirected to
|
||||
uint256 lastBalanceRedirectedByUser = currentScaledBalance.rayMul(_interestRedirectionIndexes[user]);
|
||||
|
||||
return
|
||||
lastBalanceRedirectedByUser.add(
|
||||
scaledRedirectedBalance.rayMul(index)
|
||||
).sub(actualBalanceRedirectedToUser);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -232,18 +152,6 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
|||
return super.balanceOf(user);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dev returns the scaled balance of the user. The scaled balance is the sum of all the
|
||||
* updated stored balance divided the reserve index at the moment of the update
|
||||
* @param user the address of the user
|
||||
* @return the scaled balance of the user
|
||||
**/
|
||||
function getUserInterestRedirectionIndex(address user) external override view returns (uint256) {
|
||||
return _interestRedirectionIndexes[user];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dev calculates the total supply of the specific aToken
|
||||
* since the balance of every single user increases over time, the total supply
|
||||
|
@ -274,153 +182,6 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
|||
return _pool.balanceDecreaseAllowed(UNDERLYING_ASSET_ADDRESS, user, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the address to which the interest is redirected
|
||||
* @param user address of the user
|
||||
* @return 0 if there is no redirection, an address otherwise
|
||||
**/
|
||||
function getInterestRedirectionAddress(address user) external override view returns (address) {
|
||||
return _interestRedirectionAddresses[user];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the redirected balance of the user. The redirected balance is the balance
|
||||
* redirected by other accounts to the user, that is accrueing interest for him.
|
||||
* @param user address of the user
|
||||
* @return the total redirected balance
|
||||
**/
|
||||
function getScaledRedirectedBalance(address user) external override view returns (uint256) {
|
||||
return _scaledRedirectedBalances[user];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the index stored during the last action that affected the redirected balance.
|
||||
* scaledRedirectedBalance * redirectedBalanceIndex allows to calculate the actual redirected balance
|
||||
* which is needed to calculate the interest accrued
|
||||
* @param user address of the user
|
||||
* @return the total redirected balance
|
||||
**/
|
||||
function getRedirectedBalanceIndex(address user) external override view returns (uint256) {
|
||||
return _redirectedBalanceIndexes[user];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @dev updates the redirected balance of the user. If the user is not redirecting his
|
||||
* interest, nothing is executed.
|
||||
* @param user the address of the user for which the interest is being accumulated
|
||||
* @param scaledBalanceToAdd the amount to add to the redirected balance
|
||||
* @param scaledBalanceToRemove the amount to remove from the redirected balance
|
||||
**/
|
||||
function _updateRedirectedBalanceOfRedirectionAddress(
|
||||
address origin,
|
||||
address user,
|
||||
uint256 scaledBalanceToAdd,
|
||||
uint256 scaledBalanceToRemove,
|
||||
uint256 index
|
||||
) internal {
|
||||
address redirectionAddress = _interestRedirectionAddresses[user];
|
||||
//if there isn't any redirection, nothing to be done
|
||||
if (redirectionAddress == address(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//updating the redirected balance
|
||||
_scaledRedirectedBalances[redirectionAddress] = _scaledRedirectedBalances[redirectionAddress]
|
||||
.add(scaledBalanceToAdd)
|
||||
.sub(scaledBalanceToRemove);
|
||||
|
||||
//updating the redirected balance index of the redirection target
|
||||
_redirectedBalanceIndexes[redirectionAddress] = index;
|
||||
|
||||
|
||||
//if the interest of redirectionAddress is also being redirected, we need to update
|
||||
//the redirected balance of the redirection target by adding the balance increase
|
||||
address targetOfRedirectionAddress = _interestRedirectionAddresses[redirectionAddress];
|
||||
|
||||
|
||||
// if the redirection address is also redirecting the interest, we update his index to
|
||||
// accumulate the interest until now
|
||||
// note: if the next address of redirection is the same as the one who originated the update,
|
||||
// it means a loop of redirection has been formed: in this case, we break the recursion as no
|
||||
// further updates are needed
|
||||
if (targetOfRedirectionAddress != address(0) && targetOfRedirectionAddress != origin) {
|
||||
_updateRedirectedBalanceOfRedirectionAddress(origin, redirectionAddress, 0, 0, index);
|
||||
}
|
||||
|
||||
console.log("Interest redirection completed");
|
||||
|
||||
emit RedirectedBalanceUpdated(
|
||||
redirectionAddress,
|
||||
scaledBalanceToAdd,
|
||||
scaledBalanceToRemove,
|
||||
index
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev executes the redirection of the interest from one address to another.
|
||||
* immediately after redirection, the destination address will start to accrue interest.
|
||||
* @param from the address from which transfer the aTokens
|
||||
* @param to the destination address
|
||||
**/
|
||||
function _redirectInterestStream(address from, address to) internal {
|
||||
|
||||
address currentRedirectionAddress = _interestRedirectionAddresses[from];
|
||||
|
||||
require(to != currentRedirectionAddress, Errors.INTEREST_ALREADY_REDIRECTED);
|
||||
|
||||
uint256 scaledFromBalance = super.balanceOf(from);
|
||||
|
||||
uint256 fromBalance = balanceOf(from);
|
||||
|
||||
require(fromBalance > 0, Errors.NO_VALID_BALANCE_FOR_REDIRECTION);
|
||||
|
||||
uint256 index = _pool.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS);
|
||||
|
||||
uint256 scaledBalance = fromBalance.rayDiv(index);
|
||||
|
||||
//if the user is already redirecting the interest to someone, before changing
|
||||
//the redirection address we substract the redirected balance of the previous
|
||||
//recipient
|
||||
if (currentRedirectionAddress != address(0)) {
|
||||
_updateRedirectedBalanceOfRedirectionAddress(from, from, 0, scaledFromBalance, index);
|
||||
}
|
||||
|
||||
//if the user is redirecting the interest back to himself,
|
||||
//we simply set to 0 the interest redirection address
|
||||
if (to == from) {
|
||||
_interestRedirectionAddresses[from] = address(0);
|
||||
emit InterestStreamRedirected(from, address(0), scaledBalance, index);
|
||||
return;
|
||||
}
|
||||
|
||||
//first set the redirection address to the new recipient
|
||||
_interestRedirectionAddresses[from] = to;
|
||||
_interestRedirectionIndexes[from] = index;
|
||||
|
||||
//adds the user balance to the redirected balance of the destination
|
||||
_updateRedirectedBalanceOfRedirectionAddress(from, from, scaledBalance, 0, index);
|
||||
|
||||
emit InterestStreamRedirected(from, to, fromBalance, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev function to reset the interest stream redirection and the user index, if the
|
||||
* user has no balance left.
|
||||
* @param user the address of the user
|
||||
* @return true if the user index has also been reset, false otherwise. useful to emit the proper user index value
|
||||
**/
|
||||
function _resetDataOnZeroBalance(address user) internal returns (bool) {
|
||||
//if the user has 0 principal balance, the interest stream redirection gets reset
|
||||
_interestRedirectionAddresses[user] = address(0);
|
||||
_interestRedirectionIndexes[user] = 0;
|
||||
|
||||
//emits a InterestStreamRedirected event to notify that the redirection has been reset
|
||||
emit InterestStreamRedirected(user, address(0), 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev transfers the underlying asset to the target. Used by the lendingpool to transfer
|
||||
* assets in borrow(), redeem() and flashLoan()
|
||||
|
@ -458,20 +219,6 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
|||
|
||||
super._transfer(from, to, scaledAmount);
|
||||
|
||||
//if the sender is redirecting his interest towards someone else,
|
||||
//adds to the redirected balance the accrued interest and removes the amount
|
||||
//being transferred
|
||||
_updateRedirectedBalanceOfRedirectionAddress(from, from, 0, scaledAmount, index);
|
||||
|
||||
//if the receiver is redirecting his interest towards someone else,
|
||||
//adds to the redirected balance the accrued interest and the amount
|
||||
//being transferred
|
||||
_updateRedirectedBalanceOfRedirectionAddress(to, to, scaledAmount, 0, index);
|
||||
|
||||
if(super.balanceOf(from) == 0){
|
||||
_resetDataOnZeroBalance(from);
|
||||
}
|
||||
|
||||
emit BalanceTransfer(from, to, amount, index);
|
||||
|
||||
}
|
||||
|
|
|
@ -39,63 +39,6 @@ interface IAToken is IERC20 {
|
|||
uint256 index
|
||||
);
|
||||
|
||||
/**
|
||||
* @dev emitted when the accumulation of the interest
|
||||
* by an user is redirected to another user
|
||||
* @param from the address from which the interest is being redirected
|
||||
* @param to the adress of the destination
|
||||
* @param redirectedBalance the scaled balance being redirected
|
||||
* @param index the last index of the reserve
|
||||
**/
|
||||
event InterestStreamRedirected(
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256 redirectedBalance,
|
||||
uint256 index
|
||||
);
|
||||
|
||||
/**
|
||||
* @dev emitted when the redirected balance of an user is being updated
|
||||
* @param targetAddress the address of which the balance is being updated
|
||||
* @param redirectedBalanceAdded the redirected balance being added
|
||||
* @param redirectedBalanceRemoved the redirected balance being removed
|
||||
* @param index the last index of the reserve
|
||||
**/
|
||||
event RedirectedBalanceUpdated(
|
||||
address indexed targetAddress,
|
||||
uint256 redirectedBalanceAdded,
|
||||
uint256 redirectedBalanceRemoved,
|
||||
uint256 index
|
||||
);
|
||||
|
||||
event InterestRedirectionAllowanceChanged(address indexed from, address indexed to);
|
||||
|
||||
/**
|
||||
* @dev redirects the interest generated to a target address.
|
||||
* when the interest is redirected, the user balance is added to
|
||||
* the recepient redirected balance.
|
||||
* @param to the address to which the interest will be redirected
|
||||
**/
|
||||
function redirectInterestStream(address to) external;
|
||||
|
||||
/**
|
||||
* @dev redirects the interest generated by from to a target address.
|
||||
* when the interest is redirected, the user balance is added to
|
||||
* the recepient redirected balance. The caller needs to have allowance on
|
||||
* the interest redirection to be able to execute the function.
|
||||
* @param from the address of the user whom interest is being redirected
|
||||
* @param to the address to which the interest will be redirected
|
||||
**/
|
||||
function redirectInterestStreamOf(address from, address to) external;
|
||||
|
||||
/**
|
||||
* @dev gives allowance to an address to execute the interest redirection
|
||||
* on behalf of the caller.
|
||||
* @param to the address to which the interest will be redirected. Pass address(0) to reset
|
||||
* the allowance.
|
||||
**/
|
||||
function allowInterestRedirectionTo(address to) external;
|
||||
|
||||
/**
|
||||
* @dev burns the aTokens and sends the equivalent amount of underlying to the target.
|
||||
* only lending pools can call this function
|
||||
|
@ -145,42 +88,11 @@ interface IAToken is IERC20 {
|
|||
function isTransferAllowed(address user, uint256 amount) external view returns (bool);
|
||||
|
||||
/**
|
||||
* @dev returns the address to which the interest is redirected
|
||||
* @param user address of the user
|
||||
* @return 0 if there is no redirection, an address otherwise
|
||||
**/
|
||||
function getInterestRedirectionAddress(address user) external view returns (address);
|
||||
|
||||
/**
|
||||
* @dev returns the index of the user at the moment of redirection
|
||||
* @param user address of the user
|
||||
* @return interest redirection index
|
||||
**/
|
||||
function getUserInterestRedirectionIndex(address user) external view returns (uint256);
|
||||
|
||||
/**
|
||||
* @dev returns the scaled redirected balance of the user. The scaled redirected balance is the sum of all the redirected balances
|
||||
* divided by the index at the moment of each specific redirection.
|
||||
* @param user address of the user
|
||||
* @return the total redirected balance
|
||||
**/
|
||||
function getScaledRedirectedBalance(address user) external view returns (uint256);
|
||||
|
||||
/**
|
||||
* @dev returns the redirected balance index. The redirected balance index is the
|
||||
* index at the moment the last scaled redirected balance has been performed, and allows to calculate the actual redirected balance
|
||||
* @dev transfer the amount of the underlying asset to the user
|
||||
* @param user address of the user
|
||||
* @param amount the amount to transfer
|
||||
* @return the redirected balance index
|
||||
**/
|
||||
function getRedirectedBalanceIndex(address user) external view returns (uint256);
|
||||
|
||||
/**
|
||||
* @dev transfers the underlying asset to the target. Used by the lendingpool to transfer
|
||||
* assets in borrow(), redeem() and flashLoan()
|
||||
* @param target the target of the transfer
|
||||
* @param amount the amount to transfer
|
||||
* @return the amount transferred
|
||||
**/
|
||||
|
||||
function transferUnderlyingTo(address target, uint256 amount) external returns (uint256);
|
||||
function transferUnderlyingTo(address user, uint256 amount) external returns (uint256);
|
||||
}
|
||||
|
|
|
@ -47,50 +47,6 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
|
|||
);
|
||||
});
|
||||
|
||||
it('User 1 redirects interest to user 2, transfers 500 DAI back to user 0', async () => {
|
||||
const {users, aDai, dai} = testEnv;
|
||||
await aDai.connect(users[1].signer).redirectInterestStream(users[2].address);
|
||||
|
||||
const aDAIRedirected = await convertToCurrencyDecimals(dai.address, '1000');
|
||||
|
||||
const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '500');
|
||||
|
||||
const user2RedirectedBalanceBefore = await aDai.getRedirectedBalance(users[2].address);
|
||||
expect(user2RedirectedBalanceBefore.toString()).to.be.equal(
|
||||
aDAIRedirected,
|
||||
INVALID_REDIRECTED_BALANCE_BEFORE_TRANSFER
|
||||
);
|
||||
|
||||
await aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer);
|
||||
|
||||
const user2RedirectedBalanceAfter = await aDai.getRedirectedBalance(users[2].address);
|
||||
const user1RedirectionAddress = await aDai.getInterestRedirectionAddress(users[1].address);
|
||||
|
||||
expect(user2RedirectedBalanceAfter.toString()).to.be.equal(
|
||||
aDAItoTransfer,
|
||||
INVALID_REDIRECTED_BALANCE_BEFORE_TRANSFER
|
||||
);
|
||||
expect(user1RedirectionAddress.toString()).to.be.equal(
|
||||
users[2].address,
|
||||
INVALID_REDIRECTION_ADDRESS
|
||||
);
|
||||
});
|
||||
|
||||
it('User 0 transfers back to user 1', async () => {
|
||||
const {users, aDai, dai, weth} = testEnv;
|
||||
const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '500');
|
||||
|
||||
await aDai.connect(users[0].signer).transfer(users[1].address, aDAItoTransfer);
|
||||
|
||||
const user2RedirectedBalanceAfter = await aDai.getRedirectedBalance(users[2].address);
|
||||
|
||||
const user1BalanceAfter = await aDai.balanceOf(users[1].address);
|
||||
|
||||
expect(user2RedirectedBalanceAfter.toString()).to.be.equal(
|
||||
user1BalanceAfter.toString(),
|
||||
INVALID_REDIRECTED_BALANCE_AFTER_TRANSFER
|
||||
);
|
||||
});
|
||||
|
||||
it('User 0 deposits 1 WETH and user 1 tries to borrow, but the aTokens received as a transfer are not available as collateral (revert expected)', async () => {
|
||||
const {users, pool, weth} = testEnv;
|
||||
|
@ -124,79 +80,4 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
|
|||
).to.be.revertedWith(TRANSFER_NOT_ALLOWED);
|
||||
});
|
||||
|
||||
it('User 1 repays the borrow, transfers aDAI back to user 0', async () => {
|
||||
const {users, pool, aDai, dai, weth} = testEnv;
|
||||
|
||||
await weth.connect(users[1].signer).mint(await convertToCurrencyDecimals(weth.address, '2'));
|
||||
|
||||
await weth.connect(users[1].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool
|
||||
.connect(users[1].signer)
|
||||
.repay(weth.address, MAX_UINT_AMOUNT, RateMode.Stable, users[1].address);
|
||||
|
||||
const aDAItoTransfer = await convertToCurrencyDecimals(aDai.address, '1000');
|
||||
|
||||
await aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer);
|
||||
|
||||
const user2RedirectedBalanceAfter = await aDai.getRedirectedBalance(users[2].address);
|
||||
|
||||
const user1RedirectionAddress = await aDai.getInterestRedirectionAddress(users[1].address);
|
||||
|
||||
expect(user2RedirectedBalanceAfter.toString()).to.be.equal(
|
||||
'0',
|
||||
INVALID_REDIRECTED_BALANCE_AFTER_TRANSFER
|
||||
);
|
||||
|
||||
expect(user1RedirectionAddress.toString()).to.be.equal(
|
||||
ZERO_ADDRESS,
|
||||
INVALID_REDIRECTION_ADDRESS
|
||||
);
|
||||
});
|
||||
|
||||
it('User 0 redirects interest to user 2, transfers 500 aDAI to user 1. User 1 redirects to user 3. User 0 transfers another 100 aDAI', async () => {
|
||||
const {users, pool, aDai, dai, weth} = testEnv;
|
||||
|
||||
let aDAItoTransfer = await convertToCurrencyDecimals(aDai.address, '500');
|
||||
|
||||
await aDai.connect(users[0].signer).redirectInterestStream(users[2].address);
|
||||
|
||||
await aDai.connect(users[0].signer).transfer(users[1].address, aDAItoTransfer);
|
||||
|
||||
await aDai.connect(users[1].signer).redirectInterestStream(users[3].address);
|
||||
|
||||
aDAItoTransfer = await convertToCurrencyDecimals(aDai.address, '100');
|
||||
|
||||
await aDai.connect(users[0].signer).transfer(users[1].address, aDAItoTransfer);
|
||||
|
||||
const user2RedirectedBalanceAfter = await aDai.getRedirectedBalance(users[2].address);
|
||||
const user3RedirectedBalanceAfter = await aDai.getRedirectedBalance(users[3].address);
|
||||
|
||||
const expectedUser2Redirected = await convertToCurrencyDecimals(aDai.address, '400');
|
||||
const expectedUser3Redirected = await convertToCurrencyDecimals(aDai.address, '600');
|
||||
|
||||
expect(user2RedirectedBalanceAfter.toString()).to.be.equal(
|
||||
expectedUser2Redirected,
|
||||
INVALID_REDIRECTED_BALANCE_AFTER_TRANSFER
|
||||
);
|
||||
expect(user3RedirectedBalanceAfter.toString()).to.be.equal(
|
||||
expectedUser3Redirected,
|
||||
INVALID_REDIRECTED_BALANCE_AFTER_TRANSFER
|
||||
);
|
||||
});
|
||||
|
||||
it('User 1 transfers the whole amount to himself', async () => {
|
||||
const {users, pool, aDai, dai} = testEnv;
|
||||
|
||||
const user1BalanceBefore = await aDai.balanceOf(users[1].address);
|
||||
|
||||
await aDai.connect(users[1].signer).transfer(users[1].address, user1BalanceBefore);
|
||||
|
||||
const user1BalanceAfter = await aDai.balanceOf(users[1].address);
|
||||
|
||||
expect(user1BalanceAfter.toString()).to.be.equal(
|
||||
user1BalanceBefore,
|
||||
INVALID_REDIRECTED_BALANCE_AFTER_TRANSFER
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
calcExpectedUserDataAfterStableRateRebalance,
|
||||
calcExpectedUserDataAfterSwapRateMode,
|
||||
calcExpectedUserDataAfterWithdraw,
|
||||
calcExpectedUsersDataAfterRedirectInterest,
|
||||
} from './utils/calculations';
|
||||
import {getReserveAddressFromSymbol, getReserveData, getUserData} from './utils/helpers';
|
||||
|
||||
|
@ -652,156 +651,6 @@ export const rebalanceStableBorrowRate = async (
|
|||
}
|
||||
};
|
||||
|
||||
export const redirectInterestStream = async (
|
||||
reserveSymbol: string,
|
||||
user: SignerWithAddress,
|
||||
to: tEthereumAddress,
|
||||
expectedResult: string,
|
||||
testEnv: TestEnv,
|
||||
revertMessage?: string
|
||||
) => {
|
||||
const {
|
||||
aTokenInstance,
|
||||
reserve,
|
||||
userData: fromDataBefore,
|
||||
reserveData: reserveDataBefore,
|
||||
} = await getDataBeforeAction(reserveSymbol, user.address, testEnv);
|
||||
|
||||
const {userData: toDataBefore} = await getContractsData(reserve, to, testEnv);
|
||||
|
||||
if (expectedResult === 'success') {
|
||||
const txResult = await waitForTx(
|
||||
await aTokenInstance.connect(user.signer).redirectInterestStream(to)
|
||||
);
|
||||
|
||||
const {txCost, txTimestamp} = await getTxCostAndTimestamp(txResult);
|
||||
|
||||
const {userData: fromDataAfter} = await getContractsData(reserve, user.address, testEnv);
|
||||
|
||||
const {userData: toDataAfter} = await getContractsData(reserve, to, testEnv);
|
||||
|
||||
const [expectedFromData, expectedToData] = calcExpectedUsersDataAfterRedirectInterest(
|
||||
reserveDataBefore,
|
||||
fromDataBefore,
|
||||
toDataBefore,
|
||||
user.address,
|
||||
to,
|
||||
true,
|
||||
txCost,
|
||||
txTimestamp
|
||||
);
|
||||
|
||||
console.log("Checking from data");
|
||||
|
||||
expectEqual(fromDataAfter, expectedFromData);
|
||||
console.log("Checking to data");
|
||||
expectEqual(toDataAfter, expectedToData);
|
||||
|
||||
// truffleAssert.eventEmitted(txResult, 'InterestStreamRedirected', (ev: any) => {
|
||||
// const {_from, _to} = ev;
|
||||
// return _from === user
|
||||
// && _to === (to === user ? NIL_ADDRESS : to);
|
||||
// });
|
||||
} else if (expectedResult === 'revert') {
|
||||
await expect(aTokenInstance.connect(user.signer).redirectInterestStream(to), revertMessage).to
|
||||
.be.reverted;
|
||||
}
|
||||
};
|
||||
|
||||
export const redirectInterestStreamOf = async (
|
||||
reserveSymbol: string,
|
||||
user: SignerWithAddress,
|
||||
from: tEthereumAddress,
|
||||
to: tEthereumAddress,
|
||||
expectedResult: string,
|
||||
testEnv: TestEnv,
|
||||
revertMessage?: string
|
||||
) => {
|
||||
const {
|
||||
aTokenInstance,
|
||||
reserve,
|
||||
userData: fromDataBefore,
|
||||
reserveData: reserveDataBefore,
|
||||
} = await getDataBeforeAction(reserveSymbol, from, testEnv);
|
||||
|
||||
const {userData: toDataBefore} = await getContractsData(reserve, user.address, testEnv);
|
||||
|
||||
if (expectedResult === 'success') {
|
||||
const txResult = await waitForTx(
|
||||
await aTokenInstance.connect(user.signer).redirectInterestStreamOf(from, to)
|
||||
);
|
||||
|
||||
const {txCost, txTimestamp} = await getTxCostAndTimestamp(txResult);
|
||||
|
||||
const {userData: fromDataAfter} = await getContractsData(reserve, from, testEnv);
|
||||
|
||||
const {userData: toDataAfter} = await getContractsData(reserve, to, testEnv);
|
||||
|
||||
const [expectedFromData, exptectedToData] = calcExpectedUsersDataAfterRedirectInterest(
|
||||
reserveDataBefore,
|
||||
fromDataBefore,
|
||||
toDataBefore,
|
||||
from,
|
||||
to,
|
||||
from === user.address,
|
||||
txCost,
|
||||
txTimestamp
|
||||
);
|
||||
|
||||
expectEqual(fromDataAfter, expectedFromData);
|
||||
expectEqual(toDataAfter, exptectedToData);
|
||||
|
||||
// truffleAssert.eventEmitted(
|
||||
// txResult,
|
||||
// "InterestStreamRedirected",
|
||||
// (ev: any) => {
|
||||
// const {_from, _to} = ev;
|
||||
// return (
|
||||
// _from.toLowerCase() === from.toLowerCase() &&
|
||||
// _to.toLowerCase() === to.toLowerCase()
|
||||
// );
|
||||
// }
|
||||
// );
|
||||
} else if (expectedResult === 'revert') {
|
||||
await expect(
|
||||
aTokenInstance.connect(user.signer).redirectInterestStreamOf(from, to),
|
||||
revertMessage
|
||||
).to.be.reverted;
|
||||
}
|
||||
};
|
||||
|
||||
export const allowInterestRedirectionTo = async (
|
||||
reserveSymbol: string,
|
||||
user: SignerWithAddress,
|
||||
to: tEthereumAddress,
|
||||
expectedResult: string,
|
||||
testEnv: TestEnv,
|
||||
revertMessage?: string
|
||||
) => {
|
||||
const {aTokenInstance} = await getDataBeforeAction(reserveSymbol, user.address, testEnv);
|
||||
|
||||
if (expectedResult === 'success') {
|
||||
const txResult = await waitForTx(
|
||||
await aTokenInstance.connect(user.signer).allowInterestRedirectionTo(to)
|
||||
);
|
||||
|
||||
// truffleAssert.eventEmitted(
|
||||
// txResult,
|
||||
// "InterestRedirectionAllowanceChanged",
|
||||
// (ev: any) => {
|
||||
// const {_from, _to} = ev;
|
||||
// return (
|
||||
// _from.toLowerCase() === user.toLowerCase() &&
|
||||
// _to.toLowerCase() === to.toLowerCase()
|
||||
// );
|
||||
// }
|
||||
// );
|
||||
} else if (expectedResult === 'revert') {
|
||||
await expect(aTokenInstance.connect(user.signer).allowInterestRedirectionTo(to), revertMessage)
|
||||
.to.be.reverted;
|
||||
}
|
||||
};
|
||||
|
||||
const expectEqual = (
|
||||
actual: UserReserveData | ReserveData,
|
||||
expected: UserReserveData | ReserveData
|
||||
|
|
|
@ -8,10 +8,7 @@ import {
|
|||
repay,
|
||||
setUseAsCollateral,
|
||||
swapBorrowRateMode,
|
||||
rebalanceStableBorrowRate,
|
||||
redirectInterestStream,
|
||||
redirectInterestStreamOf,
|
||||
allowInterestRedirectionTo,
|
||||
rebalanceStableBorrowRate
|
||||
} from './actions';
|
||||
import {RateMode} from '../../helpers/types';
|
||||
|
||||
|
@ -185,71 +182,7 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
|
|||
await rebalanceStableBorrowRate(reserve, user, target, expected, testEnv, revertMessage);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'redirectInterestStream':
|
||||
{
|
||||
const {to: toIndex} = action.args;
|
||||
|
||||
if (!toIndex || toIndex === '') {
|
||||
throw `A target must be selected when trying to redirect the interest`;
|
||||
}
|
||||
const toUser = users[parseInt(toIndex)];
|
||||
|
||||
await redirectInterestStream(
|
||||
reserve,
|
||||
user,
|
||||
toUser.address,
|
||||
expected,
|
||||
testEnv,
|
||||
revertMessage
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'redirectInterestStreamOf':
|
||||
{
|
||||
const {from: fromIndex, to: toIndex} = action.args;
|
||||
|
||||
if (!fromIndex || fromIndex === '') {
|
||||
throw `A from address must be specified when trying to redirect the interest`;
|
||||
}
|
||||
if (!toIndex || toIndex === '') {
|
||||
throw `A target must be selected when trying to redirect the interest`;
|
||||
}
|
||||
const toUser = users[parseInt(toIndex)];
|
||||
const fromUser = users[parseInt(fromIndex)];
|
||||
|
||||
await redirectInterestStreamOf(
|
||||
reserve,
|
||||
user,
|
||||
fromUser.address,
|
||||
toUser.address,
|
||||
expected,
|
||||
testEnv,
|
||||
revertMessage
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'allowInterestRedirectionTo':
|
||||
{
|
||||
const {to: toIndex} = action.args;
|
||||
|
||||
if (!toIndex || toIndex === '') {
|
||||
throw `A target must be selected when trying to redirect the interest`;
|
||||
}
|
||||
const toUser = users[parseInt(toIndex)];
|
||||
|
||||
await allowInterestRedirectionTo(
|
||||
reserve,
|
||||
user,
|
||||
toUser.address,
|
||||
expected,
|
||||
testEnv,
|
||||
revertMessage
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw `Invalid action requested: ${name}`;
|
||||
}
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
{
|
||||
"title": "AToken: interest rate redirection negative test cases",
|
||||
"description": "Test cases for the aToken interest rate redirection.",
|
||||
"stories": [
|
||||
{
|
||||
"description": "User 0 deposits 1000 DAI, tries to give allowance to redirect interest to himself (revert expected)",
|
||||
"actions": [
|
||||
{
|
||||
"name": "mint",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1000",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "approve",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "deposit",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1000",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "allowInterestRedirectionTo",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "0",
|
||||
"to": "0"
|
||||
},
|
||||
"expected": "revert",
|
||||
"revertMessage": "User cannot give allowance to himself"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 1 tries to redirect the interest of user 0 without allowance (revert expected)",
|
||||
"actions": [
|
||||
{
|
||||
"name": "redirectInterestStreamOf",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "1",
|
||||
"from": "0",
|
||||
"to": "2"
|
||||
},
|
||||
"expected": "revert",
|
||||
"revertMessage": "Caller is not allowed to redirect the interest of the user"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 0 tries to redirect the interest to user 2 twice (revert expected)",
|
||||
"actions": [
|
||||
{
|
||||
"name": "redirectInterestStream",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "0",
|
||||
"to": "2"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "redirectInterestStream",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "0",
|
||||
"to": "2"
|
||||
},
|
||||
"expected": "revert",
|
||||
"revertMessage": "Interest is already redirected to the user"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 3 with 0 balance tries to redirect the interest to user 2 (revert expected)",
|
||||
"actions": [
|
||||
{
|
||||
"name": "redirectInterestStream",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "3",
|
||||
"to": "2"
|
||||
},
|
||||
"expected": "revert",
|
||||
"revertMessage": "Interest stream can only be redirected if there is a valid balance"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,405 +0,0 @@
|
|||
{
|
||||
"title": "AToken: interest rate redirection",
|
||||
"description": "Test cases for the aToken interest rate redirection.",
|
||||
"stories": [
|
||||
{
|
||||
"description": "User 0 deposits 1000 DAI, redirects the interest to user 2",
|
||||
"actions": [
|
||||
{
|
||||
"name": "mint",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1000",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "approve",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "deposit",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1000",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "redirectInterestStream",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "0",
|
||||
"to": "2"
|
||||
},
|
||||
"expected": "success"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 1 deposits 1 ETH, borrows 100 DAI, repays after one year. Users 0 deposits another 1000 DAI. Redirected balance of user 2 is updated",
|
||||
"actions": [
|
||||
{
|
||||
"name": "mint",
|
||||
"args": {
|
||||
"reserve": "WETH",
|
||||
"amount": "2",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "approve",
|
||||
"args": {
|
||||
"reserve": "WETH",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "deposit",
|
||||
"args": {
|
||||
"reserve": "WETH",
|
||||
|
||||
"amount": "2",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "borrow",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "100",
|
||||
"borrowRateMode": "stable",
|
||||
"user": "1",
|
||||
"timeTravel": "365"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "mint",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1000",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "deposit",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1000",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 1 borrows another 100 DAI, repay the whole amount. Users 0 and User 2 withdraw",
|
||||
"actions": [
|
||||
{
|
||||
"name": "borrow",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "100",
|
||||
"borrowRateMode": "stable",
|
||||
"user": "1",
|
||||
"timeTravel": "365"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "mint",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "100",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "approve",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "repay",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "-1",
|
||||
"user": "1",
|
||||
"onBehalfOf": "1",
|
||||
"borrowRateMode": "stable"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "withdraw",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "-1",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "withdraw",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "-1",
|
||||
"user": "2"
|
||||
},
|
||||
"expected": "success"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 0 deposits 1000 DAI, redirects interest to user 2, user 1 borrows 100 DAI. After one year, user 0 redirects interest back to himself, user 1 borrows another 100 DAI and after another year repays the whole amount. Users 0 and User 2 withdraw",
|
||||
"actions": [
|
||||
{
|
||||
"name": "deposit",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1000",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "redirectInterestStream",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "0",
|
||||
"to": "2"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "borrow",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "100",
|
||||
"borrowRateMode": "stable",
|
||||
"user": "1",
|
||||
"timeTravel": "365"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "redirectInterestStream",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "0",
|
||||
"to": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "borrow",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "100",
|
||||
"borrowRateMode": "stable",
|
||||
"user": "1",
|
||||
"timeTravel": "365"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "mint",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "100",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "approve",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "repay",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "-1",
|
||||
"user": "1",
|
||||
"onBehalfOf": "1",
|
||||
"borrowRateMode": "stable"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "withdraw",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "-1",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "withdraw",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "-1",
|
||||
"user": "2"
|
||||
},
|
||||
"expected": "success"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 0 deposits 1000 DAI, redirects interest to user 2, user 1 borrows 100 DAI. After one year, user 2 redirects interest to user 3. user 1 borrows another 100 DAI, user 0 deposits another 100 DAI. User 1 repays the whole amount. Users 0, 2 first 1 DAI, then everything. User 3 withdraws",
|
||||
"actions": [
|
||||
{
|
||||
"name": "deposit",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1000",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "redirectInterestStream",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "0",
|
||||
"to": "2"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "borrow",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "100",
|
||||
"borrowRateMode": "stable",
|
||||
"user": "1",
|
||||
"timeTravel": "365"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "redirectInterestStream",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "2",
|
||||
"to": "3"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "borrow",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "100",
|
||||
"borrowRateMode": "stable",
|
||||
"user": "1",
|
||||
"timeTravel": "365"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "deposit",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "100",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "mint",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "100",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "approve",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "repay",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "-1",
|
||||
"user": "1",
|
||||
"onBehalfOf": "1",
|
||||
"borrowRateMode": "stable"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "withdraw",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "withdraw",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1",
|
||||
"user": "2"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "withdraw",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "-1",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "withdraw",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "-1",
|
||||
"user": "2"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "withdraw",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "-1",
|
||||
"user": "3"
|
||||
},
|
||||
"expected": "success"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -70,15 +70,6 @@ export const calcExpectedUserDataAfterDeposit = (
|
|||
expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex;
|
||||
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(amountDeposited);
|
||||
|
||||
expectedUserData.scaledRedirectedBalance = userDataBeforeAction.scaledRedirectedBalance;
|
||||
expectedUserData.redirectedBalanceIndex = userDataBeforeAction.redirectedBalanceIndex;
|
||||
expectedUserData.interestRedirectionAddress = userDataBeforeAction.interestRedirectionAddress;
|
||||
expectedUserData.interestRedirectionIndex =
|
||||
userDataBeforeAction.interestRedirectionAddress == ZERO_ADDRESS
|
||||
? new BigNumber(0)
|
||||
: reserveDataAfterAction.liquidityIndex;
|
||||
|
||||
|
||||
expectedUserData.currentStableDebt = expectedUserData.principalStableDebt = calcExpectedStableDebtTokenBalance(
|
||||
userDataBeforeAction,
|
||||
txTimestamp
|
||||
|
@ -90,14 +81,6 @@ export const calcExpectedUserDataAfterDeposit = (
|
|||
txTimestamp
|
||||
);
|
||||
|
||||
expectedUserData.redirectionAddressScaledRedirectedBalance = calcExpectedRedirectedBalance(
|
||||
expectedUserData,
|
||||
reserveDataAfterAction.liquidityIndex,
|
||||
userDataBeforeAction.redirectionAddressScaledRedirectedBalance,
|
||||
new BigNumber(amountDeposited),
|
||||
new BigNumber(0)
|
||||
);
|
||||
|
||||
return expectedUserData;
|
||||
};
|
||||
|
||||
|
@ -163,28 +146,6 @@ export const calcExpectedUserDataAfterWithdraw = (
|
|||
}
|
||||
|
||||
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.plus(amountWithdrawn);
|
||||
expectedUserData.scaledRedirectedBalance = userDataBeforeAction.scaledRedirectedBalance;
|
||||
expectedUserData.redirectedBalanceIndex = userDataBeforeAction.redirectedBalanceIndex;
|
||||
|
||||
|
||||
if (expectedUserData.currentATokenBalance.eq(0) && expectedUserData.scaledRedirectedBalance.eq(0)) {
|
||||
expectedUserData.interestRedirectionAddress = ZERO_ADDRESS;
|
||||
expectedUserData.interestRedirectionIndex = new BigNumber(0);
|
||||
} else {
|
||||
expectedUserData.interestRedirectionAddress = userDataBeforeAction.interestRedirectionAddress;
|
||||
expectedUserData.interestRedirectionIndex =
|
||||
userDataBeforeAction.interestRedirectionAddress == ZERO_ADDRESS
|
||||
? new BigNumber(0)
|
||||
: reserveDataAfterAction.liquidityIndex;
|
||||
}
|
||||
|
||||
expectedUserData.redirectionAddressScaledRedirectedBalance = calcExpectedRedirectedBalance(
|
||||
expectedUserData,
|
||||
reserveDataAfterAction.liquidityIndex,
|
||||
userDataBeforeAction.redirectionAddressScaledRedirectedBalance,
|
||||
new BigNumber(0),
|
||||
new BigNumber(amountWithdrawn)
|
||||
);
|
||||
|
||||
return expectedUserData;
|
||||
};
|
||||
|
@ -578,14 +539,7 @@ export const calcExpectedUserDataAfterBorrow = (
|
|||
currentTimestamp
|
||||
);
|
||||
expectedUserData.scaledATokenBalance = userDataBeforeAction.scaledATokenBalance;
|
||||
expectedUserData.scaledRedirectedBalance = userDataBeforeAction.scaledRedirectedBalance;
|
||||
expectedUserData.redirectedBalanceIndex = userDataBeforeAction.redirectedBalanceIndex;
|
||||
expectedUserData.interestRedirectionAddress = userDataBeforeAction.interestRedirectionAddress;
|
||||
expectedUserData.interestRedirectionIndex = userDataBeforeAction.interestRedirectionIndex;
|
||||
expectedUserData.redirectionAddressScaledRedirectedBalance =
|
||||
userDataBeforeAction.redirectionAddressScaledRedirectedBalance;
|
||||
expectedUserData.currentATokenUserIndex = userDataBeforeAction.currentATokenUserIndex;
|
||||
|
||||
|
||||
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.plus(amountBorrowed);
|
||||
|
||||
return expectedUserData;
|
||||
|
@ -669,14 +623,7 @@ export const calcExpectedUserDataAfterRepay = (
|
|||
txTimestamp
|
||||
);
|
||||
expectedUserData.scaledATokenBalance = userDataBeforeAction.scaledATokenBalance;
|
||||
expectedUserData.redirectedBalanceIndex = userDataBeforeAction.redirectedBalanceIndex;
|
||||
expectedUserData.scaledRedirectedBalance = userDataBeforeAction.scaledRedirectedBalance;
|
||||
expectedUserData.interestRedirectionAddress = userDataBeforeAction.interestRedirectionAddress;
|
||||
expectedUserData.interestRedirectionIndex = userDataBeforeAction.interestRedirectionIndex;
|
||||
expectedUserData.redirectionAddressScaledRedirectedBalance =
|
||||
userDataBeforeAction.redirectionAddressScaledRedirectedBalance;
|
||||
expectedUserData.currentATokenUserIndex = userDataBeforeAction.currentATokenUserIndex;
|
||||
|
||||
|
||||
if (user === onBehalfOf) {
|
||||
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(totalRepaid);
|
||||
} else {
|
||||
|
@ -961,78 +908,6 @@ export const calcExpectedUserDataAfterStableRateRebalance = (
|
|||
return expectedUserData;
|
||||
};
|
||||
|
||||
export const calcExpectedUsersDataAfterRedirectInterest = (
|
||||
reserveDataBeforeAction: ReserveData,
|
||||
fromDataBeforeAction: UserReserveData,
|
||||
toDataBeforeAction: UserReserveData,
|
||||
fromAddress: string,
|
||||
toAddress: string,
|
||||
isFromExecutingTx: boolean,
|
||||
txCost: BigNumber,
|
||||
txTimestamp: BigNumber
|
||||
): UserReserveData[] => {
|
||||
const expectedFromData = { ...fromDataBeforeAction };
|
||||
const expectedToData = { ...toDataBeforeAction };
|
||||
|
||||
const index = calcExpectedReserveNormalizedIncome(reserveDataBeforeAction, txTimestamp);
|
||||
|
||||
expectedFromData.currentStableDebt = calcExpectedStableDebtTokenBalance(
|
||||
fromDataBeforeAction,
|
||||
txTimestamp
|
||||
);
|
||||
|
||||
expectedToData.currentVariableDebt = calcExpectedVariableDebtTokenBalance(
|
||||
reserveDataBeforeAction,
|
||||
toDataBeforeAction,
|
||||
txTimestamp
|
||||
);
|
||||
|
||||
expectedFromData.variableBorrowIndex = fromDataBeforeAction.variableBorrowIndex;
|
||||
expectedToData.variableBorrowIndex = toDataBeforeAction.variableBorrowIndex;
|
||||
|
||||
expectedFromData.stableBorrowRate = fromDataBeforeAction.stableBorrowRate;
|
||||
expectedToData.stableBorrowRate = toDataBeforeAction.stableBorrowRate;
|
||||
|
||||
expectedFromData.scaledATokenBalance = fromDataBeforeAction.scaledATokenBalance;
|
||||
|
||||
expectedFromData.currentATokenBalance = calcExpectedATokenBalance(
|
||||
reserveDataBeforeAction,
|
||||
fromDataBeforeAction,
|
||||
txTimestamp
|
||||
);
|
||||
|
||||
|
||||
expectedToData.scaledRedirectedBalance = toDataBeforeAction.scaledRedirectedBalance.plus(
|
||||
expectedFromData.scaledATokenBalance
|
||||
);
|
||||
|
||||
expectedToData.redirectedBalanceIndex = index;
|
||||
|
||||
if (fromAddress === toAddress) {
|
||||
expectedFromData.interestRedirectionAddress = ZERO_ADDRESS;
|
||||
expectedFromData.scaledRedirectedBalance = new BigNumber(0);
|
||||
expectedFromData.redirectedBalanceIndex = new BigNumber(0);
|
||||
|
||||
expectedFromData.redirectionAddressScaledRedirectedBalance = new BigNumber(0);
|
||||
expectedToData.interestRedirectionAddress = ZERO_ADDRESS;
|
||||
expectedToData.scaledRedirectedBalance = new BigNumber(0);
|
||||
expectedToData.redirectionAddressScaledRedirectedBalance = new BigNumber(0);
|
||||
} else {
|
||||
expectedFromData.interestRedirectionAddress = toAddress;
|
||||
expectedFromData.interestRedirectionIndex = index;
|
||||
|
||||
expectedFromData.redirectionAddressScaledRedirectedBalance = calcExpectedRedirectedBalance(
|
||||
expectedFromData,
|
||||
index,
|
||||
toDataBeforeAction.scaledRedirectedBalance,
|
||||
expectedFromData.currentATokenBalance,
|
||||
new BigNumber(0)
|
||||
);
|
||||
}
|
||||
|
||||
return [expectedFromData, expectedToData];
|
||||
};
|
||||
|
||||
const calcExpectedScaledATokenBalance = (
|
||||
userDataBeforeAction: UserReserveData,
|
||||
index: BigNumber,
|
||||
|
@ -1052,27 +927,10 @@ const calcExpectedATokenBalance = (
|
|||
const index = calcExpectedReserveNormalizedIncome(reserveDataBeforeAction, currentTimestamp);
|
||||
|
||||
const {
|
||||
interestRedirectionAddress,
|
||||
interestRedirectionIndex: redirectionIndexBeforeAction,
|
||||
scaledRedirectedBalance,
|
||||
redirectedBalanceIndex: redirectedBalanceIndexBeforeAction,
|
||||
scaledATokenBalance: scaledBalanceBeforeAction,
|
||||
} = userDataBeforeAction;
|
||||
|
||||
if (scaledBalanceBeforeAction.eq(0) && scaledRedirectedBalance.eq(0)) {
|
||||
return new BigNumber(0);
|
||||
}
|
||||
|
||||
const actualRedirectedBalance = scaledRedirectedBalance.gt(0) ? scaledRedirectedBalance.rayMul(redirectedBalanceIndexBeforeAction) : new BigNumber(0);
|
||||
|
||||
if (interestRedirectionAddress === ZERO_ADDRESS) {
|
||||
return scaledBalanceBeforeAction.plus(scaledRedirectedBalance).rayMul(index).minus(actualRedirectedBalance);
|
||||
}
|
||||
|
||||
|
||||
const lastRedirectedBalance = scaledBalanceBeforeAction.rayMul(redirectionIndexBeforeAction);
|
||||
|
||||
return lastRedirectedBalance.plus(scaledRedirectedBalance.rayMul(index).minus(actualRedirectedBalance));
|
||||
return scaledBalanceBeforeAction.rayMul(index);
|
||||
};
|
||||
|
||||
const calcExpectedRedirectedBalance = (
|
||||
|
|
|
@ -63,30 +63,17 @@ export const getUserData = async (
|
|||
reserve: string,
|
||||
user: string
|
||||
): Promise<UserReserveData> => {
|
||||
const [userData, aTokenData] = await Promise.all([
|
||||
const [userData, scaledATokenBalance] = await Promise.all([
|
||||
pool.getUserReserveData(reserve, user),
|
||||
getATokenUserData(reserve, user, pool),
|
||||
]);
|
||||
|
||||
const [
|
||||
scaledRedirectedBalance,
|
||||
redirectedBalanceIndex,
|
||||
scaledATokenBalance,
|
||||
redirectionAddressScaledRedirectedBalance,
|
||||
interestRedirectionAddress,
|
||||
interestRedirectionIndex,
|
||||
] = aTokenData;
|
||||
|
||||
|
||||
const token = await getMintableErc20(reserve);
|
||||
const walletBalance = new BigNumber((await token.balanceOf(user)).toString());
|
||||
|
||||
return {
|
||||
scaledATokenBalance: new BigNumber(scaledATokenBalance),
|
||||
interestRedirectionAddress,
|
||||
interestRedirectionIndex: new BigNumber(interestRedirectionIndex),
|
||||
redirectionAddressScaledRedirectedBalance: new BigNumber(redirectionAddressScaledRedirectedBalance),
|
||||
scaledRedirectedBalance: new BigNumber(scaledRedirectedBalance),
|
||||
redirectedBalanceIndex: new BigNumber(redirectedBalanceIndex),
|
||||
currentATokenBalance: new BigNumber(userData.currentATokenBalance.toString()),
|
||||
currentStableDebt: new BigNumber(userData.currentStableDebt.toString()),
|
||||
currentVariableDebt: new BigNumber(userData.currentVariableDebt.toString()),
|
||||
|
@ -116,31 +103,8 @@ const getATokenUserData = async (reserve: string, user: string, pool: LendingPoo
|
|||
const aTokenAddress: string = (await pool.getReserveTokensAddresses(reserve)).aTokenAddress;
|
||||
|
||||
const aToken = await getAToken(aTokenAddress);
|
||||
const [
|
||||
interestRedirectionAddress,
|
||||
scaledRedirectedBalance,
|
||||
redirectedBalanceIndex,
|
||||
scaledATokenBalance,
|
||||
interestRedirectionIndex
|
||||
] = await Promise.all([
|
||||
aToken.getInterestRedirectionAddress(user),
|
||||
aToken.getScaledRedirectedBalance(user),
|
||||
aToken.getRedirectedBalanceIndex(user),
|
||||
aToken.scaledBalanceOf(user),
|
||||
aToken.getUserInterestRedirectionIndex(user)
|
||||
]);
|
||||
|
||||
const redirectionAddressScaledRedirectedBalance =
|
||||
interestRedirectionAddress !== ZERO_ADDRESS
|
||||
? new BigNumber((await aToken.getScaledRedirectedBalance(interestRedirectionAddress)).toString())
|
||||
: new BigNumber('0');
|
||||
const scaledBalance = await aToken.scaledBalanceOf(user);
|
||||
return scaledBalance.toString();
|
||||
|
||||
return [
|
||||
scaledRedirectedBalance.toString(),
|
||||
redirectedBalanceIndex.toString(),
|
||||
scaledATokenBalance.toString(),
|
||||
redirectionAddressScaledRedirectedBalance.toString(),
|
||||
interestRedirectionAddress,
|
||||
interestRedirectionIndex.toString()
|
||||
];
|
||||
};
|
||||
|
|
|
@ -3,11 +3,6 @@ import BigNumber from 'bignumber.js';
|
|||
export interface UserReserveData {
|
||||
scaledATokenBalance: BigNumber;
|
||||
currentATokenBalance: BigNumber;
|
||||
interestRedirectionAddress: string;
|
||||
interestRedirectionIndex: BigNumber;
|
||||
redirectionAddressScaledRedirectedBalance: BigNumber;
|
||||
scaledRedirectedBalance: BigNumber;
|
||||
redirectedBalanceIndex: BigNumber;
|
||||
currentStableDebt: BigNumber;
|
||||
currentVariableDebt: BigNumber;
|
||||
principalStableDebt: BigNumber;
|
||||
|
|
|
@ -12,7 +12,7 @@ BigNumber.config({DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN});
|
|||
|
||||
const scenarioFolder = './test/helpers/scenarios/';
|
||||
|
||||
const selectedScenarios: string[] = ['interest-redirection.json'];
|
||||
const selectedScenarios: string[] = [];
|
||||
|
||||
fs.readdirSync(scenarioFolder).forEach((file) => {
|
||||
if (selectedScenarios.length > 0 && !selectedScenarios.includes(file)) return;
|
||||
|
|
Loading…
Reference in New Issue
Block a user