Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use permissionless rescuable #20

Merged
merged 1 commit into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/solidity-utils
Submodule solidity-utils updated 29 files
+2 −2 .env.example
+7 −0 .github/workflows/merge-main.yml
+7 −0 .github/workflows/tests.yml
+1 −0 Makefile
+10 −9 foundry.toml
+0 −75 src-zksync/contracts/transparent-proxy/TransparentProxyFactoryZkSync.sol
+0 −22 src-zksync/contracts/transparent-proxy/interfaces/ITransparentProxyFactoryZkSync.sol
+1 −1 src/contracts/access-control/UpgradeableOwnableWithGuardian.sol
+4 −30 src/contracts/transparent-proxy/TransparentProxyFactory.sol
+19 −2 src/contracts/transparent-proxy/TransparentProxyFactoryBase.sol
+6 −6 src/contracts/utils/ChainHelpers.sol
+21 −0 src/contracts/utils/PermissionlessRescuable.sol
+4 −11 src/contracts/utils/Rescuable.sol
+32 −0 src/contracts/utils/RescuableBase.sol
+4 −0 src/contracts/utils/ScriptUtils.sol
+30 −0 src/contracts/utils/interfaces/IPermissionlessRescuable.sol
+3 −23 src/contracts/utils/interfaces/IRescuable.sol
+38 −0 src/contracts/utils/interfaces/IRescuableBase.sol
+4 −0 src/mocks/ERC20.sol
+127 −0 test/PermissionlessRescuable.t.sol
+10 −1 test/Rescuable.t.sol
+7 −0 test/Rescuable721.t.sol
+6 −6 test/UpgradeableOwnableWithGuardian.sol
+13 −0 zksync/script/DeployTransparentProxyFactoryZkSync.s.sol
+60 −0 zksync/src/contracts/transparent-proxy/TransparentProxyFactoryZkSync.sol
+10 −0 zksync/src/contracts/transparent-proxy/interfaces/ITransparentProxyFactoryZkSync.sol
+113 −0 zksync/src/contracts/utils/ScriptUtilsZkSync.sol
+10 −0 zksync/src/contracts/utils/interfaces/ICreate2Factory.sol
+5 −5 zksync/test/TransparentProxyFactoryZkSync.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
pragma solidity ^0.8.0;

import '../../interfaces/IMarketReportTypes.sol';
import {TransparentProxyFactory, ITransparentProxyFactory} from 'solidity-utils/contracts/transparent-proxy/TransparentProxyFactory.sol';
import {ITransparentProxyFactory} from 'solidity-utils/contracts/transparent-proxy/interfaces/ITransparentProxyFactory.sol';
import {TransparentProxyFactory} from 'solidity-utils/contracts/transparent-proxy/TransparentProxyFactory.sol';
import {StataTokenV2} from 'aave-v3-periphery/contracts/static-a-token/StataTokenV2.sol';
import {StataTokenFactory} from 'aave-v3-periphery/contracts/static-a-token/StataTokenFactory.sol';
import {IErrors} from '../../interfaces/IErrors.sol';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ abstract contract ERC4626StataTokenUpgradeable is ERC4626Upgradeable, IERC4626St
}

