Skip to content

Instantly share code, notes, and snippets.

@m-bo-one
Last active February 27, 2021 01:40
Show Gist options
  • Save m-bo-one/b0481c4d37732d63d6d5e347e791ace4 to your computer and use it in GitHub Desktop.
Save m-bo-one/b0481c4d37732d63d6d5e347e791ace4 to your computer and use it in GitHub Desktop.
pragma solidity >=0.7.0 <0.8.0;
interface ERC20 {
function totalSupply() external view returns (uint supply);
function balanceOf(address _owner) external view returns (uint balance);
function transfer(address _to, uint _value) external returns (bool success);
function transferFrom(address _from, address _to, uint _value) external returns (bool success);
function approve(address _spender, uint _value) external returns (bool success);
function allowance(address _owner, address _spender) external view returns (uint remaining);
function decimals() external view returns(uint digits);
event Approval(address indexed _owner, address indexed _spender, uint _value);
}
contract Bound {
uint256 public price; // in wei measurement
uint256 public dividends;
uint256 public expireDate;
uint256 public available;
mapping(address => uint256) private depositeAmount;
struct Deposit {
uint256 count;
uint256 updatedAt;
}
struct Asset {
address addr;
uint256 amount;
}
mapping(address => Deposit[]) private stakes;
// mapping(address => uint256) private assetMap;
// address[] private assetAddrs;
uint256 private minimalAmountOnBuy = 10; // at least 10 wei required minimum
uint256 private payoutDelta = 3600 * 24; // in seconds, for test 10, must be 3600 * 24
uint256 private defaultCount = 1; // one bound
uint256 private defaultMultiplier = 1000000000000000000; // 10 ^ 18
uint256 private percentageMultiplier = 100;
address private manager;
event BoundCreated(address indexed creator, uint256 price, uint256 dividends, uint256 expireDate);
event BoundBuy(address indexed buyer, uint256 amount, uint256 count);
event BoundEarn(address indexed earner, uint256 amount, uint256 timeAt);
event BoundUnlock(address indexed reciever, uint256 amount);
modifier onlyManager() {
require(manager == msg.sender, "Only manager can execute this function");
_;
}
constructor(uint256 _price, uint256 _dividends, uint256 _expireDate) {
require(msg.sender != address(0), "Zero address protection");
require(_expireDate > block.timestamp, "Expire date must be great then current block time.");
manager = msg.sender;
price = _price;
dividends = _dividends;
expireDate = _expireDate;
available = defaultCount * defaultMultiplier;
emit BoundCreated(manager, price, dividends, expireDate);
}
function getBalance() public view returns(uint256) {
return address(this).balance;
}
function buy(address sender) public payable onlyManager() {
require(expireDate > block.timestamp, "Expired.");
uint256 amount = msg.value;
require(amount >= minimalAmountOnBuy, "Amount must be not be less than minimal ammount");
uint256 count = amount * defaultMultiplier / price;
require(available >= count, "Bound limits reached");
// tracking amount of money on bound
depositeAmount[sender] += amount;
// adding stake count with timestamp proof
Deposit memory dp = Deposit(count, block.timestamp);
stakes[sender].push(dp);
// tack total available amount for next stakes
available -= count;
emit BoundBuy(sender, amount, count);
}
function earn(address sender, address payable _to) public payable onlyManager() {
require(expireDate > block.timestamp, "Expired.");
uint256 currentTime = block.timestamp;
for (uint8 i = 0; i < stakes[sender].length; i++) {
Deposit storage dp = stakes[sender][i];
// if deposit time has 24 hours from previous update time, than payout
// NOTE: For test - set ~10 seconds
if (dp.updatedAt + payoutDelta <= currentTime) {
// calculate the days delta if earn was not called
uint256 daysDelta = (currentTime - dp.updatedAt) / payoutDelta;
uint256 payout = dividends * dp.count * daysDelta / percentageMultiplier;
dp.updatedAt = currentTime;
_to.transfer(payout);
emit BoundEarn(_to, payout, dp.updatedAt);
}
}
}
function unlock(address payable sender) public payable onlyManager() {
uint256 amount = depositeAmount[sender];
require(amount > 0, "Nothing to payout");
// NOTE: For test - uncomment this line
require(expireDate <= block.timestamp, "Could unlock only after expiration of time.");
sender.transfer(depositeAmount[sender]);
depositeAmount[sender] = 0;
for (uint8 i = 0; i < stakes[sender].length; i++) {
Deposit memory dp = stakes[sender][i];
available += dp.count;
}
delete stakes[sender];
// for (uint8 i = 0; i < assetAddrs.length; i++) {
// address addr = assetAddrs[i];
// uint256 assetAmt = assetMap[addr];
// ERC20(addr).transfer(sender, assetAmt);
// delete assetMap[addr];
// }
// delete assetAddrs;
emit BoundUnlock(sender, amount);
}
function addAsset(address sender, address addr, uint256 amount) public onlyManager() {
ERC20(addr).transferFrom(sender, address(this), amount);
// if (assetMap[addr] == 0) {
// assetAddrs.push(addr);
// }
// assetMap[addr] += amount;
}
}
contract BoundManager {
address private owner;
address[] private boundList;
mapping(address => bool) private boundMap;
constructor() {
owner = msg.sender;
}
modifier exists(address addr) {
require(boundMap[addr], "Bound not found for associated manager");
_;
}
function create(uint256 price, uint256 dividends, uint256 delta, address[] memory assetAddrs, uint256[] memory assetAmounts) public payable {
require(assetAddrs.length != 0, "Assets could not be zero");
require(assetAddrs.length == assetAmounts.length, "Asset data length must be the same.");
Bound bound = new Bound(price, dividends, block.timestamp + delta);
for (uint8 i = 0; i < assetAddrs.length; i++) {
bound.addAsset(msg.sender, assetAddrs[i], assetAmounts[i]);
}
boundList.push(address(bound));
boundMap[address(bound)] = true;
}
function buy(Bound bound) public payable exists(address(bound)) {
bound.buy{value:msg.value}(msg.sender);
}
function earn(Bound bound, address payable _to) public payable exists(address(bound)) {
bound.earn{value:msg.value}(msg.sender, _to);
}
function unlock(Bound bound) public payable exists(address(bound)) {
bound.unlock{value:msg.value}(msg.sender);
}
function getBoundList() public view returns(address[] memory) {
return boundList;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment