0

I am trying to read a balance_of function of fa2 contract and updating my storage with the balance. but I am getting an issue

Error: Type Error sp.TList(sp.TPair(sp.TPair(sp.TAddress, sp.TNat), sp.TNat)) is not sp.TNat incompatible parameter type

Here is the code

# Store Value - Example for illustrative purposes only.

import smartpy as sp

class ContractA(sp.Contract):
    def __init__(self, value):
        self.init(updated_balance = value)

    @sp.entry_point
    def getBalance(self, contract_address):
        # sp.set_type(contract_address, sp.TContract)
        fa2_contract = sp.contract(
            sp.TRecord(
                callback=sp.TContract(sp.TNat),
                requests=sp.TList(
                    sp.TRecord(
                        owner=sp.TAddress,
                        token_id=sp.TNat,
                    ).layout(("owner", "token_id"))
                ),
            ).layout(("callback", "requests"))
        , contract_address, entry_point="balance_of").open_some()

        x  = sp.transfer(sp.record(callback=sp.self_entry_point("test_balance"), 
                               requests=[sp.record(owner=sp.address("tz1dhsUB43VJ58FndJrCvS6FpGb1pHi5hijN"),
                                                   token_id=1)]),
                    sp.tez(0),
                    fa2_contract)

    @sp.entry_point
    def test_balance(self, balance):
        sp.set_type(balance, sp.TList(sp.TPair(sp.TPair(sp.TAddress, sp.TNat), sp.TNat)))
        with sp.for_("x", balance) as x:
            y, token_balance = sp.match_pair(x)
            # self.data.updated_balance = token_balance

if "templates" not in __name__:
    @sp.add_test(name = "StoreValue")
    def test():
        c1 = ContractA(sp.nat(12))
        scenario = sp.test_scenario()
        scenario.h1("Store Value")
        scenario += c1
        # c1.set_balance(balance = 15)

    sp.add_compilation_target("storeValue", ContractA(12))

1 Answer 1

0

There are many changes required to make this work, here is the corrected contract. I've commented all the changes that I have done.

import smartpy as sp

# sample fa2 token to run test
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 ContractA(sp.Contract):
    def __init__(self, value):
        self.init(updated_balance = value)

    @sp.entry_point
    def getBalance(self, contract_address):
        fa2_contract = sp.contract(
            sp.TRecord(
                # sp.TRecord instead of sp.TNat, should be same as defined below in sp.set_type
                callback=sp.TContract(sp.TList(sp.TRecord(
                    balance = sp.TNat,
                    request = sp.TRecord(
                        owner = sp.TAddress,
                        token_id = sp.TNat
                    ).layout(("owner", "token_id"))
                ).layout(("request", "balance")))),
                requests=sp.TList(
                    sp.TRecord(
                        owner=sp.TAddress,
                        token_id=sp.TNat,
                    ).layout(("owner", "token_id"))
                ),
            # flipping the layout from .layout(("callback", "requests"))
            ).layout(("requests", "callback"))
        , contract_address, entry_point="balance_of").open_some()
        x  = sp.transfer(
            sp.record(
                callback=sp.self_entry_point("test_balance"), 
                requests=[sp.record(
                    owner=sp.address("tz1dhsUB43VJ58FndJrCvS6FpGb1pHi5hijN"),
                    # getting token_id 0 because here we have a single token
                    token_id=0)]
            ),
            sp.tez(0),
            fa2_contract
        )

    @sp.entry_point
    def test_balance(self, balance):
        # sp.TRecord instead of sp.TPair
        sp.set_type(balance, sp.TList(sp.TRecord(
            balance = sp.TNat,
            request = sp.TRecord(
                owner = sp.TAddress,
                token_id = sp.TNat
            ).layout(("owner", "token_id"))
        ).layout(("request", "balance"))))
        # return list has only one element, so saving the head
        with sp.match_cons(balance) as x:
            self.data.updated_balance = x.head.balance

if "templates" not in __name__:
    @sp.add_test(name = "StoreValue")
    def test():
        c1 = ContractA(sp.nat(12))
        scenario = sp.test_scenario()
        scenario.h1("Store Value")
        scenario += c1
        # creating admin account to make testing transactions
        admin = sp.test_account('admin')

        # registering sample fa2 contract
        fa2 = SingleAssetToken(
            metadata = sp.utils.metadata_of_url('ipfs://example'),
            token_metadata = sp.map({'': sp.utils.bytes_of_string('ipfs://example')})
        )
        scenario += fa2

        # getting the balance
        c1.getBalance(fa2.address).run(sender=admin)

Here is a summary of changes:

  • Imported a sample single asset FA2 token
  • Changed the type of the callback parameter in the getBalance entry point.
  • Flipped the layout of the parameters in the sp.TRecord definition for the fa2_contract variable in the getBalance entry point.
  • Changed the type of the balance parameter in the test_balance entry point.
  • Changed the way the balance is retrieved in the test_balance entry point by using match_cons instead of for_ loop, and saving the head of the returned list.

Also, there is a simpler way to call views so that you won't need to prepare callbacks. You can find that example here

1
  • 1
    Something important to notice, the entrypoint that receives the callback is responsible for checking: the sp.sender, the owner address and if required check sp.source or add any mechanism to avoid replay call.
    – Jordan_tez
    Commented Apr 26, 2023 at 12:48

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.