///@inheritdoc IERC4626StataToken
function aToken() external view returns (IERC20) {
function aToken() public view returns (IERC20) {
ERC4626StataTokenStorage storage $ = _getERC4626StataTokenStorage();
return $._aToken;
}
Expand Down
29 changes: 23 additions & 6 deletions src/periphery/contracts/static-a-token/StataTokenV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ pragma solidity ^0.8.0;

import {ERC20Upgradeable, ERC20PermitUpgradeable} from 'openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC20PermitUpgradeable.sol';
import {PausableUpgradeable} from 'openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol';
import {IRescuable, Rescuable} from 'solidity-utils/contracts/utils/Rescuable.sol';
import {IPermissionlessRescuable, PermissionlessRescuable} from 'solidity-utils/contracts/utils/PermissionlessRescuable.sol';
import {IRescuableBase, RescuableBase} from 'solidity-utils/contracts/utils/RescuableBase.sol';

import {IACLManager} from '../../../core/contracts/interfaces/IACLManager.sol';
import {ERC4626Upgradeable, ERC4626StataTokenUpgradeable, IPool} from './ERC4626StataTokenUpgradeable.sol';
import {ERC4626Upgradeable, ERC4626StataTokenUpgradeable, IPool, Math, IERC20} from './ERC4626StataTokenUpgradeable.sol';
import {ERC20AaveLMUpgradeable, IRewardsController} from './ERC20AaveLMUpgradeable.sol';
import {IStataTokenV2} from './interfaces/IStataTokenV2.sol';
import {IAToken} from './interfaces/IAToken.sol';

/**
* @title StataTokenV2
Expand All @@ -20,9 +22,11 @@ contract StataTokenV2 is
ERC20AaveLMUpgradeable,
ERC4626StataTokenUpgradeable,
PausableUpgradeable,
Rescuable,
PermissionlessRescuable,
IStataTokenV2
{
using Math for uint256;

constructor(
IPool pool,
IRewardsController rewardsController
Expand Down Expand Up @@ -53,9 +57,22 @@ contract StataTokenV2 is
else _unpause();
}

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

/// @inheritdoc IRescuableBase
function maxRescue(
address asset
) public view override(IRescuableBase, RescuableBase) returns (uint256) {
IERC20 cachedAToken = aToken();
if (asset == address(cachedAToken)) {
uint256 requiredBacking = _convertToAssets(totalSupply(), Math.Rounding.Ceil);
uint256 balance = cachedAToken.balanceOf(address(this));
return balance > requiredBacking ? balance - requiredBacking : 0;
}
return type(uint256).max;
}

///@inheritdoc IStataTokenV2
Expand Down
2 changes: 2 additions & 0 deletions src/periphery/contracts/static-a-token/interfaces/IAToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ interface IAToken {

function UNDERLYING_ASSET_ADDRESS() external view returns (address);

function RESERVE_TREASURY_ADDRESS() external view returns (address);

/**
* @notice Returns the scaled total supply of the scaled balance token. Represents sum(debt/index)
* @return The scaled total supply
Expand Down
44 changes: 25 additions & 19 deletions tests/periphery/static-a-token/StataTokenV2Rescuable.sol
Original file line number Diff line number Diff line change
@@ -1,31 +1,37 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

import {IRescuable} from 'solidity-utils/contracts/utils/Rescuable.sol';
import {IAToken} from '../../../src/periphery/contracts/static-a-token/StataTokenV2.sol';
import {BaseTest} from './TestBase.sol';

contract StataTokenV2RescuableTest is BaseTest {
function test_whoCanRescue() external view {
assertEq(IRescuable(address(stataTokenV2)).whoCanRescue(), poolAdmin);
}
event ERC20Rescued(
address indexed caller,
address indexed token,
address indexed to,
uint256 amount
);

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

function test_rescuable_shouldSuceedForOwner() external {
deal(tokenList.usdx, address(stataTokenV2), 1 ether);
vm.startPrank(poolAdmin);
IRescuable(address(stataTokenV2)).emergencyTokenTransfer(
tokenList.usdx,
address(this),
1 ether
);
function test_rescuable_shouldWorkForAToken() external {
_fundAToken(1 ether, address(stataTokenV2));
stataTokenV2.emergencyTokenTransfer(aToken, 1 ether);
}

function test_rescuable_shouldNotCauseInsolvency(uint256 donation, uint256 stake) external {
vm.assume(donation != 0 && donation <= type(uint96).max);
vm.assume(stake != 0 && stake <= type(uint96).max);
_fundAToken(donation, address(stataTokenV2));
_fund4626(stake, address(this));

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);
}
}
2 changes: 1 addition & 1 deletion tests/periphery/static-a-token/TestBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.10;

import {IERC20Metadata, IERC20} from 'openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import {TransparentUpgradeableProxy} from 'solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol';
import {ITransparentProxyFactory} from 'solidity-utils/contracts/transparent-proxy/TransparentProxyFactory.sol';
import {ITransparentProxyFactory} from 'solidity-utils/contracts/transparent-proxy/interfaces/ITransparentProxyFactory.sol';
import {StataTokenFactory} from '../../../src/periphery/contracts/static-a-token/StataTokenFactory.sol';
import {StataTokenV2} from '../../../src/periphery/contracts/static-a-token/StataTokenV2.sol';
import {IERC20AaveLM} from '../../../src/periphery/contracts/static-a-token/interfaces/IERC20AaveLM.sol';
Expand Down