文件 1 的 1:DividendDistributor.sol
pragma solidity ^0.8.10;
library SafeMath {
function tryAdd(uint256 a, uint256 b)
internal
pure
returns (bool, uint256)
{
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
function trySub(uint256 a, uint256 b)
internal
pure
returns (bool, uint256)
{
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
function tryMul(uint256 a, uint256 b)
internal
pure
returns (bool, uint256)
{
unchecked {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
function tryDiv(uint256 a, uint256 b)
internal
pure
returns (bool, uint256)
{
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
function tryMod(uint256 a, uint256 b)
internal
pure
returns (bool, uint256)
{
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return a % b;
}
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}
interface IDexRouter {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidityETH(
address token,
uint256 amountTokenDesired,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
)
external
payable
returns (
uint256 amountToken,
uint256 amountETH,
uint256 liquidity
);
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external;
}
interface IERC20Extended {
function totalSupply() external view returns (uint256);
function decimals() external view returns (uint8);
function symbol() external view returns (string memory);
function name() external view returns (string memory);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, 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 sender,
address recipient,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
interface IDividendDistributor {
function setDistributionCriteria(
uint256 _minPeriod,
uint256 _minDistribution
) external;
function setShare(address shareholder, uint256 amount) external;
function deposit() external payable;
function process(uint256 gas) external;
function claimDividend(address _user) external;
function getPaidEarnings(address shareholder)
external
view
returns (uint256);
function getUnpaidEarnings(address shareholder)
external
view
returns (uint256);
function totalDistributed() external view returns (uint256);
}
contract DividendDistributor is IDividendDistributor {
using SafeMath for uint256;
address public token;
struct Share {
uint256 amount;
uint256 totalExcluded;
uint256 totalRealised;
}
IERC20Extended public USDC =
IERC20Extended(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
IDexRouter public router;
address[] public shareholders;
mapping(address => uint256) public shareholderIndexes;
mapping(address => uint256) public shareholderClaims;
mapping(address => Share) public shares;
uint256 public totalShares;
uint256 public totalDividends;
uint256 public totalDistributed;
uint256 public dividendsPerShare;
uint256 public dividendsPerShareAccuracyFactor = 10**36;
uint256 public minPeriod = 1 hours;
uint256 public minDistribution = 1 * (10**USDC.decimals());
uint256 currentIndex;
bool initialized;
modifier initializer() {
require(!initialized);
_;
initialized = true;
}
modifier onlyToken() {
require(msg.sender == token);
_;
}
constructor(address router_) {
token = msg.sender;
router = IDexRouter(router_);
}
function setDistributionCriteria(
uint256 _minPeriod,
uint256 _minDistribution
) external override onlyToken {
minPeriod = _minPeriod;
minDistribution = _minDistribution;
}
function setShare(address shareholder, uint256 amount)
external
override
onlyToken
{
if (shares[shareholder].amount > 0) {
distributeDividend(shareholder);
}
if (amount > 0 && shares[shareholder].amount == 0) {
addShareholder(shareholder);
} else if (amount == 0 && shares[shareholder].amount > 0) {
removeShareholder(shareholder);
}
totalShares = totalShares.sub(shares[shareholder].amount).add(amount);
shares[shareholder].amount = amount;
shares[shareholder].totalExcluded = getCumulativeDividends(
shares[shareholder].amount
);
}
function deposit() external payable override onlyToken {
uint256 balanceBefore = USDC.balanceOf(address(this));
address[] memory path = new address[](2);
path[0] = router.WETH();
path[1] = address(USDC);
router.swapExactETHForTokensSupportingFeeOnTransferTokens{
value: msg.value
}(0, path, address(this), block.timestamp);
uint256 amount = USDC.balanceOf(address(this)).sub(balanceBefore);
totalDividends = totalDividends.add(amount);
dividendsPerShare = dividendsPerShare.add(
dividendsPerShareAccuracyFactor.mul(amount).div(totalShares)
);
}
function process(uint256 gas) external override onlyToken {
uint256 shareholderCount = shareholders.length;
if (shareholderCount == 0) {
return;
}
uint256 gasUsed = 0;
uint256 gasLeft = gasleft();
uint256 iterations = 0;
while (gasUsed < gas && iterations < shareholderCount) {
if (currentIndex >= shareholderCount) {
currentIndex = 0;
}
if (shouldDistribute(shareholders[currentIndex])) {
distributeDividend(shareholders[currentIndex]);
}
gasUsed = gasUsed.add(gasLeft.sub(gasleft()));
gasLeft = gasleft();
currentIndex++;
iterations++;
}
}
function shouldDistribute(address shareholder)
internal
view
returns (bool)
{
return
shareholderClaims[shareholder] + minPeriod < block.timestamp &&
getUnpaidEarnings(shareholder) > minDistribution;
}
function distributeDividend(address shareholder) internal {
if (shares[shareholder].amount == 0) {
return;
}
uint256 amount = getUnpaidEarnings(shareholder);
if (amount > 0) {
totalDistributed = totalDistributed.add(amount);
USDC.transfer(shareholder, amount);
shareholderClaims[shareholder] = block.timestamp;
shares[shareholder].totalRealised = shares[shareholder]
.totalRealised
.add(amount);
shares[shareholder].totalExcluded = getCumulativeDividends(
shares[shareholder].amount
);
}
}
function claimDividend(address _user) external {
distributeDividend(_user);
}
function getPaidEarnings(address shareholder)
public
view
returns (uint256)
{
return shares[shareholder].totalRealised;
}
function getUnpaidEarnings(address shareholder)
public
view
returns (uint256)
{
if (shares[shareholder].amount == 0) {
return 0;
}
uint256 shareholderTotalDividends = getCumulativeDividends(
shares[shareholder].amount
);
uint256 shareholderTotalExcluded = shares[shareholder].totalExcluded;
if (shareholderTotalDividends <= shareholderTotalExcluded) {
return 0;
}
return shareholderTotalDividends.sub(shareholderTotalExcluded);
}
function getCumulativeDividends(uint256 share)
internal
view
returns (uint256)
{
return
share.mul(dividendsPerShare).div(dividendsPerShareAccuracyFactor);
}
function addShareholder(address shareholder) internal {
shareholderIndexes[shareholder] = shareholders.length;
shareholders.push(shareholder);
}
function removeShareholder(address shareholder) internal {
shareholders[shareholderIndexes[shareholder]] = shareholders[
shareholders.length - 1
];
shareholderIndexes[
shareholders[shareholders.length - 1]
] = shareholderIndexes[shareholder];
shareholders.pop();
}
}