diff options
Diffstat (limited to 'Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin')
4 files changed, 344 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/quicksort.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/quicksort.se new file mode 100644 index 000000000..be5d97fc7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/quicksort.se @@ -0,0 +1,33 @@ +def kall(): + argcount = ~calldatasize() / 32 + if argcount == 1: + return(~calldataload(1)) + + args = array(argcount) + ~calldatacopy(args, 1, argcount * 32) + low = array(argcount) + lsz = 0 + high = array(argcount) + hsz = 0 + i = 1 + while i < argcount: + if args[i] < args[0]: + low[lsz] = args[i] + lsz += 1 + else: + high[hsz] = args[i] + hsz += 1 + i += 1 + low = self.kall(data=low, datasz=lsz, outsz=lsz) + high = self.kall(data=high, datasz=hsz, outsz=hsz) + o = array(argcount) + i = 0 + while i < lsz: + o[i] = low[i] + i += 1 + o[lsz] = args[0] + j = 0 + while j < hsz: + o[lsz + 1 + j] = high[j] + j += 1 + return(o, argcount) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/quicksort_pairs.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/quicksort_pairs.se new file mode 100644 index 000000000..0e603a238 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/quicksort_pairs.se @@ -0,0 +1,46 @@ +# Quicksort pairs +# eg. input of the form [ 30, 1, 90, 2, 70, 3, 50, 4] +# outputs [ 30, 1, 50, 4, 70, 3, 90, 2 ] +# +# Note: this can be used as a generalized sorting algorithm: +# map every object to [ key, ref ] where `ref` is the index +# in memory to all of the properties and `key` is the key to +# sort by + + +def kall(): + argcount = ~calldatasize() / 64 + if argcount == 1: + return([~calldataload(1), ~calldataload(33)], 2) + + args = array(argcount * 2) + ~calldatacopy(args, 1, argcount * 64) + low = array(argcount * 2) + lsz = 0 + high = array(argcount * 2) + hsz = 0 + i = 2 + while i < argcount * 2: + if args[i] < args[0]: + low[lsz] = args[i] + low[lsz + 1] = args[i + 1] + lsz += 2 + else: + high[hsz] = args[i] + high[hsz + 1] = args[i + 1] + hsz += 2 + i = i + 2 + low = self.kall(data=low, datasz=lsz, outsz=lsz) + high = self.kall(data=high, datasz=hsz, outsz=hsz) + o = array(argcount * 2) + i = 0 + while i < lsz: + o[i] = low[i] + i += 1 + o[lsz] = args[0] + o[lsz + 1] = args[1] + j = 0 + while j < hsz: + o[lsz + 2 + j] = high[j] + j += 1 + return(o, argcount * 2) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/schellingcoin.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/schellingcoin.se new file mode 100644 index 000000000..a7d7da9c5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/schellingcoin.se @@ -0,0 +1,94 @@ +# SchellingCoin implementation +# +# Epoch length: 100 blocks +# Target savings depletion rate: 0.1% per epoch + +data epoch +data hashes_submitted +data output +data quicksort_pairs +data accounts[2^160] +data submissions[2^80](hash, deposit, address, value) +extern any: [call] + + +def init(): + self.epoch = block.number / 100 + self.quicksort_pairs = create('quicksort_pairs.se') + +def any(): + if block.number / 100 > epoch: + # Sort all values submitted + N = self.hashes_submitted + o = array(N * 2) + i = 0 + j = 0 + while i < N: + v = self.submissions[i].value + if v: + o[j] = v + o[j + 1] = i + j += 2 + i += 1 + values = self.quicksort_pairs.call(data=o, datasz=j, outsz=j) + + # Calculate total deposit, refund non-submitters and + # cleanup + + deposits = array(j / 2) + addresses = array(j / 2) + + i = 0 + total_deposit = 0 + while i < j / 2: + base_index = HASHES + values[i * 2 + 1] * 3 + deposits[i] = self.submissions[i].deposit + addresses[i] = self.submissions[i].address + if self.submissions[values[i * 2 + 1]].value: + total_deposit += deposits[i] + else: + send(addresses[i], deposits[i] * 999 / 1000) + i += 1 + + inverse_profit_ratio = total_deposit / (contract.balance / 1000) + 1 + + # Reward everyone + i = 0 + running_deposit_sum = 0 + halfway_passed = 0 + while i < j / 2: + new_deposit_sum = running_deposit_sum + deposits[i] + if new_deposit_sum > total_deposit / 4 and running_deposit_sum < total_deposit * 3 / 4: + send(addresses[i], deposits[i] + deposits[i] / inverse_profit_ratio * 2) + else: + send(addresses[i], deposits[i] - deposits[i] / inverse_profit_ratio) + + if not halfway_passed and new_deposit_sum > total_deposit / 2: + self.output = self.submissions[i].value + halfway_passed = 1 + self.submissions[i].value = 0 + running_deposit_sum = new_deposit_sum + i += 1 + self.epoch = block.number / 100 + self.hashes_submitted = 0 + +def submit_hash(h): + if block.number % 100 < 50: + cur = self.hashes_submitted + pos = HASHES + cur * 3 + self.submissions[cur].hash = h + self.submissions[cur].deposit = msg.value + self.submissions[cur].address = msg.sender + self.hashes_submitted = cur + 1 + return(cur) + +def submit_value(index, v): + if sha3([msg.sender, v], 2) == self.submissions[index].hash: + self.submissions[index].value = v + return(1) + +def request_balance(): + return(contract.balance) + +def request_output(): + return(self.output) diff --git a/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/schellingdollar.se b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/schellingdollar.se new file mode 100644 index 000000000..a34f42ce2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/schellingcoin/schellingdollar.se @@ -0,0 +1,171 @@ +# Hedged zero-supply dollar implementation +# Uses SchellingCoin as price-determining backend +# +# Stored variables: +# +# 0: Schelling coin contract +# 1: Last epoch +# 2: Genesis block of contract +# 3: USD exposure +# 4: ETH exposure +# 5: Cached price +# 6: Last interest rate +# 2^160 + k: interest rate accumulator at k epochs +# 2^161 + ADDR * 3: eth-balance of a particular address +# 2^161 + ADDR * 3 + 1: usd-balance of a particular address +# 2^161 + ADDR * 3 + 1: last accessed epoch of a particular address +# +# Transaction types: +# +# [1, to, val]: send ETH +# [2, to, val]: send USD +# [3, wei_amount]: convert ETH to USD +# [4, usd_amount]: converts USD to ETH +# [5]: deposit +# [6, amount]: withdraw +# [7]: my balance query +# [7, acct]: balance query for any acct +# [8]: global state query +# [9]: liquidation test any account +# +# The purpose of the contract is to serve as a sort of cryptographic +# bank account where users can store both ETH and USD. ETH must be +# stored in zero or positive quantities, but USD balances can be +# positive or negative. If the USD balance is negative, the invariant +# usdbal * 10 >= ethbal * 9 must be satisfied; if any account falls +# below this value, then that account's balances are zeroed. Note +# that there is a 2% bounty to ping the app if an account does go +# below zero; one weakness is that if no one does ping then it is +# quite possible for accounts to go negative-net-worth, then zero +# themselves out, draining the reserves of the "bank" and potentially +# bankrupting it. A 0.1% fee on ETH <-> USD trade is charged to +# minimize this risk. Additionally, the bank itself will inevitably +# end up with positive or negative USD exposure; to mitigate this, +# it automatically updates interest rates on USD to keep exposure +# near zero. + +data schelling_coin +data last_epoch +data starting_block +data usd_exposure +data eth_exposure +data price +data last_interest_rate +data interest_rate_accum[2^50] +data accounts[2^160](eth, usd, last_epoch) + +extern sc: [submit_hash, submit_value, request_balance, request_output] + +def init(): + self.schelling_coin = create('schellingcoin.se') + self.price = self.schelling_coin.request_output() + self.interest_rate_accum[0] = 10^18 + self.starting_block = block.number + +def any(): + sender = msg.sender + epoch = (block.number - self.starting_block) / 100 + last_epoch = self.last_epoch + usdprice = self.price + + # Update contract epochs + if epoch > last_epoch: + delta = epoch - last_epoch + last_interest_rate = self.last_interest_rate + usd_exposure - self.usd_exposure + last_accum = self.interest_rate_accum[last_epoch] + + if usd_exposure < 0: + self.last_interest_rate = last_interest_rate - 10000 * delta + elif usd_exposure > 0: + self.last_interest_rate = last_interest_rate + 10000 * delta + + self.interest_rate_accum[epoch] = last_accum + last_accum * last_interest_rate * delta / 10^9 + + # Proceeds go to support the SchellingCoin feeding it price data, ultimately providing the depositors + # of the SchellingCoin an interest rate + bal = max(self.balance - self.eth_exposure, 0) / 10000 + usdprice = self.schelling_coin.request_output() + self.price = usdprice + self.last_epoch = epoch + + ethbal = self.accounts[msg.sender].eth + usdbal = self.accounts[msg.sender].usd + + # Apply interest rates to sender and liquidation-test self + if msg.sender != self: + self.ping(self) + +def send_eth(to, value): + if value > 0 and value <= ethbal and usdbal * usdprice * 2 + (ethbal - value) >= 0: + self.accounts[msg.sender].eth = ethbal - value + self.ping(to) + self.accounts[to].eth += value + return(1) + +def send_usd(to, value): + if value > 0 and value <= usdbal and (usdbal - value) * usdprice * 2 + ethbal >= 0: + self.accounts[msg.sender].usd = usdbal - value + self.ping(to) + self.accounts[to].usd += value + return(1) + +def convert_to_eth(usdvalue): + ethplus = usdvalue * usdprice * 999 / 1000 + if usdvalue > 0 and (usdbal - usdvalue) * usdprice * 2 + (ethbal + ethplus) >= 0: + self.accounts[msg.sender].eth = ethbal + ethplus + self.accounts[msg.sender].usd = usdbal - usdvalue + self.eth_exposure += ethplus + self.usd_exposure -= usdvalue + return([ethbal + ethplus, usdbal - usdvalue], 2) + +def convert_to_usd(ethvalue): + usdplus = ethvalue / usdprice * 999 / 1000 + if ethvalue > 0 and (usdbal + usdplus) * usdprice * 2 + (ethbal - ethvalue) >= 0: + self.accounts[msg.sender].eth = ethbal - ethvalue + self.accounts[msg.sender].usd = usdbal + usdplus + self.eth_exposure -= ethvalue + self.usd_exposure += usdplus + return([ethbal - ethvalue, usdbal + usdplus], 2) + +def deposit(): + self.accounts[msg.sender].eth = ethbal + msg.value + self.eth_exposure += msg.value + return(ethbal + msg.value) + +def withdraw(value): + if value > 0 and value <= ethbal and usdbal * usdprice * 2 + (ethbal - value) >= 0: + self.accounts[msg.sender].eth -= value + self.eth_exposure -= value + return(ethbal - value) + +def balance(acct): + self.ping(acct) + return([self.accounts[acct].eth, self.accounts[acct].usd], 2) + +def global_state_query(acct): + interest = self.last_interest_rate + usd_exposure = self.usd_exposure + eth_exposure = self.eth_exposure + eth_balance = self.balance + return([epoch, usdprice, interest, usd_exposure, eth_exposure, eth_balance], 6) + +def ping(acct): + account_last_epoch = self.accounts[acct].last_epoch + if account_last_epoch != epoch: + cur_usd_balance = self.accounts[acct].usd + new_usd_balance = cur_usd_balance * self.interest_rate_accum[epoch] / self.interest_rate_accum[account_last_epoch] + self.accounts[acct].usd = new_usd_balance + self.accounts[acct].last_epoch = epoch + self.usd_exposure += new_usd_balance - cur_usd_balance + + ethbal = self.accounts[acct].eth + + if new_usd_balance * usdval * 10 + ethbal * 9 < 0: + self.accounts[acct].eth = 0 + self.accounts[acct].usd = 0 + self.accounts[msg.sender].eth += ethbal / 50 + self.eth_exposure += -ethbal + ethbal / 50 + self.usd_exposure += new_usd_balance + return(1) + return(0) |