DevLog 15 - State minimized implementation on current evm

Cross posted from https://ethresear.ch/t/state-minimized-implementation-on-current-evm/1255

In an effort to implement the functionality described in the post https://ethresear.ch/t/state-minimised-executions/748/26 , I've worked up the following contract for a 'virtual token' that allows for the holding of state in a  Double-batched Merkle log accumulator  as proposed by @JustinDrake.

This is a toy implementation to show what I mean by 'virtual functions'. The idea being that users would call the Transfer() function to emit an event that indicates that they want to transfer.  The Collators respond to that call bo collecting witnesses from the provided dataHash(perhaps via IPFS) and using the vTransfer() function to calculate the logs.

My original thought was to have the virtual function emit logs as it was run in an off chain evm, but with current tooling those logs are discarded or ignored so instead accumulate the new logs in the results bytes.  This makes the code messier, but it generally works.

pragma solidity ^0.4.18;

import "./BytesLib.sol";

contract VirtualToken {

address public owner;
uint256 public totalSupply;

event NewBranch(bytes32 indexed addressFrom, bytes32 amountFrom, bytes32 indexed addressTo, bytes32 amountTo, bytes32 indexed parent);
event SwapLeaf(bytes32 indexed oldLeaf, bytes32 indexed newLeaf);

function VirtualToken() public{
    owner = msg.sender;
    totalSupply = 1000000 * 10**18;
    DataLog(keccak256(address(this), "balance", msg.sender),1,0, bytes32(totalSupply));
}

function getKeyProof(bytes32 key, bytes data) pure returns(bytes32[] result){
    result = new bytes32[](8);
    uint bytePosition = 0;
    while(bytePosition <= data.length){
        uint length = uint256(BytesLib.toBytes32(BytesLib.slice(data, bytePosition, 32)));
        bytes32 thisKey = BytesLib.toBytes32(BytesLib.slice(data, bytePosition + 32, 32));
        if(thisKey == key){
            for(uint thisGroup = 1; thisGroup < length; thisGroup++){
                bytes32 aGroup = BytesLib.toBytes32(BytesLib.slice(data,bytePosition + (32 * thisGroup), 32));

                result[thisGroup - 1] = aGroup;
            }
            return ;
        }
        bytePosition = bytePosition + (32 * length);
    }
}

event DataLog(bytes32 path, uint logType, uint nonce, bytes32 value);

bytes32[8192] public bottomLayer;
uint bottomLayerPosition;
bytes32[] public topLayer;

function pushLog(uint logType, uint nonce, bytes32 value, bytes32[] proof, bytes startBytes) internal returns(bytes result) {
  result = startBytes;
  result = BytesLib.concat(result, BytesLib.fromBytes32(proof[0]));
  result = BytesLib.concat(result, BytesLib.fromBytes32(bytes32(logType)));
  result = BytesLib.concat(result, BytesLib.fromBytes32(bytes32(nonce)));
  result = BytesLib.concat(result, BytesLib.fromBytes32(value));
  //DataLog(proof[0], logType, uint(proof[2]) + 1, value);
}

function pushDel(bytes32[] proof, bytes startBytes) internal returns(bytes result){
    result = startBytes;
    result = pushLog(2, uint(proof[2]), proof[3], proof, result);
}

function pushAdd(bytes32 newValue, bytes32[] proof, bytes startBytes) internal returns(bytes result){
    result = startBytes;
    result = pushLog(1, uint(proof[2]) + 1, newValue, proof, result);
}

function publishCollation(bytes32 newBottomCollation, bytes32 newTopCollation) public {
    if(newTopCollation == 0x0){
        bottomLayer[bottomLayerPosition] = newBottomCollation;
        bottomLayerPosition++;
    } else {
        topLayer.push(newTopCollation);
        bottomLayerPosition = 0;
    }

}


function vTransfer(bytes data) view returns(bytes results){

    address sender = address(getKeyProof(keccak256("msg.sender"), data)[1]);
    bytes32[] memory senderProof = getKeyProof(keccak256(address(this), "balance", sender), data);
    require(verifyProof(keccak256(address(this), "balance", sender), senderProof));
    uint senderBalance =  uint(senderProof[3]);

    address destination = address(getKeyProof(keccak256("destination"), data)[1]);
    bytes32[] memory destinationProof = getKeyProof(keccak256(address(this),"balance", destination), data);
    require(verifyProof(keccak256(address(this), "balance", destination), destinationProof));
    uint destinationBalance = uint(destinationProof[3]);

    uint amount = uint(getKeyProof(keccak256("amount"), data)[1]);

    require(senderBalance >= amount);

    //invalidate existing proofs
    //todo: what if an item goes to 0

    results = pushDel(senderProof, results);
    if (destinationBalance > 0) {
     results = pushDel(destinationProof, results);
    }

    //publish new proofs
    destinationBalance = destinationBalance + amount;
    senderBalance = senderBalance - amount;

    if(senderBalance > 0){
        results = pushAdd(bytes32(senderBalance), senderProof, results);
    }

    if(destinationBalance > 0){
        results = pushAdd(bytes32(destinationBalance), destinationProof, results);
    }

    return results;

}

/* things that need to be in the log
    inputDataHash: -> maybe put everything but signature here
        sender:
        gasPrice:
        maxGas:
        timeOut:
        contract:
        function:
        data:
        value:
        nonce:
    signatureInputDataHash: -> to ecrecover sender

    //value will need to be stored in escrow until the op can be proven to have run and a reciept generated

    //structure of data passed to a function

    [length][variableName][loghash][value][map][map][map][map][proof]...[proof]

    [length][path][nonce][type][value][proff]...[proof]


*/

event Transfer(bytes32 dataHash, bytes signature);

function transfer(bytes32 dataHash, bytes sig) public{
    Transfer(dataHash, sig);
}

 //utility function that can verify merkel proofs
//todo: can be optimized
//todo: move to library
function calcRoot(bytes32 path,  bytes32[] proof) constant public returns(bytes32 lastHash, uint logType, uint nonce, bytes32 proofPath, bytes32 value){
   for(uint thisProof = 0; thisProof < proof.length; thisProof++){
    if(thisProof == 0){
       //path
       require(path == proof[thisProof]);
       proofPath = path;
     } else if(thisProof == 2){
         nonce = uint(proof[thisProof]);
     } else if(thisProof == 1){
         //type
         logType = uint(proof[thisProof]);
         if(logType == 3){
             //null
             return;
         }
     } else if(thisProof == 3){
       value = proof[thisProof];
       lastHash = keccak256(path,logType, nonce, value);
     } else if(proof[thisProof] == 0x0){
        return;
     } else{
       if(proof[thisProof] == lastHash){
        if(proof[thisProof + 1] != 0x0) {
          lastHash = keccak256(lastHash, proof[thisProof + 1]);
        } else {
          lastHash = keccak256(lastHash);
        }

         thisProof++;
       } else {
         require(proof[thisProof + 1] == lastHash);
         lastHash = keccak256(proof[thisProof], lastHash);
         thisProof++;
       }
     }
    }
    return;
}

//utility function that can verify merkel proofs
//todo: can be optimized
//todo: move to library
function verifyProof(bytes32 path,  bytes32[] proof) constant public returns(bool){
   bytes32 lastHash;
   bytes32 emptyBytes;
   bytes32 value;
   uint nonce;
   uint logType;

   (lastHash, logType, nonce, path,  value) = calcRoot(path, proof);

   if(nonce == 3){
       return true;
   }

   for(uint thisLayer; thisLayer < bottomLayer.length; thisLayer++){
       if(bottomLayer[thisLayer] == lastHash){
           return true;
       }
   }

   for(thisLayer = 0; thisLayer < topLayer.length; thisLayer++){
       if(topLayer[thisLayer] == lastHash){
           return true;
       }
   }
   return false;
 }



}

   

