diff --git a/oro-contract/currency-oro.py b/oro-contract/currency-oro.py index af7050d..aa00d43 100644 --- a/oro-contract/currency-oro.py +++ b/oro-contract/currency-oro.py @@ -1,40 +1,150 @@ +# An illustrative example of a "trusted" oracle contract. +# Author: Mike Radin, github.com/anonymoussprocket +# Licensed under the Apache License, Version 2.0. + import smartpy as sp -class CurrencyConverter(sp.Contract): - def __init__(self,admin): - self.init(apidata = sp.map(tkey = sp.TString, tvalue = sp.TInt), keysset=sp.set([admin])) +class ExchangeRateConsumer(sp.Contract): + def __init__(self, oracle): + self.init( + oracle = oracle, + currencyPair = sp.string(""), + value = sp.nat(0), + precision = sp.nat(0), + timestamp = sp.timestamp(0), + source = sp.string("")) + + @sp.entry_point + def sendQuery(self, params): + rtype = sp.TRecord( + currencyPair = sp.TString, + value = sp.TNat, + precision = sp.TNat, + timestamp = sp.TTimestamp, + source = sp.TString) + ptype = sp.TRecord( + currencyPair = sp.TString, + callback = sp.TContract(rtype) + ) + + getHandle = sp.contract(ptype, self.data.oracle, entry_point = "processDataRequest").open_some() + + args = sp.record( + currencyPair = params.currencyPair, + callback = sp.contract(rtype, + sp.to_address(sp.self), + entry_point = "receiveData").open_some() + ) + sp.transfer(args, sp.tez(1000), getHandle) + + @sp.entry_point + def receiveData(self, params): + self.data.currencyPair = params.currencyPair + self.data.value = params.value + self.data.precision = params.precision + self.data.timestamp = params.timestamp + self.data.source = params.source + +class ExchangeRateOracle(sp.Contract): + def __init__(self, admin): + self.init( + feed = sp.map( + tkey = sp.TString, + tvalue = + sp.TRecord( + value = sp.TNat, + precision = sp.TNat, + timestamp = sp.TTimestamp, + source = sp.TString + )), + feedQueryCost = sp.map(tkey = sp.TString, tvalue = sp.TMutez), + admin = admin, + operators = sp.set(), + registeredPairs = sp.set() + ) + + @sp.entry_point + def processDataRequest(self, params): + sp.verify(self.data.registeredPairs.contains(params.currencyPair), message = "Unregistered currency pair") + sp.verify(sp.amount > self.data.feedQueryCost[params.currencyPair], message = "Incorrect query fee") + sp.verify(self.data.feed.contains(params.currencyPair), message = "Data not available") + + record = self.data.feed[params.currencyPair] + + sp.transfer( + sp.record( + currencyPair = params.currencyPair, + value = record.value, + precision = record.precision, + timestamp = record.timestamp, + source = record.source + ), + sp.tez(0), + params.callback) + + @sp.entry_point + def updateFeed(self, params): + sp.verify(self.data.operators.contains(sp.sender), message = "Privileged operation") + self.data.feed[params.currencyPair] = sp.record( + value = params.value, + precision = params.precision, + timestamp = params.timestamp, + source = params.source) + + @sp.entry_point + def registerFeed(self, params): + sp.verify(sp.sender == self.data.admin, message = "Privileged operation") + sp.verify(~ self.data.feed.contains(params.currencyPair), message = "Currency pair already registered") + self.data.registeredPairs.add(params.currencyPair) + self.data.feedQueryCost[params.currencyPair] = params.cost + + @sp.entry_point + def updateOperatorList(self, params): + sp.verify(sp.sender == self.data.admin, message = "Privileged operation") + self.data.operators = params.operators + + @sp.entry_point + def updateAdmin(self, params): + sp.verify(sp.sender == self.data.admin, message = "Privileged operation") + pass @sp.entry_point - def getRequestFromClient(self,params): - sp.if self.data.apidata.contains(params.convkey): - sp.if sp.amount==sp.mutez(2000): - value = self.data.apidata[params.convkey] - comments = "Success" - c = sp.contract(sp.TRecord(value = sp.TInt, comments = sp.TString),sp.sender,entry_point="receiveDataFromOrO").open_some() - mydata = sp.record(value = value,comments = comments) - sp.transfer(mydata,sp.mutez(0),c) - sp.else: - value = 0 - comments = "Invalid amount" - c = sp.contract(sp.TRecord(value = sp.TInt, comments = sp.TString),sp.sender,entry_point="receiveDataFromOrO").open_some() - mydata = sp.record(value = value,comments = comments) - sp.transfer(mydata,sp.mutez(0),c) - sp.else: - value = 0 - comments = "Bad request" - c = sp.contract(sp.TRecord(value = sp.TInt, comments = sp.TString),sp.sender,entry_point="receiveDataFromOrO").open_some() - mydata = sp.record(value = value,comments = comments) - sp.transfer(mydata,sp.mutez(0),c) - - @sp.entry_point - def feedData(self,params): - sp.if self.data.keysset.contains(sp.sender): - self.data.apidata[params.key]=params.value + def setDelegate(self, params): + sp.verify(sp.sender == self.data.admin, message = "Privileged operation") + pass -@sp.add_test(name="testCurrencyOrO") + @sp.entry_point + def transferBalance(self, params): + sp.verify(sp.sender == self.data.admin, message = "Privileged operation") + pass + +@sp.add_test(name="ExchangeRateOracle Tests") def test(): - oracle = CurrencyConverter(sp.address("tz1XrHHchSehNudgAq1aQaoB4Bw7N4hZ1nkH")) scenario = sp.test_scenario() - scenario += oracle - scenario += oracle.feedData(key = "USD_INR",value=76534).run(sender=sp.address("tz1XrHHchSehNudgAq1aQaoB4Bw7N4hZ1nkH")) - scenario += oracle.getRequestFromClient(convkey="USD_INR").run(sender=sp.address("KT1987")) + scenario.h1("ExchangeRateOracle Tests") + + admin = sp.test_account("Admin") + scenario.show(admin) + + operatorA = sp.test_account("operatorA") + scenario.show(operatorA) + + operatorB = sp.test_account("operatorB") + scenario.show(operatorB) + + caller = sp.test_account("caller") + scenario.show(caller) + + oracle = ExchangeRateOracle(admin.address) + scenario.register(oracle) + + consumer = ExchangeRateConsumer(oracle.address) + scenario.register(consumer) + + scenario += oracle.updateOperatorList(operators = sp.set([operatorA.address, operatorB.address])).run(sender = admin) + scenario += oracle.registerFeed(currencyPair = "USDEUR", cost = sp.mutez(1000)).run(sender = admin) + scenario += oracle.updateFeed(currencyPair = "USDEUR", value = sp.nat(92), precision = sp.nat(2), timestamp = sp.timestamp(0), source = "Trusted Source").run(sender = operatorA) + + scenario += consumer.sendQuery(currencyPair = "USDEUR").run(sender = caller, amount = sp.mutez(1000)) + scenario.verify(consumer.data.value == 92) + scenario.verify(consumer.data.currencyPair == "USDEUR")