Most of the example FA2 implementations I've found, as well as the deployed contracts I've investigated have the required FA2 entrypoints (transfer
, balance_of
, update_operators
) buried in the second level of the parameter.
Simplifying the linked example from minter-sdk:
type fa2_entry_points =
| Transfer of transfer list
| Balance_of of balance_of_param
| Update_operators of update_operator list
type nft_asset_entrypoints =
| Assets of fa2_entry_points
| Admin of admin_entrypoints
let nft_asset_main (param, storage : nft_asset_entrypoints * nft_asset_storage)
: operation list * nft_asset_storage =
match param with
| Assets fa2 ->
let u = fail_if_paused(storage.admin) in
let ops, new_assets = fa2_main (fa2, storage.assets) in
let new_storage = { storage with assets = new_assets; } in
ops, new_storage
...
Here, the FA2 entrypoints are keyed under "Assets," which is what I've seen in most deployed contracts as well, presumably because they're derived from the minter-sdk contracts.
This organization is more convenient from a code modularity perspective, but if this were compliant with the spec it seems like it would make it impossible to call otherwise compliant FA2 contracts in a general way because you'd need to know at what level their entrypoints live. I don't know Michelson so apologies if my terminology isn't right. Are contracts like this still considered FA2 compliant due to ambiguity of the spec, or are they actually not quite compliant?