In May of 2021 DFINITY launched the Interner Computer. We finally have a platform capable of buildings Catallax on top of without having to make a large number of concessions. The catch? None of the libraries exist that we need. We are heads down building libraries that will contribute to both the broad DFINITY ecosystem and the Catallax platform. More later.
Catallax and Enabling Peaceful Financial Violence Against the State
The events of the last couple have been fascinating to watch. I spend a lot of time thinking about how a Catallax based society would approach some of these same issues. Specifically #DefundThePolice is fundamentally built into the underlying Catallax System.
Catallax isolates State Accounts in the system in a way that financial violence can be used against the state by the people in a couple of ways. First of all, State Accounts cannot have any kind of privacy in how funds are used. The inflows and outflows are public record by default.
In a Catallax based system each government domain has a State Account. They collect taxes via the systematic decay of currency in accounts that elect that domain. These Domain Accounts can create Agency Accounts for the various agency accounts that their collected taxes fund. The police department would be one of these agency accounts. Citizens that pay taxes into the Domain Level account get a Democratic Veto right to all agency accounts. The democratic veto is described as follows in my book Immortality:
VTO. Democratic Veto
... How can the (CTZ), using selective citizenship (SCZ) and the public ledger (PLG) keep the state's power in check?
Power corrupts and the citizen must have tools to regulate out of control power without the use of force.
When a citizen pays taxes they should get a vote in that 'state'. We will do this via the democratic veto.
This veto is a 3 phase vote. Citizens can vote 'abstain', 'deny', 'override' on a state account.
This gives power to small minorities to stop the payment from state accounts until their concerns are addressed or their negative vote is overridden by other citizens.
We also don't want small issues gumming up the general working of the state. As a result we should provide for agency accounts that can be selectively vetoed when particular issues arise in the running of those agencies.
Therefore:
Establish the democratic veto for state accounts and selective veto for agency accounts.
This system allows small groups to have their voice heard and amplified. Larger groups must be proactive in overriding the veto and while this proactivity certainly hasn’t kept people from being oppressed in the past, it at least puts the burden of action on those that would seek to not meet the needs of the minority.
In a practical application, communities across the US could use the Democratic Veto to freeze the accounts of police departments across the nation and keep those accounts frozen until the departments make the demanded changes.
Catallax could soften the economic impact of pandemic
The Catallax project has been on ice for a while as we wait for the technology to catch up to the vision. ETH 2.0 and DFINITY currently are pushing forward with scalability advancements and ZK tech will have some interesting things to say about how to deal with the ‘privacy problem.’ As frustrating as it is to wait on these technologies to mature, it is even harder watching the current spread of Covid-19 and the associated recession knowing that a world with Catallax would have significantly more economic tools to help weather the storm. I’ll rip through a few of them, but if you want to know more you can check out http://catallax.info or read my book on the subject here https://amzn.to/39894up.
Cash Decays
In a Catallax based economy cash decays overtime backward through the blockchain. This means that if you hold cash, it eventually is returned to the people that paid it to you. Now this happens slowly and it is very likely that you will have spent the cash before that happens. In scenarios like this economic trumpdown the governing system can print a lot of cash and ramp up the decay rate so that it is available to get the economy ginning again, but also decays back out of the economy quickly so that inflation is avoided.
Cash Decays to you
The backflow of cash in a Catallax economy flows to the people who have spent money in proportion to how that money is used to build more value in the economy. As you spend more money over time you build up an earned basic income. The amount of your basic income payment depends on how you’ve spent your money. If you’ve spent it with sustainable and profitable businesses your income will be higher than if you spend it with companies that fail to build sustainability. Math dictates that this basic income will likely not come close to compensating for your entire income, but it is enough to help people make it through times like these with reduced employment.
Grassroots agencies
A portion of decay goes to support government agencies that are selected by the participants in a Catallax economy. In times like this groups can coordinate to fund new agencies with their tax dollars and cash starts flowing to those agences immediately.
Holding Government Accountable
In a Catallax economy, the people have the ability to shut off the account access of government agencies in order to pressure those agencies to act. Government inaction can lead to swift organization by those that participate in the system and pay taxes to those government agencies.
Conclusion
It is never fun to have to think about how to react to these things while in the midst of the emergency. Previously I dreamed up Harveycoin (http://catallax.info/news/2017/9/8/devlog-10-harveycoin-decentralized-disaster-relief) when it was the only thing I could do when I was evacuated from my home after the hurricane that hit Houston. Hopefully these ideas will inspire others that are currently social distancing and who have the time and ability to code these things in the new and scalable tech that is emerging. Please reach out if you’d like to talk more about these things or if you’d like to try to come up with some solutions.
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:
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:
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.
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:
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.
Trust Ownership can be handed to other governing contracts like multi-sig contacts or other governance contracts like Aragon or Colony.
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:
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).
ICO Owner calls the Trust.ChangeBeneficiaryOwner function to set the beneficiary of the trust to their own address.
ICO Owner calls the Trust.ChangeOwner function and assigns ownership of the trust to a governance contract.
The ICO contract uses the trust address as the deposit location for funds raised during the token sale.
Optional: The ICO contract uses another trust address as the destination of generated developer and investor tokens.
The owning governance contract starts the trust after the crowd sale by calling the Trust.StarTrust function.
During the month the Custodian will be publishing the exchange rates between the token and the currency to the blockchain.
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.