Skip to content

Commit

Permalink
Merge pull request #332 from aave/fix/329-AToken-mint-event
Browse files Browse the repository at this point in the history
Fix/329 AToken mint event
  • Loading branch information
The-3D authored Jan 3, 2022
2 parents cf64102 + ef57d1b commit 1358948
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 31 deletions.
23 changes: 16 additions & 7 deletions contracts/interfaces/IAToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,23 @@ import {IInitializableAToken} from './IInitializableAToken.sol';
interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken {
/**
* @notice Emitted after the mint action
* @param from The address performing the mint
* @param caller The address performing the mint
* @param onBehalfOf The address of the user that will receive the minted aTokens
* @param value The amount being minted (user entered amount + balance increase from interest)
* @param balanceIncrease The increase in balance since the last action of the user
* @param index The next liquidity index of the reserve
**/
event Mint(address indexed from, uint256 value, uint256 balanceIncrease, uint256 index);
event Mint(
address indexed caller,
address indexed onBehalfOf,
uint256 value,
uint256 balanceIncrease,
uint256 index
);

/**
* @notice Emitted after aTokens are burned
* @param from The owner of the aTokens, getting them burned
* @param from The address from which the aTokens will be burned
* @param target The address that will receive the underlying
* @param value The amount being burned (user entered amount - balance increase from interest)
* @param balanceIncrease The increase in balance since the last action of the user
Expand All @@ -47,13 +54,15 @@ interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken {

/**
* @notice Mints `amount` aTokens to `user`
* @param user The address receiving the minted tokens
* @param caller The address performing the mint
* @param onBehalfOf The address of the user that will receive the minted aTokens
* @param amount The amount of tokens getting minted
* @param index The next liquidity index of the reserve
* @return `true` if the the previous balance of the user was 0
*/
function mint(
address user,
address caller,
address onBehalfOf,
uint256 amount,
uint256 index
) external returns (bool);
Expand All @@ -62,13 +71,13 @@ interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken {
* @notice Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
* @dev In some instances, the mint event could be emitted from a burn transaction
* if the amount to burn is less than the interest the user earned
* @param user The owner of the aTokens, getting them burned
* @param from The address from which the aTokens will be burned
* @param receiverOfUnderlying The address that will receive the underlying
* @param amount The amount being burned
* @param index The next liquidity index of the reserve
**/
function burn(
address user,
address from,
address receiverOfUnderlying,
uint256 amount,
uint256 index
Expand Down
1 change: 1 addition & 0 deletions contracts/protocol/libraries/logic/BridgeLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ library BridgeLogic {
reserve.updateInterestRates(reserveCache, asset, 0, 0);

bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(
msg.sender,
onBehalfOf,
amount,
reserveCache.nextLiquidityIndex
Expand Down
1 change: 1 addition & 0 deletions contracts/protocol/libraries/logic/SupplyLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ library SupplyLogic {
IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, params.amount);

bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(
msg.sender,
params.onBehalfOf,
params.amount,
reserveCache.nextLiquidityIndex
Expand Down
35 changes: 18 additions & 17 deletions contracts/protocol/tokenization/AToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,57 +83,58 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {

/// @inheritdoc IAToken
function burn(
address user,
address from,
address receiverOfUnderlying,
uint256 amount,
uint256 index
) external override onlyPool {
uint256 amountScaled = amount.rayDiv(index);
require(amountScaled != 0, Errors.INVALID_BURN_AMOUNT);

uint256 scaledBalance = super.balanceOf(user);
uint256 scaledBalance = super.balanceOf(from);
uint256 balanceIncrease = scaledBalance.rayMul(index) -
scaledBalance.rayMul(_userState[user].additionalData);
scaledBalance.rayMul(_userState[from].additionalData);

_userState[user].additionalData = index.toUint128();
_userState[from].additionalData = index.toUint128();

_burn(user, amountScaled.toUint128());
_burn(from, amountScaled.toUint128());

if (receiverOfUnderlying != address(this)) {
IERC20(_underlyingAsset).safeTransfer(receiverOfUnderlying, amount);
}

if (balanceIncrease > amount) {
uint256 amountToMint = balanceIncrease - amount;
emit Transfer(address(0), user, amountToMint);
emit Mint(user, amountToMint, balanceIncrease, index);
emit Transfer(address(0), from, amountToMint);
emit Mint(from, from, amountToMint, balanceIncrease, index);
} else {
uint256 amountToBurn = amount - balanceIncrease;
emit Transfer(user, address(0), amountToBurn);
emit Burn(user, receiverOfUnderlying, amountToBurn, balanceIncrease, index);
emit Transfer(from, address(0), amountToBurn);
emit Burn(from, receiverOfUnderlying, amountToBurn, balanceIncrease, index);
}
}

/// @inheritdoc IAToken
function mint(
address user,
address caller,
address onBehalfOf,
uint256 amount,
uint256 index
) public override onlyPool returns (bool) {
uint256 amountScaled = amount.rayDiv(index);
require(amountScaled != 0, Errors.INVALID_MINT_AMOUNT);

uint256 scaledBalance = super.balanceOf(user);
uint256 scaledBalance = super.balanceOf(onBehalfOf);
uint256 balanceIncrease = scaledBalance.rayMul(index) -
scaledBalance.rayMul(_userState[user].additionalData);
scaledBalance.rayMul(_userState[onBehalfOf].additionalData);

_userState[user].additionalData = index.toUint128();
_userState[onBehalfOf].additionalData = index.toUint128();

_mint(user, amountScaled.toUint128());
_mint(onBehalfOf, amountScaled.toUint128());

uint256 amountToMint = amount + balanceIncrease;
emit Transfer(address(0), user, amountToMint);
emit Mint(user, amountToMint, balanceIncrease, index);
emit Transfer(address(0), onBehalfOf, amountToMint);
emit Mint(caller, onBehalfOf, amountToMint, balanceIncrease, index);

return scaledBalance == 0;
}
Expand All @@ -143,7 +144,7 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {
if (amount == 0) {
return;
}
mint(_treasury, amount, index);
mint(address(POOL), _treasury, amount, index);
}

/// @inheritdoc IAToken
Expand Down
10 changes: 8 additions & 2 deletions test-suites/atoken-edge.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ makeSuite('AToken: Edge cases', (testEnv: TestEnv) => {
const poolSigner = await hre.ethers.getSigner(pool.address);

await expect(
aDai.connect(poolSigner).mint(users[0].address, 0, utils.parseUnits('1', 27))
aDai
.connect(poolSigner)
.mint(users[0].address, users[0].address, 0, utils.parseUnits('1', 27))
).to.be.revertedWith(INVALID_MINT_AMOUNT);
});

Expand All @@ -139,7 +141,11 @@ makeSuite('AToken: Edge cases', (testEnv: TestEnv) => {
const poolSigner = await hre.ethers.getSigner(pool.address);

const mintingAmount = await convertToCurrencyDecimals(aDai.address, '100');
expect(aDai.connect(poolSigner).mint(ZERO_ADDRESS, mintingAmount, utils.parseUnits('1', 27)))
expect(
aDai
.connect(poolSigner)
.mint(ZERO_ADDRESS, ZERO_ADDRESS, mintingAmount, utils.parseUnits('1', 27))
)
.to.emit(aDai, 'Transfer')
.withArgs(ZERO_ADDRESS, ZERO_ADDRESS, mintingAmount);
});
Expand Down
52 changes: 48 additions & 4 deletions test-suites/atoken-event-accounting.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ makeSuite('AToken Mint and Burn Event Accounting', (testEnv) => {
);

const aTokenMintEventSignature = utils.keccak256(
utils.toUtf8Bytes('Mint(address,uint256,uint256,uint256)')
utils.toUtf8Bytes('Mint(address,address,uint256,uint256,uint256)')
);
const aTokenBurnEventSignature = utils.keccak256(
utils.toUtf8Bytes('Burn(address,address,uint256,uint256,uint256)')
Expand Down Expand Up @@ -72,6 +72,7 @@ makeSuite('AToken Mint and Burn Event Accounting', (testEnv) => {
)
.to.emit(aDai, 'Mint')
.withArgs(
depositor.address,
depositor.address,
firstDaiDeposit,
expectedBalanceIncrease,
Expand All @@ -82,6 +83,45 @@ makeSuite('AToken Mint and Burn Event Accounting', (testEnv) => {
expect(aDaiBalance).to.be.equal(firstDaiDeposit);
});

it('User 1 - Deposit dai on behalf of user 2', async () => {
const {
dai,
aDai,
users: [depositor, receiver],
pool,
helpersContract,
} = testEnv;

// mints DAI to depositor
await waitForTx(
await dai
.connect(depositor.signer)
['mint(uint256)'](await convertToCurrencyDecimals(dai.address, '10000'))
);

// approve protocol to access depositor wallet
await waitForTx(await dai.connect(depositor.signer).approve(pool.address, MAX_UINT_AMOUNT));

const daiReserveData = await helpersContract.getReserveData(dai.address);

const expectedBalanceIncrease = 0;

await expect(
pool.connect(depositor.signer).deposit(dai.address, firstDaiDeposit, receiver.address, '0')
)
.to.emit(aDai, 'Mint')
.withArgs(
depositor.address,
receiver.address,
firstDaiDeposit,
expectedBalanceIncrease,
daiReserveData.liquidityIndex
);

const aDaiBalance = await aDai.balanceOf(receiver.address);
expect(aDaiBalance).to.be.equal(firstDaiDeposit);
});

it('User 2 - deposit ETH, borrow Dai', async () => {
const {
dai,
Expand Down Expand Up @@ -170,6 +210,7 @@ makeSuite('AToken Mint and Burn Event Accounting', (testEnv) => {

// check mint event parameters
expect(parsedMintEvent.args.from).to.equal(borrower.address);
expect(parsedMintEvent.args.onBehalfOf).to.equal(borrower.address);
expect(parsedMintEvent.args.value).to.be.closeTo(totalMinted, 2);
expect(parsedMintEvent.args.balanceIncrease).to.be.closeTo(accruedDebt1, 2);
});
Expand Down Expand Up @@ -224,7 +265,8 @@ makeSuite('AToken Mint and Burn Event Accounting', (testEnv) => {
expect(parsedTransferEvent.args.value).to.be.closeTo(totalMinted, 2);

// check mint event parameters
expect(parsedMintEvent.args.from).to.equal(depositor.address);
expect(parsedMintEvent.args.caller).to.equal(depositor.address);
expect(parsedMintEvent.args.onBehalfOf).to.equal(depositor.address);
expect(parsedMintEvent.args.value).to.be.closeTo(totalMinted, 2);
expect(parsedMintEvent.args.balanceIncrease).to.be.closeTo(accruedInterest1, 2);
});
Expand Down Expand Up @@ -282,7 +324,8 @@ makeSuite('AToken Mint and Burn Event Accounting', (testEnv) => {
expect(parsedTransferEvent.args.value).to.be.closeTo(totalMinted, 2);

// check mint event
expect(parsedMintEvent.args.from).to.equal(depositor.address);
expect(parsedMintEvent.args.caller).to.equal(depositor.address);
expect(parsedMintEvent.args.onBehalfOf).to.equal(depositor.address);
expect(parsedMintEvent.args.value).to.be.closeTo(totalMinted, 2);
expect(parsedMintEvent.args.balanceIncrease).to.be.closeTo(accruedInterest2, 2);
expect(parsedMintEvent.args.index).to.equal(daiReserveData.liquidityIndex);
Expand Down Expand Up @@ -518,7 +561,8 @@ makeSuite('AToken Mint and Burn Event Accounting', (testEnv) => {
expect(parsedTransferEvent.args.value).to.be.closeTo(totalMinted, 2);

// check mint event
expect(parsedMintEvent.args.from).to.equal(depositor.address);
expect(parsedMintEvent.args.caller).to.equal(depositor.address);
expect(parsedMintEvent.args.onBehalfOf).to.equal(depositor.address);
expect(parsedMintEvent.args.value).to.be.closeTo(totalMinted, 2);
expect(parsedMintEvent.args.balanceIncrease).to.be.closeTo(totalMinted.add(smallWithdrawal), 2);
expect(parsedMintEvent.args.index).to.equal(daiReserveData.liquidityIndex);
Expand Down
4 changes: 3 additions & 1 deletion test-suites/atoken-modifiers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ makeSuite('AToken: Modifiers', (testEnv: TestEnv) => {

it('Tries to invoke mint not being the Pool (revert expected)', async () => {
const { deployer, aDai } = testEnv;
await expect(aDai.mint(deployer.address, '1', '1')).to.be.revertedWith(CALLER_MUST_BE_POOL);
await expect(aDai.mint(deployer.address, deployer.address, '1', '1')).to.be.revertedWith(
CALLER_MUST_BE_POOL
);
});

it('Tries to invoke burn not being the Pool (revert expected)', async () => {
Expand Down

0 comments on commit 1358948

Please sign in to comment.