文件 1 的 6:AggregatorV3Interface.sol
pragma solidity >=0.6.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
文件 2 的 6:Context.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
文件 3 的 6:IERC20.sol
pragma solidity >=0.6.0 <0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
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);
}
文件 4 的 6:Ownable.sol
pragma solidity >=0.6.0 <0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
文件 5 的 6:Presale.sol
pragma solidity ^0.7.6;
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {
AggregatorV3Interface
} from '@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol';
contract Presale is Ownable {
using SafeMath for uint256;
IERC20 public dpx;
struct Vest {
uint256 amount;
uint256 released;
bool ethTransferred;
}
mapping(address => Vest) public vestedBeneficiaries;
mapping(address => uint256) public ethDeposits;
address[] public beneficiaries;
uint256 public noOfBeneficiaries;
bool public bootstrapped;
uint256 public startTime;
uint256 public duration;
uint256 public dpxPrice;
AggregatorV3Interface internal priceFeed;
constructor(address _priceFeedAddress, uint256 _dpxPrice) {
require(_priceFeedAddress != address(0), 'Price feed address cannot be 0');
require(_dpxPrice > 0, 'DPX price has to be higher than 0');
priceFeed = AggregatorV3Interface(_priceFeedAddress);
dpxPrice = _dpxPrice;
addBeneficiary(0x0330414bBF9491445c102A2a8a14adB9b6a25384, uint256(5000).mul(1e18));
addBeneficiary(0x5FB8b9512684d451D4E585A1a0AabFB48A253C67, uint256(1000).mul(1e18));
addBeneficiary(0x9846338e0726d317280346c5003Db365745433D7, uint256(1200).mul(1e18));
addBeneficiary(0x2d9Bd03312814a34E6706bC81A3593788716d16a, uint256(500).mul(1e18));
addBeneficiary(0x9c5083dd4838E120Dbeac44C052179692Aa5dAC5, uint256(10000).mul(1e18));
addBeneficiary(0x0E6Aa54f683dFFC3D6BDb4057Bdb47cBc18975E7, uint256(10000).mul(1e18));
addBeneficiary(0x3E46bb5a8A10c9CA522df0b25036930cb45b0fb3, uint256(6000).mul(1e18));
addBeneficiary(0xE5442814c0d31bF9f67676B72838C0E64E9c7B4e, uint256(240).mul(1e18));
}
function bootstrap(
uint256 _startTime,
uint256 _duration,
address _dpxAddress
) external onlyOwner returns (bool) {
require(_dpxAddress != address(0), 'DPX address is 0');
require(_duration > 0, 'Duration passed cannot be 0');
require(_startTime > block.timestamp, 'Start time cannot be before current time');
startTime = _startTime;
duration = _duration;
dpx = IERC20(_dpxAddress);
uint256 totalDPXRequired;
for (uint256 i = 0; i < beneficiaries.length; i = i + 1) {
totalDPXRequired = totalDPXRequired.add(vestedBeneficiaries[beneficiaries[i]].amount);
}
require(totalDPXRequired > 0, 'Total DPX required cannot be 0');
dpx.transferFrom(msg.sender, address(this), totalDPXRequired);
bootstrapped = true;
emit Bootstrap(totalDPXRequired);
return bootstrapped;
}
function addBeneficiary(address _beneficiary, uint256 _amount) public onlyOwner returns (bool) {
require(_beneficiary != address(0), 'Beneficiary cannot be a 0 address');
require(_amount > 0, 'Amount should be larger than 0');
require(!bootstrapped, 'Cannot add beneficiary as contract has been bootstrapped');
require(vestedBeneficiaries[_beneficiary].amount == 0, 'Cannot add the same beneficiary again');
beneficiaries.push(_beneficiary);
vestedBeneficiaries[_beneficiary].amount = _amount;
noOfBeneficiaries = noOfBeneficiaries.add(1);
emit AddBeneficiary(_beneficiary, _amount);
return true;
}
function updateBeneficiary(address _beneficiary, uint256 _amount) external onlyOwner {
require(_beneficiary != address(0), 'Beneficiary cannot be a 0 address');
require(!bootstrapped, 'Cannot update beneficiary as contract has been bootstrapped');
require(
vestedBeneficiaries[_beneficiary].amount != _amount,
'New amount cannot be the same as old amount'
);
require(
!vestedBeneficiaries[_beneficiary].ethTransferred,
'Beneficiary should have not transferred ETH'
);
require(_amount > 0, 'Amount cannot be smaller or equal to 0');
require(vestedBeneficiaries[_beneficiary].amount != 0, 'Beneficiary has not been added');
vestedBeneficiaries[_beneficiary].amount = _amount;
emit UpdateBeneficiary(_beneficiary, _amount);
}
function removeBeneficiary(address payable _beneficiary) external onlyOwner returns (bool) {
require(_beneficiary != address(0), 'Beneficiary cannot be a 0 address');
require(!bootstrapped, 'Cannot remove beneficiary as contract has been bootstrapped');
if (vestedBeneficiaries[_beneficiary].ethTransferred) {
_beneficiary.transfer(ethDeposits[_beneficiary]);
}
for (uint256 i = 0; i < beneficiaries.length; i = i + 1) {
if (beneficiaries[i] == _beneficiary) {
noOfBeneficiaries = noOfBeneficiaries.sub(1);
delete beneficiaries[i];
delete vestedBeneficiaries[_beneficiary];
emit RemoveBeneficiary(_beneficiary);
return true;
}
}
return false;
}
function withdraw() external onlyOwner {
uint256 ethBalance = payable(address(this)).balance;
payable(msg.sender).transfer(ethBalance);
emit WithdrawEth(ethBalance);
}
function transferEth() external payable returns (uint256 ethAmount) {
require(
!vestedBeneficiaries[msg.sender].ethTransferred,
'Beneficiary has already transferred ETH'
);
require(vestedBeneficiaries[msg.sender].amount > 0, 'Sender is not a beneficiary');
uint256 ethPrice = getLatestPrice();
ethAmount = vestedBeneficiaries[msg.sender].amount.mul(dpxPrice).div(ethPrice);
require(msg.value >= ethAmount, 'Incorrect ETH amount sent');
if (msg.value > ethAmount) {
payable(msg.sender).transfer(msg.value.sub(ethAmount));
}
ethDeposits[msg.sender] = ethAmount;
vestedBeneficiaries[msg.sender].ethTransferred = true;
emit TransferredEth(msg.sender, ethAmount, ethPrice);
}
function release() external returns (uint256 unreleased) {
require(bootstrapped, 'Contract has not been bootstrapped');
require(vestedBeneficiaries[msg.sender].ethTransferred, 'Beneficiary has not transferred eth');
unreleased = releasableAmount(msg.sender);
require(unreleased > 0, 'No releasable amount');
vestedBeneficiaries[msg.sender].released = vestedBeneficiaries[msg.sender].released.add(
unreleased
);
dpx.transfer(msg.sender, unreleased);
emit TokensReleased(msg.sender, unreleased);
}
function releasableAmount(address beneficiary) public view returns (uint256) {
return vestedAmount(beneficiary).sub(vestedBeneficiaries[beneficiary].released);
}
function vestedAmount(address beneficiary) public view returns (uint256) {
uint256 totalBalance = vestedBeneficiaries[beneficiary].amount;
if (block.timestamp < startTime) {
return 0;
} else if (block.timestamp >= startTime.add(duration)) {
return totalBalance;
} else {
uint256 halfTotalBalance = totalBalance.div(2);
return
halfTotalBalance.mul(block.timestamp.sub(startTime)).div(duration).add(halfTotalBalance);
}
}
function getLatestPrice() public view returns (uint256) {
(, int256 price, , , ) = priceFeed.latestRoundData();
return uint256(price);
}
event TokensReleased(address beneficiary, uint256 amount);
event AddBeneficiary(address beneficiary, uint256 amount);
event RemoveBeneficiary(address beneficiary);
event UpdateBeneficiary(address beneficiary, uint256 amount);
event TransferredEth(address beneficiary, uint256 ethAmount, uint256 ethPrice);
event WithdrawEth(uint256 amount);
event Bootstrap(uint256 totalDPXRequired);
}
文件 6 的 6:SafeMath.sol
pragma solidity >=0.6.0 <0.8.0;
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
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) {
if (b == 0) return (false, 0);
return (true, a / b);
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
{
"compilationTarget": {
"contracts/Presale.sol": "Presale"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_priceFeedAddress","type":"address"},{"internalType":"uint256","name":"_dpxPrice","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AddBeneficiary","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"totalDPXRequired","type":"uint256"}],"name":"Bootstrap","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"beneficiary","type":"address"}],"name":"RemoveBeneficiary","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensReleased","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethPrice","type":"uint256"}],"name":"TransferredEth","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UpdateBeneficiary","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawEth","type":"event"},{"inputs":[{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"addBeneficiary","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"beneficiaries","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_startTime","type":"uint256"},{"internalType":"uint256","name":"_duration","type":"uint256"},{"internalType":"address","name":"_dpxAddress","type":"address"}],"name":"bootstrap","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bootstrapped","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dpx","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dpxPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"duration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"ethDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLatestPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"noOfBeneficiaries","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"beneficiary","type":"address"}],"name":"releasableAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"release","outputs":[{"internalType":"uint256","name":"unreleased","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_beneficiary","type":"address"}],"name":"removeBeneficiary","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"transferEth","outputs":[{"internalType":"uint256","name":"ethAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"updateBeneficiary","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"beneficiary","type":"address"}],"name":"vestedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"vestedBeneficiaries","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"released","type":"uint256"},{"internalType":"bool","name":"ethTransferred","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]