Endpoint Syntax
A endpoint in Coco is a callable element for code organization and reusability. A single endpoint is expected to perform simple and singular operations. Each endpoint has a particular type of Callsite, which describes the constraints of its execution.
- An endpoint can accept zero or more arguments and return some outputs. All arguments and return variables for an endpoint must be named. All return variables of the endpoint are automatically initialized at the beginning of its execution. See
Functions.DoSomething()
- Some endpoint names have a ‘persistent’ keyword. This is intended to emphasize that the endpoint modifies the state of the module. It is mandatory for any endpoint that uses the mutate keyword in its block. See
TokenLedger.SeedSupply()
- If multiple consecutive endpoint parameters are of the same data type, the type can be omitted from all but the last parameter. For example,
Functions.Combine()
can also be declared as follows:
// We have combined the type declaration of the x and y function paramaters
endpoint invoke Combine(x, y String) -> (z String):
yield z join(x, y)
Types of Endpoint Callsites
Coco supports multiple types of endpoints, each enabling different capabilities around the module. It can be useful to think each callsite type as a different endpoint for a hosted Logic, similar to REST APIs
1. Deployers
Deployers are endpoints used to initialize a persistent state and can be thought of as the constructor of the module. A given module can describe multiple deployers but only can be used at the time of activating or “deploying” the logic module. See
- A module with a persistent state must describe atleast one deployer.
2. Enlisters
Enlisters are endpoints used to initialize a ephemeral state and can be thought of as the constructor of the state for the participant (see example EFlipper). A given module can describe multiple enlisters.
If the module has both persistent and ephemeral states, only deployers can be called, so enlisters are not necessary.
3. Invokables
Invokables are endpoints that can only be invoked externally by a single participant.
They can be thought of as the external endpoint to the logic with most of their primary capabilities being wrapped in functions
4. Interactables
- Interactables are endpoints that facilitate an external interaction between two participants.
Function Syntax
Functions are used for code re usability and maintainability. They only exist within the module scope. These are the only types of function that can be called from within the module. See Functions.Combine()
coco TokenLedger
state persistent:
Symbol String
Supply U256
Balances Map[Address]U256
endpoint deploy Seed(symbol String, supply U256):
mutate symbol -> TokenLedger.Symbol
mutate supply -> TokenLedger.Supply
mutate balances <- TokenLedger.Balances:
balances[Address(Sender)] = supply
emit f"seeded {Address(Sender)} with {supply} {symbol}"
endpoint invoke Decimals() -> (decimals U64):
yield decimals 18
endpoint invoke BalanceOf(addr Address) -> (balance U256):
observe balances <- TokenLedger.Balances:
// If balance exists for address, read and return
// balance = 0, if no entry for the address
if balances[addr]?:
balance = balances[addr]
event Transfer:
topic sender Address
topic receiver Address
field amount U256
endpoint invoke persistent Transfer(amount U256, receiver Address):
memory sender = Address(Sender)
mutate balances <- TokenLedger.Balances:
if !balances[sender]?: throw "sender has no balance"
memory balance = balances[sender]
if balance < amount: throw "sender has insufficient balance"
if !balances[receiver]?:
balances[receiver] = 0
balances[sender] = balance - amount
balances[receiver] += amount
emit Transfer(sender, receiver, amount)
endpoint invoke persistent Mint(amount U256):
mutate supply <- TokenLedger.Supply:
supply += amount
mutate balances <- TokenLedger.Balances:
memory sender = Address(Sender)
if !balances[sender]?:
balances[sender] = 0
balances[sender] += amount
endpoint invoke persistent Burn(amount U256):
mutate supply <- TokenLedger.Supply:
if supply < amount: throw "invalid burn amount: exceeds supply"
supply -= amount
mutate balances <- TokenLedger.Balances:
memory sender = Address(Sender)
if !balances[sender]?:
throw "invalid burn amount: sender has no balance"
memory balance = balances[sender]
if balance < amount: throw "invalid burn amount: insufficient balance"
balances[sender] -= amount
coco Functions
func DoSomething() -> (output String):
yield output "done!"
endpoint invoke Combine(x String, y String) -> (z String):
yield z join(x, y)