0

first we deploy fa2 contract from a contract like this deployed_fa2 = sp.create_contract_operation(contract=FA2_contract.SingleAssetToken(admin=sp.self_address, metadata=metadata,token_metadata=token_metadata)).address

tried to call balance_of(onchain view) of fa2 contract in same contract user_balance= sp.view("balance_of", fa2_address, sp.record(owner=params.owner, token_id=sp.nat(0)), t=sp.TNat).open_some("Invalid view")

but got error like this Missing contract KT1TezoooozzSmartPyzzDYNAMiCzzpLu4LU for view balance_of

1 Answer 1

1

This happens because when you create a contract using sp.create_contract_operation, it isn't created until the current entry-point call is ended. As it is described here:

The CONTRACT 'p instruction applied to this resulting address will return None until the contract is actually originated, that is, after the generated operation is applied. It follows that a contract cannot directly return a list of operations that creates a contract, and call it immediately after. However, a contract A creating a contract B will get its address, so it can return a list of operations that first originates the contract B, and then calls a contract which in turns calls B: B's address being either stored in A's storage or passed as parameter, the executions following B's origination can use CONTRACT 'p on B's address in order to call it.

So in short what you can do is:

  1. Create the contract in the current call
  2. Save the address of created contract or pass this value in the call below
  3. Call the same contract again on a special entry-point after_origination which can only be called by itself
  4. In this special entry-point, fetch the balance

Here, in between of 3 and 4, the contract origination will happen. After origination point 4 will be reached.

Here is an example:

import smartpy as sp
FA2 = sp.io.import_script_from_url("https://smartpy.io/templates/fa2_lib.py")

class SingleAssetToken(
    FA2.OnchainviewBalanceOf,
    FA2.Fa2SingleAsset
):
    def __init__(self, **kwargs):
        FA2.Fa2SingleAsset.__init__(self, **kwargs)


class A(sp.Contract):
    def __init__(self):
        self.init(
            b_address = sp.address('tz1burnburnburnburnburnburnburjAYjjX'),
            balance = sp.nat(0)
        )

    @sp.entry_point
    def create_b(self, params):
        deployed_fa2 = sp.create_contract(
            contract=SingleAssetToken(
                metadata = params.metadata,
                token_metadata = params.token_metadata
            )
        )
        self.data.b_address = deployed_fa2

        # Call itself on entrypoint 'after_originate'
        me_as_contract = sp.contract(
            sp.TRecord(owner = sp.TAddress),
            sp.self_address,
            'after_origination'
        ).open_some('cannot find me')
        sp.transfer(
            sp.record(owner = params.owner),
            sp.mutez(0),
            me_as_contract
        )

    @sp.entry_point
    def after_origination(self, params):
        sp.set_type(params.owner, sp.TAddress)
        sp.verify(sp.sender == sp.self_address)
        user_balances = sp.view(
            "get_balance_of",
            self.data.b_address,
            [sp.record(
                owner=params.owner,
                token_id=sp.nat(0)
            )],
            t = sp.TList(sp.TRecord(
                balance = sp.TNat,
                request = sp.TRecord(
                    owner = sp.TAddress,
                    token_id = sp.TNat
                ).layout(("owner", "token_id"))
            ).layout(("request", "balance")))
        ).open_some('Invalid view')
        with sp.match_cons(user_balances) as x1:
            self.data.balance = x1.head.balance
        

@sp.add_test(name = "Test")
def test():
    scenario = sp.test_scenario()
    admin = sp.test_account('admin')
    a = A()
    scenario += a
    a.create_b(
        metadata = sp.utils.metadata_of_url('ipfs://example'),
        token_metadata = {'': sp.utils.bytes_of_string('ipfs://example')},
        owner = admin.address
    ).run(sender = admin)
4
  • A small remark: sp.create_contract already combines sp.create_contract_operation and the push of the operation. You may edit your answer with deployed_fa2 = sp.create_contract(...) and self.data.b_address = deployed_fa2 and remove sp.operations().push(deployed_fa2.operation).
    – Jordan_tez
    Commented Apr 25, 2023 at 20:24
  • 1
    @Jordan_tez thanks for pointing that out. I tried to keep it very similar to the question. But no problem, I've updated it according to your suggestion. Commented Apr 25, 2023 at 21:19
  • thanks brother it worked Commented May 9, 2023 at 5:22
  • You're welcome :) Commented May 10, 2023 at 11:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.