// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;
pragma abicoder v2;
function getChainId() pure returns (uint256) {
uint256 chainId;
assembly {
chainId := chainid()
}
return chainId;
}
function isContract(address account) view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly {
codehash := extcodehash(account)
}
return (codehash != accountHash && codehash != 0x0);
}
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), 'Ownable: caller is not the owner');
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), 'Ownable: new owner is the zero address');
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, 'SafeMath: addition overflow');
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, 'SafeMath: subtraction overflow');
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, 'SafeMath: multiplication overflow');
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, 'SafeMath: division by zero');
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, 'SafeMath: modulo by zero');
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
interface IVotingStrategy {
function getVotingPowerAt(address user, uint256 blockNumber) external view returns (uint256);
}
interface IProposalValidator {
/**
* @dev Called to validate a proposal (e.g when creating new proposal in Governance)
* @param governance Governance Contract
* @param user Address of the proposal creator
* @param blockNumber Block Number against which to make the test (e.g proposal creation block -1).
* @return boolean, true if can be created
**/
function validateCreatorOfProposal(
IAaveGovernanceV2 governance,
address user,
uint256 blockNumber
) external view returns (bool);
/**
* @dev Called to validate the cancellation of a proposal
* @param governance Governance Contract
* @param user Address of the proposal creator
* @param blockNumber Block Number against which to make the test (e.g proposal creation block -1).
* @return boolean, true if can be cancelled
**/
function validateProposalCancellation(
IAaveGovernanceV2 governance,
address user,
uint256 blockNumber
) external view returns (bool);
/**
* @dev Returns whether a user has enough Proposition Power to make a proposal.
* @param governance Governance Contract
* @param user Address of the user to be challenged.
* @param blockNumber Block Number against which to make the challenge.
* @return true if user has enough power
**/
function isPropositionPowerEnough(
IAaveGovernanceV2 governance,
address user,
uint256 blockNumber
) external view returns (bool);
/**
* @dev Returns the minimum Proposition Power needed to create a proposition.
* @param governance Governance Contract
* @param blockNumber Blocknumber at which to evaluate
* @return minimum Proposition Power needed
**/
function getMinimumPropositionPowerNeeded(IAaveGovernanceV2 governance, uint256 blockNumber)
external
view
returns (uint256);
/**
* @dev Returns whether a proposal passed or not
* @param governance Governance Contract
* @param proposalId Id of the proposal to set
* @return true if proposal passed
**/
function isProposalPassed(IAaveGovernanceV2 governance, uint256 proposalId)
external
view
returns (bool);
/**
* @dev Check whether a proposal has reached quorum, ie has enough FOR-voting-power
* Here quorum is not to understand as number of votes reached, but number of for-votes reached
* @param governance Governance Contract
* @param proposalId Id of the proposal to verify
* @return voting power needed for a proposal to pass
**/
function isQuorumValid(IAaveGovernanceV2 governance, uint256 proposalId)
external
view
returns (bool);
/**
* @dev Check whether a proposal has enough extra FOR-votes than AGAINST-votes
* FOR VOTES - AGAINST VOTES > VOTE_DIFFERENTIAL * voting supply
* @param governance Governance Contract
* @param proposalId Id of the proposal to verify
* @return true if enough For-Votes
**/
function isVoteDifferentialValid(IAaveGovernanceV2 governance, uint256 proposalId)
external
view
returns (bool);
/**
* @dev Calculates the minimum amount of Voting Power needed for a proposal to Pass
* @param votingSupply Total number of oustanding voting tokens
* @return voting power needed for a proposal to pass
**/
function getMinimumVotingPowerNeeded(uint256 votingSupply) external view returns (uint256);
/**
* @dev Get proposition threshold constant value
* @return the proposition threshold value (100 <=> 1%)
**/
function PROPOSITION_THRESHOLD() external view returns (uint256);
/**
* @dev Get voting duration constant value
* @return the voting duration value in seconds
**/
function VOTING_DURATION() external view returns (uint256);
/**
* @dev Get the vote differential threshold constant value
* to compare with % of for votes/total supply - % of against votes/total supply
* @return the vote differential threshold value (100 <=> 1%)
**/
function VOTE_DIFFERENTIAL() external view returns (uint256);
/**
* @dev Get quorum threshold constant value
* to compare with % of for votes/total supply
* @return the quorum threshold value (100 <=> 1%)
**/
function MINIMUM_QUORUM() external view returns (uint256);
/**
* @dev precision helper: 100% = 10000
* @return one hundred percents with our chosen precision
**/
function ONE_HUNDRED_WITH_PRECISION() external view returns (uint256);
}
interface IGovernanceStrategy {
/**
* @dev Returns the Proposition Power of a user at a specific block number.
* @param user Address of the user.
* @param blockNumber Blocknumber at which to fetch Proposition Power
* @return Power number
**/
function getPropositionPowerAt(address user, uint256 blockNumber) external view returns (uint256);
/**
* @dev Returns the total supply of Outstanding Proposition Tokens
* @param blockNumber Blocknumber at which to evaluate
* @return total supply at blockNumber
**/
function getTotalPropositionSupplyAt(uint256 blockNumber) external view returns (uint256);
/**
* @dev Returns the total supply of Outstanding Voting Tokens
* @param blockNumber Blocknumber at which to evaluate
* @return total supply at blockNumber
**/
function getTotalVotingSupplyAt(uint256 blockNumber) external view returns (uint256);
/**
* @dev Returns the Vote Power of a user at a specific block number.
* @param user Address of the user.
* @param blockNumber Blocknumber at which to fetch Vote Power
* @return Vote number
**/
function getVotingPowerAt(address user, uint256 blockNumber) external view returns (uint256);
}
interface IExecutorWithTimelock {
/**
* @dev emitted when a new pending admin is set
* @param newPendingAdmin address of the new pending admin
**/
event NewPendingAdmin(address newPendingAdmin);
/**
* @dev emitted when a new admin is set
* @param newAdmin address of the new admin
**/
event NewAdmin(address newAdmin);
/**
* @dev emitted when a new delay (between queueing and execution) is set
* @param delay new delay
**/
event NewDelay(uint256 delay);
/**
* @dev emitted when a new (trans)action is Queued.
* @param actionHash hash of the action
* @param target address of the targeted contract
* @param value wei value of the transaction
* @param signature function signature of the transaction
* @param data function arguments of the transaction or callData if signature empty
* @param executionTime time at which to execute the transaction
* @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target
**/
event QueuedAction(
bytes32 actionHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 executionTime,
bool withDelegatecall
);
/**
* @dev emitted when an action is Cancelled
* @param actionHash hash of the action
* @param target address of the targeted contract
* @param value wei value of the transaction
* @param signature function signature of the transaction
* @param data function arguments of the transaction or callData if signature empty
* @param executionTime time at which to execute the transaction
* @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target
**/
event CancelledAction(
bytes32 actionHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 executionTime,
bool withDelegatecall
);
/**
* @dev emitted when an action is Cancelled
* @param actionHash hash of the action
* @param target address of the targeted contract
* @param value wei value of the transaction
* @param signature function signature of the transaction
* @param data function arguments of the transaction or callData if signature empty
* @param executionTime time at which to execute the transaction
* @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target
* @param resultData the actual callData used on the target
**/
event ExecutedAction(
bytes32 actionHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 executionTime,
bool withDelegatecall,
bytes resultData
);
/**
* @dev Getter of the current admin address (should be governance)
* @return The address of the current admin
**/
function getAdmin() external view returns (address);
/**
* @dev Getter of the current pending admin address
* @return The address of the pending admin
**/
function getPendingAdmin() external view returns (address);
/**
* @dev Getter of the delay between queuing and execution
* @return The delay in seconds
**/
function getDelay() external view returns (uint256);
/**
* @dev Returns whether an action (via actionHash) is queued
* @param actionHash hash of the action to be checked
* keccak256(abi.encode(target, value, signature, data, executionTime, withDelegatecall))
* @return true if underlying action of actionHash is queued
**/
function isActionQueued(bytes32 actionHash) external view returns (bool);
/**
* @dev Checks whether a proposal is over its grace period
* @param governance Governance contract
* @param proposalId Id of the proposal against which to test
* @return true of proposal is over grace period
**/
function isProposalOverGracePeriod(IAaveGovernanceV2 governance, uint256 proposalId)
external
view
returns (bool);
/**
* @dev Getter of grace period constant
* @return grace period in seconds
**/
function GRACE_PERIOD() external view returns (uint256);
/**
* @dev Getter of minimum delay constant
* @return minimum delay in seconds
**/
function MINIMUM_DELAY() external view returns (uint256);
/**
* @dev Getter of maximum delay constant
* @return maximum delay in seconds
**/
function MAXIMUM_DELAY() external view returns (uint256);
/**
* @dev Function, called by Governance, that queue a transaction, returns action hash
* @param target smart contract target
* @param value wei value of the transaction
* @param signature function signature of the transaction
* @param data function arguments of the transaction or callData if signature empty
* @param executionTime time at which to execute the transaction
* @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target
**/
function queueTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 executionTime,
bool withDelegatecall
) external returns (bytes32);
/**
* @dev Function, called by Governance, that cancels a transaction, returns the callData executed
* @param target smart contract target
* @param value wei value of the transaction
* @param signature function signature of the transaction
* @param data function arguments of the transaction or callData if signature empty
* @param executionTime time at which to execute the transaction
* @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target
**/
function executeTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 executionTime,
bool withDelegatecall
) external payable returns (bytes memory);
/**
* @dev Function, called by Governance, that cancels a transaction, returns action hash
* @param target smart contract target
* @param value wei value of the transaction
* @param signature function signature of the transaction
* @param data function arguments of the transaction or callData if signature empty
* @param executionTime time at which to execute the transaction
* @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target
**/
function cancelTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 executionTime,
bool withDelegatecall
) external returns (bytes32);
}
interface IAaveGovernanceV2 {
enum ProposalState {Pending, Canceled, Active, Failed, Succeeded, Queued, Expired, Executed}
struct Vote {
bool support;
uint248 votingPower;
}
struct Proposal {
uint256 id;
address creator;
IExecutorWithTimelock executor;
address[] targets;
uint256[] values;
string[] signatures;
bytes[] calldatas;
bool[] withDelegatecalls;
uint256 startBlock;
uint256 endBlock;
uint256 executionTime;
uint256 forVotes;
uint256 againstVotes;
bool executed;
bool canceled;
address strategy;
bytes32 ipfsHash;
mapping(address => Vote) votes;
}
struct ProposalWithoutVotes {
uint256 id;
address creator;
IExecutorWithTimelock executor;
address[] targets;
uint256[] values;
string[] signatures;
bytes[] calldatas;
bool[] withDelegatecalls;
uint256 startBlock;
uint256 endBlock;
uint256 executionTime;
uint256 forVotes;
uint256 againstVotes;
bool executed;
bool canceled;
address strategy;
bytes32 ipfsHash;
}
/**
* @dev emitted when a new proposal is created
* @param id Id of the proposal
* @param creator address of the creator
* @param executor The ExecutorWithTimelock contract that will execute the proposal
* @param targets list of contracts called by proposal's associated transactions
* @param values list of value in wei for each propoposal's associated transaction
* @param signatures list of function signatures (can be empty) to be used when created the callData
* @param calldatas list of calldatas: if associated signature empty, calldata ready, else calldata is arguments
* @param withDelegatecalls boolean, true = transaction delegatecalls the taget, else calls the target
* @param startBlock block number when vote starts
* @param endBlock block number when vote ends
* @param strategy address of the governanceStrategy contract
* @param ipfsHash IPFS hash of the proposal
**/
event ProposalCreated(
uint256 id,
address indexed creator,
IExecutorWithTimelock indexed executor,
address[] targets,
uint256[] values,
string[] signatures,
bytes[] calldatas,
bool[] withDelegatecalls,
uint256 startBlock,
uint256 endBlock,
address strategy,
bytes32 ipfsHash
);
/**
* @dev emitted when a proposal is canceled
* @param id Id of the proposal
**/
event ProposalCanceled(uint256 id);
/**
* @dev emitted when a proposal is queued
* @param id Id of the proposal
* @param executionTime time when proposal underlying transactions can be executed
* @param initiatorQueueing address of the initiator of the queuing transaction
**/
event ProposalQueued(uint256 id, uint256 executionTime, address indexed initiatorQueueing);
/**
* @dev emitted when a proposal is executed
* @param id Id of the proposal
* @param initiatorExecution address of the initiator of the execution transaction
**/
event ProposalExecuted(uint256 id, address indexed initiatorExecution);
/**
* @dev emitted when a vote is registered
* @param id Id of the proposal
* @param voter address of the voter
* @param support boolean, true = vote for, false = vote against
* @param votingPower Power of the voter/vote
**/
event VoteEmitted(uint256 id, address indexed voter, bool support, uint256 votingPower);
event GovernanceStrategyChanged(address indexed newStrategy, address indexed initiatorChange);
event VotingDelayChanged(uint256 newVotingDelay, address indexed initiatorChange);
event ExecutorAuthorized(address executor);
event ExecutorUnauthorized(address executor);
/**
* @dev Creates a Proposal (needs Proposition Power of creator > Threshold)
* @param executor The ExecutorWithTimelock contract that will execute the proposal
* @param targets list of contracts called by proposal's associated transactions
* @param values list of value in wei for each propoposal's associated transaction
* @param signatures list of function signatures (can be empty) to be used when created the callData
* @param calldatas list of calldatas: if associated signature empty, calldata ready, else calldata is arguments
* @param withDelegatecalls if true, transaction delegatecalls the taget, else calls the target
* @param ipfsHash IPFS hash of the proposal
**/
function create(
IExecutorWithTimelock executor,
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
bool[] memory withDelegatecalls,
bytes32 ipfsHash
) external returns (uint256);
/**
* @dev Cancels a Proposal,
* either at anytime by guardian
* or when proposal is Pending/Active and threshold no longer reached
* @param proposalId id of the proposal
**/
function cancel(uint256 proposalId) external;
/**
* @dev Queue the proposal (If Proposal Succeeded)
* @param proposalId id of the proposal to queue
**/
function queue(uint256 proposalId) external;
/**
* @dev Execute the proposal (If Proposal Queued)
* @param proposalId id of the proposal to execute
**/
function execute(uint256 proposalId) external payable;
/**
* @dev Function allowing msg.sender to vote for/against a proposal
* @param proposalId id of the proposal
* @param support boolean, true = vote for, false = vote against
**/
function submitVote(uint256 proposalId, bool support) external;
/**
* @dev Function to register the vote of user that has voted offchain via signature
* @param proposalId id of the proposal
* @param support boolean, true = vote for, false = vote against
* @param v v part of the voter signature
* @param r r part of the voter signature
* @param s s part of the voter signature
**/
function submitVoteBySignature(
uint256 proposalId,
bool support,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Set new GovernanceStrategy
* Note: owner should be a timelocked executor, so needs to make a proposal
* @param governanceStrategy new Address of the GovernanceStrategy contract
**/
function setGovernanceStrategy(address governanceStrategy) external;
/**
* @dev Set new Voting Delay (delay before a newly created proposal can be voted on)
* Note: owner should be a timelocked executor, so needs to make a proposal
* @param votingDelay new voting delay in seconds
**/
function setVotingDelay(uint256 votingDelay) external;
/**
* @dev Add new addresses to the list of authorized executors
* @param executors list of new addresses to be authorized executors
**/
function authorizeExecutors(address[] memory executors) external;
/**
* @dev Remove addresses to the list of authorized executors
* @param executors list of addresses to be removed as authorized executors
**/
function unauthorizeExecutors(address[] memory executors) external;
/**
* @dev Let the guardian abdicate from its priviledged rights
**/
function __abdicate() external;
/**
* @dev Getter of the current GovernanceStrategy address
* @return The address of the current GovernanceStrategy contracts
**/
function getGovernanceStrategy() external view returns (address);
/**
* @dev Getter of the current Voting Delay (delay before a created proposal can be voted on)
* Different from the voting duration
* @return The voting delay in seconds
**/
function getVotingDelay() external view returns (uint256);
/**
* @dev Returns whether an address is an authorized executor
* @param executor address to evaluate as authorized executor
* @return true if authorized
**/
function isExecutorAuthorized(address executor) external view returns (bool);
/**
* @dev Getter the address of the guardian, that can mainly cancel proposals
* @return The address of the guardian
**/
function getGuardian() external view returns (address);
/**
* @dev Getter of the proposal count (the current number of proposals ever created)
* @return the proposal count
**/
function getProposalsCount() external view returns (uint256);
/**
* @dev Getter of a proposal by id
* @param proposalId id of the proposal to get
* @return the proposal as ProposalWithoutVotes memory object
**/
function getProposalById(uint256 proposalId) external view returns (ProposalWithoutVotes memory);
/**
* @dev Getter of the Vote of a voter about a proposal
* Note: Vote is a struct: ({bool support, uint248 votingPower})
* @param proposalId id of the proposal
* @param voter address of the voter
* @return The associated Vote memory object
**/
function getVoteOnProposal(uint256 proposalId, address voter) external view returns (Vote memory);
/**
* @dev Get the current state of a proposal
* @param proposalId id of the proposal
* @return The current state if the proposal
**/
function getProposalState(uint256 proposalId) external view returns (ProposalState);
}
/**
* @title Governance V2 contract
* @dev Main point of interaction with Aave protocol's governance
* - Create a Proposal
* - Cancel a Proposal
* - Queue a Proposal
* - Execute a Proposal
* - Submit Vote to a Proposal
* Proposal States : Pending => Active => Succeeded(/Failed) => Queued => Executed(/Expired)
* The transition to "Canceled" can appear in multiple states
* @author Aave
**/
contract AaveGovernanceV2 is Ownable, IAaveGovernanceV2 {
using SafeMath for uint256;
address private _governanceStrategy;
uint256 private _votingDelay;
uint256 private _proposalsCount;
mapping(uint256 => Proposal) private _proposals;
mapping(address => bool) private _authorizedExecutors;
address private _guardian;
bytes32 public constant DOMAIN_TYPEHASH = keccak256(
'EIP712Domain(string name,uint256 chainId,address verifyingContract)'
);
bytes32 public constant VOTE_EMITTED_TYPEHASH = keccak256('VoteEmitted(uint256 id,bool support)');
string public constant NAME = 'Aave Governance v2';
modifier onlyGuardian() {
require(msg.sender == _guardian, 'ONLY_BY_GUARDIAN');
_;
}
constructor(
address governanceStrategy,
uint256 votingDelay,
address guardian,
address[] memory executors
) {
_setGovernanceStrategy(governanceStrategy);
_setVotingDelay(votingDelay);
_guardian = guardian;
authorizeExecutors(executors);
}
struct CreateVars {
uint256 startBlock;
uint256 endBlock;
uint256 previousProposalsCount;
}
/**
* @dev Creates a Proposal (needs to be validated by the Proposal Validator)
* @param executor The ExecutorWithTimelock contract that will execute the proposal
* @param targets list of contracts called by proposal's associated transactions
* @param values list of value in wei for each propoposal's associated transaction
* @param signatures list of function signatures (can be empty) to be used when created the callData
* @param calldatas list of calldatas: if associated signature empty, calldata ready, else calldata is arguments
* @param withDelegatecalls boolean, true = transaction delegatecalls the taget, else calls the target
* @param ipfsHash IPFS hash of the proposal
**/
function create(
IExecutorWithTimelock executor,
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
bool[] memory withDelegatecalls,
bytes32 ipfsHash
) external override returns (uint256) {
require(targets.length != 0, 'INVALID_EMPTY_TARGETS');
require(
targets.length == values.length &&
targets.length == signatures.length &&
targets.length == calldatas.length &&
targets.length == withDelegatecalls.length,
'INCONSISTENT_PARAMS_LENGTH'
);
require(isExecutorAuthorized(address(executor)), 'EXECUTOR_NOT_AUTHORIZED');
require(
IProposalValidator(address(executor)).validateCreatorOfProposal(
this,
msg.sender,
block.number - 1
),
'PROPOSITION_CREATION_INVALID'
);
CreateVars memory vars;
vars.startBlock = block.number.add(_votingDelay);
vars.endBlock = vars.startBlock.add(IProposalValidator(address(executor)).VOTING_DURATION());
vars.previousProposalsCount = _proposalsCount;
Proposal storage newProposal = _proposals[vars.previousProposalsCount];
newProposal.id = vars.previousProposalsCount;
newProposal.creator = msg.sender;
newProposal.executor = executor;
newProposal.targets = targets;
newProposal.values = values;
newProposal.signatures = signatures;
newProposal.calldatas = calldatas;
newProposal.withDelegatecalls = withDelegatecalls;
newProposal.startBlock = vars.startBlock;
newProposal.endBlock = vars.endBlock;
newProposal.strategy = _governanceStrategy;
newProposal.ipfsHash = ipfsHash;
_proposalsCount++;
emit ProposalCreated(
vars.previousProposalsCount,
msg.sender,
executor,
targets,
values,
signatures,
calldatas,
withDelegatecalls,
vars.startBlock,
vars.endBlock,
_governanceStrategy,
ipfsHash
);
return newProposal.id;
}
/**
* @dev Cancels a Proposal.
* - Callable by the _guardian with relaxed conditions, or by anybody if the conditions of
* cancellation on the executor are fulfilled
* @param proposalId id of the proposal
**/
function cancel(uint256 proposalId) external override {
ProposalState state = getProposalState(proposalId);
require(
state != ProposalState.Executed &&
state != ProposalState.Canceled &&
state != ProposalState.Expired,
'ONLY_BEFORE_EXECUTED'
);
Proposal storage proposal = _proposals[proposalId];
require(
msg.sender == _guardian ||
IProposalValidator(address(proposal.executor)).validateProposalCancellation(
this,
proposal.creator,
block.number - 1
),
'PROPOSITION_CANCELLATION_INVALID'
);
proposal.canceled = true;
for (uint256 i = 0; i < proposal.targets.length; i++) {
proposal.executor.cancelTransaction(
proposal.targets[i],
proposal.values[i],
proposal.signatures[i],
proposal.calldatas[i],
proposal.executionTime,
proposal.withDelegatecalls[i]
);
}
emit ProposalCanceled(proposalId);
}
/**
* @dev Queue the proposal (If Proposal Succeeded)
* @param proposalId id of the proposal to queue
**/
function queue(uint256 proposalId) external override {
require(getProposalState(proposalId) == ProposalState.Succeeded, 'INVALID_STATE_FOR_QUEUE');
Proposal storage proposal = _proposals[proposalId];
uint256 executionTime = block.timestamp.add(proposal.executor.getDelay());
for (uint256 i = 0; i < proposal.targets.length; i++) {
_queueOrRevert(
proposal.executor,
proposal.targets[i],
proposal.values[i],
proposal.signatures[i],
proposal.calldatas[i],
executionTime,
proposal.withDelegatecalls[i]
);
}
proposal.executionTime = executionTime;
emit ProposalQueued(proposalId, executionTime, msg.sender);
}
/**
* @dev Execute the proposal (If Proposal Queued)
* @param proposalId id of the proposal to execute
**/
function execute(uint256 proposalId) external payable override {
require(getProposalState(proposalId) == ProposalState.Queued, 'ONLY_QUEUED_PROPOSALS');
Proposal storage proposal = _proposals[proposalId];
proposal.executed = true;
for (uint256 i = 0; i < proposal.targets.length; i++) {
proposal.executor.executeTransaction{value: proposal.values[i]}(
proposal.targets[i],
proposal.values[i],
proposal.signatures[i],
proposal.calldatas[i],
proposal.executionTime,
proposal.withDelegatecalls[i]
);
}
emit ProposalExecuted(proposalId, msg.sender);
}
/**
* @dev Function allowing msg.sender to vote for/against a proposal
* @param proposalId id of the proposal
* @param support boolean, true = vote for, false = vote against
**/
function submitVote(uint256 proposalId, bool support) external override {
return _submitVote(msg.sender, proposalId, support);
}
/**
* @dev Function to register the vote of user that has voted offchain via signature
* @param proposalId id of the proposal
* @param support boolean, true = vote for, false = vote against
* @param v v part of the voter signature
* @param r r part of the voter signature
* @param s s part of the voter signature
**/
function submitVoteBySignature(
uint256 proposalId,
bool support,
uint8 v,
bytes32 r,
bytes32 s
) external override {
bytes32 digest = keccak256(
abi.encodePacked(
'\x19\x01',
keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(NAME)), getChainId(), address(this))),
keccak256(abi.encode(VOTE_EMITTED_TYPEHASH, proposalId, support))
)
);
address signer = ecrecover(digest, v, r, s);
require(signer != address(0), 'INVALID_SIGNATURE');
return _submitVote(signer, proposalId, support);
}
/**
* @dev Set new GovernanceStrategy
* Note: owner should be a timelocked executor, so needs to make a proposal
* @param governanceStrategy new Address of the GovernanceStrategy contract
**/
function setGovernanceStrategy(address governanceStrategy) external override onlyOwner {
_setGovernanceStrategy(governanceStrategy);
}
/**
* @dev Set new Voting Delay (delay before a newly created proposal can be voted on)
* Note: owner should be a timelocked executor, so needs to make a proposal
* @param votingDelay new voting delay in terms of blocks
**/
function setVotingDelay(uint256 votingDelay) external override onlyOwner {
_setVotingDelay(votingDelay);
}
/**
* @dev Add new addresses to the list of authorized executors
* @param executors list of new addresses to be authorized executors
**/
function authorizeExecutors(address[] memory executors) public override onlyOwner {
for (uint256 i = 0; i < executors.length; i++) {
_authorizeExecutor(executors[i]);
}
}
/**
* @dev Remove addresses to the list of authorized executors
* @param executors list of addresses to be removed as authorized executors
**/
function unauthorizeExecutors(address[] memory executors) public override onlyOwner {
for (uint256 i = 0; i < executors.length; i++) {
_unauthorizeExecutor(executors[i]);
}
}
/**
* @dev Let the guardian abdicate from its priviledged rights
**/
function __abdicate() external override onlyGuardian {
_guardian = address(0);
}
/**
* @dev Getter of the current GovernanceStrategy address
* @return The address of the current GovernanceStrategy contracts
**/
function getGovernanceStrategy() external view override returns (address) {
return _governanceStrategy;
}
/**
* @dev Getter of the current Voting Delay (delay before a created proposal can be voted on)
* Different from the voting duration
* @return The voting delay in number of blocks
**/
function getVotingDelay() external view override returns (uint256) {
return _votingDelay;
}
/**
* @dev Returns whether an address is an authorized executor
* @param executor address to evaluate as authorized executor
* @return true if authorized
**/
function isExecutorAuthorized(address executor) public view override returns (bool) {
return _authorizedExecutors[executor];
}
/**
* @dev Getter the address of the guardian, that can mainly cancel proposals
* @return The address of the guardian
**/
function getGuardian() external view override returns (address) {
return _guardian;
}
/**
* @dev Getter of the proposal count (the current number of proposals ever created)
* @return the proposal count
**/
function getProposalsCount() external view override returns (uint256) {
return _proposalsCount;
}
/**
* @dev Getter of a proposal by id
* @param proposalId id of the proposal to get
* @return the proposal as ProposalWithoutVotes memory object
**/
function getProposalById(uint256 proposalId)
external
view
override
returns (ProposalWithoutVotes memory)
{
Proposal storage proposal = _proposals[proposalId];
ProposalWithoutVotes memory proposalWithoutVotes = ProposalWithoutVotes({
id: proposal.id,
creator: proposal.creator,
executor: proposal.executor,
targets: proposal.targets,
values: proposal.values,
signatures: proposal.signatures,
calldatas: proposal.calldatas,
withDelegatecalls: proposal.withDelegatecalls,
startBlock: proposal.startBlock,
endBlock: proposal.endBlock,
executionTime: proposal.executionTime,
forVotes: proposal.forVotes,
againstVotes: proposal.againstVotes,
executed: proposal.executed,
canceled: proposal.canceled,
strategy: proposal.strategy,
ipfsHash: proposal.ipfsHash
});
return proposalWithoutVotes;
}
/**
* @dev Getter of the Vote of a voter about a proposal
* Note: Vote is a struct: ({bool support, uint248 votingPower})
* @param proposalId id of the proposal
* @param voter address of the voter
* @return The associated Vote memory object
**/
function getVoteOnProposal(uint256 proposalId, address voter)
external
view
override
returns (Vote memory)
{
return _proposals[proposalId].votes[voter];
}
/**
* @dev Get the current state of a proposal
* @param proposalId id of the proposal
* @return The current state if the proposal
**/
function getProposalState(uint256 proposalId) public view override returns (ProposalState) {
require(_proposalsCount >= proposalId, 'INVALID_PROPOSAL_ID');
Proposal storage proposal = _proposals[proposalId];
if (proposal.canceled) {
return ProposalState.Canceled;
} else if (block.number <= proposal.startBlock) {
return ProposalState.Pending;
} else if (block.number <= proposal.endBlock) {
return ProposalState.Active;
} else if (!IProposalValidator(address(proposal.executor)).isProposalPassed(this, proposalId)) {
return ProposalState.Failed;
} else if (proposal.executionTime == 0) {
return ProposalState.Succeeded;
} else if (proposal.executed) {
return ProposalState.Executed;
} else if (proposal.executor.isProposalOverGracePeriod(this, proposalId)) {
return ProposalState.Expired;
} else {
return ProposalState.Queued;
}
}
function _queueOrRevert(
IExecutorWithTimelock executor,
address target,
uint256 value,
string memory signature,
bytes memory callData,
uint256 executionTime,
bool withDelegatecall
) internal {
require(
!executor.isActionQueued(
keccak256(abi.encode(target, value, signature, callData, executionTime, withDelegatecall))
),
'DUPLICATED_ACTION'
);
executor.queueTransaction(target, value, signature, callData, executionTime, withDelegatecall);
}
function _submitVote(
address voter,
uint256 proposalId,
bool support
) internal {
require(getProposalState(proposalId) == ProposalState.Active, 'VOTING_CLOSED');
Proposal storage proposal = _proposals[proposalId];
Vote storage vote = proposal.votes[voter];
require(vote.votingPower == 0, 'VOTE_ALREADY_SUBMITTED');
uint256 votingPower = IVotingStrategy(proposal.strategy).getVotingPowerAt(
voter,
proposal.startBlock
);
if (support) {
proposal.forVotes = proposal.forVotes.add(votingPower);
} else {
proposal.againstVotes = proposal.againstVotes.add(votingPower);
}
vote.support = support;
vote.votingPower = uint248(votingPower);
emit VoteEmitted(proposalId, voter, support, votingPower);
}
function _setGovernanceStrategy(address governanceStrategy) internal {
_governanceStrategy = governanceStrategy;
emit GovernanceStrategyChanged(governanceStrategy, msg.sender);
}
function _setVotingDelay(uint256 votingDelay) internal {
_votingDelay = votingDelay;
emit VotingDelayChanged(votingDelay, msg.sender);
}
function _authorizeExecutor(address executor) internal {
_authorizedExecutors[executor] = true;
emit ExecutorAuthorized(executor);
}
function _unauthorizeExecutor(address executor) internal {
_authorizedExecutors[executor] = false;
emit ExecutorUnauthorized(executor);
}
}
{
"compilationTarget": {
"AaveGovernanceV2.sol": "AaveGovernanceV2"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"governanceStrategy","type":"address"},{"internalType":"uint256","name":"votingDelay","type":"uint256"},{"internalType":"address","name":"guardian","type":"address"},{"internalType":"address[]","name":"executors","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"executor","type":"address"}],"name":"ExecutorAuthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"executor","type":"address"}],"name":"ExecutorUnauthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newStrategy","type":"address"},{"indexed":true,"internalType":"address","name":"initiatorChange","type":"address"}],"name":"GovernanceStrategyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"ProposalCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"contract IExecutorWithTimelock","name":"executor","type":"address"},{"indexed":false,"internalType":"address[]","name":"targets","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"},{"indexed":false,"internalType":"string[]","name":"signatures","type":"string[]"},{"indexed":false,"internalType":"bytes[]","name":"calldatas","type":"bytes[]"},{"indexed":false,"internalType":"bool[]","name":"withDelegatecalls","type":"bool[]"},{"indexed":false,"internalType":"uint256","name":"startBlock","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endBlock","type":"uint256"},{"indexed":false,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"bytes32","name":"ipfsHash","type":"bytes32"}],"name":"ProposalCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"initiatorExecution","type":"address"}],"name":"ProposalExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionTime","type":"uint256"},{"indexed":true,"internalType":"address","name":"initiatorQueueing","type":"address"}],"name":"ProposalQueued","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"bool","name":"support","type":"bool"},{"indexed":false,"internalType":"uint256","name":"votingPower","type":"uint256"}],"name":"VoteEmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newVotingDelay","type":"uint256"},{"indexed":true,"internalType":"address","name":"initiatorChange","type":"address"}],"name":"VotingDelayChanged","type":"event"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VOTE_EMITTED_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"__abdicate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"executors","type":"address[]"}],"name":"authorizeExecutors","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"cancel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IExecutorWithTimelock","name":"executor","type":"address"},{"internalType":"address[]","name":"targets","type":"address[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"string[]","name":"signatures","type":"string[]"},{"internalType":"bytes[]","name":"calldatas","type":"bytes[]"},{"internalType":"bool[]","name":"withDelegatecalls","type":"bool[]"},{"internalType":"bytes32","name":"ipfsHash","type":"bytes32"}],"name":"create","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getGovernanceStrategy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGuardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"getProposalById","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"contract IExecutorWithTimelock","name":"executor","type":"address"},{"internalType":"address[]","name":"targets","type":"address[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"string[]","name":"signatures","type":"string[]"},{"internalType":"bytes[]","name":"calldatas","type":"bytes[]"},{"internalType":"bool[]","name":"withDelegatecalls","type":"bool[]"},{"internalType":"uint256","name":"startBlock","type":"uint256"},{"internalType":"uint256","name":"endBlock","type":"uint256"},{"internalType":"uint256","name":"executionTime","type":"uint256"},{"internalType":"uint256","name":"forVotes","type":"uint256"},{"internalType":"uint256","name":"againstVotes","type":"uint256"},{"internalType":"bool","name":"executed","type":"bool"},{"internalType":"bool","name":"canceled","type":"bool"},{"internalType":"address","name":"strategy","type":"address"},{"internalType":"bytes32","name":"ipfsHash","type":"bytes32"}],"internalType":"struct IAaveGovernanceV2.ProposalWithoutVotes","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"getProposalState","outputs":[{"internalType":"enum IAaveGovernanceV2.ProposalState","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProposalsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"},{"internalType":"address","name":"voter","type":"address"}],"name":"getVoteOnProposal","outputs":[{"components":[{"internalType":"bool","name":"support","type":"bool"},{"internalType":"uint248","name":"votingPower","type":"uint248"}],"internalType":"struct IAaveGovernanceV2.Vote","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVotingDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"executor","type":"address"}],"name":"isExecutorAuthorized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"queue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"governanceStrategy","type":"address"}],"name":"setGovernanceStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"votingDelay","type":"uint256"}],"name":"setVotingDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"},{"internalType":"bool","name":"support","type":"bool"}],"name":"submitVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"},{"internalType":"bool","name":"support","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"submitVoteBySignature","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"executors","type":"address[]"}],"name":"unauthorizeExecutors","outputs":[],"stateMutability":"nonpayable","type":"function"}]