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:
- Create the contract in the current call
- Save the address of created contract or pass this value in the call below
- Call the same contract again on a special entry-point
after_origination
which can only be called by itself
- 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)