Created
July 16, 2020 13:34
-
-
Save chriseth/7f087fc42c4c2d115bc04b8fe6948d69 to your computer and use it in GitHub Desktop.
Templates
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.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); | |
} | |
} | |
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
import { min } from "./util.sol"; | |
export { ResizableArray, IndexOutOfBounds }; | |
error IndexOutOfBounds(); | |
error InvalidReserve(); | |
// All declarations inside a template automatically turn into templates. | |
// When accessed from outside, the value for T has to be explicitly | |
// given (unless it is auto-deduced). | |
// Inside the template area, template parameters can be mentioned | |
// when accessing other templatized declartions but they can | |
// also be omitted. | |
template[T] | |
{ | |
struct ResizableArray | |
{ | |
uint length; | |
T[] data; | |
invariant { length < data.length } | |
} | |
// "member" attaches the function as a member to the type. | |
// alternative: "bound"? | |
// It cannot be called without the type. | |
// Problem: the inner workings may be obivous if the first parameter is a struct, | |
// but it may not be that obvious if it is a complex array. | |
// Name clashes could maybe only be detected at the point where the function is used. | |
/// Reserves a certain capacity but does not change the length of | |
/// the array. Re-allocates if needed. | |
/// capacity has to be at least the current length. | |
member function reserve(ResizableArray memory array, uint capacity) | |
{ | |
require(capacity >= array.length, InvalidReserve()); | |
if (capacity <= array.data.length) | |
return; | |
T[] memory newData = new T[](capacity); | |
for (uint i = 0; i < array.length; i++) | |
newData[i] = array.data[i]; | |
array.data = newData; | |
} | |
/// Sets the length to the given value and re-allocates the | |
/// data if required. | |
member function resize(ResizableArray memory array, uint length) | |
{ | |
// grow if needed | |
uint newCapacity = array.data.length; | |
while (newCapacity < length) newCapacity *= 2; | |
array.reserve(newCapacity); | |
// zero-initialize in case we reduced the size | |
for (uint i = array.length; i < length; i++) | |
{ | |
// for template types, we allow data location even if the template | |
// parameter is a value type | |
T memory zero; | |
array.data[i] = zero; | |
} | |
array.length = length; | |
assert(array.length <= array.data.length); | |
} | |
member function at(ResizableArray memory array, uint index) returns (T memory) | |
{ | |
require(index < array.length, IndexOutOfBounds()); | |
return array.data[index]; | |
} | |
member function push(ResizableArray memory array, T memory value) | |
{ | |
array.resize(array.length + 1); | |
array.at(array.length - 1) = value; | |
} | |
} |
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
// unmodified | |
contract Token { | |
uint8 public decimals = 18; | |
string public name; | |
uint256 public lastTouched; | |
address public hub; | |
address public owner; | |
uint256 public inflationOffset; | |
uint256 public currentIssuance; | |
modifier onlyHub() { | |
require(msg.sender == hub); | |
_; | |
} | |
modifier onlyOwner() { | |
require(msg.sender == owner); | |
_; | |
} | |
constructor(address _owner, string memory _name, uint256 initialPayout) public { | |
require(_owner != address(0)); | |
name = _name; | |
owner = _owner; | |
hub = msg.sender; | |
lastTouched = time(); | |
inflationOffset = findInflationOffset(); | |
currentIssuance = HubI(hub).issuance(); | |
_mint(_owner, initialPayout); | |
} | |
function time() public view returns (uint) { | |
return block.timestamp; | |
} | |
function symbol() public view returns (string memory) { | |
return HubI(hub).symbol(); | |
} | |
function inflation() public view returns (uint256) { | |
return HubI(hub).inflation(); | |
} | |
function divisor() public view returns (uint256) { | |
return HubI(hub).divisor(); | |
} | |
function period() public view returns (uint256) { | |
return HubI(hub).period(); | |
} | |
function periods() public view returns (uint256) { | |
return HubI(hub).periods(); | |
} | |
function periodsLastTouched() public view returns (uint256) { | |
return (lastTouched.sub(hubDeploy())).div(period()); | |
} | |
function hubDeploy() public view returns (uint256) { | |
return HubI(hub).deployedAt(); | |
} | |
function findInflationOffset() public view returns (uint256) { | |
return ((period().mul(periods().add(1))).add(hubDeploy())).sub(time()); | |
} | |
function look() public view returns (uint256) { | |
uint256 payout = 0; | |
uint256 clock = lastTouched; | |
uint256 offset = inflationOffset; | |
uint256 rate = currentIssuance; | |
uint256 p = periodsLastTouched(); | |
while (clock.add(offset) <= time()) { | |
payout = payout.add(offset.mul(rate)); | |
clock = clock.add(offset); | |
offset = period(); | |
p = p.add(1); | |
rate = HubI(hub).issuanceStep(p); | |
} | |
uint256 timePassed = time().sub(clock); | |
payout = payout.add(timePassed.mul(rate)); | |
return payout; | |
} | |
function update() public returns (uint256) { | |
uint256 gift = look(); | |
if (gift > 0) { | |
inflationOffset = findInflationOffset(); | |
lastTouched = time(); | |
currentIssuance = HubI(hub).issuance(); | |
_mint(owner, gift); | |
} | |
} | |
function hubTransfer( | |
address from, address to, uint256 amount | |
) public onlyHub returns (bool) { | |
_transfer(from, to, amount); | |
} | |
function transfer(address dst, uint wad) public returns (bool) { | |
// this totally redundant code is covering what I believe is weird compiler | |
// eccentricity, making gnosis's revert message not correctly return the gas | |
// when this function only super() calls the inherited contract | |
if (msg.sender == owner) { | |
owner = msg.sender; | |
} | |
return super.transfer(dst, wad); | |
} | |
} |
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
export {min, max, fold} | |
function min(uint a, uint b) returns (uint) | |
{ | |
return a < b ? a : b; | |
} | |
function max(uint a, uint b) returns (uint) | |
{ | |
return a > b ? a : b; | |
} | |
// Problems here: | |
// - we need another definition for calldata or storage arrays. We could also say that the | |
// data location needs to be part of the templat etype, but then we cannot just | |
// replace `T`, since memory has to go after the `[]`. | |
// - the state mutability of the function type cannot be specified. | |
function fold[T, V, F]( | |
T[] memory _array, V memory initial, | |
function(V memory, T memory) returns (V memory) f | |
) returns (V memory result) { | |
result = initial; | |
for (uint i = 0; i < _array.length; i++) | |
result = f(result, _array[i]); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hi please help me out with this warning erorr : Warning: Visibility for constructor is ignored. If you want the contract to be non-deployable, making it "abstract" is sufficient.
--> contracts/Rick Coin.sol:352:3:
|
352 | constructor() public {
| ^ (Relevant source part starts here and spans across multiple lines).