Manage operations returned by lambda by hand

The following question has been asked on Telegram:

Hi all, I am facing an issue defining lambda that creates operations in new syntax.

The code below creates a lambda with type of
(lambda (pair unit (list operation)) (pair unit (list operation)))
instead of the expected
(lambda unit (list operation)).
This is true both for the result variable in storage and the exec_lambda entry point.

How can I fix this?


import smartpy as sp


@sp.module
def main():

    class MyContract3(sp.Contract):
        def __init__(self):
            self.data.result = sp.cast(
                None,
                sp.option[sp.lambda_(sp.unit, sp.unit, with_operations = True)]
            )

        @sp.entrypoint
        def exec_lambda(self, params):
            self.data.result = sp.Some(params)

        @sp.entrypoint
        def nothing(self):
            pass

@sp.add_test(name = "Lambdas")
def test():
    scenario = sp.test_scenario(main)
    scenario.h1("Lambdas")
    c3 = main.MyContract3()
    scenario += c3

A standard approach is to allow SmartPy to handle operations with the with_operations=True parameter. When this parameter is set to True, it will always end with two pairs. If, however, you have valid reasons to avoid this, you can manually manage the operations.

You can use the type sp.operation and push the operations to sp.operations.
See Operations | SmartPy

import smartpy as sp


@sp.module
def main():
    def send_to_alice(params):
        sp.cast(params, sp.unit)
        alice_address = sp.address("tz1h4EsGunH2Ue1T2uNs8mfKZ8XZoQji3HcK")
        alice_contract = sp.contract(sp.unit, alice_address).unwrap_some()
        op = sp.transfer_operation((), sp.tez(5), alice_contract)
        return [op]
        

    class MyContract3(sp.Contract):
        def __init__(self):
            self.data.lambda_ = sp.cast(
                None,
                sp.option[sp.lambda_(sp.unit, sp.list[sp.operation])]
            )

        @sp.entrypoint
        def exec_lambda(self):
            lambda_ = self.data.lambda_.unwrap_some()
            ops = lambda_(())
            for op in reversed(ops):
                sp.operations.push(op)

        @sp.entrypoint
        def set_lambda(self, lambda_):
            self.data.lambda_ = lambda_

        

@sp.add_test(name = "Lambdas")
def test():
    sc = sp.test_scenario(main)
    sc.h1("Lambdas")
    c1 = main.MyContract3()
    sc += c1
    c1.set_lambda(sp.some(main.send_to_alice))
    c1.exec_lambda().run(amount=sp.tez(5))