The following code builds a little tree generator that helps manage the state of a tree and to produce Merkle proofs.  These proofs can probably be streamlined as I'm including derived hashes in the proofs for simplicity's sake.  Although since we are doing these virtually the size of the proofs only affects the bandwidth for collators, and the proofs aren't that big.

class DataLogTree
  constructor: ()->
    @web3Utils = require('web3-utils')
    @root = null
    @layers = []
    @layers.push([])
  addItem: (logType, nonce, path, value)=>
    @layers[0].push [logType, nonce, path, value]
  getVar: (key, value)=>
    map = []
    map.push(@web3Utils.padLeft(@web3Utils.toHex(3),64))
    map.push(key)
    map.push(value)
    return map
  getNullProof: (key)=>
    map = []
    map.push(@web3Utils.padLeft(5,64))
    map.push key
    map.push(@web3Utils.padLeft(@web3Utils.toHex(3),64))
    map.push(@web3Utils.padLeft(@web3Utils.toHex(0),64))
    map.push(@web3Utils.padLeft(@web3Utils.toHex(0),64))
    return map
  proofBytes: (items)=>
    bytes = "0x"
    items.map (item)=>

      item.map (subItem)=>
        bytes = bytes + subItem.slice(2)
        return
      return
    return bytes
  proofBytesArray: (myBytes)=>
    @web3Utils.hexToBytes(myBytes)
  parseLogs: (logsBytes)=>
    logsBytes = logsBytes.slice(2)
    position = 0
    logs = []
    while position < logsBytes.length
      logs.push [
        "0x" + logsBytes.substring(position,position + 64)
        "0x" + logsBytes.substring(position + 64,position + 128)
        "0x" + logsBytes.substring(position + 128,position + 192)
        "0x" + logsBytes.substring(position + 192,position + 256)
      ]
      position = position + 256
    return logs
  getProof: (type, nonce, path)=>
    map = []
    seek = null
    console.log type
    console.log nonce
    console.log path
    # 'looking for ' + key
    # to produce a proof we look through each layer from bottom to top looking for first the
    # passed in key and then the hash combination
    for thisLayer in [0...@layers.length]
      console.log 'seeking layer '+ thisLayer
      for thisItem in [0...@layers[thisLayer].length]
        console.log 'inspecting:' + thisItem
        console.log @layers[thisLayer][thisItem]
        if thisLayer is 0
          console.log 'in 0'
          if @layers[thisLayer][thisItem][0] is path and @layers[thisLayer][thisItem][1] is type and @layers[thisLayer][thisItem][2] is nonce
            console.log 'found 0'
            map.push @layers[thisLayer][thisItem][0]
            map.push @layers[thisLayer][thisItem][1]
            map.push @layers[thisLayer][thisItem][2]
            map.push @layers[thisLayer][thisItem][3]
            console.log map
            seek = @hasher map[0], map[1], map[2], map[3]
            console.log 'new seek is ' + seek
            break
        else
          # The found item will be either on the left or right hand side
          if @layers[thisLayer][thisItem][0] is seek
            # console.log 'found seek in position 0'
            # push the item onto the proof and find the next item
            map.push seek
            if @layers[thisLayer][thisItem][1]?
              map.push @layers[thisLayer][thisItem][1]
              seek = @hasher @layers[thisLayer][thisItem][0], @layers[thisLayer][thisItem][1]
            else
              map.push @web3Utils.padLeft(0,64)
              seek = @hasher @layers[thisLayer][thisItem][0]
            console.log 'new seek is ' + seek
            console.log map
            break
          if @layers[thisLayer][thisItem][1] is seek
            #console.log 'found seek in position 1'
            # push the item onto the proof and find the next item
            map.push @layers[thisLayer][thisItem][0]
            map.push seek
            seek = @hasher @layers[thisLayer][thisItem][0], @layers[thisLayer][thisItem][1]
            console.log 'new seek is ' + seek
            console.log map
            break
          if thisItem is @layers[thisLayer].length
            throw 'seek not found'
    if seek is @root
      map.unshift(@web3Utils.padLeft(@web3Utils.toHex(map.length + 1),64))
      return map
    else
      throw 'root not found'
  hasher:(val1, val2, val3, val4) =>
    if val4?
      hash = @web3Utils.soliditySha3({t:"bytes",v:val1},{t:"bytes",v:val2},{t:"bytes",v:val3},{t:"bytes",v:val4})
    else if val2?
      hash = @web3Utils.soliditySha3({t:"bytes",v:val1},{t:"bytes",v:val2})
    else
      hash = @web3Utils.soliditySha3({t:"bytes",v:val1})
    return hash
  buildTree: ()=>
    if @layers[1]?.length > 0
      @layers = [@layers[0]]
    console.log @layers[0].length
    pair = []
    currentLayer = 0
    console.log 'currentLayer:' + currentLayer
    console.log 'currentLayer Length:' + @layers[currentLayer].length

    if @layers[currentLayer].length is 1
      console.log @layers[currentLayer]
      hash = @hasher @layers[currentLayer][0][0], @layers[currentLayer][0][1], @layers[currentLayer][0][2], @layers[currentLayer][0][3]
      @root = hash
      console.log @root
      return

    while @layers[currentLayer]? and @layers[currentLayer].length > 1
      console.log 'in layer loop'
      console.log currentLayer
      console.log @layers[currentLayer]
      console.log 'end'

      @layers.push []
      console.log @layers
      #console.log @layers[currentLayer]

      for thisItem in @layers[currentLayer]
        console.log thisItem
        #console.log 'odd item' if thisItem.length != 2

        if currentLayer is 0
          console.log 'building 0 layer'
          hash = @hasher thisItem[0], thisItem[1], thisItem[2], thisItem[3]
          console.log hash
        else
          console.log 'building layer ' + currentLayer
          if thisItem[1]?
            hash = @hasher thisItem[0], thisItem[1]
          else
            hash = @hasher thisItem[0]

        #console.log hash
        pair.push hash
        console.log 'update pair'
        console.log pair.length

        if pair.length is 2
          console.log 'pushing hash'
          @layers[currentLayer + 1].push [pair[0],pair[1]]
          pair = []

      if pair.length is 1
        console.log 'pushing leftover hash'
        @layers[currentLayer + 1].push [pair[0]]
        pair = []
      console.log 'advancing layer'
      currentLayer = currentLayer + 1
      if currentLayer > 16
        throw new Error('yo')
    console.log 'done'
    console.log @layers

    @root = @hasher @layers[@layers.length - 1][0][0], @layers[@layers.length - 1][0][1]
    console.log @root


