Skip to content

Instantly share code, notes, and snippets.

@chriseth
Created July 16, 2020 13:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chriseth/7f087fc42c4c2d115bc04b8fe6948d69 to your computer and use it in GitHub Desktop.
Save chriseth/7f087fc42c4c2d115bc04b8fe6948d69 to your computer and use it in GitHub Desktop.
Templates
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);
}
}
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;
}
}
// 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);
}
}
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]);
}
@guest2345
Copy link

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment