Transaction Hash:
Block:
24059991 at Dec-21-2025 09:09:35 AM +UTC
Transaction Fee:
0.000003606326450132 ETH
$0.007462
Gas Used:
89,948 Gas / 0.040093459 Gwei
Emitted Events:
| 704 |
CowProtocolVirtualToken.Vested( user=0xDAAe26e391e414C2693798918483283eA9514d3F, amount=202394883457346198 )
|
| 705 |
CowProtocolToken.Transfer( from=CowProtocolVirtualToken, to=0xDAAe26e391e414C2693798918483283eA9514d3F, value=202394883457346198 )
|
| 706 |
CowProtocolVirtualToken.Transfer( from=0xDAAe26e391e414C2693798918483283eA9514d3F, to=0x0000000000000000000000000000000000000000, value=202394883457346198 )
|
| 707 |
0xdaae26e391e414c2693798918483283ea9514d3f.0x6eb7af0fe8761c066484ab6b41c49a1eecb9025e21f258e3e7f0ef3631d67b4a( 0x6eb7af0fe8761c066484ab6b41c49a1eecb9025e21f258e3e7f0ef3631d67b4a, 000000000000000000000000d057b63f5e69cf1b929b356b579cba08d7688048, 0000000000000000000000000000000000000000000000000000000000000060, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000004, 3e9ffbea00000000000000000000000000000000000000000000000000000000 )
|
| 708 |
CowProtocolToken.Transfer( from=0xDAAe26e391e414C2693798918483283eA9514d3F, to=[Sender] 0x164dc5333938299b6db62f73b63f8270ff3892c2, value=199766378477380663 )
|
| 709 |
0xdaae26e391e414c2693798918483283ea9514d3f.0x6eb7af0fe8761c066484ab6b41c49a1eecb9025e21f258e3e7f0ef3631d67b4a( 0x6eb7af0fe8761c066484ab6b41c49a1eecb9025e21f258e3e7f0ef3631d67b4a, 000000000000000000000000def1ca1fb7fbcdc777520aa7f396b4e015f497ab, 0000000000000000000000000000000000000000000000000000000000000060, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000044, a9059cbb000000000000000000000000164dc5333938299b6db62f73b63f8270, ff3892c200000000000000000000000000000000000000000000000002c5b676, 7b35e43700000000000000000000000000000000000000000000000000000000 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x164Dc533...0ff3892c2 |
0.759223749087753818 Eth
Nonce: 20194
|
0.759220142761303686 Eth
Nonce: 20195
| 0.000003606326450132 | ||
| 0xD057B63f...8D7688048 | |||||
|
0xdadB0d80...24f783711
Miner
| (BuilderNet) | 142.452718560974312117 Eth | 142.452718560974492013 Eth | 0.000000000000179896 | |
| 0xDEf1CA1f...015F497aB |
Execution Trace
0x88a0a851b28018ac9bda57ced6a95c8efc7b73f8.09c5eabe( )
0xdaae26e391e414c2693798918483283ea9514d3f.bca8c7b5( )
CowProtocolVirtualToken.CALL( )
-
CowProtocolToken.transfer( recipient=0xDAAe26e391e414C2693798918483283eA9514d3F, amount=202394883457346198 ) => ( True )
-
-
CowProtocolToken.balanceOf( account=0x164Dc5333938299B6db62f73b63f8270ff3892c2 ) => ( 642921804079650009159 )
0xdaae26e391e414c2693798918483283ea9514d3f.bca8c7b5( )-
CowProtocolToken.transfer( recipient=0x164Dc5333938299B6db62f73b63f8270ff3892c2, amount=199766378477380663 ) => ( True )
-
-
CowProtocolToken.balanceOf( account=0x164Dc5333938299B6db62f73b63f8270ff3892c2 ) => ( 643121570458127389822 )
File 1 of 2: CowProtocolVirtualToken
File 2 of 2: CowProtocolToken
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.8.10;
import "./mixins/NonTransferrableErc20.sol";
import "./mixins/Vesting.sol";
import "./mixins/Claiming.sol";
import "./mixins/MerkleDistributor.sol";
import "./vendored/mixins/StorageAccessible.sol";
/// @dev The token that manages how the CoW Protocol governance token is
/// distributed to all different types of investors.
/// @title CoW Protocol Virtual Token
/// @author CoW Protocol Developers
contract CowProtocolVirtualToken is
NonTransferrableErc20,
Vesting,
Claiming,
MerkleDistributor,
StorageAccessible
{
string private constant ERC20_SYMBOL = "vCOW";
string private constant ERC20_NAME = "CoW Protocol Virtual Token";
constructor(
bytes32 merkleRoot,
address cowToken,
address payable communityFundsTarget,
address investorFundsTarget,
address usdcToken,
uint256 usdcPrice,
address gnoToken,
uint256 gnoPrice,
address wrappedNativeToken,
uint256 nativeTokenPrice,
address teamController
)
NonTransferrableErc20(ERC20_NAME, ERC20_SYMBOL)
Claiming(
cowToken,
communityFundsTarget,
investorFundsTarget,
usdcToken,
usdcPrice,
gnoToken,
gnoPrice,
wrappedNativeToken,
nativeTokenPrice,
teamController
)
MerkleDistributor(merkleRoot)
// solhint-disable-next-line no-empty-blocks
{
}
/// @dev Returns the sum of tokens that are either held as
/// instantlySwappableBalance or will be vested in the future
/// @param user The user for whom the balance is calculated
/// @return Balance of the user
function balanceOf(address user) public view returns (uint256) {
return
instantlySwappableBalance[user] +
fullAllocation[user] -
vestedAllocation[user];
}
/// @dev Returns the balance of a user assuming all vested tokens would
/// have been converted into virtual tokens
/// @param user The user for whom the balance is calculated
/// @return Balance the user would have after calling `swapAll`
function swappableBalanceOf(address user) public view returns (uint256) {
return instantlySwappableBalance[user] + newlyVestedBalance(user);
}
}
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.8.10;
import "../vendored/interfaces/IERC20.sol";
/// @dev A contract of an ERC20 token that cannot be transferred.
/// @title Non-Transferrable ERC20
/// @author CoW Protocol Developers
abstract contract NonTransferrableErc20 is IERC20 {
/// @dev The ERC20 name of the token
string public name;
/// @dev The ERC20 symbol of the token
string public symbol;
/// @dev The ERC20 number of decimals of the token
uint8 public constant decimals = 18; // solhint-disable const-name-snakecase
// solhint-disable-next-line no-empty-blocks
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
/// @dev This error is fired when trying to perform an action that is not
/// supported by the contract, like transfers and approvals. These actions
/// will never be supported.
error NotSupported();
/// @dev All types of transfers are permanently disabled.
function transferFrom(
address,
address,
uint256
) public pure returns (bool) {
revert NotSupported();
}
/// @dev All types of transfers are permanently disabled.
function transfer(address, uint256) public pure returns (bool) {
revert NotSupported();
}
/// @dev All types of approvals are permanently disabled to reduce code
/// size.
function approve(address, uint256) public pure returns (bool) {
revert NotSupported();
}
/// @dev Approvals cannot be set, so allowances are always zero.
function allowance(address, address) public pure returns (uint256) {
return 0;
}
}
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.8.10;
import "../vendored/libraries/Math.sol";
import "../interfaces/VestingInterface.sol";
/// @dev The vesting logic for distributing the COW token
/// @title Vesting Logic
/// @author CoW Protocol Developers
contract Vesting is VestingInterface {
/// @dev The timestamp of the official vesting start. This value is shared
/// between all participants.
uint256 public immutable vestingStart;
/// @dev How long it will take for all vesting to be completed. It is set to
/// four years.
uint256 public constant VESTING_PERIOD_IN_SECONDS = 4 * 365 days + 1 days;
/// @dev Stores the amount of vesting that the user has already vested.
mapping(address => uint256) public vestedAllocation;
/// @dev Stores the maximum amount of vesting available to each user. This
/// is exactly the total amount of vesting that can be converted after the
/// vesting period is completed.
mapping(address => uint256) public fullAllocation;
/// @dev Stores a bit indicating whether a vesting is cancelable
/// Important: This implementaiton implies that there can not be a
/// cancelable and non-cancelable vesting in parallel
mapping(address => bool) public isCancelable;
/// @dev Event emitted when a new vesting position is added. The amount is
/// the additional amount that can be vested at the end of the
/// claiming period.
event VestingAdded(address indexed user, uint256 amount, bool isCancelable);
/// @dev Event emitted when a vesting position is canceled. The amount is
/// the number of remaining vesting that will be given to the beneficiary.
event VestingStopped(
address indexed user,
address freedVestingBeneficiary,
uint256 amount
);
/// @dev Event emitted when the users claims (also partially) a vesting
/// position.
event Vested(address indexed user, uint256 amount);
/// @dev Error returned when trying to stop a claim that is not cancelable.
error VestingNotCancelable();
constructor() {
vestingStart = block.timestamp; // solhint-disable-line not-rely-on-time
}
/// @inheritdoc VestingInterface
function addVesting(
address user,
uint256 vestingAmount,
bool isCancelableFlag
) internal override {
if (isCancelableFlag) {
// if one cancelable vesting is made, it converts all vestings into cancelable ones
isCancelable[user] = isCancelableFlag;
}
fullAllocation[user] += vestingAmount;
emit VestingAdded(user, vestingAmount, isCancelableFlag);
}
/// @inheritdoc VestingInterface
function shiftVesting(address user, address freedVestingBeneficiary)
internal
override
returns (uint256 accruedVesting)
{
if (!isCancelable[user]) {
revert VestingNotCancelable();
}
accruedVesting = vest(user);
uint256 userFullAllocation = fullAllocation[user];
uint256 userVestedAllocation = vestedAllocation[user];
fullAllocation[user] = 0;
vestedAllocation[user] = 0;
fullAllocation[freedVestingBeneficiary] += userFullAllocation;
vestedAllocation[freedVestingBeneficiary] += userVestedAllocation;
emit VestingStopped(
user,
freedVestingBeneficiary,
userFullAllocation - userVestedAllocation
);
}
/// @inheritdoc VestingInterface
function vest(address user)
internal
override
returns (uint256 newlyVested)
{
newlyVested = newlyVestedBalance(user);
vestedAllocation[user] += newlyVested;
emit Vested(user, newlyVested);
}
/// @dev Assuming no conversions has been done by the user, calculates how
/// much vesting can be converted at this point in time.
/// @param user The user for whom the result is being calculated.
/// @return How much vesting can be converted if no conversions had been
/// done before.
function cumulativeVestedBalance(address user)
public
view
returns (uint256)
{
return
(Math.min(
block.timestamp - vestingStart, // solhint-disable-line not-rely-on-time
VESTING_PERIOD_IN_SECONDS
) * fullAllocation[user]) / (VESTING_PERIOD_IN_SECONDS);
}
/// @dev Calculates how much vesting can be converted at this point in time.
/// Unlike `cumulativeVestedBalance`, this function keeps track of previous
/// conversions.
/// @param user The user for whom the result is being calculated.
/// @return How much vesting can be converted.
function newlyVestedBalance(address user) public view returns (uint256) {
return cumulativeVestedBalance(user) - vestedAllocation[user];
}
}
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.8.10;
import "../vendored/interfaces/IERC20.sol";
import "../vendored/libraries/SafeERC20.sol";
import "../interfaces/ClaimingInterface.sol";
import "../interfaces/VestingInterface.sol";
/// @dev The logic behind the claiming of virtual tokens and the swapping to
/// real tokens.
/// @title COW Virtual Token Claiming Logic
/// @author CoW Protocol Developers
abstract contract Claiming is ClaimingInterface, VestingInterface, IERC20 {
using SafeERC20 for IERC20;
/// @dev Prices are represented as fractions. For readability, the
/// denominator is one unit of the virtual token (assuming it has 18
/// decimals), in this way the numerator of a price is the number of atoms
/// that have the same value as a unit of virtual token.
uint256 internal constant PRICE_DENOMINATOR = 10**18;
/// @dev Price numerator for the COW/USDC price. This is the number of USDC
/// atoms required to obtain a full unit of virtual token from an option.
uint256 public immutable usdcPrice;
/// @dev Price numerator for the COW/GNO price. This is the number of GNO
/// atoms required to obtain a full unit of virtual token from an option.
uint256 public immutable gnoPrice;
/// @dev Price numerator for the COW/native-token price. This is the number
/// of native token wei required to obtain a full unit of virtual token from
/// an option.
uint256 public immutable nativeTokenPrice;
/// @dev The proceeds from selling options to the community will be sent to,
/// this address.
address payable public immutable communityFundsTarget;
/// @dev All proceeds from known investors will be sent to this address.
address public immutable investorFundsTarget;
/// @dev Address of the real COW token. Tokens claimed by this contract can
/// be converted to this token if this contract stores some balance of it.
IERC20 public immutable cowToken;
/// @dev Address of the USDC token. It is a form of payment for investors.
IERC20 public immutable usdcToken;
/// @dev Address of the GNO token. It is a form of payment for users who
/// claim the options derived from holding GNO.
IERC20 public immutable gnoToken;
/// @dev Address of the wrapped native token. It is a form of payment for
/// users who claim the options derived from being users of the CoW
/// Protocol.
IERC20 public immutable wrappedNativeToken;
/// @dev Address representing the CoW Protocol/CowSwap team. It is the only
/// address that is allowed to stop the vesting of a claim, and exclusively
/// for team claims.
address public immutable teamController;
/// @dev Time at which this contract was deployed.
uint256 public immutable deploymentTimestamp;
/// @dev Returns the amount of virtual tokens in existence, including those
/// that have yet to be vested.
uint256 public totalSupply;
/// @dev How many tokens can be immediately swapped in exchange for real
/// tokens for each user.
mapping(address => uint256) public instantlySwappableBalance;
/// @dev Error presented to a user trying to claim virtual tokens after the
/// claiming period has ended.
error ClaimingExpired();
/// @dev Error presented to anyone but the team controller to stop a
/// cancelable vesting position (i.e., only team vesting).
error OnlyTeamController();
/// @dev Error resulting from sending an incorrect amount of native to the
/// contract.
error InvalidNativeTokenAmount();
/// @dev Error caused by an unsuccessful attempt to transfer native tokens.
error FailedNativeTokenTransfer();
/// @dev Error resulting from sending native tokens for a claim that cannot
/// be redeemed with native tokens.
error CannotSendNativeToken();
constructor(
address _cowToken,
address payable _communityFundsTarget,
address _investorFundsTarget,
address _usdcToken,
uint256 _usdcPrice,
address _gnoToken,
uint256 _gnoPrice,
address _wrappedNativeToken,
uint256 _nativeTokenPrice,
address _teamController
) {
cowToken = IERC20(_cowToken);
communityFundsTarget = _communityFundsTarget;
investorFundsTarget = _investorFundsTarget;
usdcToken = IERC20(_usdcToken);
usdcPrice = _usdcPrice;
gnoToken = IERC20(_gnoToken);
gnoPrice = _gnoPrice;
wrappedNativeToken = IERC20(_wrappedNativeToken);
nativeTokenPrice = _nativeTokenPrice;
teamController = _teamController;
// solhint-disable-next-line not-rely-on-time
deploymentTimestamp = block.timestamp;
}
/// @dev Allows the decorated function only to be executed before the
/// contract deployment date plus the input amount of seconds.
/// @param durationSinceDeployment Number of seconds after contract
/// deployment before which the function can be executed anymore. The
/// function reverts afterwards.
modifier before(uint256 durationSinceDeployment) {
// solhint-disable-next-line not-rely-on-time
if (block.timestamp > deploymentTimestamp + durationSinceDeployment) {
revert ClaimingExpired();
}
_;
}
/// @dev The decorated function can only be executed by the team controller.
modifier onlyTeamController() {
if (msg.sender != teamController) {
revert OnlyTeamController();
}
_;
}
/// @inheritdoc ClaimingInterface
function performClaim(
ClaimType claimType,
address payer,
address claimant,
uint256 amount,
uint256 sentNativeTokens
) internal override {
if (claimType == ClaimType.Airdrop) {
claimAirdrop(claimant, amount, sentNativeTokens);
} else if (claimType == ClaimType.GnoOption) {
claimGnoOption(claimant, amount, payer, sentNativeTokens);
} else if (claimType == ClaimType.UserOption) {
claimUserOption(claimant, amount, payer, sentNativeTokens);
} else if (claimType == ClaimType.Investor) {
claimInvestor(claimant, amount, payer, sentNativeTokens);
} else if (claimType == ClaimType.Team) {
claimTeam(claimant, amount, sentNativeTokens);
} else {
// claimType == ClaimType.Advisor
claimAdvisor(claimant, amount, sentNativeTokens);
}
// Each claiming operation results in the creation of `amount` virtual
// tokens.
totalSupply += amount;
emit Transfer(address(0), claimant, amount);
}
/// @dev Stops all vesting claims of a user. This is only applicable for
/// claims that are cancellable, i.e., team claims.
/// @param user The user whose vesting claims should be canceled.
function stopClaim(address user) external onlyTeamController {
uint256 accruedVesting = shiftVesting(user, teamController);
instantlySwappableBalance[user] += accruedVesting;
}
/// @dev Transfers all ETH stored in the contract to the community funds
// target.
function withdrawEth() external {
// We transfer ETH using .call instead of .transfer as not to restrict
// the amount of gas sent to the target address during the transfer.
// This is particularly relevant for sending ETH to smart contracts:
// since EIP 2929, if a contract sends eth using `.transfer` then the
// transaction proposed to the node needs to specify an _access list_,
// which is currently not well supported by some wallet implementations.
// There is no reentrancy risk as this call does not touch any storage
// slot and the contract balance is not used in other logic.
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = communityFundsTarget.call{
value: address(this).balance
}("");
if (!success) {
revert FailedNativeTokenTransfer();
}
}
/// @dev Performs an airdrop-type claim for the user.
/// @param account The user for which the claim is performed.
/// @param amount The full amount claimed by the user.
/// @param sentNativeTokens Amount of ETH sent along to the transaction.
function claimAirdrop(
address account,
uint256 amount,
uint256 sentNativeTokens
) private before(6 weeks) {
if (sentNativeTokens != 0) {
revert CannotSendNativeToken();
}
instantlySwappableBalance[account] += amount;
}
/// @dev Claims a Gno option for the user.
/// @param account The user for which the claim is performed.
/// @param amount The full amount claimed by the user after vesting.
/// @param payer The address that pays the amount required by the claim.
/// @param sentNativeTokens Amount of ETH sent along to the transaction.
function claimGnoOption(
address account,
uint256 amount,
address payer,
uint256 sentNativeTokens
) private before(2 weeks) {
if (sentNativeTokens != 0) {
revert CannotSendNativeToken();
}
collectPayment(gnoToken, gnoPrice, payer, communityFundsTarget, amount);
addVesting(account, amount, false);
}
/// @dev Claims a native-token-based option for the user.
/// @param account The user for which the claim is performed.
/// @param amount The full amount claimed by the user after vesting.
/// @param payer The address that pays the amount required by the claim.
/// @param sentNativeTokens Amount of ETH sent along to the transaction.
function claimUserOption(
address account,
uint256 amount,
address payer,
uint256 sentNativeTokens
) private before(2 weeks) {
if (sentNativeTokens != 0) {
collectNativeTokenPayment(amount, sentNativeTokens);
} else {
collectPayment(
wrappedNativeToken,
nativeTokenPrice,
payer,
communityFundsTarget,
amount
);
}
addVesting(account, amount, false);
}
/// @dev Claims an investor option.
/// @param account The user for which the claim is performed.
/// @param amount The full amount claimed by the user after vesting.
/// @param payer The address that pays the amount required by the claim.
/// @param sentNativeTokens Amount of ETH sent along to the transaction.
function claimInvestor(
address account,
uint256 amount,
address payer,
uint256 sentNativeTokens
) private before(2 weeks) {
if (sentNativeTokens != 0) {
revert CannotSendNativeToken();
}
collectPayment(
usdcToken,
usdcPrice,
payer,
investorFundsTarget,
amount
);
addVesting(account, amount, false);
}
/// @dev Claims a team option. Team options are granted without any payment
/// but can be canceled.
/// @param account The user for which the claim is performed.
/// @param amount The full amount claimed by the user after vesting.
/// @param sentNativeTokens Amount of ETH sent along to the transaction.
function claimTeam(
address account,
uint256 amount,
uint256 sentNativeTokens
) private before(6 weeks) {
if (sentNativeTokens != 0) {
revert CannotSendNativeToken();
}
addVesting(account, amount, true);
}
/// @dev Claims an adviser option. Team options are granted without any
/// payment and cannot be canceled.
/// @param account The user for which the claim is performed.
/// @param amount The full amount claimed by the user after vesting.
/// @param sentNativeTokens Amount of ETH sent along to the transaction.
function claimAdvisor(
address account,
uint256 amount,
uint256 sentNativeTokens
) private before(6 weeks) {
if (sentNativeTokens != 0) {
revert CannotSendNativeToken();
}
addVesting(account, amount, false);
}
/// @dev Executes a transfer from the user to the target. The transfered
/// amount is based on the input COW price and amount of COW bought.
/// @param token The token used for the payment.
/// @param price The number of atoms of the input token that are equivalent
/// to one atom of COW multiplied by PRICE_DENOMINATOR.
/// @param from The address from which to take the funds.
/// @param to The address to which to send the funds.
/// @param amount The amount of COW atoms that will be paid for.
function collectPayment(
IERC20 token,
uint256 price,
address from,
address to,
uint256 amount
) private {
uint256 tokenEquivalent = convertCowAmountAtPrice(amount, price);
token.safeTransferFrom(from, to, tokenEquivalent);
}
/// @dev Transfers native tokens from this contract to the target, assuming
/// that the amount of native tokens sent coincides with the expected amount
/// of native tokens. This amount is based on the price of the native token
/// and amount of COW bought.
/// @param amount The amount of COW atoms that will be paid for.
/// @param sentNativeTokens Amount of ETH sent along to the transaction.
function collectNativeTokenPayment(uint256 amount, uint256 sentNativeTokens)
private
view
{
uint256 nativeTokenEquivalent = convertCowAmountAtPrice(
amount,
nativeTokenPrice
);
if (sentNativeTokens != nativeTokenEquivalent) {
revert InvalidNativeTokenAmount();
}
}
/// @dev Converts input amount in COW token atoms to an amount in token
/// atoms at the specified price.
/// @param amount Amount of tokens to convert.
/// @param price The number of atoms of the input token that are equivalent
/// to one atom of COW *multiplied by PRICE_DENOMINATOR*.
function convertCowAmountAtPrice(uint256 amount, uint256 price)
private
pure
returns (uint256)
{
return (amount * price) / PRICE_DENOMINATOR;
}
/// @dev Converts an amount of (virtual) tokens from this contract to real
/// tokens based on the claims previously performed by the caller.
/// @param amount How many virtual tokens to convert into real tokens.
function swap(uint256 amount) external {
makeVestingSwappable();
_swap(amount);
}
/// @dev Converts all available (virtual) tokens from this contract to real
/// tokens based on the claims previously performed by the caller.
/// @return swappedBalance The full amount that was swapped (i.e., virtual
/// tokens burnt as well as real tokens received).
function swapAll() external returns (uint256 swappedBalance) {
swappedBalance = makeVestingSwappable();
_swap(swappedBalance);
}
/// @dev Transfers real tokens to the message sender and reduces the balance
/// of virtual tokens available. Note that this function assumes that the
/// current contract stores enough real tokens to fulfill this swap request.
/// @param amount How many virtual tokens to convert into real tokens.
function _swap(uint256 amount) private {
instantlySwappableBalance[msg.sender] -= amount;
totalSupply -= amount;
cowToken.safeTransfer(msg.sender, amount);
emit Transfer(msg.sender, address(0), amount);
}
/// @dev Adds the currently vested amount to the immediately swappable
/// balance.
/// @return swappableBalance The maximum balance that can be swapped at
/// this point in time by the caller.
function makeVestingSwappable() private returns (uint256 swappableBalance) {
swappableBalance =
instantlySwappableBalance[msg.sender] +
vest(msg.sender);
instantlySwappableBalance[msg.sender] = swappableBalance;
}
}
// SPDX-License-Identifier: LGPL-3.0-or-later
// This contract is based on Uniswap's MekleDistributor, which can be found at:
// https://github.com/Uniswap/merkle-distributor/blob/0d478d722da2e5d95b7292fd8cbdb363d98e9a93/contracts/MerkleDistributor.sol
//
// The changes between the original contract and this are:
// - the claim function doesn't trigger a transfer on a successful proof, but
// it executes a dedicated (virtual) function.
// - added a claimMany function for bundling multiple claims in a transaction
// - supported sending an amount of native tokens along with the claim
// - added the option of claiming less than the maximum amount
// - gas optimizations in the packing and unpacking of the claimed bit
// - bumped Solidity version
// - code formatting
pragma solidity ^0.8.10;
import "../vendored/interfaces/IERC20.sol";
import "../vendored/libraries/MerkleProof.sol";
import "../interfaces/ClaimingInterface.sol";
abstract contract MerkleDistributor is ClaimingInterface {
bytes32 public immutable merkleRoot;
/// @dev Event fired if a claim was successfully performed.
event Claimed(
uint256 index,
ClaimType claimType,
address claimant,
uint256 claimableAmount,
uint256 claimedAmount
);
/// @dev Error caused by a user trying to call the claim function for a
/// claim that has already been used before.
error AlreadyClaimed();
/// @dev Error caused by a user trying to claim a larger amount than the
/// maximum allowed in the claim.
error ClaimingMoreThanMaximum();
/// @dev Error caused by the caller trying to perform a partial claim while
/// not being the owner of the claim.
error OnlyOwnerCanClaimPartially();
/// @dev Error caused by calling the claim function with an invalid proof.
error InvalidProof();
/// @dev Error caused by calling claimMany with a transaction value that is
/// different from the required one.
error InvalidNativeTokenValue();
/// @dev Packed array of booleans that stores if a claim is available.
mapping(uint256 => uint256) private claimedBitMap;
constructor(bytes32 merkleRoot_) {
merkleRoot = merkleRoot_;
}
/// @dev Checks if the claim at the provided index has already been claimed.
/// @param index The index to check.
/// @return Whether the claim at the given index has already been claimed.
function isClaimed(uint256 index) public view returns (bool) {
uint256 claimedWordIndex = index >> 8;
uint256 claimedBitIndex = index & 0xff;
uint256 claimedWord = claimedBitMap[claimedWordIndex];
uint256 mask = (1 << claimedBitIndex);
return claimedWord & mask != 0;
}
/// @dev Mark the provided index as having been claimed.
/// @param index The index that was claimed.
function _setClaimed(uint256 index) private {
uint256 claimedWordIndex = index >> 8;
uint256 claimedBitIndex = index & 0xff;
claimedBitMap[claimedWordIndex] =
claimedBitMap[claimedWordIndex] |
(1 << claimedBitIndex);
}
/// @dev This function verifies the provided input proof based on the
/// provided input. If the proof is valid, the function [`performClaim`] is
/// called for the claimed amount.
/// @param index The index that identifies the input claim.
/// @param claimType See [`performClaim`].
/// @param claimant See [`performClaim`].
/// @param claimableAmount The maximum amount that the claimant can claim
/// for this claim. Should not be smaller than claimedAmount.
/// @param claimedAmount See [`performClaim`].
/// @param merkleProof A proof that the input claim belongs to the unique
/// Merkle root associated to this contract.
function claim(
uint256 index,
ClaimType claimType,
address claimant,
uint256 claimableAmount,
uint256 claimedAmount,
bytes32[] calldata merkleProof
) external payable {
_claim(
index,
claimType,
claimant,
claimableAmount,
claimedAmount,
merkleProof,
msg.value
);
}
/// @dev This function verifies and executes multiple claims in the same
/// transaction.
/// @param indices A vector of indices. See [`claim`] for details.
/// @param claimTypes A vector of claim types. See [`performClaim`] for
/// details.
/// @param claimants A vector of claimants. See [`performClaim`] for
/// details.
/// @param claimableAmounts A vector of claimable amounts. See [`claim`] for
/// details.
/// @param claimedAmounts A vector of claimed amounts. See [`performClaim`]
/// for details.
/// @param merkleProofs A vector of merkle proofs. See [`claim`] for
/// details.
/// @param sentNativeTokens A vector of native token amounts. See
/// [`performClaim`] for details.
function claimMany(
uint256[] memory indices,
ClaimType[] memory claimTypes,
address[] calldata claimants,
uint256[] calldata claimableAmounts,
uint256[] calldata claimedAmounts,
bytes32[][] calldata merkleProofs,
uint256[] calldata sentNativeTokens
) external payable {
uint256 sumSentNativeTokens;
for (uint256 i = 0; i < indices.length; i++) {
sumSentNativeTokens += sentNativeTokens[i];
_claim(
indices[i],
claimTypes[i],
claimants[i],
claimableAmounts[i],
claimedAmounts[i],
merkleProofs[i],
sentNativeTokens[i]
);
}
if (sumSentNativeTokens != msg.value) {
revert InvalidNativeTokenValue();
}
}
/// @dev This function verifies the provided input proof based on the
/// provided input. If the proof is valid, the function [`performClaim`] is
/// called for the claimed amount.
/// @param index See [`claim`].
/// @param claimType See [`performClaim`].
/// @param claimant See [`performClaim`].
/// @param claimableAmount See [`claim`].
/// @param claimedAmount See [`performClaim`].
/// @param merkleProof See [`claim`].
/// @param sentNativeTokens See [`performClaim`].
function _claim(
uint256 index,
ClaimType claimType,
address claimant,
uint256 claimableAmount,
uint256 claimedAmount,
bytes32[] calldata merkleProof,
uint256 sentNativeTokens
) private {
if (isClaimed(index)) {
revert AlreadyClaimed();
}
if (claimedAmount > claimableAmount) {
revert ClaimingMoreThanMaximum();
}
if ((claimedAmount < claimableAmount) && (msg.sender != claimant)) {
revert OnlyOwnerCanClaimPartially();
}
// Note: all types used inside `encodePacked` should have fixed length,
// otherwise the same proof could be used in different claims.
bytes32 node = keccak256(
abi.encodePacked(index, claimType, claimant, claimableAmount)
);
if (!MerkleProof.verify(merkleProof, merkleRoot, node)) {
revert InvalidProof();
}
_setClaimed(index);
performClaim(
claimType,
msg.sender,
claimant,
claimedAmount,
sentNativeTokens
);
emit Claimed(
index,
claimType,
claimant,
claimableAmount,
claimedAmount
);
}
}
// SPDX-License-Identifier: LGPL-3.0-only
// Vendored from Gnosis utility contracts, see:
// <https://raw.githubusercontent.com/gnosis/gp-v2-contracts/40c349d52d14f8f3c9f787fe2fca5a496bb10ea9/src/contracts/mixins/StorageAccessible.sol>
// The following changes were made:
// - Modified Solidity version
// - Formatted code
pragma solidity ^0.8.10;
/// @title ViewStorageAccessible - Interface on top of StorageAccessible base class to allow simulations from view functions
interface ViewStorageAccessible {
/**
* @dev Same as `simulateDelegatecall` on StorageAccessible. Marked as view so that it can be called from external contracts
* that want to run simulations from within view functions. Will revert if the invoked simulation attempts to change state.
*/
function simulateDelegatecall(
address targetContract,
bytes memory calldataPayload
) external view returns (bytes memory);
/**
* @dev Same as `getStorageAt` on StorageAccessible. This method allows reading aribtrary ranges of storage.
*/
function getStorageAt(uint256 offset, uint256 length)
external
view
returns (bytes memory);
}
/// @title StorageAccessible - generic base contract that allows callers to access all internal storage.
contract StorageAccessible {
/**
* @dev Reads `length` bytes of storage in the currents contract
* @param offset - the offset in the current contract's storage in words to start reading from
* @param length - the number of words (32 bytes) of data to read
* @return the bytes that were read.
*/
function getStorageAt(uint256 offset, uint256 length)
external
view
returns (bytes memory)
{
bytes memory result = new bytes(length * 32);
for (uint256 index = 0; index < length; index++) {
// solhint-disable-next-line no-inline-assembly
assembly {
let word := sload(add(offset, index))
mstore(add(add(result, 0x20), mul(index, 0x20)), word)
}
}
return result;
}
/**
* @dev Performs a delegetecall on a targetContract in the context of self.
* Internally reverts execution to avoid side effects (making it static). Catches revert and returns encoded result as bytes.
* @param targetContract Address of the contract containing the code to execute.
* @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments).
*/
function simulateDelegatecall(
address targetContract,
bytes memory calldataPayload
) public returns (bytes memory response) {
bytes memory innerCall = abi.encodeWithSelector(
this.simulateDelegatecallInternal.selector,
targetContract,
calldataPayload
);
// solhint-disable-next-line avoid-low-level-calls
(, response) = address(this).call(innerCall);
bool innerSuccess = response[response.length - 1] == 0x01;
setLength(response, response.length - 1);
if (innerSuccess) {
return response;
} else {
revertWith(response);
}
}
/**
* @dev Performs a delegetecall on a targetContract in the context of self.
* Internally reverts execution to avoid side effects (making it static). Returns encoded result as revert message
* concatenated with the success flag of the inner call as a last byte.
* @param targetContract Address of the contract containing the code to execute.
* @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments).
*/
function simulateDelegatecallInternal(
address targetContract,
bytes memory calldataPayload
) external returns (bytes memory response) {
bool success;
// solhint-disable-next-line avoid-low-level-calls
(success, response) = targetContract.delegatecall(calldataPayload);
revertWith(abi.encodePacked(response, success));
}
function revertWith(bytes memory response) internal pure {
// solhint-disable-next-line no-inline-assembly
assembly {
revert(add(response, 0x20), mload(response))
}
}
function setLength(bytes memory buffer, uint256 length) internal pure {
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(buffer, length)
}
}
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/token/ERC20/IERC20.sol>
// OpenZeppelin Contracts v4.4.0 (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount)
external
returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender)
external
view
returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/utils/math/Math.sol>
// OpenZeppelin Contracts v4.4.0 (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a / b + (a % b == 0 ? 0 : 1);
}
}
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.8.10;
/// @dev The contract functions that are shared between the `Vesting` and
/// `Claiming` contracts. The two components are handled and tested
/// separately and are linked to each other by the functions in this contract.
/// This contracs is for all intents and purposes an interface, however actual
/// interfaces cannot declare internal functions.
/// @title COW token vesting interface.
/// @author CoW Protocol Developers
abstract contract VestingInterface {
/// @dev Adds an amount that will be vested over time.
/// Should be called from the parent contract on redeeming a vested claim.
/// @param user The user for whom the vesting is performed.
/// @param vestingAmount The (added) amount to be vested in time.
/// @param isCancelableFlag Flag whether the vesting is cancelable
function addVesting(
address user,
uint256 vestingAmount,
bool isCancelableFlag
) internal virtual;
/// @dev Computes the current vesting from the total vested amount and marks
/// that amount as converted. This is called by the parent contract every
/// time virtual tokens from a vested claim are swapped into real tokens.
/// @param user The user for which the amount is vested.
/// @return Amount converted.
function vest(address user) internal virtual returns (uint256);
/// @dev Transfers a cancelable vesting of a user to another address.
/// Returns the amount of token that is not yet converted.
/// @param user The user for whom the vesting is removed.
/// @param freedVestingBeneficiary The address to which to assign the amount
/// that remains to be vested.
/// @return accruedVesting The total number of tokens that remain to be
/// converted
function shiftVesting(address user, address freedVestingBeneficiary)
internal
virtual
returns (uint256 accruedVesting);
}
// SPDX-License-Identifier: LGPL-3.0-or-later
// Vendored from GPv2 contracts v1.1.2, see:
// <https://raw.githubusercontent.com/gnosis/gp-v2-contracts/7fb88982021e9a274d631ffb598694e6d9b30089/src/contracts/libraries/GPv2SafeERC20.sol>
// The following changes were made:
// - Bumped up Solidity version and checked that the assembly is still valid.
// - Use own vendored IERC20 instead of custom implementation.
// - Removed "GPv2" from contract name.
// - Modified revert messages, including length.
pragma solidity ^0.8.10;
import "../interfaces/IERC20.sol";
/// @title Gnosis Protocol v2 Safe ERC20 Transfer Library
/// @author Gnosis Developers
/// @dev Gas-efficient version of Openzeppelin's SafeERC20 contract.
library SafeERC20 {
/// @dev Wrapper around a call to the ERC20 function `transfer` that reverts
/// also when the token returns `false`.
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
bytes4 selector_ = token.transfer.selector;
// solhint-disable-next-line no-inline-assembly
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, selector_)
mstore(
add(freeMemoryPointer, 4),
and(to, 0xffffffffffffffffffffffffffffffffffffffff)
)
mstore(add(freeMemoryPointer, 36), value)
if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
require(getLastTransferResult(token), "SafeERC20: failed transfer");
}
/// @dev Wrapper around a call to the ERC20 function `transferFrom` that
/// reverts also when the token returns `false`.
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
bytes4 selector_ = token.transferFrom.selector;
// solhint-disable-next-line no-inline-assembly
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, selector_)
mstore(
add(freeMemoryPointer, 4),
and(from, 0xffffffffffffffffffffffffffffffffffffffff)
)
mstore(
add(freeMemoryPointer, 36),
and(to, 0xffffffffffffffffffffffffffffffffffffffff)
)
mstore(add(freeMemoryPointer, 68), value)
if iszero(call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
require(getLastTransferResult(token), "SafeERC20: failed transferFrom");
}
/// @dev Verifies that the last return was a successful `transfer*` call.
/// This is done by checking that the return data is either empty, or
/// is a valid ABI encoded boolean.
function getLastTransferResult(IERC20 token)
private
view
returns (bool success)
{
// NOTE: Inspecting previous return data requires assembly. Note that
// we write the return data to memory 0 in the case where the return
// data size is 32, this is OK since the first 64 bytes of memory are
// reserved by Solidy as a scratch space that can be used within
// assembly blocks.
// <https://docs.soliditylang.org/en/v0.8.10/internals/layout_in_memory.html>
// solhint-disable-next-line no-inline-assembly
assembly {
/// @dev Revert with an ABI encoded Solidity error with a message
/// that fits into 32-bytes.
///
/// An ABI encoded Solidity error has the following memory layout:
///
/// ------------+----------------------------------
/// byte range | value
/// ------------+----------------------------------
/// 0x00..0x04 | selector("Error(string)")
/// 0x04..0x24 | string offset (always 0x20)
/// 0x24..0x44 | string length
/// 0x44..0x64 | string value, padded to 32-bytes
function revertWithMessage(length, message) {
mstore(0x00, "\\x08\\xc3\\x79\\xa0")
mstore(0x04, 0x20)
mstore(0x24, length)
mstore(0x44, message)
revert(0x00, 0x64)
}
switch returndatasize()
// Non-standard ERC20 transfer without return.
case 0 {
// NOTE: When the return data size is 0, verify that there
// is code at the address. This is done in order to maintain
// compatibility with Solidity calling conventions.
// <https://docs.soliditylang.org/en/v0.8.10/control-structures.html#external-function-calls>
if iszero(extcodesize(token)) {
revertWithMessage(25, "SafeERC20: not a contract")
}
success := 1
}
// Standard ERC20 transfer returning boolean success value.
case 32 {
returndatacopy(0, 0, returndatasize())
// NOTE: For ABI encoding v1, any non-zero value is accepted
// as `true` for a boolean. In order to stay compatible with
// OpenZeppelin's `SafeERC20` library which is known to work
// with the existing ERC20 implementation we care about,
// make sure we return success for any non-zero return value
// from the `transfer*` call.
success := iszero(iszero(mload(0)))
}
default {
revertWithMessage(30, "SafeERC20: bad transfer result")
}
}
}
}
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.8.10;
/// @dev The contract functions that are shared between the `Claiming` and
/// `MerkleDistributor` contracts. The two components are handled and tested
/// separately and are linked to each other by the functions in this contract.
/// This contracs is for all intents and purposes an interface, however actual
/// interfaces cannot declare internal functions.
/// @title COW token claiming interface.
/// @author CoW Protocol Developers
abstract contract ClaimingInterface {
/// @dev Exhaustive list of the different branches of the claiming logic.
enum ClaimType {
Airdrop,
GnoOption,
UserOption,
Investor,
Team,
Advisor
}
/// @dev This function is executed when a valid proof of the claim is
/// provided and executes all steps required for each claim type.
/// @param claimType Which claim will be performed. See [`ClaimType`] for
/// an exausting list.
/// @param payer The address that will pay if the claim to be performed
/// requires a payment.
/// @param claimant The account to which the claim is assigned and which
/// will receive the corresponding virtual tokens.
/// @param claimedAmount The amount that the user decided to claim (after
/// vesting if it applies).
/// @param sentNativeTokens The amount of native tokens that the user sent
/// along with the transaction.
function performClaim(
ClaimType claimType,
address payer,
address claimant,
uint256 claimedAmount,
uint256 sentNativeTokens
) internal virtual;
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/utils/cryptography/MerkleProof.sol>
// OpenZeppelin Contracts v4.4.0 (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.0;
/**
* @dev These functions deal with verification of Merkle Trees proofs.
*
* The proofs can be generated using the JavaScript library
* https://github.com/miguelmota/merkletreejs[merkletreejs].
* Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
*
* See `test/utils/cryptography/MerkleProof.test.js` for some examples.
*/
library MerkleProof {
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merklee tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*
* _Available since v4.4._
*/
function processProof(bytes32[] memory proof, bytes32 leaf)
internal
pure
returns (bytes32)
{
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
// Hash(current computed hash + current element of the proof)
computedHash = keccak256(
abi.encodePacked(computedHash, proofElement)
);
} else {
// Hash(current element of the proof + current computed hash)
computedHash = keccak256(
abi.encodePacked(proofElement, computedHash)
);
}
}
return computedHash;
}
}
File 2 of 2: CowProtocolToken
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.8.10;
import "./mixins/InflationaryToken.sol";
import "./vendored/mixins/StorageAccessible.sol";
/// @dev The governance token for the CoW Protocol.
/// @title CoW Protocol Governance Token
/// @author CoW Protocol Developers
contract CowProtocolToken is InflationaryToken, StorageAccessible {
string private constant ERC20_SYMBOL = "COW";
string private constant ERC20_NAME = "CoW Protocol Token";
constructor(
address initialTokenHolder,
address cowDao,
uint256 totalSupply
)
InflationaryToken(
initialTokenHolder,
cowDao,
totalSupply,
ERC20_NAME,
ERC20_SYMBOL
)
// solhint-disable-next-line no-empty-blocks
{
}
}
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.8.10;
import "../vendored/mixins/ERC20.sol";
import "../vendored/mixins/draft-ERC20Permit.sol";
/// @dev Contract contains the logic for minting new tokens
/// @title Mintable Token
/// @author CoW Protocol Developers
contract InflationaryToken is ERC20, ERC20Permit {
/// @dev Defines the cowDao address that is allowed to mint new tokens
address public immutable cowDao;
/// @dev Defines how frequently inflation can be triggered: Once a year
uint256 public constant TIME_BETWEEN_MINTINGS = 365 days;
/// @dev Defines the maximal inflation per year
uint256 public constant MAX_YEARLY_INFLATION = 3;
/// @dev Stores the timestamp of the last inflation event
uint256 public timestampLastMinting = 0;
/// @dev Error caused by an attempt to mint too many tokens.
error ExceedingMintCap();
/// @dev Error caused by calling the mint function more than once within one year.
error AlreadyInflated();
/// @dev Error caused by calling the mint function from a non-cowDao account.
error OnlyCowDao();
modifier onlyCowDao() {
if (msg.sender != cowDao) {
revert OnlyCowDao();
}
_;
}
constructor(
address initialTokenHolder,
address _cowDao,
uint256 totalSupply,
string memory erc20Name,
string memory erc20Symbol
) ERC20(erc20Name, erc20Symbol) ERC20Permit(erc20Name) {
_mint(initialTokenHolder, totalSupply);
cowDao = _cowDao;
// solhint-disable-next-line not-rely-on-time
timestampLastMinting = block.timestamp;
}
/// @dev This function allows to mint new tokens
/// @param target The address that should receive the new tokens
/// @param amount The amount of tokens to be minted.
function mint(address target, uint256 amount) external onlyCowDao {
if (amount > (totalSupply() * MAX_YEARLY_INFLATION) / 100) {
revert ExceedingMintCap();
}
// solhint-disable-next-line not-rely-on-time
if (timestampLastMinting + TIME_BETWEEN_MINTINGS > block.timestamp) {
revert AlreadyInflated();
}
timestampLastMinting = block.timestamp; // solhint-disable-line not-rely-on-time
_mint(target, amount);
}
}
// SPDX-License-Identifier: LGPL-3.0-only
// Vendored from Gnosis utility contracts, see:
// <https://raw.githubusercontent.com/gnosis/gp-v2-contracts/40c349d52d14f8f3c9f787fe2fca5a496bb10ea9/src/contracts/mixins/StorageAccessible.sol>
// The following changes were made:
// - Modified Solidity version
// - Formatted code
pragma solidity ^0.8.10;
/// @title ViewStorageAccessible - Interface on top of StorageAccessible base class to allow simulations from view functions
interface ViewStorageAccessible {
/**
* @dev Same as `simulateDelegatecall` on StorageAccessible. Marked as view so that it can be called from external contracts
* that want to run simulations from within view functions. Will revert if the invoked simulation attempts to change state.
*/
function simulateDelegatecall(
address targetContract,
bytes memory calldataPayload
) external view returns (bytes memory);
/**
* @dev Same as `getStorageAt` on StorageAccessible. This method allows reading aribtrary ranges of storage.
*/
function getStorageAt(uint256 offset, uint256 length)
external
view
returns (bytes memory);
}
/// @title StorageAccessible - generic base contract that allows callers to access all internal storage.
contract StorageAccessible {
/**
* @dev Reads `length` bytes of storage in the currents contract
* @param offset - the offset in the current contract's storage in words to start reading from
* @param length - the number of words (32 bytes) of data to read
* @return the bytes that were read.
*/
function getStorageAt(uint256 offset, uint256 length)
external
view
returns (bytes memory)
{
bytes memory result = new bytes(length * 32);
for (uint256 index = 0; index < length; index++) {
// solhint-disable-next-line no-inline-assembly
assembly {
let word := sload(add(offset, index))
mstore(add(add(result, 0x20), mul(index, 0x20)), word)
}
}
return result;
}
/**
* @dev Performs a delegetecall on a targetContract in the context of self.
* Internally reverts execution to avoid side effects (making it static). Catches revert and returns encoded result as bytes.
* @param targetContract Address of the contract containing the code to execute.
* @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments).
*/
function simulateDelegatecall(
address targetContract,
bytes memory calldataPayload
) public returns (bytes memory response) {
bytes memory innerCall = abi.encodeWithSelector(
this.simulateDelegatecallInternal.selector,
targetContract,
calldataPayload
);
// solhint-disable-next-line avoid-low-level-calls
(, response) = address(this).call(innerCall);
bool innerSuccess = response[response.length - 1] == 0x01;
setLength(response, response.length - 1);
if (innerSuccess) {
return response;
} else {
revertWith(response);
}
}
/**
* @dev Performs a delegetecall on a targetContract in the context of self.
* Internally reverts execution to avoid side effects (making it static). Returns encoded result as revert message
* concatenated with the success flag of the inner call as a last byte.
* @param targetContract Address of the contract containing the code to execute.
* @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments).
*/
function simulateDelegatecallInternal(
address targetContract,
bytes memory calldataPayload
) external returns (bytes memory response) {
bool success;
// solhint-disable-next-line avoid-low-level-calls
(success, response) = targetContract.delegatecall(calldataPayload);
revertWith(abi.encodePacked(response, success));
}
function revertWith(bytes memory response) internal pure {
// solhint-disable-next-line no-inline-assembly
assembly {
revert(add(response, 0x20), mload(response))
}
}
function setLength(bytes memory buffer, uint256 length) internal pure {
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(buffer, length)
}
}
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/token/ERC20/ERC20.sol>
// The following changes were made:
// - Vendored imports
// OpenZeppelin Contracts v4.4.0 (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "../interfaces/IERC20.sol";
import "../interfaces/IERC20Metadata.sol";
import "../interfaces/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account)
public
view
virtual
override
returns (uint256)
{
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount)
public
virtual
override
returns (bool)
{
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender)
public
view
virtual
override
returns (uint256)
{
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount)
public
virtual
override
returns (bool)
{
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(
currentAllowance >= amount,
"ERC20: transfer amount exceeds allowance"
);
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue)
public
virtual
returns (bool)
{
_approve(
_msgSender(),
spender,
_allowances[_msgSender()][spender] + addedValue
);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue)
public
virtual
returns (bool)
{
uint256 currentAllowance = _allowances[_msgSender()][spender];
require(
currentAllowance >= subtractedValue,
"ERC20: decreased allowance below zero"
);
unchecked {
_approve(_msgSender(), spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `sender` to `recipient`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(
address sender,
address recipient,
uint256 amount
) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
uint256 senderBalance = _balances[sender];
require(
senderBalance >= amount,
"ERC20: transfer amount exceeds balance"
);
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
_afterTokenTransfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/token/ERC20/extensions/draft-ERC20Permit.sol>
// The following changes were made:
// - Vendored imports
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/draft-ERC20Permit.sol)
pragma solidity ^0.8.0;
import "../interfaces/draft-IERC20Permit.sol";
import "./ERC20.sol";
import "./draft-EIP712.sol";
import "../libraries/ECDSA.sol";
import "../libraries/Counters.sol";
/**
* @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* _Available since v3.4._
*/
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
using Counters for Counters.Counter;
mapping(address => Counters.Counter) private _nonces;
// solhint-disable-next-line var-name-mixedcase
bytes32 private immutable _PERMIT_TYPEHASH =
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
);
/**
* @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
*
* It's a good idea to use the same `name` that is defined as the ERC20 token name.
*/
constructor(string memory name) EIP712(name, "1") {}
/**
* @dev See {IERC20Permit-permit}.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual override {
require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
bytes32 structHash = keccak256(
abi.encode(
_PERMIT_TYPEHASH,
owner,
spender,
value,
_useNonce(owner),
deadline
)
);
bytes32 hash = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(hash, v, r, s);
require(signer == owner, "ERC20Permit: invalid signature");
_approve(owner, spender, value);
}
/**
* @dev See {IERC20Permit-nonces}.
*/
function nonces(address owner)
public
view
virtual
override
returns (uint256)
{
return _nonces[owner].current();
}
/**
* @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view override returns (bytes32) {
return _domainSeparatorV4();
}
/**
* @dev "Consume a nonce": return the current value and increment.
*
* _Available since v4.1._
*/
function _useNonce(address owner)
internal
virtual
returns (uint256 current)
{
Counters.Counter storage nonce = _nonces[owner];
current = nonce.current();
nonce.increment();
}
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/token/ERC20/IERC20.sol>
// OpenZeppelin Contracts v4.4.0 (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount)
external
returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender)
external
view
returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/token/ERC20/extensions/IERC20Metadata.sol>
// The following changes were made:
// - Vendored imports
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/utils/Context.sol>
// OpenZeppelin Contracts v4.4.0 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @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 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) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/token/ERC20/extensions/draft-IERC20Permit.sol>
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/utils/cryptography/draft-EIP712.sol>
// The following changes were made:
// - Vendored imports
// OpenZeppelin Contracts v4.4.0 (utils/cryptography/draft-EIP712.sol)
pragma solidity ^0.8.0;
import "../libraries/ECDSA.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* _Available since v3.4._
*/
abstract contract EIP712 {
/* solhint-disable var-name-mixedcase */
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
uint256 private immutable _CACHED_CHAIN_ID;
address private immutable _CACHED_THIS;
bytes32 private immutable _HASHED_NAME;
bytes32 private immutable _HASHED_VERSION;
bytes32 private immutable _TYPE_HASH;
/* solhint-enable var-name-mixedcase */
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
bytes32 hashedName = keccak256(bytes(name));
bytes32 hashedVersion = keccak256(bytes(version));
bytes32 typeHash = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
_HASHED_NAME = hashedName;
_HASHED_VERSION = hashedVersion;
_CACHED_CHAIN_ID = block.chainid;
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(
typeHash,
hashedName,
hashedVersion
);
_CACHED_THIS = address(this);
_TYPE_HASH = typeHash;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (
address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID
) {
return _CACHED_DOMAIN_SEPARATOR;
} else {
return
_buildDomainSeparator(
_TYPE_HASH,
_HASHED_NAME,
_HASHED_VERSION
);
}
}
function _buildDomainSeparator(
bytes32 typeHash,
bytes32 nameHash,
bytes32 versionHash
) private view returns (bytes32) {
return
keccak256(
abi.encode(
typeHash,
nameHash,
versionHash,
block.chainid,
address(this)
)
);
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash)
internal
view
virtual
returns (bytes32)
{
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
}
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/utils/cryptography/ECDSA.sol>
// The following changes were made:
// - Vendored imports
// OpenZeppelin Contracts v4.4.0 (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "./Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
} else if (error == RecoverError.InvalidSignatureV) {
revert("ECDSA: invalid signature 'v' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature)
internal
pure
returns (address, RecoverError)
{
// Check the signature length
// - case 65: r,s,v signature (standard)
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return tryRecover(hash, r, vs);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature)
internal
pure
returns (address)
{
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s;
uint8 v;
assembly {
s := and(
vs,
0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
)
v := add(shr(255, vs), 27)
}
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (
uint256(s) >
0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
) {
return (address(0), RecoverError.InvalidSignatureS);
}
if (v != 27 && v != 28) {
return (address(0), RecoverError.InvalidSignatureV);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash)
internal
pure
returns (bytes32)
{
// 32 is the length in bytes of hash,
// enforced by the type signature above
return
keccak256(
abi.encodePacked("\\x19Ethereum Signed Message:\
32", hash)
);
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s)
internal
pure
returns (bytes32)
{
return
keccak256(
abi.encodePacked(
"\\x19Ethereum Signed Message:\
",
Strings.toString(s.length),
s
)
);
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash)
internal
pure
returns (bytes32)
{
return
keccak256(
abi.encodePacked("\\x19\\x01", domainSeparator, structHash)
);
}
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/utils/Counters.sol>
// OpenZeppelin Contracts v4.4.0 (utils/Counters.sol)
pragma solidity ^0.8.0;
/**
* @title Counters
* @author Matt Condon (@shrugs)
* @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
* of elements in a mapping, issuing ERC721 ids, or counting request ids.
*
* Include with `using Counters for Counters.Counter;`
*/
library Counters {
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
// this feature: see https://github.com/ethereum/solidity/issues/4637
uint256 _value; // default: 0
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
unchecked {
counter._value += 1;
}
}
function decrement(Counter storage counter) internal {
uint256 value = counter._value;
require(value > 0, "Counter: decrement overflow");
unchecked {
counter._value = value - 1;
}
}
function reset(Counter storage counter) internal {
counter._value = 0;
}
}
// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin Contracts v4.4.0, see:
// <https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.4.0/contracts/utils/Strings.sol>
// OpenZeppelin Contracts v4.4.0 (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length)
internal
pure
returns (string memory)
{
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}