Address Contract Partially Verified
Address
0x9ef444a6d7F4A5adcd68FD5329aA5240C90E14d2
Balance
0 ETH
Nonce
1
Code Size
7703 bytes
Creator
0x45d146CA...c873 at tx 0x97ef0a57...931d52
Indexed Transactions
0
Contract Bytecode
7703 bytes
0x6080604081815260048036101561001557600080fd5b600092833560e01c90816306fdde031461124f57508063095ea7b31461122557806318160ddd146112065780631bfa4c04146110ef5780631d49d66c1461104b57806323b872dd14610f805780632e1a7d4d14610f63578063313ce56714610e935780633950935114610e445780634216f97214610de95780634e71d92d14610dd05780637026122214610bbf57806370a0823114610b88578063715018a614610b2257806372f702f314610ade57806375619ab5146109fd57806378e3214f146107035780638da5cb5b146106dd57806395d89b41146105db578063a457c2d71461051b578063a9059cbb146104ea578063b6b55f251461031f578063bfe10928146102f7578063d1af0c7d146102b3578063dd62ed3e1461026a578063e9fad8ee146102355763f2fde38b1461014c57600080fd5b346102315760203660031901126102315761016561138c565b9061016e6119e9565b6001600160a01b038092169283156101c857505082548273ffffffffffffffffffffffffffffffffffffffff198216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b906020608492519162461bcd60e51b8352820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b8280fd5b50503461026657816003193601126102665761025b9033835260016020528220546114b7565b6102636116af565b80f35b5080fd5b5050346102665780600319360112610266578060209261028861138c565b6102906113a7565b6001600160a01b0391821683526002865283832091168252845220549051908152f35b505034610266578160031936011261026657602090516001600160a01b037f000000000000000000000000ba3335588d9403515223f109edc4eb7269a9ab5d168152f35b5050346102665781600319360112610266576020906001600160a01b03600654169051908152f35b503461023157602091826003193601126104e65781359233156104a557831515808061049d575b610407575b5060019061035b85600354611756565b6003553386528181528286208581540190558251858152867fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef833393a3338652526d04ee2d6d415b85acef810000000081852054116103e157836102638430337f000000000000000000000000da00000035fef4082f78def6a8903bee419fbf8e6118ae565b517f24691f6b000000000000000000000000000000000000000000000000000000008152fd5b61040f611779565b9061041d8383015192611411565b9080610495575b61042f575b5061034b565b4264ffffffffff16602882901b64ffffffffff19161760028301556003906104579087611848565b9133885201825282862080548783820193841291129080158216911516176104825755600138610429565b602487601187634e487b7160e01b835252fd5b506001610424565b506001610346565b60649291519162461bcd60e51b8352820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b8380fd5b50503461026657806003193601126102665760209061051461050a61138c565b6024359033611a41565b5160018152f35b5082346105d857826003193601126105d85761053561138c565b91836024359233815260026020528181206001600160a01b038616825260205220549082821061056f576020856105148585038733611cad565b608490602086519162461bcd60e51b8352820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152fd5b80fd5b509034610231578260031936011261023157805191836005549060019082821c9282811680156106d3575b60209586861082146106c0575084885290811561069e5750600114610645575b6106418686610637828b03836113ef565b5191829182611343565b0390f35b929550600583527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b82841061068b575050508261064194610637928201019438610626565b805486850188015292860192810161066e565b60ff191687860152505050151560051b83010192506106378261064138610626565b836022602492634e487b7160e01b835252fd5b93607f1693610606565b5050346102665781600319360112610266576001600160a01b0360209254169051908152f35b5090346102315780600319360112610231576001600160a01b038235818116929183820361092c576024938435928260065416918233036109ee578061085c575050506006541681471061081a578580809381935af13d15610815573d67ffffffffffffffff811161080357825190610786601f8201601f1916602001836113ef565b81528560203d92013e5b1561079b5750505080f35b608492916020603a92519362461bcd60e51b85528401528201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152fd5b8386604187634e487b7160e01b835252fd5b610790565b606485601d86602087519362461bcd60e51b85528401528201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152fd5b909395919492809897987f000000000000000000000000da00000035fef4082f78def6a8903bee419fbf8e16908183146000146109425750602091508351928380926370a0823160e01b8252308c8301525afa908115610938578691610903575b506108ca85600354611756565b116108db575061026393945061194f565b8590517f356680b7000000000000000000000000000000000000000000000000000000008152fd5b90506020813d8211610930575b8161091d602093836113ef565b8101031261092c5751386108bd565b8580fd5b3d9150610910565b82513d88823e3d90fd5b90507f000000000000000000000000ba3335588d9403515223f109edc4eb7269a9ab5d1680911461097c575b50505061026393945061194f565b6020908351928380926370a0823160e01b8252308c8301525afa9081156109385786916109bd575b506109b185600854611756565b116108db57808061096e565b90506020813d82116109e6575b816109d7602093836113ef565b8101031261092c5751386109a4565b3d91506109ca565b878651634ca8886760e01b8152fd5b50903461023157602036600319011261023157610a1861138c565b90610a216119e9565b6001600160a01b03809216928315610ab75760065492831690818514610a905750817fa9f739537fc57540bed0a44e33e27baa63290d865cc15f0f16cf17d38c998a4d9173ffffffffffffffffffffffffffffffffffffffff19949351908152856020820152a1161760065580f35b82517f8750ee90000000000000000000000000000000000000000000000000000000008152fd5b90517f71702079000000000000000000000000000000000000000000000000000000008152fd5b505034610266578160031936011261026657602090516001600160a01b037f000000000000000000000000da00000035fef4082f78def6a8903bee419fbf8e168152f35b83346105d857806003193601126105d857610b3b6119e9565b806001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50503461026657602036600319011261026657806020926001600160a01b03610baf61138c565b1681526001845220549051908152f35b5090346102315780600319360112610231578135916024356001600160a01b03600654163303610dc157610bf1611779565b610c20610c02602083015192611411565b4264ffffffffff1660289190911b64ffffffffff1916176002830155565b84928215610d9a5763ffffffff90818411610d735782549064ffffffffff90818316926001860193845495814210610d2c575b5050506d04ee2d6d415b85acef81000000008711610d05575091859391610cad7f3efe2b1ade87153c913a322f09a35c930d7fd699770b8d7cdd294e1debf6e9e4978795610ca66102639c9b9942611756565b1693611756565b905568ffffffffffffffffff198460481b1668ffffffff00000000008460281b161717905582519182526020820152a130337f000000000000000000000000ba3335588d9403515223f109edc4eb7269a9ab5d6118ae565b87517f06250401000000000000000000000000000000000000000000000000000000008152fd5b610d6a939950610d5a85610d538594670de0b6b3a764000094610d649760281c1690611824565b16896117b8565b049060481c61183b565b89611756565b95388080610c53565b85517f426a1073000000000000000000000000000000000000000000000000000000008152fd5b84517f68d5686e000000000000000000000000000000000000000000000000000000008152fd5b509051634ca8886760e01b8152fd5b83346105d857806003193601126105d8576102636116af565b50503461026657602036600319011261026657610e3d602092610e0a61138c565b83610e13611779565b926001600160a01b038316815260018752205490610e376002878501510193611411565b92611874565b9051908152f35b505034610266578060031936011261026657610514602092610e8c610e6761138c565b91338152600286528481206001600160a01b0384168252865284602435912054611756565b9033611cad565b5091346105d857806003193601126105d8576020825180947f313ce567000000000000000000000000000000000000000000000000000000008252816001600160a01b037f000000000000000000000000da00000035fef4082f78def6a8903bee419fbf8e165afa928315610f57578193610f16575b60208460ff855191168152f35b9092506020813d8211610f4f575b81610f31602093836113ef565b8101031261023157519160ff831683036105d8575060ff6020610f09565b3d9150610f24565b509051903d90823e3d90fd5b8382346102665760203660031901126102665761026390356114b7565b5082903461026657606036600319011261026657610f9c61138c565b610fa46113a7565b9184604435946001600160a01b038416815260026020528181203382526020522054906000198203610fdf575b602086610514878787611a41565b8482106110085750918391610ffd6020969561051495033383611cad565b919394819350610fd1565b606490602087519162461bcd60e51b8352820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152fd5b5050346102665781600319360112610266576080916060825161106d816113bd565b828152826020820152828482015201528051611088816113bd565b6007549176ffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff8416938484526020840163ffffffff90818360281c1681528486019260481c835260606008549601958652845196875251166020860152511690830152516060820152f35b50346102315782600319360112610231576001600160a01b036006541633036111f857507f3efe2b1ade87153c913a322f09a35c930d7fd699770b8d7cdd294e1debf6e9e461113c611779565b9161116c61114e602085015194611411565b4264ffffffffff1660289190911b64ffffffffff1916176002850155565b8254926111a264ffffffffff94670de0b6b3a7640000610d5a8761119b63ffffffff8560281c16828616611824565b16856117b8565b93600182016111b286825461183b565b9055421690558051848152846020820152a1806111cd575080f35b61026390337f000000000000000000000000ba3335588d9403515223f109edc4eb7269a9ab5d61194f565b9051634ca8886760e01b8152fd5b5050346102665781600319360112610266576020906003549051908152f35b50503461026657806003193601126102665760209061051461124561138c565b6024359033611cad565b90508284346105d857806003193601126105d857809380549160019083821c92828516948515611339575b60209586861081146113265785895290811561130257506001146112aa575b6106418787610637828c03836113ef565b81529295507f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b8284106112ef57505050826106419461063792820101948680611299565b80548685018801529286019281016112d1565b60ff19168887015250505050151560051b8301019250610637826106418680611299565b602484602285634e487b7160e01b835252fd5b93607f169361127a565b6020808252825181830181905290939260005b82811061137857505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501611356565b600435906001600160a01b03821682036113a257565b600080fd5b602435906001600160a01b03821682036113a257565b6080810190811067ffffffffffffffff8211176113d957604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176113d957604052565b602081016002815101549064ffffffffff82169160281c92824203611437575b50505090565b61143f61148d565b505160031461145e57634e487b7160e01b600052605160045260246000fd5b600354908115611431576114849261147f9161147861148d565b50516117b8565b611798565b01388080611431565b604051906040820182811067ffffffffffffffff8211176113d95760405260006020838281520152565b331561164557801515808061163d575b6115c2575b503360005260016020526040600020549080821061155857806115569233600052600160205203604060002055806003540360035560006040518281527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203392a3337f000000000000000000000000da00000035fef4082f78def6a8903bee419fbf8e61194f565b565b608460405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152fd5b6115ca611779565b906115d9602083015192611411565b9080611635575b6115eb575b506114cc565b4264ffffffffff16602882901b64ffffffffff19161760028301556003906116139084611848565b91336000520160205261162c604060002091825461185b565b905538806115e5565b5060016115e0565b5060016114c7565b608460405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152fd5b6116b7611779565b3360005260016020526040600020546116d4602083015192611411565b6116e381833360028701611874565b928315159283611723575b5050506116f85750565b61155690337f000000000000000000000000ba3335588d9403515223f109edc4eb7269a9ab5d61194f565b60019261172f91611848565b33600052600382016020526040600020550161174c83825461183b565b90553880806116ee565b9190820180921161176357565b634e487b7160e01b600052601160045260246000fd5b61178161148d565b5061178a61148d565b600381526007602082015290565b81156117a2570490565b634e487b7160e01b600052601260045260246000fd5b91906000925464ffffffffff81169063ffffffff8160281c1692836117dd5750505050565b61181394955090670de0b6b3a7640000929182421060001461181e5742925b808210156118165750905b60481c91030202611798565b90565b905090611807565b826117fc565b64ffffffffff918216908216039190821161176357565b9190820391821161176357565b8181029291811591840414171561176357565b8181039291600013801582851316918412161761176357565b6001906001600160a01b03611895670de0b6b3a7640000966118aa96611848565b9316600052016020526040600020549061185b565b0490565b9260209291606491600093604051927f23b872dd00000000000000000000000000000000000000000000000000000000845260048401526024830152604482015282855af1908161192c575b501561190257565b60046040517ff4059071000000000000000000000000000000000000000000000000000000008152fd5b90503d156119475750600160005114601f3d11165b386118fa565b3b1515611941565b916044602092600092604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083526004830152602482015282855af190816119c6575b501561199c57565b60046040517ffb7f5079000000000000000000000000000000000000000000000000000000008152fd5b90503d156119e15750600160005114601f3d11165b38611994565b3b15156119db565b6001600160a01b036000541633036119fd57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b6001600160a01b03809116918215611c435716918215611bd9578015158080611bcf575b611b33575b5060008281526001602052604081205491808310611ac957604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef95876020965260018652038282205586815220818154019055604051908152a3565b608460405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152fd5b611b3b611779565b90611b4a602083015192611411565b9080611bc5575b611b5c575b50611a6a565b611b6860039184611848565b91016000908482528060205260408220611b8384825461185b565b905585825260205260408120908154818482019485129112908015821691151617611bb15750553880611b56565b80634e487b7160e01b602492526011600452fd5b5084841415611b51565b5083831415611a65565b608460405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152fd5b6001600160a01b03809116918215611d785716918215611d0e5760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260028252604060002085600052825280604060002055604051908152a3565b608460405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152fdfea26469706673582212204e68fa9ad45b2cc2ff79ead6675b57e3f00659b79f13bf6c62490a8ea896614764736f6c63430008110033
Verified Source Code Partial Match
Compiler: v0.8.17+commit.8df45f5f
EVM: london
Optimization: Yes (1000 runs)
FarmingLib.sol 144 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { FarmAccounting } from "./accounting/FarmAccounting.sol";
import { UserAccounting } from "./accounting/UserAccounting.sol";
/// @title FarmingLib
/// @dev A library for farming logic, using FarmAccounting and UserAccounting.
library FarmingLib {
using FarmAccounting for FarmAccounting.Info;
using UserAccounting for UserAccounting.Info;
using FarmingLib for FarmingLib.Info;
/// @dev Struct containing farm and user detailed info for farming operations. See {FarmAccounting.Info} and {UserAccounting.Info} for.
struct Data {
FarmAccounting.Info farmInfo;
UserAccounting.Info userInfo;
}
/// @dev Struct containing the total supply function and a data slot for EVM storage.
struct Info {
function() internal view returns(uint256) getTotalSupply;
bytes32 dataSlot;
}
/**
* @notice Creates a new Info struct.
* @param getTotalSupply The function to get the total supply.
* @param data The data struct for storage.
* @return info The created Info struct.
*/
function makeInfo(function() internal view returns(uint256) getTotalSupply, Data storage data) internal pure returns(Info memory info) {
info.getTotalSupply = getTotalSupply;
bytes32 dataSlot;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
dataSlot := data.slot
}
info.dataSlot = dataSlot;
}
/**
* @notice Retrieves the Data struct from an Info struct.
* @param self The Info struct.
* @return data The retrieved Data struct.
*/
function getData(Info memory self) internal pure returns(Data storage data) {
bytes32 dataSlot = self.dataSlot;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
data.slot := dataSlot
}
}
/**
* @notice Begins farming for a specified period.
* @param self The Info struct.
* @param amount The amount to farm.
* @param period The farming period.
* @return reward The farming reward.
*/
function startFarming(Info memory self, uint256 amount, uint256 period) internal returns(uint256 reward) {
Data storage data = self.getData();
data.userInfo.updateFarmedPerToken(_farmedPerToken(self));
reward = data.farmInfo.startFarming(amount, period);
}
/**
* @notice Stops farming immediately.
* @param self The FarmingLib.Info struct to retrieve data from storage.
* @return leftover Amount of reward tokens remaining after farming.
*/
function stopFarming(Info memory self) internal returns(uint256 leftover) {
Data storage data = self.getData();
data.userInfo.updateFarmedPerToken(_farmedPerToken(self));
leftover = data.farmInfo.stopFarming();
}
/**
* @notice Gets the farmed amount for an account.
* @param self The Info struct.
* @param account The account to check.
* @param balance The account balance.
* @return result The farmed amount.
*/
function farmed(Info memory self, address account, uint256 balance) internal view returns(uint256) {
return self.getData().userInfo.farmed(account, balance, _farmedPerToken(self));
}
/**
* @notice Claims the farmed amount for an account.
* @param self The Info struct.
* @param account The account to claim for.
* @param balance The account balance.
* @return amount The claimed amount.
*/
function claim(Info memory self, address account, uint256 balance) internal returns(uint256 amount) {
Data storage data = self.getData();
uint256 fpt = _farmedPerToken(self);
amount = data.userInfo.farmed(account, balance, fpt);
if (amount > 0) {
data.userInfo.eraseFarmed(account, balance, fpt);
data.farmInfo.claim(amount);
}
}
/**
* @notice Updates the balances of two accounts.
* @param self The Info struct.
* @param from The account to transfer from.
* @param to The account to transfer to.
* @param amount The amount to transfer.
*/
function updateBalances(Info memory self, address from, address to, uint256 amount) internal {
self.getData().userInfo.updateBalances(from, to, amount, _farmedPerToken(self));
}
function _farmedPerToken(Info memory self) private view returns (uint256) {
return self.getData().userInfo.farmedPerToken(_infoToContext(self), _lazyGetSupply, _lazyGetFarmed);
}
// UserAccounting bindings
function _lazyGetSupply(bytes32 context) private view returns(uint256) {
Info memory self = _contextToInfo(context);
return self.getTotalSupply();
}
function _lazyGetFarmed(bytes32 context, uint256 checkpoint) private view returns(uint256) {
Info memory self = _contextToInfo(context);
return self.getData().farmInfo.farmedSinceCheckpointScaled(checkpoint);
}
function _contextToInfo(bytes32 context) private pure returns(Info memory self) {
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
self := context
}
}
function _infoToContext(Info memory self) private pure returns(bytes32 context) {
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
context := self
}
}
}
FarmingPool.sol 146 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IERC20, ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { SafeERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
import { IFarmingPool } from "./interfaces/IFarmingPool.sol";
import { FarmAccounting, FarmingLib } from "./FarmingLib.sol";
contract FarmingPool is IFarmingPool, Ownable, ERC20 {
using SafeERC20 for IERC20;
using Address for address payable;
using FarmingLib for FarmingLib.Info;
error SameStakingAndRewardsTokens();
error ZeroStakingTokenAddress();
error ZeroRewardsTokenAddress();
error ZeroDistributorAddress();
error SameDistributor();
error AccessDenied();
error InsufficientFunds();
error MaxBalanceExceeded();
uint256 internal constant _MAX_BALANCE = 1e32;
IERC20 public immutable stakingToken;
IERC20 public immutable rewardsToken;
address private _distributor;
FarmingLib.Data private _farm;
modifier onlyDistributor {
if (msg.sender != _distributor) revert AccessDenied();
_;
}
constructor(IERC20Metadata stakingToken_, IERC20 rewardsToken_)
ERC20(
string(abi.encodePacked("Farming of ", stakingToken_.name())),
string(abi.encodePacked("farm", stakingToken_.symbol()))
)
{
if (stakingToken_ == rewardsToken_) revert SameStakingAndRewardsTokens();
if (address(stakingToken_) == address(0)) revert ZeroStakingTokenAddress();
if (address(rewardsToken_) == address(0)) revert ZeroRewardsTokenAddress();
stakingToken = stakingToken_;
rewardsToken = rewardsToken_;
}
function decimals() public view virtual override returns (uint8) {
return IERC20Metadata(address(stakingToken)).decimals();
}
function farmInfo() public view returns(FarmAccounting.Info memory) {
return _farm.farmInfo;
}
function distributor() public view virtual returns (address) {
return _distributor;
}
function setDistributor(address distributor_) public virtual onlyOwner {
if (distributor_ == address(0)) revert ZeroDistributorAddress();
address oldDistributor = _distributor;
if (distributor_ == oldDistributor) revert SameDistributor();
emit DistributorChanged(oldDistributor, distributor_);
_distributor = distributor_;
}
function startFarming(uint256 amount, uint256 period) public virtual onlyDistributor {
uint256 reward = _makeInfo().startFarming(amount, period);
emit RewardUpdated(reward, period);
rewardsToken.safeTransferFrom(msg.sender, address(this), amount);
}
function stopFarming() public virtual onlyDistributor {
uint256 leftover = _makeInfo().stopFarming();
emit RewardUpdated(0, 0);
if (leftover > 0) {
rewardsToken.safeTransfer(msg.sender, leftover);
}
}
function farmed(address account) public view virtual returns (uint256) {
return _makeInfo().farmed(account, balanceOf(account));
}
function deposit(uint256 amount) public virtual {
_mint(msg.sender, amount);
if (balanceOf(msg.sender) > _MAX_BALANCE) revert MaxBalanceExceeded();
stakingToken.safeTransferFrom(msg.sender, address(this), amount);
}
function withdraw(uint256 amount) public virtual {
_burn(msg.sender, amount);
stakingToken.safeTransfer(msg.sender, amount);
}
function claim() public virtual {
uint256 amount = _makeInfo().claim(msg.sender, balanceOf(msg.sender));
if (amount > 0) {
_transferReward(rewardsToken, msg.sender, amount);
}
}
function _transferReward(IERC20 reward, address to, uint256 amount) internal virtual {
reward.safeTransfer(to, amount);
}
function exit() public virtual {
withdraw(balanceOf(msg.sender));
claim();
}
function rescueFunds(IERC20 token, uint256 amount) public virtual onlyDistributor {
if (token == IERC20(address(0))) {
payable(_distributor).sendValue(amount);
} else {
if (token == stakingToken) {
if (stakingToken.balanceOf(address(this)) < totalSupply() + amount) revert InsufficientFunds();
} else if (token == rewardsToken) {
if (rewardsToken.balanceOf(address(this)) < _farm.farmInfo.balance + amount) revert InsufficientFunds();
}
token.safeTransfer(_distributor, amount);
}
}
function _makeInfo() private view returns(FarmingLib.Info memory) {
return FarmingLib.makeInfo(totalSupply, _farm);
}
// ERC20 overrides
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
super._beforeTokenTransfer(from, to, amount);
if (amount > 0 && from != to) {
_makeInfo().updateBalances(from, to, amount);
}
}
}
IFarmingPool.sol 29 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { FarmAccounting } from "../accounting/FarmAccounting.sol";
interface IFarmingPool is IERC20 {
event DistributorChanged(address oldDistributor, address newDistributor);
event RewardUpdated(uint256 reward, uint256 duration);
// View functions
function distributor() external view returns(address);
function farmInfo() external view returns(FarmAccounting.Info memory);
function farmed(address account) external view returns(uint256);
// User functions
function deposit(uint256 amount) external;
function withdraw(uint256 amount) external;
function claim() external;
function exit() external;
// Owner functions
function setDistributor(address distributor_) external;
// Distributor functions
function startFarming(uint256 amount, uint256 period) external;
function rescueFunds(IERC20 token, uint256 amount) external;
}
FarmAccounting.sol 68 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
library FarmAccounting {
error ZeroDuration();
error DurationTooLarge();
error AmountTooLarge();
struct Info {
uint40 finished;
uint32 duration;
uint184 reward;
uint256 balance;
}
uint256 internal constant _MAX_REWARD_AMOUNT = 1e32; // 108 bits
uint256 internal constant _SCALE = 1e18; // 60 bits
/// @dev Requires extra 18 decimals for precision, result fits in 168 bits
function farmedSinceCheckpointScaled(Info storage info, uint256 checkpoint) internal view returns(uint256 amount) {
unchecked {
(uint40 finished, uint32 duration, uint184 reward) = (info.finished, info.duration, info.reward);
if (duration > 0) {
uint256 elapsed = Math.min(block.timestamp, finished) - Math.min(checkpoint, finished);
// size of (type(uint32).max * _MAX_REWARD_AMOUNT * _SCALE) is less than 200 bits, so there is no overflow
return elapsed * reward * _SCALE / duration;
}
}
}
function startFarming(Info storage info, uint256 amount, uint256 period) internal returns(uint256) {
if (period == 0) revert ZeroDuration();
if (period > type(uint32).max) revert DurationTooLarge();
// If something left from prev farming add it to the new farming
(uint40 finished, uint32 duration, uint184 reward, uint256 balance) = (info.finished, info.duration, info.reward, info.balance);
if (block.timestamp < finished) {
amount += reward - farmedSinceCheckpointScaled(info, finished - duration) / _SCALE;
}
if (amount > _MAX_REWARD_AMOUNT) revert AmountTooLarge();
(info.finished, info.duration, info.reward, info.balance) = (
uint40(block.timestamp + period),
uint32(period),
uint184(amount),
balance + amount
);
return amount;
}
function stopFarming(Info storage info) internal returns(uint256 leftover) {
leftover = info.reward - farmedSinceCheckpointScaled(info, info.finished - info.duration) / _SCALE;
(info.finished, info.duration, info.reward, info.balance) = (
uint40(block.timestamp),
uint32(0),
uint184(0),
info.balance - leftover
);
}
function claim(Info storage info, uint256 amount) internal {
info.balance -= amount;
}
}
UserAccounting.sol 63 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { FarmAccounting } from "./FarmAccounting.sol";
library UserAccounting {
struct Info {
uint40 checkpoint;
uint216 farmedPerTokenStored;
mapping(address => int256) corrections;
}
function farmedPerToken(
Info storage info,
bytes32 context,
function(bytes32) internal view returns(uint256) lazyGetSupply,
function(bytes32, uint256) internal view returns(uint256) lazyGetFarmed
) internal view returns(uint256) {
(uint256 checkpoint, uint256 fpt) = (info.checkpoint, info.farmedPerTokenStored);
if (block.timestamp != checkpoint) {
uint256 supply = lazyGetSupply(context);
if (supply > 0) {
// fpt increases by 168 bit / supply
unchecked { fpt += lazyGetFarmed(context, checkpoint) / supply; }
}
}
return fpt;
}
function farmed(Info storage info, address account, uint256 balance, uint256 fpt) internal view returns(uint256) {
// balance * fpt is less than 168 bit
return uint256(int256(balance * fpt) - info.corrections[account]) / FarmAccounting._SCALE;
}
function eraseFarmed(Info storage info, address account, uint256 balance, uint256 fpt) internal {
// balance * fpt is less than 168 bit
info.corrections[account] = int256(balance * fpt);
}
function updateFarmedPerToken(Info storage info, uint256 fpt) internal {
(info.checkpoint, info.farmedPerTokenStored) = (uint40(block.timestamp), uint216(fpt));
}
function updateBalances(Info storage info, address from, address to, uint256 amount, uint256 fpt) internal {
bool fromZero = (from == address(0));
bool toZero = (to == address(0));
if (amount > 0 && from != to) {
if (fromZero || toZero) {
updateFarmedPerToken(info, fpt);
}
// fpt is less than 168 bit, so amount should be less 98 bit
int256 diff = int256(amount * fpt);
if (!fromZero) {
info.corrections[from] -= diff;
}
if (!toZero) {
info.corrections[to] += diff;
}
}
}
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
Context.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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;
}
}
Ownable.sol 83 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @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.
*/
abstract 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() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(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");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Math.sol 339 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @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 == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
ERC20.sol 365 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/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.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* 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}.
*
* 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 default value returned by this function, unless
* it's 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:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, 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}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, 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}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, 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) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, 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) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* 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:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, 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;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_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;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_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 Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - 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 {}
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}
IWETH.sol 15 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWETH is IERC20 {
event Deposit(address indexed dst, uint wad);
event Withdrawal(address indexed src, uint wad);
function deposit() external payable;
function withdraw(uint256 amount) external;
}
IPermit2.sol 40 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IPermit2 {
struct PermitDetails {
// ERC20 token address
address token;
// the maximum amount allowed to spend
uint160 amount;
// timestamp at which a spender's token allowances become invalid
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice The permit message signed for a single token allownce
struct PermitSingle {
// the permit data for a single token alownce
PermitDetails details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice Packed allowance
struct PackedAllowance {
// amount allowed
uint160 amount;
// permission expiry
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
function transferFrom(address user, address spender, uint160 amount, address token) external;
function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
function allowance(address user, address token, address spender) external view returns (PackedAllowance memory);
}
SafeERC20.sol 471 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
import "../interfaces/IDaiLikePermit.sol";
import "../interfaces/IPermit2.sol";
import "../interfaces/IWETH.sol";
import "../libraries/RevertReasonForwarder.sol";
/**
* @title Implements efficient safe methods for ERC20 interface.
* @notice Compared to the standard ERC20, this implementation offers several enhancements:
* 1. more gas-efficient, providing significant savings in transaction costs.
* 2. support for different permit implementations
* 3. forceApprove functionality
* 4. support for WETH deposit and withdraw
*/
library SafeERC20 {
error SafeTransferFailed();
error SafeTransferFromFailed();
error ForceApproveFailed();
error SafeIncreaseAllowanceFailed();
error SafeDecreaseAllowanceFailed();
error SafePermitBadLength();
error Permit2TransferAmountTooHigh();
// Uniswap Permit2 address
address private constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
bytes4 private constant _PERMIT_LENGTH_ERROR = 0x68275857; // SafePermitBadLength.selector
uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;
/**
* @notice Fetches the balance of a specific ERC20 token held by an account.
* Consumes less gas then regular `ERC20.balanceOf`.
* @param token The IERC20 token contract for which the balance will be fetched.
* @param account The address of the account whose token balance will be fetched.
* @return tokenBalance The balance of the specified ERC20 token held by the account.
*/
function safeBalanceOf(
IERC20 token,
address account
) internal view returns(uint256 tokenBalance) {
bytes4 selector = IERC20.balanceOf.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
mstore(0x00, selector)
mstore(0x04, account)
let success := staticcall(gas(), token, 0x00, 0x24, 0x00, 0x20)
tokenBalance := mload(0)
if or(iszero(success), lt(returndatasize(), 0x20)) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
/**
* @notice Attempts to safely transfer tokens from one address to another.
* @dev If permit2 is true, uses the Permit2 standard; otherwise uses the standard ERC20 transferFrom.
* Either requires `true` in return data, or requires target to be smart-contract and empty return data.
* @param token The IERC20 token contract from which the tokens will be transferred.
* @param from The address from which the tokens will be transferred.
* @param to The address to which the tokens will be transferred.
* @param amount The amount of tokens to transfer.
* @param permit2 If true, uses the Permit2 standard for the transfer; otherwise uses the standard ERC20 transferFrom.
*/
function safeTransferFromUniversal(
IERC20 token,
address from,
address to,
uint256 amount,
bool permit2
) internal {
if (permit2) {
safeTransferFromPermit2(token, from, to, amount);
} else {
safeTransferFrom(token, from, to, amount);
}
}
/**
* @notice Attempts to safely transfer tokens from one address to another using the ERC20 standard.
* @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
* @param token The IERC20 token contract from which the tokens will be transferred.
* @param from The address from which the tokens will be transferred.
* @param to The address to which the tokens will be transferred.
* @param amount The amount of tokens to transfer.
*/
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
bytes4 selector = token.transferFrom.selector;
bool success;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), from)
mstore(add(data, 0x24), to)
mstore(add(data, 0x44), amount)
success := call(gas(), token, 0, data, 100, 0x0, 0x20)
if success {
switch returndatasize()
case 0 {
success := gt(extcodesize(token), 0)
}
default {
success := and(gt(returndatasize(), 31), eq(mload(0), 1))
}
}
}
if (!success) revert SafeTransferFromFailed();
}
/**
* @notice Attempts to safely transfer tokens from one address to another using the Permit2 standard.
* @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
* @param token The IERC20 token contract from which the tokens will be transferred.
* @param from The address from which the tokens will be transferred.
* @param to The address to which the tokens will be transferred.
* @param amount The amount of tokens to transfer.
*/
function safeTransferFromPermit2(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
if (amount > type(uint160).max) revert Permit2TransferAmountTooHigh();
bytes4 selector = IPermit2.transferFrom.selector;
bool success;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), from)
mstore(add(data, 0x24), to)
mstore(add(data, 0x44), amount)
mstore(add(data, 0x64), token)
success := call(gas(), _PERMIT2, 0, data, 0x84, 0x0, 0x0)
if success {
success := gt(extcodesize(_PERMIT2), 0)
}
}
if (!success) revert SafeTransferFromFailed();
}
/**
* @notice Attempts to safely transfer tokens to another address.
* @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
* @param token The IERC20 token contract from which the tokens will be transferred.
* @param to The address to which the tokens will be transferred.
* @param value The amount of tokens to transfer.
*/
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
if (!_makeCall(token, token.transfer.selector, to, value)) {
revert SafeTransferFailed();
}
}
/**
* @notice Attempts to approve a spender to spend a certain amount of tokens.
* @dev If `approve(from, to, amount)` fails, it tries to set the allowance to zero, and retries the `approve` call.
* @param token The IERC20 token contract on which the call will be made.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
*/
function forceApprove(
IERC20 token,
address spender,
uint256 value
) internal {
if (!_makeCall(token, token.approve.selector, spender, value)) {
if (
!_makeCall(token, token.approve.selector, spender, 0) ||
!_makeCall(token, token.approve.selector, spender, value)
) {
revert ForceApproveFailed();
}
}
}
/**
* @notice Safely increases the allowance of a spender.
* @dev Increases with safe math check. Checks if the increased allowance will overflow, if yes, then it reverts the transaction.
* Then uses `forceApprove` to increase the allowance.
* @param token The IERC20 token contract on which the call will be made.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to increase the allowance by.
*/
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 allowance = token.allowance(address(this), spender);
if (value > type(uint256).max - allowance) revert SafeIncreaseAllowanceFailed();
forceApprove(token, spender, allowance + value);
}
/**
* @notice Safely decreases the allowance of a spender.
* @dev Decreases with safe math check. Checks if the decreased allowance will underflow, if yes, then it reverts the transaction.
* Then uses `forceApprove` to increase the allowance.
* @param token The IERC20 token contract on which the call will be made.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to decrease the allowance by.
*/
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 allowance = token.allowance(address(this), spender);
if (value > allowance) revert SafeDecreaseAllowanceFailed();
forceApprove(token, spender, allowance - value);
}
/**
* @notice Attempts to execute the `permit` function on the provided token with the sender and contract as parameters.
* Permit type is determined automatically based on permit calldata (IERC20Permit, IDaiLikePermit, and IPermit2).
* @dev Wraps `tryPermit` function and forwards revert reason if permit fails.
* @param token The IERC20 token to execute the permit function on.
* @param permit The permit data to be used in the function call.
*/
function safePermit(IERC20 token, bytes calldata permit) internal {
if (!tryPermit(token, msg.sender, address(this), permit)) RevertReasonForwarder.reRevert();
}
/**
* @notice Attempts to execute the `permit` function on the provided token with custom owner and spender parameters.
* Permit type is determined automatically based on permit calldata (IERC20Permit, IDaiLikePermit, and IPermit2).
* @dev Wraps `tryPermit` function and forwards revert reason if permit fails.
* @param token The IERC20 token to execute the permit function on.
* @param owner The owner of the tokens for which the permit is made.
* @param spender The spender allowed to spend the tokens by the permit.
* @param permit The permit data to be used in the function call.
*/
function safePermit(IERC20 token, address owner, address spender, bytes calldata permit) internal {
if (!tryPermit(token, owner, spender, permit)) RevertReasonForwarder.reRevert();
}
/**
* @notice Attempts to execute the `permit` function on the provided token with the sender and contract as parameters.
* @dev Invokes `tryPermit` with sender as owner and contract as spender.
* @param token The IERC20 token to execute the permit function on.
* @param permit The permit data to be used in the function call.
* @return success Returns true if the permit function was successfully executed, false otherwise.
*/
function tryPermit(IERC20 token, bytes calldata permit) internal returns(bool success) {
return tryPermit(token, msg.sender, address(this), permit);
}
/**
* @notice The function attempts to call the permit function on a given ERC20 token.
* @dev The function is designed to support a variety of permit functions, namely: IERC20Permit, IDaiLikePermit, and IPermit2.
* It accommodates both Compact and Full formats of these permit types.
* Please note, it is expected that the `expiration` parameter for the compact Permit2 and the `deadline` parameter
* for the compact Permit are to be incremented by one before invoking this function. This approach is motivated by
* gas efficiency considerations; as the unlimited expiration period is likely to be the most common scenario, and
* zeros are cheaper to pass in terms of gas cost. Thus, callers should increment the expiration or deadline by one
* before invocation for optimized performance.
* @param token The address of the ERC20 token on which to call the permit function.
* @param owner The owner of the tokens. This address should have signed the off-chain permit.
* @param spender The address which will be approved for transfer of tokens.
* @param permit The off-chain permit data, containing different fields depending on the type of permit function.
* @return success A boolean indicating whether the permit call was successful.
*/
function tryPermit(IERC20 token, address owner, address spender, bytes calldata permit) internal returns(bool success) {
// load function selectors for different permit standards
bytes4 permitSelector = IERC20Permit.permit.selector;
bytes4 daiPermitSelector = IDaiLikePermit.permit.selector;
bytes4 permit2Selector = IPermit2.permit.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
// Switch case for different permit lengths, indicating different permit standards
switch permit.length
// Compact IERC20Permit
case 100 {
mstore(ptr, permitSelector) // store selector
mstore(add(ptr, 0x04), owner) // store owner
mstore(add(ptr, 0x24), spender) // store spender
// Compact IERC20Permit.permit(uint256 value, uint32 deadline, uint256 r, uint256 vs)
{ // stack too deep
let deadline := shr(224, calldataload(add(permit.offset, 0x20))) // loads permit.offset 0x20..0x23
let vs := calldataload(add(permit.offset, 0x44)) // loads permit.offset 0x44..0x63
calldatacopy(add(ptr, 0x44), permit.offset, 0x20) // store value = copy permit.offset 0x00..0x19
mstore(add(ptr, 0x64), sub(deadline, 1)) // store deadline = deadline - 1
mstore(add(ptr, 0x84), add(27, shr(255, vs))) // store v = most significant bit of vs + 27 (27 or 28)
calldatacopy(add(ptr, 0xa4), add(permit.offset, 0x24), 0x20) // store r = copy permit.offset 0x24..0x43
mstore(add(ptr, 0xc4), shr(1, shl(1, vs))) // store s = vs without most significant bit
}
// IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
}
// Compact IDaiLikePermit
case 72 {
mstore(ptr, daiPermitSelector) // store selector
mstore(add(ptr, 0x04), owner) // store owner
mstore(add(ptr, 0x24), spender) // store spender
// Compact IDaiLikePermit.permit(uint32 nonce, uint32 expiry, uint256 r, uint256 vs)
{ // stack too deep
let expiry := shr(224, calldataload(add(permit.offset, 0x04))) // loads permit.offset 0x04..0x07
let vs := calldataload(add(permit.offset, 0x28)) // loads permit.offset 0x28..0x47
mstore(add(ptr, 0x44), shr(224, calldataload(permit.offset))) // store nonce = copy permit.offset 0x00..0x03
mstore(add(ptr, 0x64), sub(expiry, 1)) // store expiry = expiry - 1
mstore(add(ptr, 0x84), true) // store allowed = true
mstore(add(ptr, 0xa4), add(27, shr(255, vs))) // store v = most significant bit of vs + 27 (27 or 28)
calldatacopy(add(ptr, 0xc4), add(permit.offset, 0x08), 0x20) // store r = copy permit.offset 0x08..0x27
mstore(add(ptr, 0xe4), shr(1, shl(1, vs))) // store s = vs without most significant bit
}
// IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0x104, 0, 0)
}
// IERC20Permit
case 224 {
mstore(ptr, permitSelector)
calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
// IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
}
// IDaiLikePermit
case 256 {
mstore(ptr, daiPermitSelector)
calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
// IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0x104, 0, 0)
}
// Compact IPermit2
case 96 {
// Compact IPermit2.permit(uint160 amount, uint32 expiration, uint32 nonce, uint32 sigDeadline, uint256 r, uint256 vs)
mstore(ptr, permit2Selector) // store selector
mstore(add(ptr, 0x04), owner) // store owner
mstore(add(ptr, 0x24), token) // store token
calldatacopy(add(ptr, 0x50), permit.offset, 0x14) // store amount = copy permit.offset 0x00..0x13
// and(0xffffffffffff, ...) - conversion to uint48
mstore(add(ptr, 0x64), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x14))), 1))) // store expiration = ((permit.offset 0x14..0x17 - 1) & 0xffffffffffff)
mstore(add(ptr, 0x84), shr(224, calldataload(add(permit.offset, 0x18)))) // store nonce = copy permit.offset 0x18..0x1b
mstore(add(ptr, 0xa4), spender) // store spender
// and(0xffffffffffff, ...) - conversion to uint48
mstore(add(ptr, 0xc4), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x1c))), 1))) // store sigDeadline = ((permit.offset 0x1c..0x1f - 1) & 0xffffffffffff)
mstore(add(ptr, 0xe4), 0x100) // store offset = 256
mstore(add(ptr, 0x104), 0x40) // store length = 64
calldatacopy(add(ptr, 0x124), add(permit.offset, 0x20), 0x20) // store r = copy permit.offset 0x20..0x3f
calldatacopy(add(ptr, 0x144), add(permit.offset, 0x40), 0x20) // store vs = copy permit.offset 0x40..0x5f
// IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0)
}
// IPermit2
case 352 {
mstore(ptr, permit2Selector)
calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
// IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0)
}
// Unknown
default {
mstore(ptr, _PERMIT_LENGTH_ERROR)
revert(ptr, 4)
}
}
}
/**
* @dev Executes a low level call to a token contract, making it resistant to reversion and erroneous boolean returns.
* @param token The IERC20 token contract on which the call will be made.
* @param selector The function signature that is to be called on the token contract.
* @param to The address to which the token amount will be transferred.
* @param amount The token amount to be transferred.
* @return success A boolean indicating if the call was successful. Returns 'true' on success and 'false' on failure.
* In case of success but no returned data, validates that the contract code exists.
* In case of returned data, ensures that it's a boolean `true`.
*/
function _makeCall(
IERC20 token,
bytes4 selector,
address to,
uint256 amount
) private returns (bool success) {
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), to)
mstore(add(data, 0x24), amount)
success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
if success {
switch returndatasize()
case 0 {
success := gt(extcodesize(token), 0)
}
default {
success := and(gt(returndatasize(), 31), eq(mload(0), 1))
}
}
}
}
/**
* @notice Safely deposits a specified amount of Ether into the IWETH contract. Consumes less gas then regular `IWETH.deposit`.
* @param weth The IWETH token contract.
* @param amount The amount of Ether to deposit into the IWETH contract.
*/
function safeDeposit(IWETH weth, uint256 amount) internal {
if (amount > 0) {
bytes4 selector = IWETH.deposit.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
mstore(0, selector)
if iszero(call(gas(), weth, amount, 0, 4, 0, 0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
}
/**
* @notice Safely withdraws a specified amount of wrapped Ether from the IWETH contract. Consumes less gas then regular `IWETH.withdraw`.
* @dev Uses inline assembly to interact with the IWETH contract.
* @param weth The IWETH token contract.
* @param amount The amount of wrapped Ether to withdraw from the IWETH contract.
*/
function safeWithdraw(IWETH weth, uint256 amount) internal {
bytes4 selector = IWETH.withdraw.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
mstore(0, selector)
mstore(4, amount)
if iszero(call(gas(), weth, 0, 0, 0x24, 0, 0)) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
/**
* @notice Safely withdraws a specified amount of wrapped Ether from the IWETH contract to a specified recipient.
* Consumes less gas then regular `IWETH.withdraw`.
* @param weth The IWETH token contract.
* @param amount The amount of wrapped Ether to withdraw from the IWETH contract.
* @param to The recipient of the withdrawn Ether.
*/
function safeWithdrawTo(IWETH weth, uint256 amount, address to) internal {
safeWithdraw(weth, amount);
if (to != address(this)) {
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
if iszero(call(_RAW_CALL_GAS_LIMIT, to, amount, 0, 0, 0, 0)) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
}
}
IDaiLikePermit.sol 16 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IDaiLikePermit {
function permit(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/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);
}
IERC20Metadata.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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);
}
RevertReasonForwarder.sol 16 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Revert reason forwarder.
library RevertReasonForwarder {
/// @dev Forwards latest externall call revert.
function reRevert() internal pure {
// bubble up revert reason from latest external call
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
draft-IERC20Permit.sol 8 lines
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; // EIP-2612 is Final as of 2022-11-01. This file is deprecated. import "./IERC20Permit.sol";
Read Contract
allowance 0xdd62ed3e → uint256
balanceOf 0x70a08231 → uint256
decimals 0x313ce567 → uint8
distributor 0xbfe10928 → address
farmInfo 0x1d49d66c → tuple
farmed 0x4216f972 → uint256
name 0x06fdde03 → string
owner 0x8da5cb5b → address
rewardsToken 0xd1af0c7d → address
stakingToken 0x72f702f3 → address
symbol 0x95d89b41 → string
totalSupply 0x18160ddd → uint256
Write Contract 15 functions
These functions modify contract state and require a wallet transaction to execute.
approve 0x095ea7b3
address spender
uint256 amount
returns: bool
claim 0x4e71d92d
No parameters
decreaseAllowance 0xa457c2d7
address spender
uint256 subtractedValue
returns: bool
deposit 0xb6b55f25
uint256 amount
exit 0xe9fad8ee
No parameters
increaseAllowance 0x39509351
address spender
uint256 addedValue
returns: bool
renounceOwnership 0x715018a6
No parameters
rescueFunds 0x78e3214f
address token
uint256 amount
setDistributor 0x75619ab5
address distributor_
startFarming 0x70261222
uint256 amount
uint256 period
stopFarming 0x1bfa4c04
No parameters
transfer 0xa9059cbb
address to
uint256 amount
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 amount
returns: bool
transferOwnership 0xf2fde38b
address newOwner
withdraw 0x2e1a7d4d
uint256 amount
Recent Transactions
No transactions found for this address