exports.DataLogTree = DataLogTree

    

Here is a truffle scenario that runs three transactions

console.log 'hello'
web3Utils = require('web3-utils')
VirtualToken =  artifacts.require('./VirtualToken.sol')
DataLogTree = require('../src/DataLogTree.js')



contract 'VirtualToken', (paccount)->
  owner = paccount[0]
  firstPayee = paccount[1]
  secondPayee = paccount[2]

  setUpNetwork = (options)=>
    return new Promise (resolve, reject)=>
      results = {}
      console.log 'creating virtual token'
      tree = new DataLogTree.DataLogTree()
      web3.eth.getBlockNumber (err, result)=>
        startBlock = result
        console.log startBlock
        token = await VirtualToken.new(from: owner)
        resolve
          token: token
          startBlock: startBlock
          tree: tree

  it "can set crete token", ->
    network = await setUpNetwork()
    logs = await new Promise (resolve, reject)=>
      network.token.DataLog({},{fromBlock: network.startBlock,toBlock:'latest'}).get (err, foundLogs)=>
        resolve foundLogs



    console.log logs

    genesisLog = null
    logs.map (o)->
      console.log o
      if o.event is 'DataLog'
        genesisLog = o
        #console.log [o.args.logType]#, web3Utils.padLeft(web3Utils.toHex(o.nonce),64), o.path, o.value]
        network.tree.addItem(o.args.path, web3Utils.padLeft(web3Utils.toHex(o.args.logType),64), web3Utils.padLeft(web3Utils.toHex(o.args.nonce),64), o.args.value)
        #console.log o.args

    console.log 'building tree'
    network.tree.buildTree()

    console.log network.tree.root

    console.log 'getting proof'
    proof = network.tree.getProof(web3Utils.padLeft(web3Utils.toHex(genesisLog.args.logType),64), web3Utils.padLeft(web3Utils.toHex(genesisLog.args.nonce),64), genesisLog.args.path)
    console.log proof

    expectedRoot = web3Utils.soliditySha3(proof[1],proof[2],proof[3],proof[4])

    assert.equal expectedRoot, network.tree.root

    txn = await network.token.publishCollation(expectedRoot, "0x0")

    transactionBytes = []
    transactionBytes.push network.tree.getVar(web3Utils.soliditySha3("msg.sender"), web3Utils.padLeft(owner, 64))
    transactionBytes.push proof
    transactionBytes.push network.tree.getVar(web3Utils.soliditySha3("amount"), web3Utils.padLeft(web3Utils.toHex(1000), 64))
    transactionBytes.push network.tree.getVar(web3Utils.soliditySha3("destination"), web3Utils.padLeft(firstPayee,64))
    transactionBytes.push network.tree.getNullProof(web3Utils.soliditySha3(network.token.address,"balance", firstPayee))

    console.log transactionBytes
    myBytes = network.tree.proofBytes(transactionBytes)

    console.log myBytes

    console.log '["' + network.tree.proofBytesArray(myBytes).join('","') + '"]'

    txn = await network.token.vTransfer.call(myBytes)

    console.log ' trying second transaction ********'

    logs = network.tree.parseLogs txn

    console.log logs

    state1Tree = new DataLogTree.DataLogTree()


    for thisItem in logs
      state1Tree.addItem(thisItem[0], thisItem[1], thisItem[2], thisItem[3])

    console.log 'building state1Tree'

    state1Tree.buildTree()

    txn = await network.token.publishCollation(state1Tree.root, "0x0")

    console.log 'getting proof'
    proof = state1Tree.getProof(web3Utils.padLeft(web3Utils.toHex(1),64), web3Utils.padLeft(web3Utils.toHex(1),64), web3Utils.soliditySha3(network.token.address,"balance", firstPayee))
    console.log proof

    transactionBytes = []
    transactionBytes.push network.tree.getVar(web3Utils.soliditySha3("msg.sender"), web3Utils.padLeft(firstPayee, 64))
    transactionBytes.push proof
    transactionBytes.push network.tree.getVar(web3Utils.soliditySha3("amount"), web3Utils.padLeft(web3Utils.toHex(500), 64))
    transactionBytes.push network.tree.getVar(web3Utils.soliditySha3("destination"), web3Utils.padLeft(secondPayee,64))
    transactionBytes.push network.tree.getNullProof(web3Utils.soliditySha3(network.token.address,"balance", secondPayee))

    console.log 'transaction bytes for second transaction'
    console.log transactionBytes

    myBytes = network.tree.proofBytes(transactionBytes)


    txn = await network.token.vTransfer.call(myBytes)

    console.log 'second transaction results'

    logs = network.tree.parseLogs txn

    console.log logs

    console.log ' trying third transaction ******************************************'

    state2Tree = new DataLogTree.DataLogTree()

    for thisItem in logs
      state2Tree.addItem(thisItem[0], thisItem[1], thisItem[2], thisItem[3])

    console.log state2Tree.layers[0]

    state2Tree.buildTree()

    txn = await network.token.publishCollation(state2Tree.root, "0x0")

    proof = state2Tree.getProof(web3Utils.padLeft(web3Utils.toHex(1),64), web3Utils.padLeft(web3Utils.toHex(1),64), web3Utils.soliditySha3(network.token.address,"balance", secondPayee))
    console.log proof

    secondProof = state1Tree.getProof(web3Utils.padLeft(web3Utils.toHex(1),64), web3Utils.padLeft(web3Utils.toHex(1),64), web3Utils.soliditySha3(network.token.address,"balance", owner))


    transactionBytes = []
    transactionBytes.push network.tree.getVar(web3Utils.soliditySha3("msg.sender"), web3Utils.padLeft(secondPayee, 64))
    transactionBytes.push proof
    transactionBytes.push network.tree.getVar(web3Utils.soliditySha3("amount"), web3Utils.padLeft(web3Utils.toHex(250), 64))
    transactionBytes.push network.tree.getVar(web3Utils.soliditySha3("destination"), web3Utils.padLeft(owner,64))
    transactionBytes.push secondProof

    myBytes = network.tree.proofBytes(transactionBytes)


    txn = await network.token.vTransfer.call(myBytes)

    logs = network.tree.parseLogs txn

    console.log logs

    assert.equal 1, 0, "hello"

   

