--- id: entrypoints-contracts title: Entrypoints, Contracts --- ## Entrypoints Each LIGO smart contract is essentially a single main function, referring to the following types: ```pascaligo group=a type parameter_t is unit type storage_t is unit type return_t is (list(operation) * storage_t) ``` ```cameligo group=a type parameter_t = unit type storage_t = unit type return_t = (operation list * storage_t) ``` ```reasonligo group=a type parameter_t = unit; type storage_t = unit; type return_t = (list(operation) , storage_t); ``` Each main function receives two arguments: - `parameter` - this is the parameter received in the invocation operation - `storage` - this is the current (real) on-chain storage value Storage can only be modified by running the smart contract entrypoint, which is responsible for returning a pair holding a list of operations, and a new storage. Here is an example of a smart contract main function: > 💡 The contract below literally does *nothing* ```pascaligo group=a function main(const parameter: parameter_t; const store: storage_t): return_t is ((nil : list(operation)), store) ``` ```cameligo group=a let main (parameter, store: parameter_t * storage_t) : return_t = (([]: operation list), store) ``` ```reasonligo group=a let main = ((parameter, store): (parameter_t, storage_t)) : return_t => { (([]: list(operation)), store); }; ``` A contract entrypoints are the constructors of the parameter type (variant) and you must use pattern matching (`case`, `match`, `switch`) on the parameter in order to associate each entrypoint to its corresponding handler. > The Ligo variant's are compiled to Michelson annotated pairs. ```pascaligo group=recordentry type parameter_t is | Entrypoint_a of int | Entrypoint_b of string type storage_t is unit type return_t is (list(operation) * storage_t) function handle_a (const p : int; const store : storage_t) : return_t is ((nil : list(operation)), store) function handle_b (const p : string; const store : storage_t) : return_t is ((nil : list(operation)), store) function main(const parameter: parameter_t; const store: storage_t): return_t is case parameter of | Entrypoint_a (p) -> handle_a(p,store) | Entrypoint_b (p) -> handle_b(p,store) end ``` ```cameligo group=recordentry type parameter_t = | Entrypoint_a of int | Entrypoint_b of string type storage_t = unit type return_t = (operation list * storage_t) let handle_a (parameter, store: int * storage_t) : return_t = (([]: operation list), store) let handle_b (parameter, store: string * storage_t) : return_t = (([]: operation list), store) let main (parameter, store: parameter_t * storage_t) : return_t = match parameter with | Entrypoint_a p -> handle_a (p,store) | Entrypoint_b p -> handle_b (p,store) ``` ```reasonligo group=recordentry type parameter_t = | Entrypoint_a(int) | Entrypoint_b(string); type storage_t = unit; type return_t = (list(operation) , storage_t); let handle_a = ((parameter, store): (int, storage_t)) : return_t => { (([]: list(operation)), store); }; let handle_b = ((parameter, store): (string, storage_t)) : return_t => { (([]: list(operation)), store); }; let main = ((parameter, store): (parameter_t, storage_t)) : return_t => { switch (parameter) { | Entrypoint_a(p) => handle_a((p,store)) | Entrypoint_b(p) => handle_b((p,store)) } }; ``` ## Built-in contract variables Each LIGO smart contract deployed on the Tezos blockchain, has access to certain built-in variables/constants that can be used to determine a range of useful things. In this section you'll find how those built-ins can be utilized. ### Accepting/declining money in a smart contract This example shows how `amount` and `failwith` can be used to decline a transaction that sends more tez than `0mutez`. ```pascaligo group=b function main (const p : unit ; const s : unit) : (list(operation) * unit) is block { if amount > 0mutez then failwith("This contract does not accept tez") else skip } with ((nil : list(operation)), unit); ``` ```cameligo group=b let main (p, s: unit * unit) : operation list * unit = if amount > 0mutez then (failwith "This contract does not accept tez": operation list * unit) else (([]: operation list), unit) ``` ```reasonligo group=b let main = ((p,s): (unit, unit)) : (list(operation), unit) => { if (amount > 0mutez) { (failwith("This contract does not accept tez"): (list(operation), unit)); } else { (([]: list(operation)), ()); }; }; ``` ### Access Control This example shows how `sender` or `source` can be used to deny access to an entrypoint. ```pascaligo group=c const owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); function main (const p : unit ; const s : unit) : (list(operation) * unit) is block { if source =/= owner then failwith("This address can't call the contract") else skip } with ((nil : list(operation)), unit); ``` ```cameligo group=c let owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) let main (p,s: unit * unit) : operation list * unit = if source <> owner then (failwith "This address can't call the contract": operation list * unit) else (([]: operation list), ()) ``` ```reasonligo group=c let owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); let main = ((p,s): (unit, unit)) : (list(operation), unit) => { if (source != owner) { (failwith("This address can't call the contract"): (list(operation), unit)); } else { (([]: list(operation)), ()); }; }; ``` ### Cross contract calls This example shows how a contract can invoke another contract by emiting a transaction operation at the end of an entrypoint. > The same technique can be used to transfer tez to an implicit account (tz1, ...), all you have to do is use `unit` instead of a parameter for a smart contract. In our case, we have a `counter.ligo` contract that accepts a parameter of type `action`, and we have a `proxy.ligo` contract that accepts the same parameter type, and forwards the call to the deployed counter contract. ```pascaligo // counter.ligo type action is | Increment of int | Decrement of int | Reset of unit ``` ```pascaligo skip // proxy.ligo type action is | Increment of int | Decrement of int | Reset of unit const dest: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address); function proxy(const param: action; const store: unit): (list(operation) * unit) is block { const counter: contract(action) = get_contract(dest); // re-use the param passed to the proxy in the subsequent transaction // e.g.: // const mockParam: action = Increment(5); const op: operation = transaction(param, 0mutez, counter); const opList: list(operation) = list op; end; } with (opList, store) ``` ```cameligo // counter.mligo type action = | Increment of int | Decrement of int | Reset of unit // ... ``` ```cameligo // proxy.mligo type action = | Increment of int | Decrement of int | Reset of unit let dest: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address) let proxy (param, storage: action * unit): operation list * unit = let counter: action contract = Operation.get_contract dest in let op: operation = Operation.transaction param 0mutez counter in [op], storage ``` ```reasonligo // counter.religo type action = | Increment(int) | Decrement(int) | Reset(unit); // ... ``` ```reasonligo // proxy.religo type action = | Increment(int) | Decrement(int) | Reset(unit); let dest: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address); let proxy = ((param, s): (action, unit)): (list(operation), unit) => let counter: contract(action) = Operation.get_contract(dest); let op: operation = Operation.transaction(param, 0mutez, counter); ([op], s); ```