Examples of contracts
In this chapter, we will present the structure of simplified versions of a number of classical smart contracts:
- FA1.2 - Fungible token
- FA2 - NFTs: Non-Fungible Tokens
- NFT Marketplace
- Escrow
- DAO: Decentralized Autonomous Organization
- DeFi: Flash loan
The goal is for you to get a good understanding of what a smart contract is, how the storage and entry points are used. We will also introduce how and why contracts interact with each other.
Finally, this will give you an overview of what some of the most common smart contract may look like.
The contracts below are simplified contracts, provided for educational purposes only. They are not meant to be implemented and used as is, as some of them may contain potential flaws.
FA1.2 - Fungible token
The goal of this contract is to create and manage a single fungible token.
It implements the FA1.2 standard, which makes it compatible with wallets, decentralized exchanges and other tools.
It only supports a small number of features:
- Each user can own a certain number of tokens
- Users can transfer tokens to other users
- Users can allow another contract, for example a decentralized exchange, to transfer some amount of their tokens for them.
The contract contains two main entry points:
transfer
, to transfer a number of tokens from one address to anotherapprove
, for a caller to indicate that they allow another address to transfer a number of their tokens
To be compatible with FA1.2, and so that other contacts can access to information, it also contains three entry points that have no effect other than sending information back to the caller:
getBalance
sends the number of tokens owned by a given addressgetAllowance
sends the amount of tokens belonging to a certain address that another address is allowed to transfer for themgetTotalSupply
sends the total amount of tokens managed by this contract
Storage | Entry points effects |
|
|
FA2 - NFTs: Non-Fungible Tokens
The FA2 standard specifies contracts that can be of different types:
- Single fungible token
- Multiple fungible tokens
- Non fungible tokens (NFTs)
Implementing the FA2 standard allows the contract to be compatible with wallets, explorers, marketplaces, etc.
Here, we will present an implementation for NFTs. The entry points for the other types are the same, but the implementation differs.
FA2 contracts must have the following entry points:
transfer
can be called either by the owner of tokens to be transferred, or by an operator allowed to do so on their behalf.
It takes a list of transfers of different tokens from the owner, to different addresses.update_operator
can be called by the owner of tokens to add or remove operators allowed to perform transfers for them.
It takes a list of variants, each consisting of either adding or removing an operator for a given token.balance_of
is used to access the balance of a user for a given token.
FA2 supports a number of optional entry points to access information, but we won't provide them here.
Storage | Entry points effects |
|
|
NFT Marketplace
The goal of this contract is to manage sales of NFTs from one address to another. It pays a share of the selling price to the admin of the marketplace, in exchange for providing a dApp that facilitates finding and purchasing NFTs.
It provides the following entry points:
add
is called by a seller who puts their one of their NFTs on sale for a given price.
The seller must indicate which FA2 contract holds the NFT, and what the id of the NFT is within that contract.
It requires for the marketplace to have been set as an operator in the FA2 contract, for this token.remove
can be called by a seller to remove their NFT from the marketplace, if it hasn't been sold.buy
is to be called by a buyer who pays the set price to buy a given NFT.
The admin account of the marketplace receives a share of the selling price.
Storage | Entry points effects |
|
|
Escrow
An escrow is a contract that temporarily holds funds in reserve, for example tokens paid by a buyer of a service, while their request is being processed.
Its goal is to provide trust between the parties of a transaction that can't be atomic:
- the buyer can't send the payment to the service until the request has been fulfilled.
- the service can't start working on the request without a guarantee that it will get paid.
There are a number of different types of escrow contracts. In our contract, the service to be provided is some data that needs to be sent by the service, where the escrow contract has the ability to verify the validity of the data.
For example, the request could consist in the service sending the decrypted version of some encrypted data.
Our contract has three entry points:
send_request
creates a new request with a deadline and collects the payment, that will be held in the escrow.
Along with the data, the request contains the code that will verify the validity of the answer (a lambda)fulfill_request
is to be called later by the service.
It verifies that the request has been performed and transfers the funds to the service.cancel_request
can be called buy the buyer if the request had not been fulfilled after the deadline.
It transfers the funds back to them.
Storage | Entry points effects |
|
|
DAO: Decentralized Autonomous Organization
A DAO is a contract that represents an entity composed of a number of participants. It provides a way for these participants to collectively take decisions, for example on how to use tokens held in the balance of the DAO contract.
There can be all kinds of DAOs, and we will present a simple but powerful version.
Our DAO stores the addresses of all its members, a list of all the proposals, and keeps track of who voted for them.
It has the following entry points :
propose
can be called by any member to make a new proposal, in the form of a piece of code to execute (a lambda).vote
can be called by any member to vote in favor of the request.
When the majority of members voted in favor, the proposal is executed.add_member
adds a new member to the DAO.
It may only be called by the DAO itself, which means the call has to go through a proposal and be voted on.remove_member
removes a member from the DAO.
It may only be called by the DAO itself.
When deployed, an initial list of members needs to be put in the storage, and typically some tez put in the balance.
Storage | Entry points effects |
|
|
DeFi: Flash loan
A Flash loan is one of the many tools of decentralized Finance (deFi).
It provides a way for a user to temporarily get access to large amounts of tez, without any collateral. This allows them to take advantage of opportunities and make a profit, without the need to have their own funds.
The idea is that the following steps will be done in an atomic way:
- the borrower receives the requested amount from the contract
- the borrower uses the amount in a series of calls to other contracts, that allow him to make some instant profit
- the borrower then pays the requested amount plus some interest to the contract, and gets the rest of the profit.
The key aspect to understand is that all this is done atomically, which means that if any of these step fails, and if for example the borrower is not able to pay back the borrowed amount plus interest, then the whole sequence is canceled, as if it never happens. There is no risk at all for the lender contract.
This contract can even lend the same tez to multiple different people within the same block, as each loan is paid back immediately, so the tokens can be used again for another loan.
One may use a flash loan to take advantage of an arbitrage situation, for example if two different exchanges offer to buy/sell the same type of tokens, at a different price from each other. The user can buy tokens from one exchange at a low price, then sell it to the other exchange at a higher price, making a profit.
Our contract has three entry points:
borrow
is called by the borrower, indicating how many tez they need.
The amount is transferred to the caller, then a callback he provided is executed. At the end of this entry point, we verify that this callback has repaid the loan.repay
is to be called by this callback once the actions that generate a profit are done. The call should come with payment of the borrowed amount, plus interest.check_repaid
is called by theborrow
entry point after the call to the callback. Indeed,borrow
can't do the verification itself, since the execution of the callback is done after all the code of the entry point is executed.
Storage | Entry points effects |
|
|