Apologies for the coffeescript...it is 2.0 now so you get all the ES6 goodies.

So far only the red highlighted areas are working:

Scalable Stateless Contracts on Today's EVM Diagram step1.png

 

There are obviously a ton of areas that have big questions:

1.  Can we create a viable consensus amongst collators?
2.  How do we incentivize them?
3.  How to handle no ops?
4.  Have we really just put ethereum inside of ethereum and erased a lot of the checks that ethereum puts on run away gas costs?

I'm open to suggestions for 1-3.

For number four I think it is valuable to keep pushing this string.  There are certainly times where one would want the current EVM to do bigger things.  For example:  Using this I could write an air drop contract that loads up 1,000 balances for way less gas than it would currently cost...if I can get the collators to validate the transaction for me.

*The proof format here is [length, path, type(1=add, 2=del, 3=null), nonce, value, left leaf0, right leaf0.... left leafn, right leafn] where odd layers have an 0x0 on the end but the hash for those odd layers is calculated as the hash of just the left item.  Open to better suggestions.

DevLog 14 - Using a Catallax Trust to Pay Bug Bounties

Fresh off DevCon 3 we have our first bug submitted on the Catallax Trust.  Github user NickErrant pointed out in this github issue that a franchisee could lock up funds by pointing their payment address to a contract that throws on the fall back function.

Generally you can trust that people that want to get paid by a contract won’t do this, but in the case of a Catallax Trust the franchisee could be disgruntled about their payout or choose to protest for some other reason and lock up the contract.

The reason this can happen is that if address.transfer function fails then the function will throw.  Because our withdraw() function pays out to the franchisee if they exist they can block the payment.  The simple fix (and the one we’ve taken at the moment) is to use address.send instead.  If this fails it just returns false and the rest of the function continues to function.

A better solution is to use the withdraw pattern and just set the money aside for the franchisee to come get later.  This requires another storage variable in the contract so it has some cost.

We haven’t deployed the factory to produce this new contract yet so don’t set up a franchise contract until we do so. If you are interested in setting one of these up, please reach out to us and we will work with you to get everything setup correctly.

Nick raised a good point when reporting the bug that bug bounty contracts where you just try to steal the funds have pretty poor incentives for bug hunters.  As a result, we’ve made Nick the beneficiary of the bug bounty contract.  On 11/16 he’ll be able to call withdraw and get out 1/24th of the current balance.

We will leave Nick as the beneficiary until we get our next valid bug report.  At that time, provided the bug is as serious as Nick’s, we will unlock the beneficiary and transfer it to the new bug hunter 36 days later.

Of course, if you'd like to try to steal the money out of the trust you can do that too.

If you’d like to use a Catallax Trust for one of your own bug bounties please reach out to us and we can help you set it up.  The trust supports ETH and ERC20 tokens.

You can find the source code for the contracts here: https://github.com/skilesare/catallaxtrust

Pull down the repo and load them up in remix to interact with the contracts.

If this is interesting to you and you'd like to see where we are going with Catallax, please pick up my book Immortality (Purchase of a physical or kindle copy helps support this project).

Donations always accepted at:

BTC: 1AAfkhg1NEQwGmwW36dwDZjSAvNLtKECas

ETH and Tokens: 0x148311c647ec8a584d896c04f6492b5d9cb3a9b0

If you would like more code articles like this please consider becoming a patron on patreon.

You can discuss this article and more at our reddit page r/Catallax.

Bug Bounty Doubled - $400 - Truffle Tests Released

We are back from DevCon 3 in Cancun.  It was a great week.  We learned a lot and had some great conversations.  We are really excited about moving Catallax forward and getting our decaying currency up and running on the main net.

In the meantime, we've doubled the bug bounty on the Catallax Trust to $400.  We are getting closer to our first opportunity to do a withdrawal on November 16th.

I've also released the truffle tests source.  Reviewing these should give you some idea of how the contract works.

You can find the source code for the contracts here: https://github.com/skilesare/catallaxtrust

Pull down the repo and load them up in remix to interact with the contracts.

If this is interesting to you and you'd like to see where we are going with Catallax, please pick up my book Immortality (Purchase of a physical or kindle copy helps support this project).

Donations always accepted at:

BTC: 1AAfkhg1NEQwGmwW36dwDZjSAvNLtKECas

ETH and Tokens: 0x148311c647ec8a584d896c04f6492b5d9cb3a9b0

If you would like more code articles like this please consider becoming a patron on patreon.

You can discuss this article and more at our reddit page r/Catallax.

Bug Bounty Doubled - $200

No big update this week. We are getting ready for Dev Con.  Looking forward to visiting with the community.  If you want to hear more about the Catallax Trust and the next phase of our project where we'll have a decaying currency working on the blockchain please reach out to austin at catallax dot com.

