Skip to content

Commit

Permalink
fix: resolve merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
sakulstra committed Sep 19, 2024
2 parents dd50736 + 105baaf commit 4beb9b8
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 31 deletions.
Binary file modified audits/11-09-2024_Certora_StataTokenV2.pdf
Binary file not shown.
20 changes: 10 additions & 10 deletions certora/stata/specs/StataToken/StataToken.spec
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ import "../methods/methods_base.spec";
f.contract == currentContract &&
!harnessOnlyMethods(f) &&
f.selector != sig:initialize(address, string, string).selector) &&
f.selector != sig:emergencyEtherTransfer(uint256).selector &&
f.selector != sig:emergencyTokenTransfer(address,uint256).selector
f.selector != sig:emergencyEtherTransfer(address,uint256).selector &&
f.selector != sig:emergencyTokenTransfer(address,address,uint256).selector
} {
// Assuming single reward
single_RewardToken_setup();
Expand Down Expand Up @@ -137,7 +137,7 @@ import "../methods/methods_base.spec";
&& !harnessMethodsMinusHarnessClaimMethods(f)
&& !claimFunctions(f)
&& f.selector != sig:claimDoubleRewardOnBehalfSame(address, address, address).selector
&& f.selector != sig:emergencyEtherTransfer(uint256).selector
&& f.selector != sig:emergencyEtherTransfer(address,uint256).selector
}
{
preserved redeem(uint256 shares, address receiver, address owner) with (env e1) {
Expand All @@ -152,7 +152,7 @@ import "../methods/methods_base.spec";
requireInvariant solvency_total_asset_geq_total_supply();
require balanceOf(owner) <= totalSupply();
}
preserved emergencyTokenTransfer(address asset, uint256 amount) with (env e3) {
preserved emergencyTokenTransfer(address asset, address to, uint256 amount) with (env e3) {
require rate() >= RAY();
}
}
Expand All @@ -170,7 +170,7 @@ import "../methods/methods_base.spec";
f.contract == currentContract
&& !harnessMethodsMinusHarnessClaimMethods(f)
&& !claimFunctions(f)
&& f.selector != sig:emergencyEtherTransfer(uint256).selector
&& f.selector != sig:emergencyEtherTransfer(address,uint256).selector
&& f.selector != sig:claimDoubleRewardOnBehalfSame(address, address, address).selector }
{
preserved withdraw(uint256 assets, address receiver, address owner) with (env e3) {
Expand Down Expand Up @@ -198,7 +198,7 @@ import "../methods/methods_base.spec";
preserved redeemATokens(uint256 shares, address receiver, address owner) with (env e2) {
require balanceOf(owner) <= totalSupply();
}
preserved emergencyTokenTransfer(address asset, uint256 amount) with (env e1) {
preserved emergencyTokenTransfer(address asset, address to, uint256 amount) with (env e1) {
require rate() >= RAY();
}
}
Expand All @@ -216,7 +216,7 @@ import "../methods/methods_base.spec";
=> (_RewardsController.getUserAccruedReward(_asset, reward, user) == _RewardsController.getUserAccruedRewards(reward, user)))
filtered {f ->
f.contract == currentContract &&
f.selector != sig:emergencyEtherTransfer(uint256).selector &&
f.selector != sig:emergencyEtherTransfer(address,uint256).selector &&
!harnessOnlyMethods(f)
}
{
Expand Down Expand Up @@ -256,8 +256,8 @@ import "../methods/methods_base.spec";
&& !collectAndUpdateFunction(f)
&& !harnessOnlyMethods(f)
&& f.selector != sig:initialize(address,string,string).selector
&& f.selector != sig:emergencyEtherTransfer(uint256).selector
&& f.selector != sig:emergencyTokenTransfer(address,uint256).selector
&& f.selector != sig:emergencyEtherTransfer(address,uint256).selector
&& f.selector != sig:emergencyTokenTransfer(address,address,uint256).selector
}
{
env e;
Expand Down Expand Up @@ -301,7 +301,7 @@ rule getClaimableRewards_stable(method f)
&& !claimFunctions(f)
&& !collectAndUpdateFunction(f)
&& f.selector != sig:initialize(address,string,string).selector
&& f.selector != sig:emergencyEtherTransfer(uint256).selector
&& f.selector != sig:emergencyEtherTransfer(address,uint256).selector
&& !harnessOnlyMethods(f)
}
{
Expand Down
2 changes: 1 addition & 1 deletion certora/stata/specs/StataToken/aTokenProperties.spec
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ import "../methods/methods_base.spec";
!f.isView &&
f.selector != sig:redeem(uint256,address,address).selector &&
f.selector != sig:redeemATokens(uint256,address,address).selector &&
f.selector != sig:emergencyEtherTransfer(uint256).selector &&
f.selector != sig:emergencyEtherTransfer(address,uint256).selector &&
!harnessOnlyMethods(f)}
{
preserved with (env e){
Expand Down
2 changes: 1 addition & 1 deletion certora/stata/specs/erc4626/erc4626Extended.spec
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ import "../methods/methods_base.spec";
f.contract == currentContract &&
!f.isView &&
!harnessOnlyMethods(f) &&
f.selector != sig:emergencyEtherTransfer(uint256).selector &&
f.selector != sig:emergencyEtherTransfer(address,uint256).selector &&
f.selector != sig:deposit(uint256,address).selector &&
f.selector != sig:depositWithPermit(uint256,address,uint256,IERC4626StataToken.SignatureParams,bool).selector &&
f.selector != sig:withdraw(uint256,address,address).selector &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ abstract contract BaseParaSwapBuyAdapter is BaseParaSwapAdapter {
* @param maxAmountToSwap Max amount to be swapped
* @param amountToReceive Amount to be received from the swap
* @return amountSold The amount sold during the swap
* @return amountBought The amount bought during the swap
*/
function _buyOnParaSwap(
uint256 toAmountOffset,
Expand All @@ -47,7 +48,7 @@ abstract contract BaseParaSwapBuyAdapter is BaseParaSwapAdapter {
IERC20Detailed assetToSwapTo,
uint256 maxAmountToSwap,
uint256 amountToReceive
) internal returns (uint256 amountSold) {
) internal returns (uint256 amountSold, uint256 amountBought) {
(bytes memory buyCalldata, IParaSwapAugustus augustus) = abi.decode(
paraswapData,
(bytes, IParaSwapAugustus)
Expand Down Expand Up @@ -75,7 +76,6 @@ abstract contract BaseParaSwapBuyAdapter is BaseParaSwapAdapter {
uint256 balanceBeforeAssetTo = assetToSwapTo.balanceOf(address(this));

address tokenTransferProxy = augustus.getTokenTransferProxy();
assetToSwapFrom.safeApprove(tokenTransferProxy, 0);
assetToSwapFrom.safeApprove(tokenTransferProxy, maxAmountToSwap);

if (toAmountOffset != 0) {
Expand All @@ -101,12 +101,15 @@ abstract contract BaseParaSwapBuyAdapter is BaseParaSwapAdapter {
}
}

// Reset allowance
assetToSwapFrom.safeApprove(tokenTransferProxy, 0);

uint256 balanceAfterAssetFrom = assetToSwapFrom.balanceOf(address(this));
amountSold = balanceBeforeAssetFrom - balanceAfterAssetFrom;
require(amountSold <= maxAmountToSwap, 'WRONG_BALANCE_AFTER_SWAP');
uint256 amountReceived = assetToSwapTo.balanceOf(address(this)).sub(balanceBeforeAssetTo);
require(amountReceived >= amountToReceive, 'INSUFFICIENT_AMOUNT_RECEIVED');
amountBought = assetToSwapTo.balanceOf(address(this)).sub(balanceBeforeAssetTo);
require(amountBought >= amountToReceive, 'INSUFFICIENT_AMOUNT_RECEIVED');

emit Bought(address(assetToSwapFrom), address(assetToSwapTo), amountSold, amountReceived);
emit Bought(address(assetToSwapFrom), address(assetToSwapTo), amountSold, amountBought);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ abstract contract BaseParaSwapSellAdapter is BaseParaSwapAdapter {
revert(0, returndatasize())
}
}

require(
assetToSwapFrom.balanceOf(address(this)) == balanceBeforeAssetFrom - amountToSwap,
'WRONG_BALANCE_AFTER_SWAP'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ contract ParaSwapRepayAdapter is BaseParaSwapBuyAdapter, ReentrancyGuard {
// Pull aTokens from user
_pullATokenAndWithdraw(address(collateralAsset), msg.sender, collateralAmount, permitSignature);
//buy debt asset using collateral asset
uint256 amountSold = _buyOnParaSwap(
(uint256 amountSold, uint256 amountBought) = _buyOnParaSwap(
buyAllBalanceOffset,
paraswapData,
collateralAsset,
Expand All @@ -127,15 +127,23 @@ contract ParaSwapRepayAdapter is BaseParaSwapBuyAdapter, ReentrancyGuard {

//deposit collateral back in the pool, if left after the swap(buy)
if (collateralBalanceLeft > 0) {
IERC20(collateralAsset).safeApprove(address(POOL), 0);
IERC20(collateralAsset).safeApprove(address(POOL), collateralBalanceLeft);
POOL.deposit(address(collateralAsset), collateralBalanceLeft, msg.sender, 0);
IERC20(collateralAsset).safeApprove(address(POOL), 0);
}

// Repay debt. Approves 0 first to comply with tokens that implement the anti frontrunning approval fix
IERC20(debtAsset).safeApprove(address(POOL), 0);
IERC20(debtAsset).safeApprove(address(POOL), debtRepayAmount);
POOL.repay(address(debtAsset), debtRepayAmount, debtRateMode, msg.sender);
IERC20(debtAsset).safeApprove(address(POOL), 0);

{
//transfer excess of debtAsset back to the user, if any
uint256 debtAssetExcess = amountBought - debtRepayAmount;
if (debtAssetExcess > 0) {
IERC20(debtAsset).safeTransfer(msg.sender, debtAssetExcess);
}
}
}

/**
Expand Down Expand Up @@ -170,7 +178,7 @@ contract ParaSwapRepayAdapter is BaseParaSwapBuyAdapter, ReentrancyGuard {
initiator
);

uint256 amountSold = _buyOnParaSwap(
(uint256 amountSold, uint256 amountBought) = _buyOnParaSwap(
buyAllBalanceOffset,
paraswapData,
collateralAsset,
Expand All @@ -180,9 +188,9 @@ contract ParaSwapRepayAdapter is BaseParaSwapBuyAdapter, ReentrancyGuard {
);

// Repay debt. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix.
IERC20(debtAsset).safeApprove(address(POOL), 0);
IERC20(debtAsset).safeApprove(address(POOL), debtRepayAmount);
POOL.repay(address(debtAsset), debtRepayAmount, rateMode, initiator);
IERC20(debtAsset).safeApprove(address(POOL), 0);

uint256 neededForFlashLoanRepay = amountSold.add(premium);

Expand All @@ -194,6 +202,14 @@ contract ParaSwapRepayAdapter is BaseParaSwapBuyAdapter, ReentrancyGuard {
permitSignature
);

{
//transfer excess of debtAsset back to the user, if any
uint256 debtAssetExcess = amountBought - debtRepayAmount;
if (debtAssetExcess > 0) {
IERC20(debtAsset).safeTransfer(initiator, debtAssetExcess);
}
}

// Repay flashloan. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix.
IERC20(collateralAsset).safeApprove(address(POOL), 0);
IERC20(collateralAsset).safeApprove(address(POOL), collateralAmount.add(premium));
Expand Down
10 changes: 5 additions & 5 deletions src/contracts/extensions/static-a-token/StataTokenV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.0;
import {ERC20Upgradeable, ERC20PermitUpgradeable} from 'openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC20PermitUpgradeable.sol';
import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import {PausableUpgradeable} from 'openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol';
import {IPermissionlessRescuable, PermissionlessRescuable} from 'solidity-utils/contracts/utils/PermissionlessRescuable.sol';
import {IRescuable, Rescuable} from 'solidity-utils/contracts/utils/Rescuable.sol';
import {IRescuableBase, RescuableBase} from 'solidity-utils/contracts/utils/RescuableBase.sol';
import {IERC20Permit} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol';

Expand All @@ -24,7 +24,7 @@ contract StataTokenV2 is
ERC20AaveLMUpgradeable,
ERC4626StataTokenUpgradeable,
PausableUpgradeable,
PermissionlessRescuable,
Rescuable,
IStataTokenV2
{
using Math for uint256;
Expand Down Expand Up @@ -59,9 +59,9 @@ contract StataTokenV2 is
else _unpause();
}

/// @inheritdoc IPermissionlessRescuable
function whoShouldReceiveFunds() public view override returns (address) {
return IAToken(address(aToken())).RESERVE_TREASURY_ADDRESS();
/// @inheritdoc IRescuable
function whoCanRescue() public view override returns (address) {
return POOL_ADDRESSES_PROVIDER.getACLAdmin();
}

/// @inheritdoc IRescuableBase
Expand Down
25 changes: 21 additions & 4 deletions tests/extensions/static-a-token/StataTokenV2Rescuable.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

import {IRescuable} from 'solidity-utils/contracts/utils/Rescuable.sol';
import {IAToken} from '../../../src/contracts/extensions/static-a-token/StataTokenV2.sol';
import {IERC20} from 'openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import {BaseTest} from './TestBase.sol';

contract StataTokenV2RescuableTest is BaseTest {
Expand All @@ -12,14 +14,28 @@ contract StataTokenV2RescuableTest is BaseTest {
uint256 amount
);

function test_rescuable_shouldRevertForInvalidCaller() external {
deal(tokenList.usdx, address(stataTokenV2), 1 ether);
vm.expectRevert('ONLY_RESCUE_GUARDIAN');
IRescuable(address(stataTokenV2)).emergencyTokenTransfer(
tokenList.usdx,
address(this),
1 ether
);
}

function test_rescuable_shouldTransferAssetsToCollector() external {
deal(tokenList.usdx, address(stataTokenV2), 1 ether);
stataTokenV2.emergencyTokenTransfer(tokenList.usdx, 1 ether);
vm.startPrank(poolAdmin);
stataTokenV2.emergencyTokenTransfer(tokenList.usdx, address(this), 1 ether);
assertEq(IERC20(tokenList.usdx).balanceOf(address(this)), 1 ether);
}

function test_rescuable_shouldWorkForAToken() external {
_fundAToken(1 ether, address(stataTokenV2));
stataTokenV2.emergencyTokenTransfer(aToken, 1 ether);
vm.startPrank(poolAdmin);
stataTokenV2.emergencyTokenTransfer(aToken, address(this), 1 ether);
assertApproxEqAbs(IERC20(aToken).balanceOf(address(this)), 1 ether, 1);
}

function test_rescuable_shouldNotCauseInsolvency(uint256 donation, uint256 stake) external {
Expand All @@ -31,7 +47,8 @@ contract StataTokenV2RescuableTest is BaseTest {
address treasury = IAToken(aToken).RESERVE_TREASURY_ADDRESS();

vm.expectEmit(true, true, true, true);
emit ERC20Rescued(address(this), aToken, treasury, donation);
stataTokenV2.emergencyTokenTransfer(aToken, donation + stake);
emit ERC20Rescued(poolAdmin, aToken, address(this), donation);
vm.startPrank(poolAdmin);
stataTokenV2.emergencyTokenTransfer(aToken, address(this), donation + stake);
}
}

0 comments on commit 4beb9b8

Please sign in to comment.