# Silent Token: Technical Documentation
The following is the latest version of Silent token, as-is in the commit #[8f68945d32d485351775dbeac9f745e6004ed82c](https://github.com/Silent-Protocol/silent/commit/8f68945d32d485351775dbeac9f745e6004ed82c)
in the branch `develop` in official github monorepo hosted by Silent Protocol.
***
***
## Overview
The Silent token is an OFT token derived from [LayerZero's Omnichain Fungible Token](https://layerzero.gitbook.io/docs/) (OFTV2) contracts which enable tokens to be sent across EVM and non EVM chains without requirement for liquidity or fees.
## Background
Omnichain Fungible Tokens (OFT) is a standard introduced by LayerZero (LZ), a blockchain infrastructure designed to enhance scalability, security, and interoperability of decentralized networks. OFT aims to provide a unified standard for tokens across different blockchains, similar to the widely adopted ERC-20 standard on Ethereum-compatible blockchains. OFT allows tokens to be externally composed in various types of decentralized applications (dApps) within any DeFi ecosystem. This standard enables seamless token swapping and usage across chains and protocols.
## Architecture

## Silent Token Contract
### Events
The following are the events that are emitted upon successful calls to different functions in the `SilentToken` contract:
```
event MintingPaused(uint256 _mintRate, uint256 _timestamp)
```
This event is emitted when token minting is paused by succesfully calling the **pauseMinting** function.
***
```
event MintingResumed(uint256 _mintRate, uint256 _timestamp)
```
This event is emitted when token minting is resumed by succesfully calling the **resumeMinting** function.
***
```
event MintRateUpdated(uint256 _mintRate, uint256 _timestamp)
```
This event is emitted when a new mint rate is set by succesfully calling the **setMintRate** function.
***
```
event BurnerStatus(address _burner, bool _status)
```
This event is emitted when a when an address is whitelisted or blacklisted by succesfully calling the **manageBurnerStatus** function.
***
### Custom Errors
The following are the custom errors thrown by different function calls when various required conditions are not met.
```
invalid address
```
This error is thrown when the **`setTreasury`** and **`manageBurnerStatus`** functions are called the caller is attempting to pass the zero address as the input.
***
```
burner not whitelisted
```
This error is thrown when the **`burn`** function is called but the caller is not whitelisted to burn tokens.
***
```
minting currently paused
```
This error is thrown when the **`pauseMinting`** and **`updateSupply`** functions are called but minting is currently paused.
***
```
insufficient balance
```
This error is thrown when the **`burn`** function is called but the caller does not have the required amount of Silent tokens they are attempting to burn.
***
```
minting currently ongoing
```
This error is thrown when the **`resumeMinting`** and **`setMintRate`** functions are called but the mint process is currently ongoing.
***
```
invalid timestamp
```
This error is thrown when the **`updateSupply`** function is called and the current timestamp is less than the timestamp of when tokens were previously minted.
***
```
input rate equal to current rate
```
This error is thrown when the **`setMintRate`** function is called and caller is attempting to change mint rate to the same rate as the exisitng rate.
***
```
burner already whitelisted
```
This error is thrown when the **`manageBurnerStatus`** function is called and caller is attempting whitelist an address that is already whitelisted.
***
```
burner already blacklisted
```
This error is thrown when the **`manageBurnerStatus`** function is called and caller is attempting blacklist an address that is already blacklisted.
***
`max mint limit reached`
This error is thrown when the **`mint`** function is called and the total token supply after minting exceeds 15 million tokens.
***
### State Variables
#### Public state variables
```
bool public mintingPaused
```
This variable is used to store the current status of mint process. It is `true` by default.
***
```
uint256 public mintRate
```
This variable is used to store the amount of Silent tokens to be minted per second whiles minting is ongoing. It is set to 0.07 Silent by default.
***
`uint256 public totalMinted`
This variable is used to store the total amount of Silent tokens minted.
***
```
uint256 public lastMintTimestamp
```
This variable is used to store the timestamp at which the last minting process was initiated or updated. It gets updated everytime **`resumeMinting`** or **`updateSupply`** functions are called.
***
```
address public treasury
```
This variable is used to store the address of Silent Protocol treasury account. All minted tokens are sent to this address.
***
#### Mappings
```
mapping (address => bool) public whitelistedBurners
```
This mapping is used to store information about whether a particular address has been whitelisted with burning privileges or not.
***
### Constructor
```
constructor(
address _endpoint,
address _treasury
) OFTV2("Silent Protocol", "SILENT", 6, _endpoint) {
require(_endpoint != address(0), "invalid address");
require(_treasury != address(0), "invalid address");
treasury = _treasury;
}
```
The constructor allows the `SilentToken` contract to exist on a chain. During this process, the name (Silent Protocol), symbol (SILENT), decimals (6) and layerZero endpoint for the chain its being deployed on is initialzed using the OFTV2 contract.
The process is the same as the creation that of the ERC20 token standard with a slight variation:
- The `_endpoint` paramter is the address of the LayerZero Relayer on the deployment chain. In order to successfully interact with Silent token contracts on other chains, the contract communicates with the LZ Relayer
Additionally during deployment:
- `treasury` is set to address within `_treasury`
***
### Mutative Functions
1. **burn**
```
function burn(uint256 _amount) external {
require(whitelistedBurners[_msgSender()], "burner not whitelisted");
require(_amount > 0, "invalid amount");
require(balanceOf(_msgSender()) >= _amount, "insufficient balance");
_burn(_msgSender(), _amount);
}
```
**Summary**
This function is used to burn a caller's tokens by sending it to the burn address and reducing the token supply.
**Inputs:**
* `uint256 _amount`: Amount of Silent tokens to be burnt.
**Modifiers**:
* `external`
**Revert cases:**
* If the `_msgSender()` is not whitelisted to burn tokens: `burner not whitelisted`
* If the`_amount` is less than or equal to 0: `insufficient allowance`
* If the`_msgSender()` token balance is less than `_amount`: `insufficient balance`
**Storage changes**
* The `totalSupply` of tokens is reduced by `_amount`
**Events emitted**
* None
**Return values:**
* None
***
2. **updateSupply**
```
function updateSupply() public {
require(!mintingPaused, "minting currently paused");
uint256 currentTimestamp = block.timestamp;
require(currentTimestamp > lastMintTimestamp, "invalid timestamp");
uint256 timeSinceLastMint = currentTimestamp.sub(lastMintTimestamp);
uint256 amountToMint = timeSinceLastMint.mul(mintRate);
_mint(treasury, amountToMint);
totalMinted += amountToMint;
lastMintTimestamp = currentTimestamp;
emit SupplyUpdated(amountToMint, totalSupply(), lastMintTimestamp, _msgSender());
}
```
**Summary**
This function is used to update the token supply when minting is ongoing. It can only be called when minting is active (not paused). The purpose of this function is to allow anybody to update token supply when mint is currently ongoing. All tokens minted when update supply is called are transferred to the `treasury` address. In the event it is not called by any party, token supply would be updated when `pauseMinting` function is called by owner.
**Inputs:**
* None
**Modifiers**:
* `public`
**Revert cases:**
* If `mintingPaused` is true (minting is not active): `minting currently paused`
* If the current timestamp is before the `lastMintTimestamp`: `invalid timestamp`
**Storage changes**
* The `totalSupply` of tokens is increased by $timeSinceLastMint * mintRate$, where $timeSinceLastMint = currentTimestamp - lastMintTimestamp$
* The `lastMintTimestamp` is updated to the current timestamp.
**Events emitted**
* `SupplyUpdated` with arguments `amountToMint`, `totalSupply`, `lastMintTimestamp` and `_msgSender()`
**Return values:**
* None
***
***
### Restricted Functions
**Note**:
* All functions in this section revert with `Ownable: caller is not the owner` if the caller of the function is not the owner of the `SilentToken` contract.
* All functions in this section have the modifier `onlyOwner` attached.
* All functions in this section are external.
1. **mint**
```
function mint(address _recipient, uint256 _amount) external onlyOwner {
if ((totalMinted + _amount) > 15000000 * 10**decimals()) {
revert("max mint limit reached");
}
_mint(_recipient, _amount);
to
}
```
**Summary**
This function is used to manually mint tokens to a recipient. Beyond 15M minted tokens, this function would be inexecutable.
**Inputs:**
* `address _recipient`: Address of the recipient
* `uint256 _amount`: Amount of Silent tokens to be minted per second
**Revert cases:**
* If the total minted token supply after minting exceeds 15M tokens: `max mint limit reached`
**Storage changes**
* `totalMinted` is increased by `_amount`.
**Events emitted**
* None
**Return values:**
* None
***
2. **pauseMinting**
```
function pauseMinting() external onlyOwner {
require(!mintingPaused, "minting currently paused");
updateSupply();
mintingPaused = true;
emit MintingPaused(mintRate, block.timestamp);
}
```
**Summary**
This function is used to pause / halt an ongoing mint process.
**Inputs:**
* None
**Revert cases:**
* `uint256 _amount` is true (minting is not active): `minting currently paused`
**Storage changes**
* `mintingPaused` is set to true.
* Any state changes within `updateSupply()`
**Events emitted**
* `MintingPaused` with arguments `mintRate` and `block.timestamp`
**Return values:**
* None
***
3. **resumeMinting**
```
function resumeMinting() external onlyOwner {
require(mintingPaused, "minting currently ongoing");
mintingPaused = false;
lastMintTimestamp = block.timestamp;
emit MintingResumed(mintRate, lastMintTimestamp);
}
```
**Summary**
This function is used to resume / start an automatic mint process.
**Inputs:**
None
**Revert cases:**
* If `mintingPaused` is `false (minting is active): `minting currently ongoing`
**Storage changes**
* `mintingPaused` is set to false.
* `lastMintTimestamp` is set to `block.timestamp`
**Events emitted**
* `MintingResumed` with arguments `mintRate` and `block.timestamp`
**Return values:**
* None
***
4. **setMintRate**
```
function setMintRate(uint256 _mintRate) external onlyOwner {
require(mintingPaused, "minting currently ongoing");
require(_mintRate != mintRate, "input rate equal to current rate");
mintRate = _mintRate;
emit MintRateUpdated(_mintRate, block.timestamp);
}
```
**Summary**
This function is used to set the mint rate. Rate is tokens per second. Can only be set when minting is paused / not active.
**Inputs:**
* `uint256 _mintRate`: Amount of Silent tokens to be minted per second
**Revert cases:**
* If `mintingPaused` is false (minting is active): `minting currently ongoing`
* If `_mintRate` is equal to existing `mintRate`: `input rate equal to current rate`
**Storage changes**
* `mintRate` is set to `_mintRate`
**Events emitted**
* `MintRateUpdated` with arguments `mintRate` and `block.timestamp`
**Return values:**
* None
***
5. **setTreasury**
```
function setTreasury(address _treasury) external onlyOwner {
require(_treasury != address(0), "invalid address");
treasury = _treasury;
}
```
**Summary**
This function is used to set the address of the treasury. The treasury holds all newly minted Silent tokens.
**Inputs:**
* `address _treasury`: Address of the treasury
**Revert cases:**
* If `_treasury` is the zero address: `invalid address`
**Storage changes**
* `treasury` is set to `_treasury`
**Events emitted**
* None
**Return values:**
* None
***
6. **manageBurnerStatus**
```
function manageBurnerStatus(address _burner, bool _status) external onlyOwner {
require(_burner != address(0), "invalid address");
if (whitelistedBurners[_burner] == _status) {
if (_status) {
revert("burner already whitelisted");
} else {
revert("burner already blacklisted");
}
}
whitelistedBurners[_burner] = _status;
emit BurnerStatus(_burner, _status);
}
```
**Summary**
This function is used to whitelist or blacklist a burner. A burner is an account that is capable of burning their Silent tokens and effectively reducing the token supply.
**Inputs:**
* `address _burner`: Address of an account
* `bool _status`: Status indicating whether an address should be whielisted (**true**) or blacklisted (**false**)
**Revert cases:**
* If caller is attempting to whitelist an already whitelisted address: `burner already whitelisted`
* If caller is attempting to blacklist an already blacklisted address: `burner already blacklisted`
**Storage changes**
* The maping `whitelistedBurners` at key `_burner` is set to `true`
**Events emitted**
* `BurnerStatus` with arguments `_burner`, `_status` and block.timestamp
**Return values:**
* None
***
***
## Silent Token Alt
The Silent Token Alt contract is the Silent Token contract which will be deployed on other chains the protocol exists on besides mainnet. It has the same implementation as the Silent Token contract, however all minting functionalities have been removed to enable liquidity to only be generated by the Silent Token contract on mainnet.
Aside from the constructor, the Silent Token Alt contract functionalities are the same as those in the Silent Token Contract
### Events
The following are the events that are emitted upon successful calls to different functions in the `SilentTokenAlt` contract:
```
event BurnerStatus(address _burner, bool _status, uint256 _timestamp)
```
See SilentToken-BurnerStatus
***
### Custom Errors
The following are the custom errors thrown by different function calls when various required conditions are not met.
```
invalid address
```
See SilentToken-invalid address
***
```
burner not whitelisted
```
See SilentToken-burner not whitelisted
***
```
insufficient balance
```
See SilentToken-insufficient balance
***
```
burner already whitelisted
```
See SilentToken-burner already whitelisted
***
```
burner already blacklisted
```
See SilentToken-burner already blacklisted
***
### State Variables
#### Mappings
```
mapping (address => bool) public whitelistedBurners
```
See SilentToken-whitelistedBurners
***
### Constructor
```
constructor(
address _endpoint,
uint256 _initialSupply
) OFTV2("Silent Protocol", "SILENT", 6, _endpoint) {
require(_endpoint != address(0), "invalid address");
if (_initialSupply > 0) {
_mint(_msgSender(), _initialSupply);
}
}
```
See SilentToken-constructor
Differences:
- `treasury` does not exist because there is not minting on the SilentTokenAlt contract
- `_initialSupply` mints an amount of tokens to the owner's address during contract deployment.
***
### Mutative Functions
**burn**
See SilentToken-burn
***
### Restricted Functions
**manageBurnerStatus**
See SilentToken-manageBurnerStatus
***
***
## Cross Chain Implementation
One of the main purpose and motivation behind the Silent token is its cross-chainability. However before any cross-chain communication or token transfers can take place between Silent token contracts on multiple chains,
two actions must occur:
1. Set correct LayerZero **Endpoint** during deployment
2. Establish a link with other Silent token contracts on different chains (remote) using LayerZero **Trusted Remote** functionality.
These steps are security measures set by LayerZero in their OFT token contract to ensure smooth and secure cross-chain communication.
### Endpoint
Cross-chain messages are transmitted and received through LayerZero Endpoints, which are responsible for managing the process of message transmission, verification, and receipt. Visit the LayerZero [docs on endpoints](https://layerzero.gitbook.io/docs/faq/layerzero-endpoint) to learn more.
Since Silent tokens contract inherit LayerZero's OFTV2 contracts, it is required to initialize the OFTV2 contract with the appropriate address of the endpoint for the chain which the Silent token contract is being deployed on.
The list of Enpoints associated with the various supported EVM and non-EVM can be found [here](https://layerzero.gitbook.io/docs/technical-reference/mainnet/supported-chain-ids).
### Trusted Remote
For a contract using LayerZero, a trusted remote is another contract it will accept messages from. Visit the LayerZero [docs](https://layerzero.gitbook.io/docs/evm-guides/master/set-trusted-remotes) on trusted remotes to learn more.
In the context of Silent Protocol, remotes are Silent token contracts existing on multiple chains. For every token contract, we deploy on a chain, we will call this function to enable cross-chain communication between all our token contracts on other chains.
For example, if we already have 3 existing token contracts on 3 different chains and want to deploy a new contract on a 4th chain:
- Once the 4th token contract is deployed, we will call **setTrustedRemotes** 3 times on the recently deployed contract to initiate cross-chain communication with the 3 other contracts.
- Next on each of the 3 existing contracts, we will call **setTrustedRemotes** with respect to the newly deployed contract to complete linking and enable the cross-chain communication.
- The process can be asynchronous and order in which calls are made does not matter.
Doing this manually can be tedious and errors are bound to arise. To circumvent this we leveraged a script developed by LayerZero solely for this purpose. This script can be found our Github [repository](https://github.com/Silent-Protocol/set-trusted-remotes). More information of how the script runs can be found in the [README.md](https://github.com/Silent-Protocol/set-trusted-remotes/blob/main/README.md) of the repository.
### Architecture

<center>The communication flow in a single LayerZero cross-chain transaction</center>
</br>
In the diagram above, the main components include:
- Endpoints: Enables contracts to send messages to each other and ensures the valid delivery of the messages.
- Relayer: The Relayer is an off-chain service that fetches and relays the proof for a specified transaction.
- Oracle: The Oracle serves as a third-party service that offers a mechanism to read a block header from one chain and independently transmit it to another chain.
- Silent Token contracts (Remotes in the LZ context): Client facing interface responsible for collating and initiating user cross-chain requests.
- sendFrom: This function initiates a cross-chain token transfer from a source to a destination chain.
- onOFTReceived: This function is called by the Silent token contract when tokens are received from source chain.