How to create a contract
This tutorial will teach you to create a BTC/USD future smart contract. It assumes you already went through the other steps of mastering distributed oracles.
If you're looking to participate in (not build) an options market, this might not be for you. But contact support@orisi.org - perhaps we can help.
This is basic tutorial on how to create your first Orisi contract.
We will create a contract that after specific time checks Bitcoin price on Bitstamp and compares it to given value. It will send funds to address A if price will be greater than value or to B in other case.
We need to do few steps.
- Fork Orisi repository and clone it with
git clone https://github.com/<your_github_nick>/orisi
.
- Enter orisi directory and copy
cp -r src/oracle/handlers/timelock_contract src/oracle/handlers/pricecheck_contract
.
- Further operations will happen in pricecheck folder.
cd src/oracle/handlers/pricecheck_contract
- Change file name:
mv timelock_create_handler.py pricecheck_create_handler.py
- In file
pricecheck_create_handler.py
change class name fromTimelockCreateHandler
toPricecheckCreateHandler
. - Change all occurences of word timelock to pricecheck inside
pricecheck_create_handler.py
. For example in line 28 change timelock_created to pricecheck_created. Etc. (especially in debugging code - that might be useful) - At the beggining of the same file add
from shared.liburl_wrapper import safe_read
from decimal import Decimal
- Now let’s change function handle_task. After line
message = json.loads(task[‘json_data’])
add following code:
response = safe_read("https://www.bitstamp.net/api/ticker/", 10)
if not response:
if not 'retries_number' in message:
message['retries_number'] = 0
if message['retries_number'] > 10:
return
message['retries_number'] += 1
self.oracle.task_queue.save({
"operation": 'pricecheck_create',
"json_data": json.dumps(message),
"done": 0,
"next_check": int(task['locktime']) + 600
})
response_dict = json.loads(response)
price = Decimal(response_dict['last'])
expected_price = Decimal(message['price'])
if price > expected_price:
return_address = message['return_if_greater']
else:
return_address = message['return_if_lesser']
message['return_address'] = return_address
- We need to do some modifications in file
src/oracle/handlers/handlers.py
. InOPERATION_REQUIRED_FIELDS
add following field:
'pricecheck_create': ['message_id', 'sum_satoshi', 'prevtxs', 'outputs', 'miners_fee_satoshi', 'return_if_greater', 'return_if_lesser', 'price', 'locktime', 'pubkey_list', 'req_sigs'],
At the beginning add import line:
from pricecheck_contract.pricecheck_create_handler import pricecheckcreatehandler
And in op_handlers add:
‘pricecheck_create’: PricecheckCreateHandler,
- Create client part - add following function after function
main2
in filesrc/client/main.py
:
def pricecheck_create(args):
if len(args)<5:
print "USAGE: `%s pricecheck_create <pubkey_once> <locktime_minutes> <return_address_if_greater> <return_address_if_smaller> <value>`" % START_COMMAND
print "- run `%s main` to obtain pubkey_once" % START_COMMAND
print "- keep in mind that this is alpha, don't expect oracles to run properly for any extended periods of time"
return
btc = BitcoinClient()
request = {}
client_pubkey = args[0]
request['locktime'] = time.time() + int(args[1])*60
request['return_if_greater'] = args[2]
request['return_if_lesser'] = args[3]
request['price'] = float(args[4])
print "fetching charter url" # hopefully it didn't check between running main1 and main2
charter = fetch_charter(CHARTER_URL)
oracle_pubkeys = []
oracle_fees = {}
oracle_bms = []
for o in charter['nodes']:
oracle_pubkeys.append(o['pubkey'])
oracle_fees[o['address']] = o['fee']
oracle_bms.append(o['bm'])
oracle_fees[charter['org_address']] = charter['org_fee']
min_sigs = int(ceil(float(len(oracle_pubkeys))/2))
key_list = [client_pubkey] + oracle_pubkeys
response = btc.create_multisig_address(min_sigs, key_list)
msig_addr = response['address'] # we're using this as an identificator
redeemScript = response['redeemScript']
request['message_id'] = "%s-%s" % (msig_addr, str(randrange(1000000000,9000000000)))
request['pubkey_list'] = key_list
request['miners_fee_satoshi'] = MINERS_FEE
print "fetching transactions incoming to %s ..." % msig_addr
# for production purposes you might want to fetch the data using bitcoind, but that's expensive
address_json = liburl_wrapper.safe_read("https://blockchain.info/address/%s?format=json" % msig_addr, timeout_time=10)
try:
address_history = json.loads(address_json)
except:
print "blockchain.info problem"
print address_json
return
prevtxs = []
sum_satoshi = 0
for tx in address_history['txs']:
outputs = []
if 'out' in tx:
outputs = outputs + tx['out']
if 'outputs' in tx:
outputs = outputs + tx['outputs']
for vout in tx['out']:
print vout
if vout['addr'] == msig_addr:
prevtx = {
'scriptPubKey' : vout['script'],
'vout': vout['n'],
'txid': tx['hash'],
'redeemScript': redeemScript,
}
sum_satoshi += vout['value']
prevtxs.append(prevtx)
if len(prevtxs) == 0:
print "ERROR: couldn't find transactions sending money to %s" % msig_addr
return
request['prevtxs'] = prevtxs
request['outputs'] = oracle_fees
request["req_sigs"] = min_sigs
request['operation'] = 'pricecheck_create'
request['sum_satoshi'] = sum_satoshi
bm = BitmessageClient()
print "sending: %r" % json.dumps(request)
print bm.chan_address
request_content = json.dumps(request)
print bm.send_message(bm.chan_address, request['operation'], request_content)
This function is basically a copy of function main2
, but that'll do, we just want to test functionality.
- Still in file
src/client/main.py
change two dictionaries. ToOPERATIONS
add:
'pricecheck_create': pricecheck_create,
and to SHORT_DESCRIPTIONS
add:
'pricecheck_create': 'create pricecheck transaction and pushes it into network',
- Call
git add .
fromsrc
. git commit -m "Pricecheck contract"
git push
- Run an oracle on vagrant. Learn how to do that.
Please note: One of the steps there is to
git clone https://github.com/orisi/orisi
(step 7). Please remember to clone your repository instead.. - On vagrant machine create new screen where oracle will work. Do that with
screen -S oracle
. - On that screen run
./runoracle.sh
. In debug code you’ll seepublic_key
,bitcoin_address
andbm_address
. Copy them - we’ll use them later - Leave screen with
Ctrl-a
and thenCtrl-d
. Leave vagrant box. - Create your own JSON with charter file. Look at example charter file and create similar file with pubkey, address and bm_address you’ve got earlier from your vagrant machine.
- (TODO USABILITY) Put that file on FTP server or any other server. The point is to get URL to that file. (we’re working on making that easier, sorry)
- In file
src/client/main.py
change CHARTER_URL to your url from previous point. - Call
git add .
fromsrc
. git commit -m "charter url changed"
git push
- Create second vagrant box (again with tutorial).
- ssh to second vagrant box and call
./runclient main
- Add funds to given Bitcoin address.
- Call
./runclient pricecheck_create <pubkey_once> <locktime> <return_address_if_greater> <return_address_if_smaller> <price>
- Wait for transaction to get through network, observe messages from Oracle to know if it works.