编译器
0.8.25+commit.b61c2a91
文件 1 的 11:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 2 的 11:ERC20.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
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;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return 18;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
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;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
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;
}
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;
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
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 {
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
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);
}
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);
}
}
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
文件 3 的 11:ERC20Helpers.sol
import "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
pragma solidity >=0.6.0;
library ERC20Helpers {
function safeApprove(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'ERC20Helpers::safeApprove: approve failed'
);
}
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'ERC20Helpers::safeTransfer: transfer failed'
);
}
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'ERC20Helpers::transferFrom: transferFrom failed'
);
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'ERC20Helpers::safeTransferETH: ETH transfer failed');
}
function tryDecimals(IERC20Metadata token) internal view returns (uint8) {
try token.decimals{gas: 20000}() returns (uint8 value) {
return value;
} catch {
return 18;
}
}
function trySymbol(IERC20Metadata token) internal view returns (string memory) {
try token.symbol{gas: 20000}() returns (string memory value) {
return value;
} catch {
return "UNKNOWN";
}
}
function tryName(IERC20Metadata token) internal view returns (string memory) {
try token.name{gas: 20000}() returns (string memory value) {
return value;
} catch {
return "Unknown Token";
}
}
function tryLpSymbol(address _pair) internal view returns (string memory) {
IUniswapV2Pair pair = IUniswapV2Pair(_pair);
address token0 = pair.token0();
address token1 = pair.token1();
string memory sym0 = trySymbol(IERC20Metadata(token0));
string memory sym1 = trySymbol(IERC20Metadata(token1));
string memory pairSym = trySymbol(IERC20Metadata(_pair));
return string(abi.encodePacked("(", sym0, " + ", sym1, ") ", pairSym));
}
function tryLpName(address _pair) internal view returns (string memory) {
IUniswapV2Pair pair = IUniswapV2Pair(_pair);
address token0 = pair.token0();
address token1 = pair.token1();
string memory name0 = tryName(IERC20Metadata(token0));
string memory name1 = tryName(IERC20Metadata(token1));
return string(abi.encodePacked("(", name0, " + ", name1, ") LP Token"));
}
}
文件 4 的 11: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);
}
文件 5 的 11:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../token/ERC20/extensions/IERC20Metadata.sol";
文件 6 的 11:IFARM.sol
pragma solidity ^0.8.20;
interface IFARM {
function activateRewards(address _uniswapPair, address _weth, address _router) external;
}
文件 7 的 11:IRewardFarmers.sol
pragma solidity ^0.8.22;
interface IRewardFarmers {
struct TokenInfo {
address contractAddress;
uint256 decimals;
string symbol;
string name;
}
function addRewards(uint256 distributionId, address token, uint256 amount) external payable;
function uncommonRewardCount() external view returns (uint256);
function ethPerToken(address token) external view returns (uint256);
function tokenInfo(address token) external view returns (TokenInfo memory);
}
文件 8 的 11:IUniswapV2Pair.sol
pragma solidity >=0.5.0;
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}
文件 9 的 11:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 10 的 11:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
文件 11 的 11:RiceFields.sol
pragma solidity ^0.8.18;
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/IRewardFarmers.sol";
import "./libraries/ERC20Helpers.sol";
import "./interfaces/IFARM.sol";
contract RiceFields is ReentrancyGuard, Ownable {
uint256 public PRECISION = 10**18;
IRewardFarmers public rewardFarmers;
struct DistributionInfo {
Distribution distribution;
uint256 uncommonRewardId;
uint256 currentTimestamp;
bool hasUncommonReward;
uint256 userAllocated;
uint256 tokenOutPrice;
uint256 tokenInPrice;
uint256 userDeposits;
bool userHasClaimed;
IRewardFarmers.TokenInfo tokenOUT;
IRewardFarmers.TokenInfo tokenIN;
uint256 rewardId;
}
mapping(uint256 => mapping(address => Deposit)) public deposits;
struct Distribution {
uint256 totalTokensPerDeposit;
uint256 uncommonRewardId;
uint256 percentReturned;
uint256 tokensPerSecond;
bool hasUncommonReward;
uint256 totalDeposited;
bool rewardDistributed;
uint256 lastDepositAt;
bool ownerWithdrawn;
uint256 platformFee;
uint256 startTime;
uint256 amountOUT;
uint256 duration;
uint256 endTime;
string ipfsHash;
address creator;
uint256 id;
string description;
string name;
uint256 nextActiveId;
uint256 prevActiveId;
IERC20Metadata tokenIN;
IERC20Metadata tokenOUT;
}
uint256 public newestActiveDistributionId;
bool public canChangeRewardAddress = true;
struct Deposit {
uint256 previouslyAllocated;
uint256 amount;
bool claimed;
}
Distribution[] public distributions;
mapping(address => uint256[]) public userDistributions;
event DistributionCreated(
uint256 id, address creator, string name, string description,
uint256 duration, string ipfsHash, uint256 amountOUT,
uint256 percentReturned, address tokenOUT, address tokenIN
);
event Deposited(uint256 distributionId, address indexed user, uint256 amount);
event Claim(uint256 distributionId, address indexed user, uint256 amount, uint256 ethAmount);
uint256 public platformFee = 100;
constructor(IRewardFarmers _farm) {
rewardFarmers = _farm;
}
function distributionCount() public view returns (uint256) {
return distributions.length;
}
function setPlatformFee(uint256 newFee) external onlyOwner {
require(newFee <= 500, 'Platform fee can not be greater than 5%');
platformFee = newFee;
}
function createDistribution(
IERC20Metadata tokenIN,
IERC20Metadata tokenOUT,
uint256 duration,
uint256 amountOUT,
string calldata name,
string calldata description,
string calldata ipfsHash,
uint256 percentReturned
) public payable {
if (address(tokenOUT) != address(0)) {
uint256 balanceBefore = tokenOUT.balanceOf(address(this));
ERC20Helpers.safeTransferFrom(address(tokenOUT), msg.sender, address(this), amountOUT);
require(tokenOUT.balanceOf(address(this)) - balanceBefore == amountOUT, "Incorrect number of tokens received");
require(msg.value == 0, 'msg.value must be zero for distributions that do not use ETH for tokenOUT');
} else require(amountOUT == msg.value, 'amountOUT and msg.value must be exactly the same');
require(amountOUT / duration > 0, "Tokens per second must be greater than 0");
require(percentReturned <= 10_000 - platformFee, "Percent returned must be less than or equal to 100% minus platformFee");
distributions.push();
uint256 id = distributions.length;
Distribution storage distribution = distributions[id - 1];
userDistributions[msg.sender].push(id);
if (newestActiveDistributionId != 0)
distributions[newestActiveDistributionId - 1].prevActiveId = id;
distribution.nextActiveId = newestActiveDistributionId;
newestActiveDistributionId = id;
distribution.tokensPerSecond = PRECISION * amountOUT / duration;
distribution.percentReturned = percentReturned;
distribution.platformFee = platformFee;
distribution.amountOUT = amountOUT;
distribution.creator = msg.sender;
distribution.ipfsHash = ipfsHash;
distribution.duration = duration;
distribution.tokenOUT = tokenOUT;
distribution.tokenIN = tokenIN;
distribution.id = id;
distribution.name = name;
distribution.description = description;
emit DistributionCreated(
id,
distribution.creator,
name,
description,
duration,
ipfsHash,
amountOUT,
percentReturned,
address(tokenOUT),
address(tokenIN)
);
}
function getInfo(uint256 distributionId, address user) public view returns (DistributionInfo memory) {
require(distributionId > 0 && distributionId <= distributions.length, "Distribution ID is out of range");
Distribution storage distribution = distributions[distributionId - 1];
DistributionInfo memory info;
info.distribution = distribution;
info.currentTimestamp = block.timestamp;
info.tokenOUT = rewardFarmers.tokenInfo(address(distribution.tokenOUT));
info.tokenIN = rewardFarmers.tokenInfo(address(distribution.tokenIN));
info.tokenOutPrice = ethPerToken(address(distribution.tokenOUT));
info.tokenInPrice = ethPerToken(address(distribution.tokenIN));
info.uncommonRewardId = distribution.uncommonRewardId;
info.hasUncommonReward = distribution.hasUncommonReward;
if (user != address(0)) {
Deposit storage deposited = deposits[distributionId][user];
info.userAllocated = allocatedAmount(distributionId, user);
info.userHasClaimed = deposited.claimed;
info.userDeposits = deposited.amount;
}
return info;
}
function getUserDistributions(address user, uint256 page, uint256 pageSize) public view returns (DistributionInfo[] memory) {
uint256 total = userDistributions[user].length;
uint256 startIndex = (page - 1) * pageSize;
if (startIndex > total) startIndex = total;
uint256 endIndex = startIndex + pageSize;
if (endIndex > total) endIndex = total;
uint256 resultCount = endIndex - startIndex;
DistributionInfo[] memory results = new DistributionInfo[](resultCount);
for (uint256 i = 0; i < resultCount; i++)
results[i] = getInfo(userDistributions[user][startIndex + i], user);
return results;
}
function getActiveDistributions(address user, uint256 page, uint256 pageSize) public view returns (DistributionInfo[] memory) {
uint256 currentIndex = newestActiveDistributionId;
uint256 skipped = 0;
while (currentIndex != 0 && skipped < (page - 1) * pageSize) {
currentIndex = distributions[currentIndex - 1].nextActiveId;
skipped++;
}
uint256 remainingDistributions = 0;
uint256 countIndex = currentIndex;
while (countIndex != 0 && remainingDistributions < pageSize) {
remainingDistributions++;
countIndex = distributions[countIndex - 1].nextActiveId;
}
DistributionInfo[] memory results = new DistributionInfo[](remainingDistributions);
uint256 count = 0;
while (currentIndex != 0 && count < remainingDistributions) {
results[count] = getInfo(currentIndex, user);
currentIndex = distributions[currentIndex - 1].nextActiveId;
count++;
}
return results;
}
function getRecentDistributions(address user, uint256 page, uint256 pageSize) external view returns (DistributionInfo[] memory) {
uint256 totalDistributions = distributions.length;
uint256 startIndex = ((page - 1) * pageSize);
if (startIndex > totalDistributions)
startIndex = totalDistributions;
uint256 endIndex = startIndex + pageSize;
if (endIndex > totalDistributions)
endIndex = totalDistributions;
uint256 resultCount = endIndex - startIndex;
DistributionInfo[] memory recentDistributions = new DistributionInfo[](resultCount);
for (uint256 i = 0; i < resultCount; i++)
recentDistributions[i] = getInfo(totalDistributions - startIndex - i, user);
return recentDistributions;
}
function startDistribution(Distribution storage distribution) private {
distribution.startTime = block.timestamp;
distribution.endTime = distribution.startTime + distribution.duration;
}
function deposit(uint256 distributionId, uint256 amount) external payable nonReentrant {
Distribution storage distribution = distributions[distributionId - 1];
if (distribution.startTime == 0) startDistribution(distribution);
require(block.timestamp <= distribution.endTime, "Distribution period has ended");
require(deposits[distributionId][msg.sender].amount == 0, "Can only deposit once");
if (address(distribution.tokenIN) != address(0)) {
require(msg.value == 0, "Cannot send ETH to token-based distribution");
ERC20Helpers.safeTransferFrom(address(distribution.tokenIN), msg.sender, address(this), amount);
} else require(amount == msg.value, "deposit amount and msg.value must be exactly the same");
require(amount > 0, "Cannot deposit zero tokens");
uint256 elapsedTime = block.timestamp - distribution.lastDepositAt;
if (distribution.lastDepositAt > 0)
distribution.totalTokensPerDeposit += elapsedTime * distribution.tokensPerSecond / distribution.totalDeposited;
deposits[distributionId][msg.sender] = Deposit({
previouslyAllocated: distribution.totalTokensPerDeposit,
amount: amount,
claimed: false
});
distribution.totalDeposited += amount;
distribution.lastDepositAt = block.timestamp;
if (distribution.creator != msg.sender)
userDistributions[msg.sender].push(distributionId);
emit Deposited(distributionId, msg.sender, amount);
}
function distributeReward(uint256 distributionId) external nonReentrant {
Distribution storage distribution = distributions[distributionId - 1];
require(block.timestamp > distribution.endTime, "Distribution period has not yet ended");
_distributeReward(distribution);
}
function _distributeReward(Distribution storage distribution) internal {
if (distribution.rewardDistributed) return;
distribution.rewardDistributed = true;
if (distribution.prevActiveId != 0)
distributions[distribution.prevActiveId - 1].nextActiveId = distribution.nextActiveId;
if (distribution.nextActiveId != 0)
distributions[distribution.nextActiveId - 1].prevActiveId = distribution.prevActiveId;
if (newestActiveDistributionId == distribution.id)
newestActiveDistributionId = distribution.nextActiveId;
uint256 rewardAmount = distribution.platformFee * distribution.totalDeposited / 10_000;
uint256 uncommonRewardLength = rewardFarmers.uncommonRewardCount();
if (address(distribution.tokenIN) == address(0))
rewardFarmers.addRewards{value: rewardAmount}(distribution.id, address(0), rewardAmount);
else {
ERC20Helpers.safeApprove(address(distribution.tokenIN), address(rewardFarmers), rewardAmount);
rewardFarmers.addRewards(distribution.id, address(distribution.tokenIN), rewardAmount);
if (uncommonRewardLength != rewardFarmers.uncommonRewardCount()) {
distribution.uncommonRewardId = uncommonRewardLength;
distribution.hasUncommonReward = true;
}
}
}
function withdraw(uint256 distributionId) external nonReentrant {
Distribution storage distribution = distributions[distributionId - 1];
require(msg.sender == distribution.creator, "Only the creator can withdraw the received tokens");
require(block.timestamp > distribution.endTime, "Distribution period has not yet ended");
require(!distribution.ownerWithdrawn, "Creator has already withdrawn");
distribution.ownerWithdrawn = true;
_distributeReward(distribution);
uint256 rewardAmount = distribution.platformFee * distribution.totalDeposited / 10_000;
uint256 amountIn = (10_000 - distribution.percentReturned) * distribution.totalDeposited / 10_000;
amountIn -= rewardAmount;
if (address(distribution.tokenIN) == address(0))
payable(msg.sender).transfer(amountIn);
else ERC20Helpers.safeTransfer(address(distribution.tokenIN), msg.sender, amountIn);
}
function claim(uint256 distributionId) external nonReentrant {
Distribution storage distribution = distributions[distributionId - 1];
require(block.timestamp > distribution.endTime, "Distribution period has not yet ended");
_distributeReward(distribution);
Deposit storage userDeposit = deposits[distributionId][msg.sender];
require(!userDeposit.claimed, "Already claimed");
userDeposit.claimed = true;
uint256 yieldAmount = allocatedAmount(distributionId, msg.sender);
uint256 amountReturned = userDeposit.amount * distribution.percentReturned / 10_000;
if (address(distribution.tokenOUT) == address(0))
payable(msg.sender).transfer(yieldAmount);
else ERC20Helpers.safeTransfer(address(distribution.tokenOUT), msg.sender, yieldAmount);
if (address(distribution.tokenIN) == address(0))
payable(msg.sender).transfer(amountReturned);
else ERC20Helpers.safeTransfer(address(distribution.tokenIN), msg.sender, amountReturned);
emit Claim(distributionId, msg.sender, yieldAmount, amountReturned);
}
function currentAllocationPeriod(uint256 distributionId) public view returns (uint256) {
Distribution storage distribution = distributions[distributionId - 1];
if (distribution.endTime < block.timestamp)
return distribution.endTime - distribution.lastDepositAt;
else return block.timestamp - distribution.lastDepositAt;
}
function allocatedAmount(uint256 distributionId, address user) public view returns (uint256) {
Distribution storage distribution = distributions[distributionId - 1];
if (distribution.totalDeposited == 0) return 0;
Deposit storage deposited = deposits[distributionId][user];
uint256 currentAllocationAmount = currentAllocationPeriod(distributionId) * distribution.tokensPerSecond * deposited.amount / distribution.totalDeposited;
uint256 previousAllocationAmount = deposited.amount * (distribution.totalTokensPerDeposit - deposited.previouslyAllocated);
return (previousAllocationAmount + currentAllocationAmount) / PRECISION;
}
function currentAllocation(uint256 distributionId, address user) public view returns (uint256) {
Distribution storage distribution = distributions[distributionId - 1];
if (distribution.totalDeposited == 0) return 0;
Deposit storage deposited = deposits[distributionId][user];
return currentAllocationPeriod(distributionId) * distribution.tokensPerSecond * deposited.amount / distribution.totalDeposited;
}
function previousAllocation(uint256 distributionId, address user) public view returns (uint256) {
Distribution storage distribution = distributions[distributionId - 1];
if (distribution.totalDeposited == 0) return 0;
Deposit storage deposited = deposits[distributionId][user];
return deposited.amount * (distribution.totalTokensPerDeposit - deposited.previouslyAllocated);
}
function ethPerToken(address token) public view returns (uint) {
return rewardFarmers.ethPerToken(token);
}
function revokeCanChangeRewardAddress() external onlyOwner {
canChangeRewardAddress = false;
}
function setRewardFarmers(IRewardFarmers _farm) external onlyOwner {
require(canChangeRewardAddress, 'can not update RewardFarmersAddress');
rewardFarmers = _farm;
}
}
{
"compilationTarget": {
"contracts/RiceFields.sol": "RiceFields"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@ensdomains/=node_modules/@ensdomains/",
":@ganache/=node_modules/@ganache/",
":@openzeppelin/=node_modules/@openzeppelin/",
":@truffle/=node_modules/@truffle/",
":@uniswap/=node_modules/@uniswap/",
":base64-sol/=node_modules/base64-sol/",
":hardhat/=node_modules/hardhat/",
":truffle/=node_modules/truffle/"
],
"viaIR": true
}
[{"inputs":[{"internalType":"contract IRewardFarmers","name":"_farm","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"distributionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"distributionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"description","type":"string"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"},{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"},{"indexed":false,"internalType":"uint256","name":"amountOUT","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"percentReturned","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenOUT","type":"address"},{"indexed":false,"internalType":"address","name":"tokenIN","type":"address"}],"name":"DistributionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionId","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"allocatedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canChangeRewardAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionId","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"tokenIN","type":"address"},{"internalType":"contract IERC20Metadata","name":"tokenOUT","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"amountOUT","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"uint256","name":"percentReturned","type":"uint256"}],"name":"createDistribution","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionId","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"currentAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionId","type":"uint256"}],"name":"currentAllocationPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"deposits","outputs":[{"internalType":"uint256","name":"previouslyAllocated","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"claimed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionId","type":"uint256"}],"name":"distributeReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributionCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"distributions","outputs":[{"internalType":"uint256","name":"totalTokensPerDeposit","type":"uint256"},{"internalType":"uint256","name":"uncommonRewardId","type":"uint256"},{"internalType":"uint256","name":"percentReturned","type":"uint256"},{"internalType":"uint256","name":"tokensPerSecond","type":"uint256"},{"internalType":"bool","name":"hasUncommonReward","type":"bool"},{"internalType":"uint256","name":"totalDeposited","type":"uint256"},{"internalType":"bool","name":"rewardDistributed","type":"bool"},{"internalType":"uint256","name":"lastDepositAt","type":"uint256"},{"internalType":"bool","name":"ownerWithdrawn","type":"bool"},{"internalType":"uint256","name":"platformFee","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"amountOUT","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"nextActiveId","type":"uint256"},{"internalType":"uint256","name":"prevActiveId","type":"uint256"},{"internalType":"contract IERC20Metadata","name":"tokenIN","type":"address"},{"internalType":"contract IERC20Metadata","name":"tokenOUT","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"ethPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"page","type":"uint256"},{"internalType":"uint256","name":"pageSize","type":"uint256"}],"name":"getActiveDistributions","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"totalTokensPerDeposit","type":"uint256"},{"internalType":"uint256","name":"uncommonRewardId","type":"uint256"},{"internalType":"uint256","name":"percentReturned","type":"uint256"},{"internalType":"uint256","name":"tokensPerSecond","type":"uint256"},{"internalType":"bool","name":"hasUncommonReward","type":"bool"},{"internalType":"uint256","name":"totalDeposited","type":"uint256"},{"internalType":"bool","name":"rewardDistributed","type":"bool"},{"internalType":"uint256","name":"lastDepositAt","type":"uint256"},{"internalType":"bool","name":"ownerWithdrawn","type":"bool"},{"internalType":"uint256","name":"platformFee","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"amountOUT","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"nextActiveId","type":"uint256"},{"internalType":"uint256","name":"prevActiveId","type":"uint256"},{"internalType":"contract IERC20Metadata","name":"tokenIN","type":"address"},{"internalType":"contract IERC20Metadata","name":"tokenOUT","type":"address"}],"internalType":"struct RiceFields.Distribution","name":"distribution","type":"tuple"},{"internalType":"uint256","name":"uncommonRewardId","type":"uint256"},{"internalType":"uint256","name":"currentTimestamp","type":"uint256"},{"internalType":"bool","name":"hasUncommonReward","type":"bool"},{"internalType":"uint256","name":"userAllocated","type":"uint256"},{"internalType":"uint256","name":"tokenOutPrice","type":"uint256"},{"internalType":"uint256","name":"tokenInPrice","type":"uint256"},{"internalType":"uint256","name":"userDeposits","type":"uint256"},{"internalType":"bool","name":"userHasClaimed","type":"bool"},{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"name","type":"string"}],"internalType":"struct IRewardFarmers.TokenInfo","name":"tokenOUT","type":"tuple"},{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"name","type":"string"}],"internalType":"struct IRewardFarmers.TokenInfo","name":"tokenIN","type":"tuple"},{"internalType":"uint256","name":"rewardId","type":"uint256"}],"internalType":"struct RiceFields.DistributionInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionId","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"getInfo","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"totalTokensPerDeposit","type":"uint256"},{"internalType":"uint256","name":"uncommonRewardId","type":"uint256"},{"internalType":"uint256","name":"percentReturned","type":"uint256"},{"internalType":"uint256","name":"tokensPerSecond","type":"uint256"},{"internalType":"bool","name":"hasUncommonReward","type":"bool"},{"internalType":"uint256","name":"totalDeposited","type":"uint256"},{"internalType":"bool","name":"rewardDistributed","type":"bool"},{"internalType":"uint256","name":"lastDepositAt","type":"uint256"},{"internalType":"bool","name":"ownerWithdrawn","type":"bool"},{"internalType":"uint256","name":"platformFee","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"amountOUT","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"nextActiveId","type":"uint256"},{"internalType":"uint256","name":"prevActiveId","type":"uint256"},{"internalType":"contract IERC20Metadata","name":"tokenIN","type":"address"},{"internalType":"contract IERC20Metadata","name":"tokenOUT","type":"address"}],"internalType":"struct RiceFields.Distribution","name":"distribution","type":"tuple"},{"internalType":"uint256","name":"uncommonRewardId","type":"uint256"},{"internalType":"uint256","name":"currentTimestamp","type":"uint256"},{"internalType":"bool","name":"hasUncommonReward","type":"bool"},{"internalType":"uint256","name":"userAllocated","type":"uint256"},{"internalType":"uint256","name":"tokenOutPrice","type":"uint256"},{"internalType":"uint256","name":"tokenInPrice","type":"uint256"},{"internalType":"uint256","name":"userDeposits","type":"uint256"},{"internalType":"bool","name":"userHasClaimed","type":"bool"},{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"name","type":"string"}],"internalType":"struct IRewardFarmers.TokenInfo","name":"tokenOUT","type":"tuple"},{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"name","type":"string"}],"internalType":"struct IRewardFarmers.TokenInfo","name":"tokenIN","type":"tuple"},{"internalType":"uint256","name":"rewardId","type":"uint256"}],"internalType":"struct RiceFields.DistributionInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"page","type":"uint256"},{"internalType":"uint256","name":"pageSize","type":"uint256"}],"name":"getRecentDistributions","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"totalTokensPerDeposit","type":"uint256"},{"internalType":"uint256","name":"uncommonRewardId","type":"uint256"},{"internalType":"uint256","name":"percentReturned","type":"uint256"},{"internalType":"uint256","name":"tokensPerSecond","type":"uint256"},{"internalType":"bool","name":"hasUncommonReward","type":"bool"},{"internalType":"uint256","name":"totalDeposited","type":"uint256"},{"internalType":"bool","name":"rewardDistributed","type":"bool"},{"internalType":"uint256","name":"lastDepositAt","type":"uint256"},{"internalType":"bool","name":"ownerWithdrawn","type":"bool"},{"internalType":"uint256","name":"platformFee","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"amountOUT","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"nextActiveId","type":"uint256"},{"internalType":"uint256","name":"prevActiveId","type":"uint256"},{"internalType":"contract IERC20Metadata","name":"tokenIN","type":"address"},{"internalType":"contract IERC20Metadata","name":"tokenOUT","type":"address"}],"internalType":"struct RiceFields.Distribution","name":"distribution","type":"tuple"},{"internalType":"uint256","name":"uncommonRewardId","type":"uint256"},{"internalType":"uint256","name":"currentTimestamp","type":"uint256"},{"internalType":"bool","name":"hasUncommonReward","type":"bool"},{"internalType":"uint256","name":"userAllocated","type":"uint256"},{"internalType":"uint256","name":"tokenOutPrice","type":"uint256"},{"internalType":"uint256","name":"tokenInPrice","type":"uint256"},{"internalType":"uint256","name":"userDeposits","type":"uint256"},{"internalType":"bool","name":"userHasClaimed","type":"bool"},{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"name","type":"string"}],"internalType":"struct IRewardFarmers.TokenInfo","name":"tokenOUT","type":"tuple"},{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"name","type":"string"}],"internalType":"struct IRewardFarmers.TokenInfo","name":"tokenIN","type":"tuple"},{"internalType":"uint256","name":"rewardId","type":"uint256"}],"internalType":"struct RiceFields.DistributionInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"page","type":"uint256"},{"internalType":"uint256","name":"pageSize","type":"uint256"}],"name":"getUserDistributions","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"totalTokensPerDeposit","type":"uint256"},{"internalType":"uint256","name":"uncommonRewardId","type":"uint256"},{"internalType":"uint256","name":"percentReturned","type":"uint256"},{"internalType":"uint256","name":"tokensPerSecond","type":"uint256"},{"internalType":"bool","name":"hasUncommonReward","type":"bool"},{"internalType":"uint256","name":"totalDeposited","type":"uint256"},{"internalType":"bool","name":"rewardDistributed","type":"bool"},{"internalType":"uint256","name":"lastDepositAt","type":"uint256"},{"internalType":"bool","name":"ownerWithdrawn","type":"bool"},{"internalType":"uint256","name":"platformFee","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"amountOUT","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"nextActiveId","type":"uint256"},{"internalType":"uint256","name":"prevActiveId","type":"uint256"},{"internalType":"contract IERC20Metadata","name":"tokenIN","type":"address"},{"internalType":"contract IERC20Metadata","name":"tokenOUT","type":"address"}],"internalType":"struct RiceFields.Distribution","name":"distribution","type":"tuple"},{"internalType":"uint256","name":"uncommonRewardId","type":"uint256"},{"internalType":"uint256","name":"currentTimestamp","type":"uint256"},{"internalType":"bool","name":"hasUncommonReward","type":"bool"},{"internalType":"uint256","name":"userAllocated","type":"uint256"},{"internalType":"uint256","name":"tokenOutPrice","type":"uint256"},{"internalType":"uint256","name":"tokenInPrice","type":"uint256"},{"internalType":"uint256","name":"userDeposits","type":"uint256"},{"internalType":"bool","name":"userHasClaimed","type":"bool"},{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"name","type":"string"}],"internalType":"struct IRewardFarmers.TokenInfo","name":"tokenOUT","type":"tuple"},{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"name","type":"string"}],"internalType":"struct IRewardFarmers.TokenInfo","name":"tokenIN","type":"tuple"},{"internalType":"uint256","name":"rewardId","type":"uint256"}],"internalType":"struct RiceFields.DistributionInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newestActiveDistributionId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionId","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"previousAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeCanChangeRewardAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardFarmers","outputs":[{"internalType":"contract IRewardFarmers","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"setPlatformFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IRewardFarmers","name":"_farm","type":"address"}],"name":"setRewardFarmers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userDistributions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]