You can use raw Michelson as you were expecting.
Here is a generic contract for that:
import smartpy as sp
class ProxyInterface(sp.Contract):
def __init__(self, initial_storage, storage_type, code, entrypoints):
self.init(initial_storage)
self.init_type(storage_type)
self.parameter_type = sp.TVariant(**entrypoints).right_comb()
self.storage_type = storage_type
self.code = code
def create_ep(ep_name, params_type):
def ep(self, params):
sp.set_type(params, params_type)
self.main_computation(
sp.set_type_expr(
sp.variant(ep_name, params),
self.parameter_type))
return ep
for ep_name, params_type in entrypoints.items():
ep = create_ep(ep_name, params_type)
ep.__name__ = ep_name
setattr(self, ep_name, sp.entrypoint(ep))
@sp.private_lambda(with_storage="read-write", with_operations=True, wrap_call=True)
def main_computation(self, params):
sp.set_type(params, self.parameter_type)
f = sp.lambda_michelson(
self.code,
source=sp.TPair(self.parameter_type, self.storage_type),
target=sp.TPair(sp.TList(sp.TOperation), self.storage_type)
)
r = sp.compute(f(sp.pair(params, self.data)))
o, d = sp.match_pair(r)
self.data = d
sp.add_operations(o)
You can instantiate it in a scenario. It will init the storage, create the entrypoints and a michelson lambda based on the parameters you gave.
You may want to use the SmartPy explorer to get the SmartPy types of the contract or at least something similar to what you'll have to use.
Here is an example using it with the store value contract.
Original contract and tests:
import smartpy as sp
class StoreValue(sp.Contract):
def __init__(self, value):
self.init(storedValue = value)
@sp.entrypoint(new_syntax = True)
def replace(self, params):
self.data.storedValue = params.value
@sp.entrypoint(new_syntax = True)
def double(self):
self.data.storedValue *= 2
@sp.entrypoint(new_syntax = True)
def divide(self, params):
assert params.divisor > 5
self.data.storedValue /= params.divisor
if "templates" not in __name__:
@sp.add_test(name = "StoreValue")
def test():
c1 = StoreValue(12)
scenario = sp.test_scenario()
scenario.h1("Store Value")
scenario += c1
c1.replace(value = 15)
scenario.p("Some computation").show(c1.data.storedValue * 12)
c1.replace(value = 25)
c1.double()
c1.divide(divisor = 2).run(valid = False, exception="WrongCondition: params.divisor > 5")
scenario.verify(c1.data.storedValue == 50)
c1.divide(divisor = 6)
scenario.verify(c1.data.storedValue == 8)
sp.add_compilation_target("storeValue", StoreValue(12))
Equivalent with the Michelson proxy:
if "templates" not in __name__:
@sp.add_test(name = "ProxyInterface")
def test():
sc = sp.test_scenario()
c1 = ProxyInterface(
initial_storage=42,
storage_type=sp.TInt,
code="""UNPAIR; IF_LEFT { PUSH int 5; DUP 2; COMPARE; GT; IF {} { PUSH int 19; FAILWITH; }; SWAP; EDIV; IF_NONE { PUSH int 20; FAILWITH; } { CAR; }; } { IF_LEFT { DROP; PUSH int 2; SWAP; MUL; } { SWAP; DROP; }; }; NIL operation; PAIR; """,
entrypoints= {
"divide": sp.TInt,
"double": sp.TUnit,
"replace": sp.TInt,
},
)
sc.h1("Store Value")
sc += c1
c1.replace(15)
sc.p("Some computation").show(c1.data * 12)
c1.replace(25)
c1.double()
c1.divide(2).run(valid = False, exception=19)
sc.verify(c1.data == 50)
c1.divide(6)
sc.verify(c1.data == 8)
Notice that I got a strange error so I had to replace the string
error message by an int
. If you experience the same issue, you can do the same replacement or open another question with your problem.