We've sent another $100 worth of ether over to our Bug Bounty.  Try to get it out!

You can find the source code for the contracts here: https://github.com/skilesare/catallaxtrust

Pull down the repo and load them up in remix to interact with the contracts.

If this is interesting to you and you'd like to see where we are going with Catallax, please pick up my book Immortality (Purchase of a physical or kindle copy helps support this project).

Donations always accepted at:

BTC: 1AAfkhg1NEQwGmwW36dwDZjSAvNLtKECas

ETH and Tokens: 0x148311c647ec8a584d896c04f6492b5d9cb3a9b0

If you would like more code articles like this please consider becoming a patron on patreon.

You can discuss this article and more at our reddit page r/Catallax.

DevLog 13 - $100 Catallax Trust Bug Bounty

The plan was to go with a push out to Ropsten today with the web app up and running.  I’ve had a muted response to the application so far so I’m going to try something different.  I really need some more eyes on this contract before I pour a bunch of dev cycles at the dapp.  So I’ve thrown all caution to the wind and deployed the contracts to mainnet.

I’m created a Catallax Trust Custodian(0x1ed1ee3d6cf25754046e8769f4f2feff57ede7a3), Factory(0x6824457c6c5f711b71dc28c804c6ca767fc84046), Trust Storage(0x79244a86de9b499b03d8c0afe29460d029c5e7a6), and Catallax Trust(0xb8c7842b4451c440f14f0ccfa7cc4bb9734e5df5).  The trust is a two year trust that pays out $30,000 a month.  I’ve loaded it up with $100 worth of ETH.  Please do your best to pull this eth out of the trust.  If you can break the trust please let me know what you did to break it at austin at catallax dot com.

You can find the source code for the contracts here: https://github.com/skilesare/catallaxtrust

Pull down the repo and load them up in remix to interact with the contracts.

I have a bit of time off this week and I’m not sure if I’ll make much progress on the dapp or not.  Some positive feedback and some potential customers might light a fire under me to make the contracts easier to interact with.

If you would like to start a trust right now you can do so by calling the Custodian.CreateTrust function,  Funding the created trust, and then calling the Trust.StartTrust function.

If you’d like to start one but want to see more scrutiny on the contract feel free to send ETH to the Trust address at 0xb8c7842b4451c440f14f0ccfa7cc4bb9734e5df5 to increase the bounty.

Have fun exploring the contracts!  Post questions and issues in our github or reddit here.

If this is interesting to you and you'd like to see where we are going with Catallax, please pick up my book Immortality (Purchase of a physical or kindle copy helps support this project).

Donations always accepted at:

BTC: 1AAfkhg1NEQwGmwW36dwDZjSAvNLtKECas

ETH and Tokens: 0x148311c647ec8a584d896c04f6492b5d9cb3a9b0

If you would like more code articles like this please consider becoming a patron on patreon.

You can discuss this article and more at our reddit page r/Catallax.

DevLog 12 - Catallax Trust Contract Code Release

I’m really excited to release our code for the Catallax Trust today.  It has been a long time coming and it will be nice to get some new eyes on the code.

If this is your first time reading about the Catallax Trust you can read the white paper here.  A Catallax Trust is a contract that will hold crypto and pay it out in the future in fiat-based chunks based on the exchange rate at the time of the withdrawal.  I can be used for employment contracts, donating to a charity, securing ICO funds, or simply helping you HODL with a budget.

We have had one code review done and implemented a number of improvements identified in that code review.  Of course, we hope that the contracts are production ready, but if you think otherwise, please let us know by sending the issue to bugs@catallax.com or filing an issue on the github repo:   https://github.com/skilesare/catallaxtrust

The Catallax Trust is made up of the following main contracts:

FiatTrustCustodian.sol - This is the custodian contract that oversees the creation of new trusts and tracks the fiat to crypto conversion rates for supported crypto / fiat pairs.

FiatTrustFactory.sol - This is the factory contract that the custodian uses to create new trusts.  We can swap this out if we create a better trust without having to republish the custodian.

FiatTrust.sol - This contract is the core trust. Users will deposit their ETH and / or ERC20 tokens in these contracts and be able to withdraw from them over time.

The following support contracts are included as well:

DateTime.sol - a date time library

SafeMath.sol - safe arithmetic functions

ERC20.sol - interacting with tokens

TrustStorage.sol - we have to store the historical history of crypto / fiat exchange rates. This is held in a storage contract so that we can easily move the history to upgraded custodians if necessary.

iLicensor.sol - a stub contract for future governance of the custodian

The general process for setting up the contracts follows the following process(this is the code from our test suite):

prepEnvironment = (custodianOwner)->
 return new Promise (resolve, reject)->
   custodian = null
   factory = null
   token = null
   storage = null
   #Create the custodian
   FiatTrustCustodian.new(from: custodianOwner).then (instance)->
     custodian = instance
     #create the factory
     FiatTrustFactory.new(custodian.address, from: custodianOwner)
   .then (instance)->
     console.log 'new factory'
     factory = instance
     #create a datetime library(this has been published previously on mainnet so you can use one of those)
     DateTime.new(from: custodianOwner)
   .then (instance)->
     console.log 'new DateTime'
     #set the date time library
     custodian.SetDateTimeLibrary(instance.address, from: custodianOwner)
   .then (result)->
     console.log 'dt set'
     #set the factory location
     custodian.SetFactory(factory.address, from:custodianOwner)
   .then (result)->
     console.log 'factory set'
     #Create an ERC20 token to test with
     HumanStandardToken.new(tokenStartBalance,"token",0,'tkn', from: custodianOwner)
   .then (instance)->
     console.log 'new token'
     token = instance
     #create a new storage contract
     TokenStorage.new(from: custodianOwner)
   .then (instance)->
     console.log 'new storage'
     storage = instance
     #set the storage contract
     custodian.SetStorage(storage.address, from: custodianOwner)
   .then (instance) ->
     console.log 'storage set'
     #update the owner of the storage to include the custodian contract
     storage.UpdateOwner(custodian.address, true, from: custodianOwner)
   .then (instance)->
     console.log 'first conversion set'
     #set an old conversion for ETH to USD
     custodian.SetConversion(ethTokenAddress, usdCurrencybytes, 1989,1, 1, web3.toWei(0.01,"ether"),1, from: custodianOwner)
   .then (instance)->
     console.log 'conversion set'
     #set an old conversion for ERC20 token to USD
     custodian.SetConversion(token.address, usdCurrencybytes, 1989,1, 1, web3.toWei(0.01,"ether"),1, from: custodianOwner)
   .then (instance)->
     console.log 'max fee set'
     #set the max fee
     custodian.SetMaxFee(usdCurrencybytes, 50, from: custodianOwner)
   .then (instance)->
     console.log 'origination fee set set'
     #set the origination fee
     custodian.SetOriginationFee(usdCurrencybytes, 25, from: custodianOwner)
   .then ->
     resolve
       custodian: custodian
       token: token

