6.5 KiB
id | title |
---|---|
entrypoints-contracts | Entrypoints, Contracts |
Entrypoints
Each LIGO smart contract is essentially a single function, that has the following (pseudo) type signature:
(const parameter: my_type, const store: my_store_type): (list(operation), my_store_type)
(parameter, store: my_type * my_store_type) : operation list * my_store_type
(parameter_store: (my_type, my_store_type)) : (list(operation), my_store_type)
This means that every smart contract needs at least one entrypoint function, here's an example:
💡 The contract below literally does nothing
type parameter is unit;
type store is unit;
function main(const parameter: parameter; const store: store): (list(operation) * store) is
block { skip } with ((nil : list(operation)), store)
type parameter = unit
type store = unit
let main (parameter, store: parameter * store) : operation list * store =
(([]: operation list), store)
type parameter = unit;
type store = unit;
let main = (parameter_store: (parameter, store)) : (list(operation), store) => {
let parameter, store = parameter_store;
(([]: list(operation)), store);
};
Each entrypoint function receives two arguments:
parameter
- this is the parameter received in the invocation operationstorage
- 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 list of operations, and a new storage at the end of its execution.
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
.
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);
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)
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.
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);
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), ())
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.
// counter.ligo
type action is
| Increment of int
| Decrement of int
| Reset of unit
// 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)
// counter.mligo
type action =
| Increment of int
| Decrement of int
| Reset of unit
// ...
// 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
// counter.religo
type action =
| Increment(int)
| Decrement(int)
| Reset(unit);
// ...
// 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_s[0], 0mutez, counter);
([op], param_s[1]);