Solution 1: standard
You can get the balance by calling the balance_of
entrypoint and giving a callback address on which the answer will be given.
Don’t forget to check that the sp.sender
of the answer is the desired address and add avoid replay as anyone can call the fa2 and give your contract as the callback.
An example using the new syntax:
import smartpy as sp
import templates.fa2_lib_new as fa2_lib
@sp.module
def m():
balance_of_request: type = sp.record(owner=sp.address, token_id=sp.nat).layout(
("owner", "token_id")
)
balance_of_requests: type = sp.list[balance_of_request]
balance_of_response: type = sp.record(
request=balance_of_request, balance=sp.nat
).layout(("request", "balance"))
balance_of_responses: type = sp.list[balance_of_response]
balance_of_param: type = sp.record(
callback=sp.contract[balance_of_responses],
requests=balance_of_requests,
).layout(("requests", "callback"))
class Getter(sp.Contract):
def __init__(self):
self.data.balances = sp.big_map()
@sp.entrypoint
def getter(self, fa2_address, requests):
contract = sp.contract(
balance_of_param,
fa2_address,
"balance_of"
).unwrap_some(error="Fa2BalanceOfNotFound")
param = sp.record(
callback=sp.self_entrypoint("_setter"),
requests=requests,
)
sp.transfer(param, sp.tez(0), contract)
@sp.entrypoint
def _setter(self, responses):
sp.cast(responses, balance_of_responses)
for response in responses:
key = sp.record(
fa2=sp.sender,
token_id=response.request.token_id,
owner=response.request.owner)
self.data.balances[key] = sp.record(
balance=response.balance,
now=sp.now)
if "templates" not in __name__:
@sp.add_test(name="MyContract")
def test():
admin = sp.test_account("admin")
alice = sp.test_account("alice")
sc = sp.test_scenario([fa2_lib.t, fa2_lib.main, m])
fa2 = fa2_lib.main.FungibleTestFull(
administrator=admin.address,
metadata=sp.big_map(),
ledger={(alice.address, 0): 42},
token_metadata=[sp.map()],
)
sc += fa2
c1 = m.Getter()
sc += c1
c1.getter(
fa2_address=fa2.address,
requests=[sp.record(owner=alice.address, token_id=0)])
sc.verify(c1.data.balances[
sp.record(fa2=fa2.address, token_id=0, owner=alice.address)
] == sp.record(balance=42, now=sp.timestamp(0)))
Solution 2: non standard
If your FA2 contract inherited the mixin OnchainviewBalanceOf
of the FA2 library, you can call the get_balance_of
view. Contrary to the balance_of
entrypoint, you get the result instantly and not after a callback.