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?


You don't need to know the position of an entrypoint to call it. For this reason, the position does not matter much so it is not imposed by the standard.

  • I figured this might be the case, but since the parameters encode to michelson differently it didn't seem right. Are entrypoint "names" unique then?
    – Nox
    Dec 16 '21 at 23:18
  • Yes, entrypoints are uniquely identified by their names. Jan 3 at 15:25

I found a further explanation of how entrypoints work in Michelson here. The names of entrypoints are unique, so specifying the name of an entrypoint when passing the parameter will automatically wrap it.

[A] contract cannot define two entrypoints with the same name.

An external transaction can include an entrypoint name alongside the parameter value. In that case, if there is a constructor annotation with this name at any position in the nesting of or types, the value is automatically wrapped into the according constructors.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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