Contract type variable

How can I declare a contract type in Sophia? As address type cannot hold ct_ literal, and if I try something like:

record state = {
    ct_logic : contract,
    ...

I get a compiler error.

There is no generic contract type. You define a contract type (it might help to think about it as the contract interface) like:

contract FooContractType =
  entrypoint foo : string => unit

and then you can add contracts of that type to your state:

record state = {
    ct_logic : FooContractType,
    ...

and later use it to call foo:

...
    state.ct_logic.foo("a string")
...
2 Likes

Thanks, I actually figured out during sleep having an interface is probably the idea, as you can call methods then etc. :smiley:
Just to give some context, I have two contracts, storage and logic. For logic to access storage it is necessary to have the right interface, but storage doesn’t need to access logic, it only needs to do a check that the caller address matches it’s privileged stored address, so:
require(state.ct_logic == Call.caller, “Caller needs to be the logic contract”)

My question is following…since the logic contract will be changing in the future much more often than the storage contract (which will hopefully never change), is it enough to declare logic contract as blank interface inside storage source file, as I need it there only for this caller check that I mentioned previously?
A simpler alternative I guess would be to simply store logic contract address as a string, and then convert Call.caller to string and do a string comparison.

No need to convert to string, you can keep it as an address in that case I think?! If you only want to check it for equality that is probably the best option :thinking:

address type cannot contain value ct_… only ak_…
This is how I coded it first but got a compiler error :slight_smile:

You can convert a contract to an address using one of the contract primitives:

  • Contract.address is the address of the contract account.

Also note that Call.caller will be of type address so if you want to compare it to a contract you either have to convert the contract to an address (using Address.to_contract) or the address to a contract (or both of them to strings but that would be a worse option).

2 Likes

Btw is there documentation on declaring contract interfaces?
For example in the example you gave above,

contract FooContractType =
  entrypoint foo : string => unit

unit return type would mean no return value?
What about a function that takes no arguments, how is that declared?

Well, => unit means it returns unit, which is a special value, but to an untrained eye that is quite close to no return value :slight_smile: (In functional languages a function will return the last thing that is computed - that can’t really be nothing, hence unit.)

A function foo that doesn’t take any arguments and returns a string would be declared as: entrypoint foo : () => string. The observant reader then realizes that string => int is shorthand for (string) => int

The syntax for function types is defined here

1 Like

Thanks. One more question, do I need to declare in the interface all the entrypoints that the contract has, or only those that I am interested in will suffice?
I am getting “Invocation failed: cb_Xfbg4g==. Decoded: ]���”
when calling a function from another contract…

You only need to specify the part of the interface that you (plan to) use!

cb_Xfbg4g== is the empty error message (in base64check encoding). (And by design all error messages on-chain are empty or else the actual error messages would be under consensus!) If you dry-run the the call you will get a (hopefully) more helpful error message.

1 Like

Ok, I figured out why the storage contract wasn’t recognizing the call coming from the privileged logic contract. Call.caller in case of the call coming from another contract starts with ak_xyz… (so like a regular address) even though the contract address is ct_xyz… I find this somewhat confusing, not sure if it is intended behavior.
Anyway, now I made it work :slight_smile:

1 Like

Great you got it working!

And yes, that is indeed the intended behavior. It is always an account that makes a call (and each contract has an associated address ct_…/ak_… only accounts can have funds etc.).

1 Like

You can take a look at Milen’s example here waellet-contracts/token-registry.aes at master · waellet/waellet-contracts · GitHub could be of use

1 Like