How to check if an address is a smart contract and has a function?

How to check inside a smart contract (using Sophia) if a supplied address is a smart contract and implements an interface (eg has defined function pay(dest: address))?

You try to lookup the contract, i.e. if the address is ak_123... you look for v2/contracts/ct_123....

That will give you a contract object. It will contain bytecode. This bytecode can be Base64Check decoded, and then deserialized. Right now there is only AEVM bytecode, so let’s assume that. For AEVM the deserialized object will contain a Type Info field. This is a list of the exported functions (and their argument types + return type) in the contract, so look for a function that is named pay and has a single argument of type word. (There are only AEVM types at this level, and an address is a word at this level).

@hanssv - thanks

My question is about Sophia - how to do it inside a smart-contract (I’ve just clarified it in the post above).

With v2/contracts/ct_123... - do you refer to an API form some explorer / indexer? Which one?

I was referring to the HTTP API of the standard Aeternity node.

You can’t do that in Sophia (only) - you can check whether an address is in fact a contract using Address.is_contract(addr); but there is no way to convert an address into a contract (there is in fact not a generic contract type…)

Remote contracts have an interface type, so you can call a pay function in a remote contract in this fashion (though the example make little practical sense!):

contract Payable =
  entrypoint pay : (address) => bool

contract MyContract =
  stateful entrypoint do_pay(p : Payable, x : int) =
    p.pay(value = x, Call.caller)

But there is no way to go from an address to something of type Payable inside Sophia.

1 Like

As I understand, every contract which has pay : (address) function will work, correct?

It would be a great feature request to have an address cast mechanism. The use case is to handle token payments by a receiver contract, example:

stateful entrypoint transfer(dest: address, x: int, data: string) = 
  switch(dest as Payable) 
    None => ...
    Some(p) => ... // p is an instance of a contract at address `dest` which implements Payable interface

Yes, any contract that has a matching set of functions will work.

You mean switch (dest as Payable)? Something along those lines could certainly be added, though it wouldn’t be entirely trivial to build - and the runtime checks would be somewhat expensive. We have thought about it, but not yet had the time and manpower to get there.

yes, it should be dest as Payable (not address, I’ve just fixed the post).

according to you explanation above this problem can be reduced to a regular expression matching (to avoid dest contract deserialization. And appropriate regular expression engine could be constructed during compilation time to speed up the runtime / cost.

Definitely, it would be a useful feature. What’s the best place to propose it?