Once your custodian is configured you need to create, fund, and start your trust.  The following code will do that and then make time pass so you can do your first withdrawal:

custodian.CreateTrust(ethTokenAddress, usdCurrencybytes, 12, 1, {from: accounts[0]})
   .then (txn)->
     trustAddress = null
     txn.logs.map (o)->
       if o.event is 'TrustCreated'
         console.log 'found new Trust at' + o.args.location
         i = FiatTrust.at(o.args.location)
         trustAddress = o.args.location
     console.log 'have instance'
     #fund the wallet
     web3.eth.sendTransaction({ from: accounts[1], to: i.address, value: web3.toWei(0.44,"ether") })
   .then (result)->
     #Start the Trust
     i.StartTrust(from:accounts[0])
   .then (result)->
     console.log result
     console.log 'sending ether'
     #this function advances time in our test client by 34 days
     return new Promise (tResolve, tReject)->
       web3.currentProvider.sendAsync
         jsonrpc: "2.0",
         method: "evm_increaseTime",
         params: [86400 * 34],  # 86400 seconds in a day
         id: new Date().getTime()
       , (err)->
         tResolve true
   .then (result)->
     #calculate the next withdraw date
     i.NextWithdraw()
   .then (result)->
     console.log 'next ' + result
     nextPayout = result.toNumber()
     aDate = nextPayout * 1000
     aDate = moment.utc(new Date(aDate))
     console.log aDate
     console.log 'conversion set for ' + aDate.year() + (aDate.month() + 1) + aDate.date()
     #set the conversion for that tdate
     custodian.SetConversion(ethTokenAddress, usdCurrencybytes, aDate.year(), aDate.month() + 1, aDate.date(), web3.toWei(0.01,"ether"),1)
   .then (result)->#call the withdraw fucntion.  0.1 eth shold move from the contract to account[0]
     console.log 'conversion set'
     aDate = nextPayout * 1000
     aDate = moment.utc(new Date(aDate))
     console.log aDate
     #check the current balance
     web3.eth.getBalance(custodian.address)
   .then (result)->
     console.log result
     startCustodianBalance = result.toNumber()
     assert.equal 250000000000000000, startCustodianBalance, 'custodian has ether it shouldnt'
     startBalance = web3.eth.getBalance(accounts[0])
     console.log 'Start Balance' + startBalance
     #withdraw from the trust
     i.Withdraw(from: accounts[0])
   .then (result)->
     console.log result
     web3.eth.getBalance(i.address)
   .then (result)->
     console.log 'withdrawl:' + result
     assert.equal result.toNumber(), parseInt(web3.toWei(0.18,"ether")) - parseInt(web3.toWei(0.01,"ether")) * 0.005, 'withdraw wasnt right'
     web3.eth.getBalance(accounts[0])
   .then (result)->
     console.log result
     #since payout is 0 eth per usd we multiply fiatpayout 1
     #we only test .9 eth and 1.1 because gas costs weigh in
     # so much gas cost
     #96 413397700000000000
     #96 502336500000000000
     #
     console.log 'eth increased' + (result.toNumber() - startBalance.toNumber())
     assert.equal result.toNumber() > startBalance.toNumber(), true, 'eth didnt transfer'
     assert.equal result.toNumber() < startBalance.toNumber() + parseInt(web3.toWei(0.01,"ether")), true, 'too much eth transfered'
     web3.eth.getBalance(custodian.address)
   .then (result)->
     console.log 'fee paid' + result
     assert.equal result.toNumber(), 250000000000000000 + parseInt(web3.toWei(0.01,"ether")) * 0.005, 'Fee wasnt paid'

Have fun exploring the contracts!  Post questions and issues in our github or reddit here.

If this is interesting to you and you'd like to see where we are going with Catallax, please pick up my book Immortality (Purchase of a physical or kindle copy helps support this project).

Donations always accepted at:

BTC: 1AAfkhg1NEQwGmwW36dwDZjSAvNLtKECas

ETH and Tokens: 0x148311c647ec8a584d896c04f6492b5d9cb3a9b0

If you would like more code articles like this please consider becoming a patron on patreon.

You can discuss this article and more at our reddit page r/Catallax.

Give ICO Investors more confidence with a Catallax Trust

The Catallax Trust has been designed to support a number of different scenarios.  I’m outlining each one of these in a different post over the next few weeks as we move toward the release.

Today we are going to talk about how to use a Catallax Trust increase investor confidence in your ICO.

An ICO can funnel the proceeds of their token sale into a Catallax Trust and have it pay out a set fiat denominated amount of those proceeds to cover costs.  They can further increase confidence by assigning ownership of the trust to a multi-sig account overseen by trusted members of the community.

For example, ZkCoin is holding a token sale to raise money for their ZkSnark based token.  They expect to need $50,000 USD per month for the first year to get the project launched.  Excitement is high and they expect to raise much more than is required.  Some in the community do have some fear that the team is untrustworthy and will run off with the raised funds.  To increase confidence in the token sale and project ZkCoin can create a Catallax Trust that pays out $50,000 of raised ETH each month for the first year.  At the end of the year, they don’t know how much they are going to need so they assign ownership of the trust to a group of community member including members of the Ethereum Foundation.  Once the trust matures these members will need to sign the withdraw function that pushes remaining funds into a new trust with an agreed upon monthly budget. If the price of ETH fluctuates wildly one of two things will happen:

  1. If the price of ETH goes up he the trust will pay out less ETH and at the end of the contract ZkCoin will get the remaining ETH back and it can be assigned to a new Trust with the agreement of the community.

  2. If the price of ETH goes down, the trust will auto adjust the payout down so that the cash flows continue for the entire two years with funds equal to less than the desired $50,000.

