Last active
February 27, 2021 01:40
-
-
Save m-bo-one/b0481c4d37732d63d6d5e347e791ace4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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