文件 1 的 5:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
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");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
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");
}
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);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
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);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
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);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
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 {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 5:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
文件 3 的 5:IStaker.sol
pragma solidity 0.8.1;
interface IStaker {
function increaseDepositNb() external;
function decreaseDepositNb() external;
function pending(address) external view returns (uint);
function distribute() external;
function withdraw(address, uint) external;
}
文件 4 的 5:IZap.sol
pragma solidity 0.8.1;
interface IZap {
function zapInETH(address) external payable returns (uint, uint);
}
文件 5 的 5:Staking.sol
pragma solidity 0.8.1;
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IStaker.sol";
import "./IZap.sol";
contract Staking {
struct Share {
uint depositTime;
uint initialDeposit;
uint sumETH;
}
mapping(address => Share) public shares;
address private immutable taxWallet;
uint public sumETH;
uint private constant PRECISION = 1e18;
uint public totalETH;
uint public totalLP;
uint public immutable delay;
IStaker public immutable staker;
IZap public immutable zap;
uint private isZapping = 2;
constructor(IStaker staker_, IZap zap_, uint delay_) {
taxWallet = msg.sender;
staker = staker_;
zap = zap_;
delay = delay_;
}
receive() external payable {
if (msg.sender == address(staker)) _distribute();
else if (isZapping == 2) _deposit();
}
function _distribute() internal {
if (msg.value == 0) return;
uint totalLPCached = totalLP;
if (totalLPCached == 0) return Address.sendValue(payable(taxWallet), msg.value);
uint gpus = msg.value * PRECISION / totalLPCached;
sumETH += gpus;
totalETH += msg.value;
}
function _deposit() internal {
require(msg.value > 0, "Amount must be greater than zero");
Share memory share = shares[msg.sender];
isZapping = 1;
(uint amountLP, uint amountETH) = zap.zapInETH{value: msg.value}(address(staker));
isZapping = 2;
if (share.initialDeposit == 0) staker.increaseDepositNb();
uint gains = _computeGainsUpdateShare(msg.sender, share, share.initialDeposit + amountLP, true, false);
_sendETH(msg.sender, gains + amountETH);
}
function withdraw() external {
Share memory share = shares[msg.sender];
require(share.initialDeposit > 0, "No initial deposit");
require(share.depositTime + delay < block.timestamp, "withdraw too soon");
staker.withdraw(msg.sender, share.initialDeposit);
staker.decreaseDepositNb();
uint gains = _computeGainsUpdateShare(msg.sender, share, 0, true, true);
_sendETH(msg.sender, gains);
}
function claim() external {
Share memory share = shares[msg.sender];
require(share.initialDeposit > 0, "No initial deposit");
uint gains = _computeGainsUpdateShare(msg.sender, share, share.initialDeposit, false, false);
_sendETH(msg.sender, gains);
}
function _sendETH(address to, uint amount) private {
if (amount > 0) Address.sendValue(payable(to), amount);
}
function _computeGainsUpdateShare(address who, Share memory share, uint newAmount, bool resetTimer, bool withdrawn)
private
returns (uint gains)
{
staker.distribute();
if (share.initialDeposit != 0) gains = share.initialDeposit * (sumETH - share.sumETH) / PRECISION;
if (newAmount == 0) delete shares[who];
else if (resetTimer) shares[who] = Share(block.timestamp, newAmount, sumETH);
else shares[who] = Share(share.depositTime, newAmount, sumETH);
if (withdrawn) totalLP -= share.initialDeposit;
else if (newAmount != share.initialDeposit) totalLP += (newAmount - share.initialDeposit);
}
function pending(address who) external view returns (uint) {
Share memory share = shares[who];
uint sumETHUpdated = sumETH + staker.pending(address(this)) * PRECISION / totalLP;
return share.initialDeposit * (sumETHUpdated - share.sumETH) / PRECISION;
}
}
{
"compilationTarget": {
"contracts/Staking.sol": "Staking"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IStaker","name":"staker_","type":"address"},{"internalType":"contract IZap","name":"zap_","type":"address"},{"internalType":"uint256","name":"delay_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"pending","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"shares","outputs":[{"internalType":"uint256","name":"depositTime","type":"uint256"},{"internalType":"uint256","name":"initialDeposit","type":"uint256"},{"internalType":"uint256","name":"sumETH","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"staker","outputs":[{"internalType":"contract IStaker","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sumETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"zap","outputs":[{"internalType":"contract IZap","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]