Using a trust for increasing confidence in your IC to a charity has a number of advantages:

  1. Investors know that a Token owner will only be able to get a set amount of ETH out of the trust per month and won’t be able to blow the full raised amount before proper learning in the marketplace has occurred.

  2. Trust Ownership can be handed to other governing contracts like multi-sig contacts or other governance contracts like Aragon or Colony.

  3. The trust owners can reassign the beneficiary of the trust if the project goes off the rails or dies, perhaps redirecting funds to the Ethereum Foundation.

A Catallax Trust can also be used to help reduce the market manipulation risk for the issued ZkCoin by creating a ZkCoin to USD denominated Catallax Trust.  Many ICOs hold back large amounts of coins to give to developers and investors in the project.  By issuing these coins to a Catallax Trust that only allows a certain amount to be paid out per month the risk to investors is limited.  For example, if 30% of the coins are held back to pay to developers a Catallax Trust could hold these coins and only let $10,000 worth be issued each month.  As the market price of these new ZkCoins fluctuate the amount released will adjust each month and only $10,000 worth could be dumped on the market each month.

Creating the Trust and Assigning it to an ICO requires the following steps:

  1. ICO owner calls the Custodian.CreateTrust function to create a new Catallax Trust contract.  This function takes a token(ETH or ERC20), a currency(USD, EUR, ect.), term(number of months), and fiat payout(amount of currency per term to pay out).

  2. ICO Owner calls the Trust.ChangeBeneficiaryOwner function to set the beneficiary of the trust to their own address.

  3. ICO Owner calls the Trust.ChangeOwner function and assigns ownership of the trust to a governance contract.

  4. The ICO contract uses the trust address as the deposit location for funds raised during the token sale.

  5. Optional:  The ICO contract uses another trust address as the destination of generated developer and investor tokens.

  6. The owning governance contract starts the trust after the crowd sale by calling the Trust.StarTrust function.

  7. During the month the Custodian will be publishing the exchange rates between the token and the currency to the blockchain.

  8. After one month the ICO  can call the Trust.Withdraw function to get the exchange rate adjusted amount of ETH sent to their beneficiary address.

If you are interested in using a Catallax Trust to manage your employment contracts please reach out to us so that we can make sure that we support the Token / Currency combinations that you would like to use.

Please head over to this thread on our Reddit to pick the white paper apart and ask questions.  You can download the Catallax Trust white paper here.

If this is interesting to you and you'd like to see where we are going with Catallax, please pick up my book Immortality (Purchase of a physical or kindle copy helps support this project).

Donations always accepted at:

BTC: 1AAfkhg1NEQwGmwW36dwDZjSAvNLtKECas

ETH and Tokens: 0x148311c647ec8a584d896c04f6492b5d9cb3a9b0

If you would like more code articles like this please consider becoming a patron on patreon.

You can discuss this article and more at our reddit page r/Catallax.



 

Giving to a Charity with Strings Attached with a Catallax Trust

The Catallax Trust has been designed to support a number of different scenarios.  I’m outlining each one of these in a different post over the next few weeks as we move toward the release.

Today we are going to talk about how to use a Catallax Trust to set up recurring giving to a charity of your choice.

The Catallax Trust allows for a benefactor to set up a fiat based monthly donation of crypto to their favorite charity.

For example, Annie wants to help out the Ethereum foundation but doesn’t want to just sign over a bunch of her ETH at one time. She puts 1,000 ETH into a Catallax Trust and wants $10,000 USD per month to go to the Ethereum Foundation over the next 24 months. She wants to maintain some control over this and be able to revoke her donation if the foundation is taken over by a bunch of miners and Proof of Stake is taken off the board. If the price of ETH fluctuates wildly one of two things will happen:

  1. If the price of ETH goes up he the trust will pay out less ETH and at the end of the contact Annie will get her remaining ETH back at the end of the 2 year period

  2. If the price of ETH goes down Annie the contract will auto adjust the payout down so that the cash flows continue for the entire two years.

Using a trust for donating to a charity has a number of advantages:

  1. Charities know that the account that will be paying them is fully funded and that funds can’t be moved right before the next month’s payout.

  2. The contract requires a 36 day waiting period if you are going to change the beneficiary of a trust.  This Guarantees at least one month of lead time for charities to know that their funds available are going to go down in the next month.

  3. Benefactors won’t have to worry about monitoring exchange rates on donation day.

  4. Charities can monitor the account as the value of the denominated crypto fluctuates to make sure that they plan well if crypto price crash.

  5. Benefactors avoid the tax implications of converting crypto to fiat.  The burden of doing so is transferred to the charity that may benefit from tax exemptions.

Creating the Trust and Assigning it to a Charity requires the following steps:

  1. Benefactor calls the Custodian.CreateTrust function to create a new Catallax Trust contract.  This function takes a token(ETH or ERC20), a currency(USD, EUR, ect.), term(number of months), and fiat payout(amount of currency per term to pay out).

  2. Employer calls the Trust.ChangeBeneficiaryOwner function to set the beneficiary of the trust to the charity’s address.

  3. Benefactor funds the Trust by sending ETH and or ERC20.  Fees are paid in ETH, ERC20 can be required if the trust pays out ERC20.

  4. Benefactor starts the trust on the first day of the contract by calling the Trust.StarTrust function.

  5. During the month the Custodian will be publishing the exchange rates between the token and the currency to the blockchain.

  6. After one month the charity can call the Trust.Withdraw function to get the exchange rate adjusted amount of crypto sent to their beneficiary address.

If you are interested in using a Catallax Trust to manage your employment contracts please reach out to us so that we can make sure that we support the Token / Currency combinations that you would like to use.

Please head over to this thread on our Reddit to pick the white paper apart and ask questions.  You can download the Catallax Trust white paper here.

If this is interesting to you and you'd like to see where we are going with Catallax, please pick up my book Immortality (Purchase of a physical or kindle copy helps support this project).

Donations always accepted at:

BTC: 1AAfkhg1NEQwGmwW36dwDZjSAvNLtKECas

ETH and Tokens: 0x148311c647ec8a584d896c04f6492b5d9cb3a9b0

If you would like more code articles like this please consider becoming a patron on patreon.

You can discuss this article and more at our reddit page r/Catallax.


 

