pragma solidity ^0.5.0; import "./Token.sol"; import "./util.sol"; import { ResizableArray } from "./resizableArray.sol"; export {Hub} struct Flow { address identity; uint256 sent; uint256 received; } struct Transfer { address tokenOwner; address src; address dst; uint amount; } error PathTooComplex(); error PathInvalid(string reason); error TrustLimitExceeded(Transfer transfer, uint maxAmount); function appendIfNotExists(ResizableArray[Flow] memory flows, address user) returns (Flow memory) { for (uint i = 0; i < flows.length; i++) if (flows.at(i).identity == user) return flows.at(i); flows.push(Flow{user, 0, 0}); return flows.at(flows.length - 1); } function integrateTransfer(ResizableArray[Flow] memory flows, Transfer calldata transfer) { appendIfNotExists(flows, transfer.src).sent += transfer.amount; appendIfNotExists(flows, transfer.dst).received += transfer.amount; } function validateTransferThrough(ResizableArray[Flow] memory flows) internal { // a valid path has only one true sender and reciever, for all other // addresses in the path, sent = received // also, the sender should be msg.sender address src; address dest; for (uint i = 0; i < flows.length; i++) { Flow memory f = flows.at(i); if (f.sent > f.received) { require(src == address(0), PathInvalid("Path sends from more than one src")); require(f.identity == msg.sender, PathInvalid("Path doesn't send from transaction sender")); require(f.received == 0, PathInvalid("Sender is receiving")); src = f.identity; } if (f.received > f.sent) { require(dest == address(0), PathInvalid("Path sends to more than one dest")); require(f.sent == 0, PathInvalid("Receiver is sending")); dest = f.identity; } } require(src != address(0), PathInvalid("Transaction must have a src")); require(dest != address(0), PathInvalid("Transaction must have a dest")); // the total amounts sent and received by src and dest should match require( appendIfNotExists(flows, src).sent == appendIfNotExists(flows, dest).received, "Unequal sent and received amounts" ); emit HubTransfer(src, dest, validation[src].sent); } contract Hub { address public owner; uint256 public inflation; uint256 public divisor; uint256 public period; string public symbol; // = 'CRC'; uint256 public initialPayout; uint256 public initialIssuance; uint256 public deployedAt; mapping (address => Token) public userToToken; mapping (address => address) public tokenToUser; mapping (address => mapping (address => uint256)) public limits; event Signup(address indexed user, address token); event Trust(address indexed canSendTo, address indexed user, uint256 limit); event HubTransfer(address indexed from, address indexed to, uint256 amount); mapping (address => transferValidator) public validation; address[] public seen; modifier onlyOwner() { require (msg.sender == owner); _; } constructor(address _owner, uint256 _inflation, uint256 _period, string memory _symbol, uint256 _initialPayout, uint256 _initialIssuance) public { require (_owner != address(0)); owner = _owner; inflation = _inflation; divisor = findDivisor(_inflation); period = _period; symbol = _symbol; initialPayout = _initialPayout; initialIssuance = _initialIssuance; deployedAt = block.timestamp; } function findDivisor(uint256 _inf) internal pure returns (uint256) { uint256 iter = 0; while (_inf.div(pow(10, iter)) > 9) { iter += 1; } return pow(10, iter); } function periods() public view returns (uint256) { return (block.timestamp.sub(deployedAt)).div(period); } function issuance() public view returns (uint256) { return inflate(initialIssuance, periods()); } function issuanceStep(uint256 _periods) public view returns (uint256) { return inflate(initialIssuance, _periods); } function inflate(uint256 _initial, uint256 _periods) public view returns (uint256) { uint256 q = pow(inflation, _periods); uint256 d = pow(divisor, _periods); return (_initial.mul(q)).div(d); } function changeOwner(address _newOwner) public onlyOwner returns (bool) { require(_newOwner != address(0)); owner = _newOwner; return true; } function updateInflation(uint256 _inflation) public onlyOwner returns (bool) { inflation = _inflation; return true; } function updateRate(uint256 _initialIssuance) public onlyOwner returns (bool) { initialIssuance = _initialIssuance; return true; } function updateSymbol(string memory _symbol) public onlyOwner returns (bool) { symbol = _symbol; return true; } function time() public view returns (uint256) { return block.timestamp; } // No exit allowed. Once you create a personal token, you're in for good. function signup(string memory _name) public returns (bool) { require(address(userToToken[msg.sender]) == address(0)); Token token = new Token(msg.sender, _name, initialPayout); userToToken[msg.sender] = token; tokenToUser[address(token)] = msg.sender; _trust(msg.sender, 100); emit Signup(msg.sender, address(token)); return true; } // Trust does not have to be reciprocated. // (e.g. I can trust you but you don't have to trust me) function trust(address user, uint limit) public { require(address(userToToken[msg.sender]) != address(0), "You can only trust people after you've signed up!"); require(msg.sender != user, "You can't untrust yourself"); _trust(user, limit); } function _trust(address user, uint limit) internal { limits[msg.sender][user] = limit; emit Trust(msg.sender, user, limit); } function pow(uint256 base, uint256 exponent) public pure returns (uint256) { if (base == 0) { return 0; } if (exponent == 0) { return 1; } if (exponent == 1) { return base; } uint256 y = 1; while(exponent > 1) { if(exponent.mod(2) == 0) { base = base.mul(base); exponent = exponent.div(2); } else { y = base.mul(y); base = base.mul(base); exponent = (exponent.sub(1)).div(2); } } return base.mul(y); } function checkSendLimit(address tokenOwner, address src, address dest) public view returns (uint256) { // there is no trust if (limits[dest][tokenOwner] == 0) { return 0; } // if dest hasn't signed up, they cannot trust anyone if (address(userToToken[dest]) == address(0)) { return 0; } // if the token doesn't exist, return max uint256 max = (userToToken[dest].totalSupply().mul(limits[dest][tokenOwner])).div(100); if (address(userToToken[tokenOwner]) == address(0)) { return max; } // if sending dest's token to dest, src can send 100% of their holdings uint256 srcBalance = userToToken[tokenOwner].balanceOf(src); if (tokenOwner == dest) { return srcBalance; } uint256 destBalance = userToToken[tokenOwner].balanceOf(dest); // if trustLimit has already been overriden by a direct transfer, nothing more can be sent if (max < destBalance) return 0; return max.sub(destBalance); } function transferThrough(Transfer[] calldata transfers) public mut { require(transfers.length <= 10, PathTooComplex()); ResizableArray[Flow] memory flows; // Template paramater values are auto-deduced here. // we still provide the [] to indicate it is a template function. fold[](transfers, flows, function(ResizableArray[Flow] memory flows, Transfer calldata transfer) view returns (ResizableArray[Flow] memory) { if (transfer.tokenOwner != transfer.dst) { uint256 max = checkSendLimit(transfer.tokenOwner, transfer.src, transfer.dst); require( userToToken[transfer.tokenOwner].balanceOf(dest) + transfer.amount <= max, TrustLimitExceeded(transfer, max) ); } integrateTransfer(flows, transfer); userToToken[transfer.tokenOwner].hubTransfer(transfer.src, transfer.dest, transfer.amount); return flows; }); validateTransferThrough(flows); } }