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

Atoken miscellaneous cleanup #27

Merged
merged 6 commits into from
Jan 9, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
185 changes: 95 additions & 90 deletions src/ATokenFlexVoting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,32 @@ import {WadRayMath} from "aave-v3-core/contracts/protocol/libraries/math/WadRayM
import {SafeCast} from "openzeppelin-contracts/contracts/utils/math/SafeCast.sol";
import {Math} from "openzeppelin-contracts/contracts/utils/math/Math.sol";
import {Checkpoints} from "openzeppelin-contracts/contracts/utils/Checkpoints.sol";
import {IFractionalGovernor} from "src/interfaces/IFractionalGovernor.sol";
import {IVotingToken} from "src/interfaces/IVotingToken.sol";
// forgefmt: disable-end

interface IFractionalGovernor {
function token() external returns (address);
function proposalSnapshot(uint256 proposalId) external view returns (uint256);
function proposalDeadline(uint256 proposalId) external view returns (uint256);
function castVoteWithReasonAndParams(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params
) external returns (uint256);
}

interface IVotingToken {
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function delegate(address delegatee) external;
function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
}

/// @notice This is an extension of Aave V3's AToken contract which makes it possible for AToken
/// holders to still vote on governance proposals. This way, holders of governance tokens do not
/// have to choose between earning yield on Aave and voting. They can do both.
///
/// AToken holders are able to call `expressVote` to signal their preference on open governance
/// proposals. When they do so, this extension records that preference with weight proportional to
/// the users's AToken balance at the proposal snapshot.
///
/// When the proposal deadline nears, the AToken's public `castVote` function is called to roll up
/// all internal voting records into a single delegated vote to the Governor contract -- a vote
/// which specifies the exact For/Abstain/Against totals expressed by AToken holders.
///
/// This extension has the following requirements:
/// (a) the underlying token be a governance token
/// (b) the related governor contract supports flexible voting (see GovernorCountingFractional)
///
/// Participating in governance via AToken voting is completely optional. Users otherwise still
/// supply, borrow, and hold tokens with Aave as usual.
///
/// The original AToken that this contract extends is viewable here:
///
/// https://github.com/aave/aave-v3-core/blob/c38c627683c0db0449b7c9ea2fbd68bde3f8dc01/contracts/protocol/tokenization/AToken.sol
contract ATokenFlexVoting is AToken {
using WadRayMath for uint256;
using SafeCast for uint256;
Expand Down Expand Up @@ -90,6 +95,77 @@ contract ATokenFlexVoting is AToken {
CAST_VOTE_WINDOW = _castVoteWindow;
}

// forgefmt: disable-start
//===========================================================================
// BEGIN: Aave overrides
//===========================================================================
/// Note: this has been modified from Aave v3's AToken to delegate voting
/// power to itself during initialization.
///
/// @inheritdoc AToken
function initialize(
IPool initializingPool,
address treasury,
address underlyingAsset,
IAaveIncentivesController incentivesController,
uint8 aTokenDecimals,
string calldata aTokenName,
string calldata aTokenSymbol,
bytes calldata params
) public override initializer {
AToken.initialize(
initializingPool,
treasury,
underlyingAsset,
incentivesController,
aTokenDecimals,
aTokenName,
aTokenSymbol,
params
);

selfDelegate();
}

/// Note: this has been modified from Aave v3's MintableIncentivizedERC20 to
/// checkpoint raw balances accordingly.
///
/// @inheritdoc MintableIncentivizedERC20
function _burn(address account, uint128 amount) internal override {
MintableIncentivizedERC20._burn(account, amount);
_checkpointRawBalanceOf(account);
totalDepositCheckpoints.push(totalDepositCheckpoints.latest() - amount);
}

/// Note: this has been modified from Aave v3's MintableIncentivizedERC20 to
/// checkpoint raw balances accordingly.
///
/// @inheritdoc MintableIncentivizedERC20
function _mint(address account, uint128 amount) internal override {
MintableIncentivizedERC20._mint(account, amount);
_checkpointRawBalanceOf(account);
totalDepositCheckpoints.push(totalDepositCheckpoints.latest() + amount);
}

/// Note: this has been modified from Aave v3's AToken contract to
/// checkpoint raw balances accordingly.
///
/// @inheritdoc AToken
function _transfer(
address from,
address to,
uint256 amount,
bool validate
) internal virtual override {
AToken._transfer(from, to, amount, validate);
_checkpointRawBalanceOf(from);
_checkpointRawBalanceOf(to);
}
//===========================================================================
// END: Aave overrides
//===========================================================================
// forgefmt: disable-end

// Self-delegation cannot be done in the constructor because the aToken is
// just a proxy -- it won't share an address with the implementation (i.e.
// this code). Instead we do it at the end of `initialize`. But even that won't
Expand Down Expand Up @@ -226,75 +302,4 @@ contract ATokenFlexVoting is AToken {
function getPastTotalBalances(uint256 _blockNumber) public view returns (uint256) {
return totalDepositCheckpoints.getAtProbablyRecentBlock(_blockNumber);
}

// forgefmt: disable-start
//===========================================================================
// BEGIN: Aave overrides
//===========================================================================
/// Note: this has been modified from Aave v3's AToken to delegate voting
/// power to itself during initialization.
///
/// @inheritdoc AToken
function initialize(
IPool initializingPool,
address treasury,
address underlyingAsset,
IAaveIncentivesController incentivesController,
uint8 aTokenDecimals,
string calldata aTokenName,
string calldata aTokenSymbol,
bytes calldata params
) public override initializer {
AToken.initialize(
initializingPool,
treasury,
underlyingAsset,
incentivesController,
aTokenDecimals,
aTokenName,
aTokenSymbol,
params
);

selfDelegate();
}

/// Note: this has been modified from Aave v3's MintableIncentivizedERC20 to
/// checkpoint raw balances accordingly.
///
/// @inheritdoc MintableIncentivizedERC20
function _burn(address account, uint128 amount) internal override {
MintableIncentivizedERC20._burn(account, amount);
_checkpointRawBalanceOf(account);
totalDepositCheckpoints.push(totalDepositCheckpoints.latest() - amount);
}

/// Note: this has been modified from Aave v3's MintableIncentivizedERC20 to
/// checkpoint raw balances accordingly.
///
/// @inheritdoc MintableIncentivizedERC20
function _mint(address account, uint128 amount) internal override {
MintableIncentivizedERC20._mint(account, amount);
_checkpointRawBalanceOf(account);
totalDepositCheckpoints.push(totalDepositCheckpoints.latest() + amount);
}

/// Note: this has been modified from Aave v3's AToken contract to
/// checkpoint raw balances accordingly.
///
/// @inheritdoc AToken
function _transfer(
address from,
address to,
uint256 amount,
bool validate
) internal virtual override {
AToken._transfer(from, to, amount, validate);
_checkpointRawBalanceOf(from);
_checkpointRawBalanceOf(to);
}
//===========================================================================
// END: Aave overrides
//===========================================================================
// forgefmt: disable-end
}
22 changes: 3 additions & 19 deletions src/FractionalPool.sol
Original file line number Diff line number Diff line change
@@ -1,26 +1,10 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
pragma solidity ^0.8.10;

import "openzeppelin-contracts/contracts/utils/math/SafeCast.sol";
import "openzeppelin-contracts/contracts/utils/math/Math.sol";

interface IFractionalGovernor {
function proposalSnapshot(uint256 proposalId) external returns (uint256);
function proposalDeadline(uint256 proposalId) external view returns (uint256);
function castVoteWithReasonAndParams(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params
) external returns (uint256);
}

interface IVotingToken {
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function delegate(address delegatee) external;
function getPastVotes(address account, uint256 blockNumber) external returns (uint256);
}
import {IFractionalGovernor} from "src/interfaces/IFractionalGovernor.sol";
import {IVotingToken} from "src/interfaces/IVotingToken.sol";

/// @notice A proof-of-concept implementation demonstrating how Flexible Voting can be used to
/// allow holders of governance tokens to use them in DeFi but still participate in governance. The
Expand Down
15 changes: 15 additions & 0 deletions src/interfaces/IFractionalGovernor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: UNLICENSED
mds1 marked this conversation as resolved.
Show resolved Hide resolved
pragma solidity 0.8.10;

/// @dev The interface that flexible voting-compatbile governors are expected to support.
interface IFractionalGovernor {
function token() external returns (address);
function proposalSnapshot(uint256 proposalId) external view returns (uint256);
function proposalDeadline(uint256 proposalId) external view returns (uint256);
function castVoteWithReasonAndParams(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params
) external returns (uint256);
}
10 changes: 10 additions & 0 deletions src/interfaces/IVotingToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;

/// @dev The interface that flexible voting-compatible voting tokens are expected to support.
interface IVotingToken {
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function delegate(address delegatee) external;
function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
}
2 changes: 1 addition & 1 deletion test/FractionalPool.t.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
pragma solidity ^0.8.10;

import {Test} from "forge-std/Test.sol";
import {Vm} from "forge-std/Vm.sol";
Expand Down