Skip to content

Commit

Permalink
Merge branch 'main' into certora. Syncing with main of aave-dao
Browse files Browse the repository at this point in the history
  • Loading branch information
nisnislevi committed Aug 26, 2024
2 parents 326b49c + 6948864 commit f7213cd
Show file tree
Hide file tree
Showing 14 changed files with 784 additions and 73 deletions.
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ optimizer_runs = 200
solc='0.8.19'
evm_version = 'paris'
bytecode_hash = 'none'
ignored_warnings_from = ["src/periphery/contracts/treasury/RevenueSplitter.sol"]
out = 'out'
libs = ['lib']
remappings = [
Expand Down
79 changes: 49 additions & 30 deletions src/deployments/contracts/procedures/AaveV3SetupProcedure.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ import {IEmissionManager} from 'aave-v3-periphery/contracts/rewards/interfaces/I
import {IRewardsController} from 'aave-v3-periphery/contracts/rewards/interfaces/IRewardsController.sol';

contract AaveV3SetupProcedure {
struct AddressProviderInput {
InitialReport initialReport;
address poolImplementation;
address poolConfiguratorImplementation;
address protocolDataProvider;
address poolAdmin;
address aaveOracle;
address rewardsControllerProxy;
address rewardsControllerImplementation;
address priceOracleSentinel;
}

function _initialDeployment(
address providerRegistry,
address marketOwner,
Expand Down Expand Up @@ -44,14 +56,17 @@ contract AaveV3SetupProcedure {
_validateMarketSetup(roles);

SetupReport memory report = _setupPoolAddressesProvider(
initialReport,
poolImplementation,
poolConfiguratorImplementation,
protocolDataProvider,
roles.poolAdmin,
aaveOracle,
rewardsControllerImplementation,
priceOracleSentinel
AddressProviderInput(
initialReport,
poolImplementation,
poolConfiguratorImplementation,
protocolDataProvider,
roles.poolAdmin,
aaveOracle,
config.incentivesProxy,
rewardsControllerImplementation,
priceOracleSentinel
)
);

report.aclManager = _setupACL(
Expand Down Expand Up @@ -90,38 +105,42 @@ contract AaveV3SetupProcedure {
}

function _setupPoolAddressesProvider(
InitialReport memory initialReport,
address poolImplementation,
address poolConfiguratorImplementation,
address protocolDataProvider,
address poolAdmin,
address aaveOracle,
address rewardsControllerImplementation,
address priceOracleSentinel
AddressProviderInput memory input
) internal returns (SetupReport memory) {
SetupReport memory report;

IPoolAddressesProvider provider = IPoolAddressesProvider(initialReport.poolAddressesProvider);
provider.setPriceOracle(aaveOracle);
provider.setPoolImpl(poolImplementation);
provider.setPoolConfiguratorImpl(poolConfiguratorImplementation);
provider.setPoolDataProvider(protocolDataProvider);
IPoolAddressesProvider provider = IPoolAddressesProvider(
input.initialReport.poolAddressesProvider
);
provider.setPriceOracle(input.aaveOracle);
provider.setPoolImpl(input.poolImplementation);
provider.setPoolConfiguratorImpl(input.poolConfiguratorImplementation);
provider.setPoolDataProvider(input.protocolDataProvider);

report.poolProxy = address(provider.getPool());
report.poolConfiguratorProxy = address(provider.getPoolConfigurator());

if (priceOracleSentinel != address(0)) {
provider.setPriceOracleSentinel(priceOracleSentinel);
if (input.priceOracleSentinel != address(0)) {
provider.setPriceOracleSentinel(input.priceOracleSentinel);
}

bytes32 controllerId = keccak256('INCENTIVES_CONTROLLER');
provider.setAddressAsProxy(controllerId, rewardsControllerImplementation);
report.rewardsControllerProxy = provider.getAddress(controllerId);
IEmissionManager emissionManager = IEmissionManager(
IRewardsController(report.rewardsControllerProxy).EMISSION_MANAGER()
);
emissionManager.setRewardsController(report.rewardsControllerProxy);
IOwnable(address(emissionManager)).transferOwnership(poolAdmin);
if (input.rewardsControllerProxy == address(0)) {
require(
input.rewardsControllerImplementation != address(0),
'rewardsControllerImplementation must be set'
);
provider.setAddressAsProxy(controllerId, input.rewardsControllerImplementation);
report.rewardsControllerProxy = provider.getAddress(controllerId);
IEmissionManager emissionManager = IEmissionManager(
IRewardsController(report.rewardsControllerProxy).EMISSION_MANAGER()
);
emissionManager.setRewardsController(report.rewardsControllerProxy);
IOwnable(address(emissionManager)).transferOwnership(input.poolAdmin);
} else {
provider.setAddress(controllerId, input.rewardsControllerProxy);
report.rewardsControllerProxy = provider.getAddress(controllerId);
}
return report;
}

Expand Down
20 changes: 2 additions & 18 deletions src/deployments/contracts/procedures/AaveV3TreasuryProcedure.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import '../../interfaces/IMarketReportTypes.sol';
contract AaveV3TreasuryProcedure {
struct TreasuryReport {
address treasuryImplementation;
address proxyAdmin;
address treasury;
}

Expand All @@ -26,20 +25,12 @@ contract AaveV3TreasuryProcedure {
if (salt != '') {
Collector treasuryImplementation = new Collector{salt: salt}();
treasuryImplementation.initialize(address(0), 0);

treasuryReport.treasuryImplementation = address(treasuryImplementation);

if (deployedProxyAdmin == address(0)) {
treasuryReport.proxyAdmin = address(new ProxyAdmin{salt: salt}());
IOwnable(treasuryReport.proxyAdmin).transferOwnership(treasuryOwner);
} else {
treasuryReport.proxyAdmin = deployedProxyAdmin;
}

treasuryReport.treasury = address(
new TransparentUpgradeableProxy{salt: salt}(
treasuryReport.treasuryImplementation,
treasuryReport.proxyAdmin,
deployedProxyAdmin,
abi.encodeWithSelector(
treasuryImplementation.initialize.selector,
address(treasuryOwner),
Expand All @@ -52,17 +43,10 @@ contract AaveV3TreasuryProcedure {
treasuryImplementation.initialize(address(0), 0);
treasuryReport.treasuryImplementation = address(treasuryImplementation);

if (deployedProxyAdmin == address(0)) {
treasuryReport.proxyAdmin = address(new ProxyAdmin());
IOwnable(treasuryReport.proxyAdmin).transferOwnership(treasuryOwner);
} else {
treasuryReport.proxyAdmin = deployedProxyAdmin;
}

treasuryReport.treasury = address(
new TransparentUpgradeableProxy(
treasuryReport.treasuryImplementation,
treasuryReport.proxyAdmin,
deployedProxyAdmin,
abi.encodeWithSelector(
treasuryImplementation.initialize.selector,
address(treasuryOwner),
Expand Down
6 changes: 6 additions & 0 deletions src/deployments/interfaces/IMarketReportTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ struct MarketReport {
address staticATokenFactoryImplementation;
address staticATokenFactoryProxy;
address staticATokenImplementation;
address revenueSplitter;
}

struct LibrariesReport {
Expand Down Expand Up @@ -123,6 +124,10 @@ struct MarketConfig {
address proxyAdmin;
uint128 flashLoanPremiumTotal;
uint128 flashLoanPremiumToProtocol;
address incentivesProxy;
address treasury; // let empty for deployment of collector, otherwise reuse treasury address
address treasuryPartner; // let empty for single treasury, or add treasury partner for revenue split between two organizations.
uint16 treasurySplitPercent; // ignored if treasuryPartner is empty, otherwise the split percent for the first treasury (recipientA, values between 00_01 and 100_00)
}

struct DeployFlags {
Expand Down Expand Up @@ -176,6 +181,7 @@ struct PeripheryReport {
address treasuryImplementation;
address emissionManager;
address rewardsControllerImplementation;
address revenueSplitter;
}

struct ParaswapReport {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,18 @@ library AaveV3BatchOrchestration {
PeripheryReport memory peripheryReport,
AaveV3TokensBatch.TokensReport memory tokensReport
) internal returns (ConfigEngineReport memory) {
address treasury = peripheryReport.treasury;
if (peripheryReport.revenueSplitter != address(0)) {
treasury = peripheryReport.revenueSplitter;
}

AaveV3HelpersBatchOne helpersBatchOne = new AaveV3HelpersBatchOne(
setupReport.poolProxy,
setupReport.poolConfiguratorProxy,
miscReport.defaultInterestRateStrategy,
peripheryReport.aaveOracle,
setupReport.rewardsControllerProxy,
peripheryReport.treasury,
treasury,
tokensReport.aToken,
tokensReport.variableDebtToken,
tokensReport.stableDebtToken
Expand Down Expand Up @@ -328,6 +333,7 @@ library AaveV3BatchOrchestration {
report.staticATokenFactoryProxy = staticATokenReport.staticATokenFactoryProxy;
report.staticATokenImplementation = staticATokenReport.staticATokenImplementation;
report.transparentProxyFactory = staticATokenReport.transparentProxyFactory;
report.revenueSplitter = peripheryReport.revenueSplitter;

return report;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {AaveV3TreasuryProcedure} from '../../../contracts/procedures/AaveV3Treas
import {AaveV3OracleProcedure} from '../../../contracts/procedures/AaveV3OracleProcedure.sol';
import {AaveV3IncentiveProcedure} from '../../../contracts/procedures/AaveV3IncentiveProcedure.sol';
import {AaveV3DefaultRateStrategyProcedure} from '../../../contracts/procedures/AaveV3DefaultRateStrategyProcedure.sol';
import {IOwnable} from 'solidity-utils/contracts/transparent-proxy/interfaces/IOwnable.sol';
import '../../../interfaces/IMarketReportTypes.sol';
import {IRewardsController} from '../../../../periphery/contracts/rewards/interfaces/IRewardsController.sol';
import {IOwnable} from 'solidity-utils/contracts/transparent-proxy/interfaces/IOwnable.sol';
import {RevenueSplitter} from '../../../../periphery/contracts/treasury/RevenueSplitter.sol';

contract AaveV3PeripheryBatch is
AaveV3TreasuryProcedure,
Expand All @@ -20,19 +24,45 @@ contract AaveV3PeripheryBatch is
address poolAddressesProvider,
address setupBatch
) {
TreasuryReport memory treasuryReport = _deployAaveV3Treasury(
poolAdmin,
config.proxyAdmin,
config.salt
);
if (config.proxyAdmin == address(0)) {
_report.proxyAdmin = address(new ProxyAdmin{salt: config.salt}());
IOwnable(_report.proxyAdmin).transferOwnership(poolAdmin);
} else {
_report.proxyAdmin = config.proxyAdmin;
}

_report.aaveOracle = _deployAaveOracle(config.oracleDecimals, poolAddressesProvider);
_report.proxyAdmin = treasuryReport.proxyAdmin;
_report.treasury = treasuryReport.treasury;
_report.treasuryImplementation = treasuryReport.treasuryImplementation;

(_report.emissionManager, _report.rewardsControllerImplementation) = _deployIncentives(
setupBatch
);
if (config.treasury == address(0)) {
TreasuryReport memory treasuryReport = _deployAaveV3Treasury(
poolAdmin,
_report.proxyAdmin,
config.salt
);

_report.treasury = treasuryReport.treasury;
_report.treasuryImplementation = treasuryReport.treasuryImplementation;
} else {
_report.treasury = config.treasury;
}

if (
config.treasuryPartner != address(0) &&
config.treasurySplitPercent > 0 &&
config.treasurySplitPercent < 100_00
) {
_report.revenueSplitter = address(
new RevenueSplitter(_report.treasury, config.treasuryPartner, config.treasurySplitPercent)
);
}

if (config.incentivesProxy == address(0)) {
(_report.emissionManager, _report.rewardsControllerImplementation) = _deployIncentives(
setupBatch
);
} else {
_report.emissionManager = IRewardsController(config.incentivesProxy).getEmissionManager();
}
}

function getPeripheryReport() external view returns (PeripheryReport memory) {
Expand Down
43 changes: 43 additions & 0 deletions src/periphery/contracts/treasury/IRevenueSplitter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from 'aave-v3-core/contracts/dependencies/openzeppelin/contracts/IERC20.sol';

interface IRevenueSplitterErrors {
error InvalidPercentSplit();
}

/// @title IRevenueSplitter
/// @notice Interface for RevenueSplitter contract
/// @dev The `RevenueSplitter` is a state-less non-upgradeable contract that supports 2 recipients (A and B), and defines the percentage split of the recipient A, with a value between 1 and 99_99.
/// The `RevenueSplitter` contract must be attached to the `AaveV3ConfigEngine` as `treasury`, making new listings to use `RevenueSplitter` as treasury (instead of `Collector` ) at the `AToken` initialization, making all revenue managed by ATokens redirected to the RevenueSplitter contract.
/// Once parties want to share their revenue, anyone can call `function splitRevenue(IERC20[] memory tokens)` to check the accrued ERC20 balance inside this contract, and split the amounts between the two recipients.
/// It also supports split of native currency via `function splitNativeRevenue() external`, in case the instance receives native currency.
///
/// Warning: For recipients, you can use any address, but preferable to use `Collector`, a Safe smart contract multisig or a smart contract that can handle both ERC20 and native transfers, to prevent balances to be locked.
interface IRevenueSplitter is IRevenueSplitterErrors {
/// @notice Split token balances in RevenueSplitter and transfer between two recipients
/// @param tokens List of tokens to check balance and split amounts
/// @dev Specs:
/// - Does not revert if token balance is zero (no-op).
/// - Rounds in favor of RECIPIENT_B (1 wei round).
/// - Anyone can call this function anytime.
/// - This method will always send ERC20 tokens to recipients, even if the recipients does NOT support the ERC20 interface. At deployment time is recommended to ensure both recipients can handle ERC20 and native transfers via e2e tests.
function splitRevenue(IERC20[] memory tokens) external;

/// @notice Split native currency in RevenueSplitter and transfer between two recipients
/// @dev Specs:
/// - Does not revert if native balance is zero (no-op)
/// - Rounds in favor of RECIPIENT_B (1 wei round).
/// - Anyone can call this function anytime.
/// - This method will always send native currency to recipients, and does NOT revert if one or both recipients doesn't support handling native currency. At deployment time is recommended to ensure both recipients can handle ERC20 and native transfers via e2e tests.
/// - If one recipient can not receive native currency, repeatedly calling the function will rescue/drain the funds of the second recipient (50% per call), allowing manual recovery of funds.
function splitNativeRevenue() external;

function RECIPIENT_A() external view returns (address payable);

function RECIPIENT_B() external view returns (address payable);

/// @dev Percentage of the split that goes to RECIPIENT_A, the diff goes to RECIPIENT_B, from 1 to 99_99
function SPLIT_PERCENTAGE_RECIPIENT_A() external view returns (uint16);
}
Loading

0 comments on commit f7213cd

Please sign in to comment.