Skip to main content

Interfaces

Interfaces enable your logic to access data and invoke endpoints on other (external) logics. You can read external state directly but must invoke endpoints to mutate it.

Defining an Interface

An interface has four optional sections:

interface TokenInterface:
state logic:
total_supply U256
owner Identifier

state actor:
balance U256

endpoint:
dynamic Transfer(to Identifier, amount U256)
static GetBalance(account Identifier) -> (balance U256)

asset:
asset Mint(amount U256) -> (new_supply U256)
SectionPurpose
state logicFields on external logic's state
state actorFields on external actor's state
endpointRegular endpoints to invoke
assetAsset endpoints to invoke
tip

Only define the fields and endpoints you need — you don't have to mirror the entire external logic.

Binding to a Logic

Bind an interface to a deployed logic using its identifier:

endpoint dynamic UseExternal(token_id Identifier):
memory token = TokenInterface(token_id)

// Read external state
observe supply <- token.Logic.total_supply
observe bal <- token.Sender.balance

// Call external endpoint
token.Transfer(to: recipient, amount: 100)

Specify Sender

By default, cross-logic calls are made as Sender. You can specify a different participant:

// Bind with specific sender
memory token = TokenInterface(token_id, participant_id)
token.Transfer(to: recipient, amount: 100) // Called as participant_id

This is useful for swaps where two participants exchange assets:

interface AssetInterface:
asset:
Transfer(to Identifier, amount U256)

endpoint dynamic Swap(
asset1 Identifier, user1 Identifier, amount1 U256,
asset2 Identifier, user2 Identifier, amount2 U256
):
// User1 sends to User2
memory a1 = AssetInterface(asset1, user1)
a1.Transfer(to: user2, amount: amount1)

// User2 sends to User1
memory a2 = AssetInterface(asset2, user2)
a2.Transfer(to: user1, amount: amount2)

Complete Example

External logic (answer.coco):

coco answer

state actor:
ActorAnswer U64

endpoint dynamic SetActorAnswer(actor_id Identifier, new_answer U64):
mutate new_answer -> answer.Actor(actor_id).ActorAnswer

Your logic (question.coco):

coco question

interface Answer:
state actor:
ActorAnswer U64
endpoint:
dynamic SetActorAnswer(actor_id Identifier, new_answer U64)

endpoint dynamic SetAnswer(answer_logic Identifier, value U64):
memory iface = Answer(answer_logic)
iface.SetActorAnswer(actor_id: Sender, new_answer: value)

endpoint static ReadAnswer(answer_logic Identifier) -> (value U64):
memory iface = Answer(answer_logic)
observe value <- iface.Sender.ActorAnswer
note

The same interface can be bound to different logics at runtime, as long as they implement the expected fields and endpoints.

Reading Complex External State

Use gather inside observe blocks to read complex objects from external state:

observe gg <- iface.Logic.guru:
memory fn []String
gather fn <- gg.friend_names
guru = Person{
name: gg.name,
age: gg.age,
friend_names: fn,
}

Capturing Endpoint Return Values

// Call endpoint with return capture
endpointResult = (balance) <- iface.GetBalance(account: Sender)

// Call endpoint without return (side effects only)
iface.Transfer(to: recipient, amount: 100)