Setting up a Smart Contract recurring income from your ETH stack

The Catallax Trust has been designed to support a number of different scenarios.  I’m outlining each one of these in a different post over the next few weeks as we move toward the release.

Today we are going to talk about how to use a Catallax Trust to set up a recurring income for yourself.  Were you an early investor in ETH?  Do you have some valuable ERC20 tokens sitting around that you want to HODL?  The Catallax trust will allow you to set a USD based amount to take out each month and this will be enforced by the contract.

For example, Janet was early on the ETH train.  She wants to start a new blockchain based project and now she doesn’t have to worry about money as much.  Selling all her ETH has big tax implication and she is still bullish on the price.  She doesn’t want to sell. Using a Catallax Trust she can put her ETH into a Trust that pays out $20,000 USD worth of ETH each month for three years. If the price of ETH fluctuates wildly one of two things will happen:

  1. If the price of ETH goes up he the trust will pay out less ETH and at the end of the contract Janet will get his remaining ETH back.

  2. If the price of ETH goes down Janet the contract adjusts to pay her what it can so that she has income over the length of the trust.

Putting her ETH into a contract has the following advantages

  1. The Trust owner is committed to HODLing. The ETH cannot be pulled out of the contract any faster than the trust allows.

  2. If ETH goes up the owner will have a big chunk of ETH available at the termination of the trust.

  3. If ETH goes down then the trust will adjust to pay out as much as it can each month so that the trust lasts the entire 3 years.

  4. In future iterations of the trust we hope to be able to allow the owner to elect to take soe risk and potentially make a return on assets locked up in the trust.

Creating the Trust:

  1. Owner calls the Custodian.CreateTrust function to create a new Catallax Trust contract.  This function takes a token(ETH or ERC20), a currency(USD, EUR, ect), term(number of months), and fiat payout(amount of currency per term to pay out).

  2. Owner funds the Trust by sending ETH and or ERC20.  Fees are paid in ETH, ERC20 can be required if the trust pays out ERC20.

  3. Owner starts the trust on the first day of the contract by calling the Trust.StarTrust function.

  4. During the month the Custodian will be publishing the exchange rates between the token and the currency to the blockchain.

  5. After one month the Owner can call the Trust.Withdraw function to get the exchange rate adjusted the amount of crypto sent to their beneficiary address.

  6. When the Trust Expires the owner can withdraw any remaining ETH or ERC20 tokens out of the trust.

If you are interested in using a Catallax Trust to set up a recurring income, please reach out to us so that we can make sure that we support the Token / Currency combinations that you would like to use.

Please head over to this thread on our Reddit to pick the white paper apart and ask questions.  You can download the Catallax Trust white paper here.

If this is interesting to you and you'd like to see where we are going with Catallax, please pick up my book Immortality (Purchase of a physical or kindle copy helps support this project).

Donations always accepted at:

BTC: 1AAfkhg1NEQwGmwW36dwDZjSAvNLtKECas

ETH and Tokens: 0x148311c647ec8a584d896c04f6492b5d9cb3a9b0

If you would like more code articles like this please consider becoming a patron on patreon.

You can discuss this article and more at our reddit page r/Catallax.

 

Using a Catallax Trust to pay and get paid crypto securely and accurately

The Catallax Trust has been designed to support a number of different scenarios.  I’m outlining each one of these in a different post over the next few weeks as we move toward the release.

Today we are going to talk about how to use a Catallax Trust for an employment contract.

The Catallax Trust allows for a contractor and employer to agree to a fiat based payment of crypto over a number of months.

For example, John is hiring Beth to build a new smart contact based facebook.  He agrees to pay her $25,000 USD per month for 6 months.  John doesn’t actually have that much cash but he holds a large amount of ETH.  He can put $150,000 worth of ETH into a Catallax Trust and it will pay out to Beth on a monthly schedule.  If the price of ETH fluctuates wildly one of two things will happen:

  1. If the price of ETH goes up he the trust will pay out less ETH and at the end of the contract, John will get his remaining ETH back.

  2. If the price of ETH goes down Beth can let him know that the trust is no longer fully funded and that he needs to send more ETH into the trust if he wants her to continue with the contract.

Using a trust for an employment contract, especially in the crypto space where many contracts pay out in crypto, has a number of advantages:

  1. Contractors know that the account that will be paying them is fully funded and that funds can’t be moved right before payday.

  2. The contract requires a 36 day waiting period if you are going to change the beneficiary of a trust.  This Guarantees at least one month of lead time for contractors to know that their contract has been terminated and that they will need to find new employment.

  3. Employers won’t have to worry about monitoring exchange rates on payday.

  4. Contractors can monitor the account as the value of the denominated crypto fluctuates to make sure that the employer has adequately funded the account.

Creating the Trust and Assigning it to a Contractor requires the following steps:

  1. Employer calls the Custodian.CreateTrust function to create a new Catallax Trust contract.  This function takes a token(ETH or ERC20), a curreny(USD, EUR, ect), term(number of months), and fiat payout(amount of currency per term to pay out).

  2. Employer calls the Trust.ChangeBeneficiaryOwner function to set the beneficiary of the trust to the contractor’s address.

  3. Employer funds the Trust by sending ETH and or ERC20.  Fees are paid in ETH, ERC20 can be required if the trust pays out ERC20.

  4. Employer starts the trust on the first day of the contract by calling the Trust.StarTrust function.

  5. During the month the Custodian will be publishing the exchange rates between the token and the currency to the blockchain.

  6. After one month the Contractor can call the Trust.Withdraw function to get the exchange rate adjusted the amount of crypto sent to their beneficiary address.

If you are interested in using a Catallax Trust to manage your employment contracts please reach out to us so that we can make sure that we support the Token / Currency combinations that you would like to use.

Please head over to this thread on our Reddit to pick the white paper apart and ask questions.  You can download the Catallax Trust white paper here.

If this is interesting to you and you'd like to see where we are going with Catallax, please pick up my book Immortality (Purchase of a physical or kindle copy helps support this project).

Donations always accepted at:

BTC: 1AAfkhg1NEQwGmwW36dwDZjSAvNLtKECas

ETH and Tokens: 0x148311c647ec8a584d896c04f6492b5d9cb3a9b0

If you would like more code articles like this please consider becoming a patron on patreon.

You can discuss this article and more at our reddit page r/Catallax.