---
# System prepended metadata

title: TONStarter Whitepaper

---

# TONStarter Whitepaper


# Disclaimer
The following whitepaper contains Onther's (Onther PTE. LTD.) prediction of the future for the continuous support for our project. TONStarter mentioned in the whitepaper is under development and technical functions such as governance are constantly being updated. TONStarter token (TOS) may not come to fruition in becoming what we have mentioned in the whitepaper and is based on an experimental platform(software) and the development and use of technology related to it. Once TONStarter is complete, what is written in the whitepaper may be significantly different from reality. Onther (Onther PTE. LTD.) does not guarantee any and all of Onther's plans, predictions, and future achievements, and any and all parts of this document should not be regarded as Onther's promise for the future. 

This whitepaper is not a recommendation nor a suggestion to purchase or partake in offers for TONStarter token (TOS). This whitepaper should not be inferred nor interpreted as a recommendation for partaking in offerings, investments, or purchases of TONStarter tokens (TOS), and this whitepaper itself does not become a crucial basis for any contracts or investments.

Participants shall determine directly whether they are eligible to participate in the TONStarter project in accordance with laws and subordinate provisions, regulations and contracts applicable to themselves, and Onther(Onther PTE. LTD.) does not provide any guarantee nor guarantee anything regarding to the project. This document is strictly confidential and intended to be viewed exclusively by those recipients (“Recipient(s)”) specifically authorized by the company. Simply receiving this whitepaper does not guarantee eligibility and does not guarantee participation in the project. Participants must complete the purchase and sale of TONStarter tokens (TOS) and all types of licensing and reporting required to participate in the TONStarter project under their own responsibility and Onther(Onther PTE. LTD.)bears no responsibility in this regard. Participants must agree that they will not participate in any form of activities limited by money laundering, illegal currency transactions, and other international agreements and applicable laws through the participation of the TONStarter token (TOS) and TONStarter projects. Each participant is aware that not using TOS and TONStarter platform purpose of money laundering, financing of terrorism and proliferation of weapons of mass destruction directly and indirectly.

# 1. Why TONStarter?

TONStarter is the first decentralized launchpad platform based on Ethereum and Tokamak Network. Unlike existing IDO platforms, TONStarter helps the building of layer 2 based projects. With TONStarter, users can fairly obtain initial investment opportunities for promising projects that the TONStarter community directly validated and selected. Project teams can raise funds in the most decentralized manner.

## Background

After the ICO craze that heated up the market in 2017, several new projects have tried to realize new values despite market uncertainties in the blockchain ecosystem.

Although these projects each proposed different innovative values, they faced the common problem of funding, which is fundamental for project operation and development. Various funding methods have been introduced to address this issue but have failed to solve it completely.

To begin with, some projects focused on private sales(in which only a few people could participate) in order to alleviate legal uncertainty and increase the success of funding. However, this inevitably led to several problems as it sacrificed the core value of a blockchain-based project, 'decentralization'. 

To compensate for such limitations, the method of launching on DEX such as Uniswap was introduced. Even so, issues such as front-running bots led to unfair funding for ordinary investors. 

The blockchain community is exploring more decentralized and transparent funding methods. This stems from the hope of the creation of an environment in which the project team is not exposed to legal uncertainty, and the community can fairly participate in funding so the team can focus on project development.

In addition, the rapid growth of the DeFi market lasting for over a year, has once again raised the scalability issue of the existing blockchain, the Ethereum ecosystem.

![Figure: DeFi TVL](https://i.imgur.com/06MebJo.png)

To solve scalability issues, the Ethereum community has been developing Ethereum 2.0 and layer 2 for a long time. Starting from July 2021, Optimistic Rollup-based layer 2 such as Optimism and Arbitrum will launch. Subsequently, the Ethereum ecosystem will expand to layer 2, and its size will continue to increase. 

While the community is set to make a significant transition, the reality is that the discussion of building a layer 2 ecosystem still revolves around existing layer 1 based projects. In other words, the discussion of projects that will newly start from the growing layer 2 is not being held. Layer 2 is expected to significantly increase its scalability and freedom. It is not difficult to predict that it will expand significantly more than the previous Ethereum layer 1 DApp ecosystem.

Therefore, layer 2 projects being completely newly developed will require layer 2 based project building and funding. In addition, this will be fundamental for successful project building. Currently, except for already successful existing projects such as Uniswap, most layer 2 projects face difficulty in project and community building. Project teams face a high barrier of the technical understanding of layer 2, suffer from insufficient material for layer 2 based project building, and have difficulty in community building appropriate for layer 2. 


TONStarter aims to solve these problems by launching a fair and decentralized funding platform for new layer 2 projects.

## Vision

TONStarter exists to accelerate the building and funding of layer 2 projects. TONStarter ultimately aims to expand the Tokamak Network ecosystem.

## Market Opportunities

The exponential growth of the layer 2 market is due soon(based on when this Whitepaper is being written). Approximately 70% of DeFi projects that have already been successful in layer 1 (TODO: Add references?) have confirmed their migration to layer 2, and this ratio will continue to grow. In addition, for most upcoming projects, it's harder to find a reason to not choose layer 2. 

At such critical timing, a platform in which the community can continue to provide help from funding to the successful launch or layer 2 projects, may possibly face the greatest market opportunity in history. This will also be an important opportunity for the on-demand layer 2 platform, Tokamak Network. Since many of the newly launched projects through TONStarter will all be developed based on the Tokamak Network, the Tokamak community will be able to maximize the benefits gained through the growth of the layer 2 market.

# Problems

### Unfairness in Investment Opportunities and Capital Distribution 

Blockchain community members are not properly provided with fair investment opportunities for new promising projects. This creates significant problems regarding fairness in investment opportunities and capital distribution. The specific reasons are as follows.

- Investment in new projects is still dominated by private sale funding centered around large venture capitals. As a result, most of the initial volume is concentrated on a small number of investors, and ordinary investors do not even get proper investment opportunities.
- Whitelisting is introduced as an alternative to this problem. This too, however, is not completely fair as only a few lucky investors are offered investment opportunities. The problem escalated as people started using bots optimized for whitelist applications.
- In addition, methods such as launching on DEX such as Uniswap has been introduced to address these challenges. However, token prices are often manipulated due to front-running bots. In the process, ordinary investors are once again losing fair investment opportunities.

### Lack of Investment Support Specific to Layer 2 Projects 

The blockchain community is facing the implementation and expansion of layer 2 ecosystem. The reality, however, is that the investment support for such layer 2 projects is lacking and insufficient. 

- Although the IDO platforms for individual blockchains ecosystems such as Polkastarter, Solstarter, and Polystarter are already active, there is no platform to help build layer 2 projects.
- Discussion of layer 2 ecosystem revolves only around the migration of successful layer 1 projects to layer 2.
- However, demand from the community to build projects entirely on layer 2 from scratch is expected to grow exponentially.
- In this regard, teams and communities that want to build projects based on layer 2 are lacking the resource to effectively raise funds, build, and launch projects.

# Solution: TONStarter

TONStarter is the first decentralized launchpad platform to help fund and build layer 2 projects. Unlike traditional IDO platforms, it is designed to provide fair opportunities and rewards to all members of the community by implementing the features of DeFi and Fair Launch.

Through TONStarter, all community members receive a fair initial investment opportunity for promising projects directly validated by the community. Project teams can also raise funds in a decentralized manner and receive the necessary technical and business support for a layer 2  project building.

The fundamental difference between TONStarter and existing IDO platforms is that all TONStarter projects are launched on the Tokamak Network based layer 2. Through this, TONStarter and Tokamak Network will play a key role in terms of ecosystem expansion.

## Key Features

TONStarter provides TONStarter users and project teams with the following key features.

### Dual Profit

- TONStarter users can share the profit gained from the growth of the platform and each starter project.
- A portion of the tokens issued from each starter project will be allocated to TONStarter users and more details will be provided in the TOS Token section.

### Layer 2 Accelerating

- All TONStarter's starter projects will be launched based on Tokamak Network layer 2.
- Each team can launch its project through Tokamak Network layer 2 as a service. In addition, each team can easily onboard its project on layer 2 operated by Tokamak Network.
- Support for layer 2 accelerating will be provided by Tokamak Network, and each starter team can build their layer 2 based projects easily by only investing a minimum amount of resources.

### Fair, Decentralized and Permisionless

- Anyone around the world can freely join TONStarter as long as one has a Web3 Wallet.
- Everyone will be given a fair and open chance to TONStarter investment opportunities.

### Secure and Transparent

- All TONStarter's starter project will be meticulously validated by the TONStarter community.
- All of TONStarter's decision-making will be transparent and be open to the public. Holders of TOS, TONStarter's governance token, can participate in every TONStarter governance.
- All of TONStarter's investment process will be transparent and open to the public, and anyone can check it.

## For Community and Investors

The community and investors are the utmost important members of every project, not just limited to TONStarter. Even a technologically advanced project cannot sufficiently grow without a community's support and users. On the other hand, without investors, there would be limitations in getting the necessary funds which also creates a significant problem in the development of a project. To aid every starter project to achieve successful community building and raising the necessary funds, TONStarter plans to provide as many rights and profits to the community and investors. The following are the key points of what TONStarter plans to provide.

- Providing fair and equal investment opportunities
- Continuous staking and airdrop rewards and sharing of platform profits
- Providing the opportunity to participate in TONStarter's decision-making
- Providing initial investment opportunities for validated and promising projects
- Low gas price and fast transaction speed through Tokamak Network layer 2
- Minimizing risks that comes from centralization

## For Teams

In the Tokamak Network ecosystem, teams that continuously provide new and unique values are just as important as the members of the community and investors. These teams, however, are facing difficulties in building new projects based on layer 2. They are still searching for fair and decentralized ways of raising funds. Above all, they are struggling to secure the technical and business resources needed to launch successful layer 2 projects. TONStarter plans to solve these problems by providing the following resources.

- Providing numerous fundraising options (eg: Limit order, offering, dutch auction, bonding curve, and more)
- Alleviating legal uncertainty and over-reliance on private investors with fair and decentralized funding methods
- Providing support for marketing, forming partnerships, and community building, and business-related operations
- Providing technical support for layer 2, smart contract, and DApp planning and development

## TOS and Tokamak Network

**What Tokamak Network provides to TOS**

- High scalability
- Low transaction fee (gas price)
- Layer 2 as a service

**What TOS provides to Tokamak Network**

- Expansion of TON ecosystem
- Creating an autonomous project ecosystem
- Improving TON user experience

In terms of expansion of the ecosystem, TONStarter and Tokamak Network are tightly intertwined. Through TONStarter, new and exceptional projects based on Tokamak Network will be created. This will further expand the Tokamak Network ecosystem and provide even more opportunities for the Tokamak community. Specifically speaking, with the start of Phase 3, once projects such as Tokamak layer DEX are launched, more users will join the community as well as generating more transaction fees. We believe this will bring immense profits for the Tokamak Network community. 

Conversely, through Tokamak Network, TONStarter will allow various projects with validated scalability and usability to be easily launched based on Tokamak Network's solid layer 2. Tokamak Network will give its complete technical and business support to every project launching through TONStarter. In short, TONStarter and Tokamak Network have a symbiotic and complementary relationship.

# TOS Token

TOS token is TONStarter's governance token that manages the TONStarter ecosystem and governance in a fair and decentralized manner. TOS token holders have a say in the decision of every major agenda and in setting TONStarter ecosystem's development direction. Moreover, TOS token will improve the TONStarter community and has many functions that allow sharing of the profit generated through the TONStarter ecosystem. Specifically speaking, TOS token has the following functions and authority. 

1. **Staking to Get sTOS**

    Users can get Staked TOS (sTOS) by staking TOS tokens and setting up a lockup period. More details will be provided in the sTOS section.

2. **Access to Starter Projects**

    TOS holders will be given access to make initial investments in promising TONStarter projects with sTOS. 

3. **Governance**

    TOS holders can participate in TONStarter's decision-making and make suggestions for the community and vote for them with sTOS.

4. **Liquidity Mining**

    TOS holders can participate in TOS liquidity mining to gain additional rewards.

5. **Share Profits**

    TOS holders share all profits generated from TONStarter through sTOS

## sTOS

Staked TOS(sTOS) token can only be issued through the staking of TOS token. sTOS token is required to obtain the rights for decision-making or sharing additional profit made from the TONStarter platform.

To obtain the sTOS token, on top of staking the TOS token, you need to set up a lockup period. The length of the lockup period determines the additional amount of sTOS token received proportionate to the amount of staked TOS token. In other words, in order to obtain more sTOS tokens, more TOS tokens need to be staked for a longer period of time. The purpose of such design is to ensure that more rights and rewards are given to members who have provided relatively more support and contribution to the TONStarter community.

The main functions of sTOS token are as follows.

- Investment rights in promising starter projects on the TONStarter platform
- TONStarter platform profit share
- Listing rights on starter projects
- TONStarter governance participation rights


### Tier

There are a total of 4 tiers in TONStarter which are determined by the amount of TOS tokens, sTOS tokens, and TON tokens held. Each tier is provided with different investment opportunities. While this differs by the project's funding method, typically, the higher the tier, the earlier the investment opportunity is given and the larger the amount of investment. 

There is tier 1 ~ tier 4 and the highest tier, tier 4, requires a minimum amount of TON token unlike other tiers. Therefore, in order to become tier 4 you must satisfy the minimum requirement for all three tokens : TOS, sTOS, and TON. 

Specific information on the minimum amount of tokens required, and the benefits and privileges for each tier will be **released later**.

## TONStarter Governance

TONStarter governance, or TONStarter DAO, is an organization for the constructive development of the TOS ecosystem. It has the final decision-making authority for all of TONStarter's agendas. To ensure more rights to community members that provided the most contribution and commitment to TONStarter, only those who hold sTOS token can participate in TONStarter governance. 

Any member of the TONStarter community can participate in decision-making through TONStarter governance. This will allow everyone to operate and develop TONStarter in a  democratic and decentralized manner. Early on in the launch of TONStarter governance, it will primarily address issues such as listing eligibility of starter projects. However with new agendas proposed by governance, one can obtain diverse rights and responsibilities. In short, TONStarter governance allows all community members to become actual holders of TONStarter. In this respect, building healthy governance is critical, and this can only be achieved through the active contribution of the TONStarter community.

# 2. Token Metrics

## Token Allocation

The token matrix and distribution plan for the TONSarter token (TOS) are as follows. 

### TOS Distribution Plan and Token Supply Schedule

**Token Distribution Overview**

Total Supply:  100,000,000 TOS

Initial Circulating Supply: 1,605,000 TOS  

Initial Token Distribution Date: 2021.08.02


![Token Allocation](https://i.imgur.com/wALLsQI.png)

<br>

**[Token Distribution Plan Overview]**

1) **DAO Token Distribution Schedule**

Tokens that will be utilized in the TONStarter DAO will not be circulated until the DAO is created. The DAO tokens will be distributed after the creation of the DAO and upon the proposal and voting of the DAO. (25%of Total Supply)

2) **Liquidity Token Distribution Schedule**

The tokens allocated to liquidity are used to supply liquidity to DEX.  10% on the day of initial token distribution, remaining tokens distributed evenly over 12 months(20% of Total Supply)

3) **Marketing Token Distribution Schedule**

The tokens allocated to marketing are used for TONStarter's marketing activities. 0.7% on the day of initial token distribution, remaining tokens distributed evenly over 36 months(15% of Total Supply)

4) **Initial Contributor Token Distribution Schedule**

The tokens assigned to the Initial Contributor are used for project support activities funded through Starter. 0% on the day of initial token distribution, remaining tokens distributed evenly over 36months(15% of Total Supply)

5) **Liquidity Mining Token Distribution Schedule**

The tokens assigned to the Liquidity Mining are issued for 3years, and TOS is issued per block.(20% of Total Supply)

6) **Airdrop Token Distribution Schedule**

The tokens assigned to airdrop are used for those who participated in the Tokamak Network ecosystem. 10% on the day of initial token distribution, remaining tokens distributed evenly over 24months. The airdrop quantity distributed every month is provided to new participants in the Tokamak Network ecosystem and paid monthly. If no one is eligible, the quantity will be carried over to next month.(5% of Total Supply)

### TOS Unlock and Total Circulating Supply Schedule

Disclaimer: The following schedule represents the total circulating supply of TOS. The circulating supply includes liquidity, liquidity mining, Initial Contributor and marketing tokens.  

**※ Unlock and Total Circulating Supply Schedule**

- Date : Unlocked Amount ⎜ Total Circulating Supply
- D-day Intial Token Distribution :1,605,000 ⎜1,605,000
- D+1month : 566,211⎜2,171,211
- D+2months : 3,167,461⎜5,338,671
- D+3months : 3,149,196⎜8,487,867
- D+4months : 3,167,460⎜11,655,328
- D+5months : 3,149,196⎜14,804,524
- D+6months : 3,167,460⎜17,971,984
- D+7months : 3,167,460⎜21,139,445
- D+8months : 3,112,664⎜24,252,109
- D+9months : 3,167,460⎜27,419,569
- D+10months :3,149,196⎜30,568,765
- D+11months : 3,167,460⎜33,736,226
- D+12months : 3,149,196⎜36,885,422
- D+13months : 3,167,460⎜40,052,882
- D+14months : 1,584,127⎜41,637,009
- D+15months : 1,565,863⎜43,202,872
- D+16months : 1,584,127⎜44,787,000
- D+17months : 1,565,863⎜46,352,863
- D+18months : 1,584,127⎜47,936,990
- D+19months : 1,584,127⎜49,521,117
- D+20months : 1,529,331⎜51,050,449
- D+21months : 1,584,127⎜52,634,576
- D+22months : 1,565,863⎜54,200,439
- D+23months : 1,584,127⎜55,784,567
- D+24months : 1,565,863⎜57,350,429
- D+25months : 1,584,127⎜58,934,557
- D+26months : 1,396,627⎜60,331,184
- D+27months : 1,378,363⎜61,709,547
- D+28months : 1,396,627⎜63,106,175
- D+29months : 1,378,363⎜64,484,538
- D+30months : 1,396,627⎜65,881,165
- D+31months : 1,396,627⎜67,277,792
- D+32months : 1,360,096⎜68,637,888
- D+33months : 1,396,627⎜70,034,516
- D+34months : 1,378,363⎜71,412,878
- D+35months : 1,396,627⎜72,809,506
- D+36months : 1,378,363⎜74,187,869

# 3. Roadmap

### **Phase 1: TOS Liquidity Mining Launch**

Liquidity mining of TOS, the TONStarter governance token, will be launched. Initially, TON staking will be supported.

### **Phase 2: TOS staking, LP staking**

TOS staking, which plays a pivotal role in the TONStarter ecosystem will be available. We also plan to support various LP(Uniswap V3) staking to develop a healthy TOS ecosystem.

### **Phase 3: Project Starter Open, TONStarter Governance Launch**

Starter, which offers project sales, will be launched. Starter menu offers project teams various opportunities of raising funds in a decentralized manner. TONStarter governance will be launched to build a decentralized ecosystem based on TOS staking.

### **Phase 4: Tokamak Network Layer 2 Integration**

TONStarter will integrate Tokamak Network Layer 2. Users will be able to enjoy the fast and economic TONStarter platform. 

# 4. Conclusion

TONStart is a fair, decentralized IDO platform for layer 2 projects launching in a timely manner  as the implementation of layer 2 is expected. The success of layer 2 depends on how many promising and innovative projects are built on it. This means that we need to pay attention to funding and building of layer 2 based projects. 

TONStarter will help competent teams successfully launch innovative projects based on layer 2, making the TONStarter and Tokamak Network the center of the layer 2 ecosystem. In addition, the TONStarter community will continue to make efforts to create a positive impact on the entire layer 2 ecosystem, which will enable layer 2 to become the center of the entire blockchain ecosystem.

Welcome all to this great journey, TONStarter community!

<br />

# Appendix: Contracts

The core contracts constructing the TONStarter platform is designed and implemented as follows.



## TOS : Platform Token

The governance token of TONStarter is TOS, an ERC20 token which inherits the ERC20 interface. It applies [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) that enables permit via signature.

By supporting the `permit` function, both approval and retrieval tasks can be executed through a single transaction. This feature can be used via signature approval interface of [EIP-712](https://eips.ethereum.org/EIPS/eip-712). 

DOMAIN_SEPARATOR is defined according to EIP-712.

```jsx
	uint256 chainId;
        assembly {
            chainId := chainid()
        }

        DOMAIN_SEPARATOR = keccak256(
                abi.encode(
                    // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)')
                    0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
                    keccak256(bytes(name_)),
                    keccak256(bytes(version_)),
                    chainId,
                    address(this)
                )
            );
```

In addition to ERC20 ABI, the following interface has been added.

```jsx
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.6;

interface ITOS {
    /// @dev Issue a token.
    /// @param to  who takes the issue
    /// @param amount the amount to issue
    function mint(address to, uint256 amount) external returns (bool);

    // @dev burn a token.
    /// @param from Whose tokens are burned
    /// @param amount the amount to burn
    function burn(address from, uint256 amount) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);

    function nonces(address owner) external view returns (uint256);

    /// @dev Authorizes the owner's token to be used by the spender as much as the value.
    /// @dev The signature must have the owner's signature.
    /// @param owner the token's owner
    /// @param spender the account that spend owner's token
    /// @param value the amount to be approve to spend
    /// @param deadline the deadline that valid the owner's signature
    /// @param v the owner's signature - v
    /// @param r the owner's signature - r
    /// @param s the owner's signature - s
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /// @dev verify the signature
    /// @param owner the token's owner
    /// @param spender the account that spend owner's token
    /// @param value the amount to be approve to spend
    /// @param deadline the deadline that valid the owner's signature
    /// @param _nounce the _nounce
    /// @param sigR the owner's signature - r
    /// @param sigS the owner's signature - s
    /// @param sigV the owner's signature - v
    function verify(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint256 _nounce,
        bytes32 sigR,
        bytes32 sigS,
        uint8 sigV
    ) external view returns (bool);

    /// @dev the hash of Permit
    /// @param owner the token's owner
    /// @param spender the account that spend owner's token
    /// @param value the amount to be approve to spend
    /// @param deadline the deadline that valid the owner's signature
    /// @param _nounce the _nounce
    function hashPermit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint256 _nounce
    ) external view returns (bytes32);
}
```

---

## Platform configuration

TONStarter platform consists of platform management, platform DAO, vault contracts, project management, and project DAO.

![Platform Configuration](https://i.imgur.com/GcIxvr3.png)


---

### PART I. Platform Management

Platform management functions can be controlled by platform DAO, and managers will be replaced by platform DAO in the future. 

![Platform Management](https://i.imgur.com/GDMtt3D.png)


The platform management contract is consisted of storage, logic, and proxy to enable updates. Users run functions through proxy. 

---

### (1) Entry Point of Platform : Stake1Proxy

Manager(DAO) can create vaults(`createVault`) via Stake1Proxy and stake contract(`createStakeContract`). It can also support new type of stake contract by configuring factory address of `StakeType` in `StakeFactory`.

Functions that can be called by any user are as follows: `vaultsOfPhase , stakeContractsOfVault , closeSale, tokamakStaking, tokamakRequestUnStaking, tokamakRequestUnStakingAll , tokamakProcessUnStaking, exchangeWTONtoTOS`

- `vaultsOfPhase` : It returns list of vault addresses at target phase.
- `stakeContractsOfVault` : It returns the list of all stake contract addresses in the target vault.
- `closeSale` : When the sale period of stake control is over and the staking period begins, closeSale function is called to calculate the reward value assigned to each stake contract. If this function is not called, features such as reward billing, withdrawal, etc. will not be available.
- `tokamakStaking` : It stakes the staked funds in TON staking contract to the Tokamak layer 2.
- `tokamakRequestUnStaking`, tokamakRequestUnStakingAll : TON staking contract requests the withdrawal of funds staked in Tokamak layer 2.
- `tokamakProcessUnStaking` : It requests to withdraw staked funds from Tokamak layer 2.
- `tokamakProcessUnStaking` : Funds requested to be withdrawn from Tokamak layer 2 can be withdrawn through this function after the withdrawal delay block has passed.
- `exchangeWTONtoTOS` : Except for staked funds, additional TON reward emitted through TON staking can be swapped to TOS through UniswapV3.

 

`Stake1Logic` interface executed by `Stake1Proxy` is as follows.

```jsx
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.6;

interface IStake1Logic {
    /// Set initial variables
    /// @param _tos  TOS token address
    /// @param _stakeRegistry the registry address
    /// @param _stakeFactory the StakeFactory address
    /// @param _stakeVaultFactory the StakeVaultFactory address
    /// @param _ton  TON address in Tokamak
    /// @param _wton WTON address in Tokamak
    /// @param _depositManager DepositManager address in Tokamak
    /// @param _seigManager SeigManager address in Tokamak
    function setStore(
        address _tos,
        address _stakeRegistry,
        address _stakeFactory,
        address _stakeVaultFactory,
        address _ton,
        address _wton,
        address _depositManager,
        address _seigManager
    ) external;

    /// @dev Set factory address by StakeType
    /// @param _stakeType the stake type , 0:TON, 1: Simple, 2: UniswapV3LP
    /// @param _factory the factory address
    function setFactoryByStakeType(uint256 _stakeType, address _factory)
        external;

    /// @dev create vault
    /// @param _paytoken the token used for staking by user
    /// @param _cap  allocated reward amount
    /// @param _saleStartBlock  the start block that can stake by user
    /// @param _stakeStartBlock the start block that end staking by user and start that can claim reward by user
    /// @param _phase  phase of TOS platform
    /// @param _vaultName  vault's name's hash
    /// @param _stakeType  stakeContract's type, if 0, StakeTON, else if 1 , StakeSimple , else if 2, StakeDefi
    /// @param _defiAddr  extra defi address , default is zero address
    function createVault(
        address _paytoken,
        uint256 _cap,
        uint256 _saleStartBlock,
        uint256 _stakeStartBlock,
        uint256 _phase,
        bytes32 _vaultName,
        uint256 _stakeType,
        address _defiAddr
    ) external;

    /// @dev create stake contract in vault
    /// @param _phase the phase of TOS platform
    /// @param _vault  vault's address
    /// @param token  the reward token's address
    /// @param paytoken  the token used for staking by user
    /// @param periodBlock  the period that generate reward
    /// @param _name  the stake contract's name
    function createStakeContract(
        uint256 _phase,
        address _vault,
        address token,
        address paytoken,
        uint256 periodBlock,
        string memory _name
    ) external;

    /// @dev create stake contract in vault
    /// @param _phase phase of TOS platform
    /// @param _vaultName vault's name's hash
    /// @param _vault vault's address
    function addVault(
        uint256 _phase,
        bytes32 _vaultName,
        address _vault
    ) external;

    /// @dev end to staking by user
    /// @param _vault vault's address
    function closeSale(address _vault) external;

    /// @dev list of stakeContracts in vault
    /// @param _vault vault's address
    function stakeContractsOfVault(address _vault)
        external
        view
        returns (address[] memory);

    /// @dev list of vaults in _phase
    /// @param _phase the phase number
    function vaultsOfPhase(uint256 _phase)
        external
        view
        returns (address[] memory);

    /// @dev stake in tokamak's layer2
    /// @param _stakeContract the stakeContract's address
    /// @param _layer2 the layer2 address in Tokamak
    /// @param stakeAmount the amount that stake to layer2
    function tokamakStaking(
        address _stakeContract,
        address _layer2,
        uint256 stakeAmount
    ) external;

    /// @dev Requests unstaking in tokamak's layer2
    /// @param _stakeContract the stakeContract's address
    /// @param _layer2 the layer2 address in Tokamak
    /// @param amount the amount of unstaking
    function tokamakRequestUnStaking(
        address _stakeContract,
        address _layer2,
        uint256 amount
    ) external;

    /// @dev Requests unstaking the amount of all  in tokamak's layer2
    /// @param _stakeContract the stakeContract's address
    /// @param _layer2 the layer2 address in Tokamak
    function tokamakRequestUnStakingAll(address _stakeContract, address _layer2)
        external;

    /// @dev Processes unstaking the requested unstaking amount in tokamak's layer2
    /// @param _stakeContract the stakeContract's address
    /// @param _layer2 the layer2 address in Tokamak
    function tokamakProcessUnStaking(address _stakeContract, address _layer2)
        external;

    /// @dev Swap TON to TOS using uniswap v3
    /// @dev this function used in StakeTON ( stakeType=0 )
    /// @param _stakeContract the stakeContract's address
    /// @param amountIn the input amount
    /// @param amountOutMinimum the minimun output amount
    /// @param deadline deadline
    /// @param sqrtPriceLimitX96 sqrtPriceLimitX96
    /// @param _type the function type, if 0, use exactInputSingle function, else if, use exactInput function
    function exchangeWTONtoTOS(
        address _stakeContract,
        uint256 amountIn,
        uint256 amountOutMinimum,
        uint256 deadline,
        uint160 sqrtPriceLimitX96,
        uint256 _type
    ) external returns (uint256 amountOut);
}
```

---

### (2) Platform : StakeRegistry

The created vault and stake contracts are registered in StakeRegistry, providing function `phasesAll`, which allows to get the vault contracts registered in a particular phase, and function `stakeContractsOfVaultAll`, which allows to get the list of stake contract addresses in a particular vault. 

`StakeRegistry` also manages the address related to the Tokamak.

`DefiInfo` struct also supports an interface that can manage external addresses such as UniswapV3, adding information about external DeFi projects that can be linked later for use in new stake contracts. 

Currently, `defiInfo` has Uniswap V3 router information with "UNISWAP_V3" as the name.

```jsx
    struct DefiInfo {
        string name;
        address router;
        address ext1;
        address ext2;
        uint256 fee;
        address routerV2;
    }

    /// Defi Info
    mapping(bytes32 => LibTokenStake1.DefiInfo) public defiInfo;

    /// @dev Add information related to Defi
    /// @param _name name . ex) UNISWAP_V3
    /// @param _router entry point of defi
    /// @param _ex1  additional variable . ex) positionManagerAddress in Uniswap V3
    /// @param _ex2  additional variable . ex) WETH Address in Uniswap V3
    /// @param _fee  fee
    /// @param _routerV2 In case of uniswap, router address of uniswapV2
    function addDefiInfo(
        string calldata _name,
        address _router,
        address _ex1,
        address _ex2,
        uint256 _fee,
        address _routerV2
    ) external;
```

---

### (3) Vault Factory : StakeVaultFactory 

Vault is also defined as upgradable contract with storage, logic, proxy. `Stake1Vault` is created from `StakeVaultFactory`

To create vault from `StakeVaultFactory`, it must create `vaultLogics` first which is `Stake1Vault`. After setting through function `setVaultLogicByPhase`, it can create vault. However, vault used in phase 1 is `Stake1Vault`, so it must be provided as parameter of constructor of `StakeVaultFactory`. 

Therefore, when new vaults are created from `StakeVaultFactory`, only vault storage and proxy are newly created and it uses existing logic. 

Since vaultLogics variable is `mapping` type, other logics can be added to it so that configure various type of vaults in each phase.

```jsx
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;

import "../interfaces/IStakeVaultFactory.sol";
import {StakeVaultProxy} from "../stake/StakeVaultProxy.sol";
import "../common/AccessibleCommon.sol";

/// @title A factory that creates a vault that hold reward
contract StakeVaultFactory is AccessibleCommon, IStakeVaultFactory {

    mapping(uint256 => address) public vaultLogics;

    modifier nonZero(address _addr) {
        require(_addr != address(0), "StakeVaultFactory: zero");
        _;
    }

    /// @dev constructor of StakeVaultFactory
    /// @param _stakeVaultLogic the logic address used in StakeVault
    constructor(address _stakeVaultLogic) {
        require(
            _stakeVaultLogic != address(0),
            "StakeVaultFactory: logic zero"
        );
        vaultLogics[1] = _stakeVaultLogic;
    }

    /// @dev Set stakeVaultLogic address by _phase
    /// @param _phase the stake type
    /// @param _logic the vault logic address
    function setVaultLogicByPhase(uint256 _phase, address _logic)
        external
        override
        onlyOwner
        nonZero(_logic)
    {
        vaultLogics[_phase] = _logic;
    }

    /// @dev Create a vault that hold reward, _cap is allocated reward amount.
    /// @param _phase phase number
    /// @param _addr the array of [token, paytoken, _stakefactory, defiAddr]
    /// @param _intInfo array of [_stakeType, _cap, _saleStartBlock, _stakeStartBlock]
    /// @param owner the owner adderess
    /// @return a vault address
    function create(
        uint256 _phase,
        address[4] calldata _addr,
        uint256[4] calldata _intInfo,
        address owner
    ) external override returns (address) {
        require(vaultLogics[_phase] != address(0), "StakeVaultFactory: zero vault logic ");

        address _tos = _addr[0];
        address _paytoken = _addr[1];
        address _stakefactory = _addr[2];
        address _defiAddr = _addr[3];
        uint256 _stakeType = _intInfo[0];
        uint256 _cap = _intInfo[1];
        uint256 _saleStartBlock = _intInfo[2];
        uint256 _stakeStartBlock = _intInfo[3];

        StakeVaultProxy proxy = new StakeVaultProxy(vaultLogics[_phase]);
        require(address(proxy) != address(0), "StakeVaultFactory: proxy zero");

        proxy.initialize(
            _tos,
            _paytoken,
            _cap,
            _saleStartBlock,
            _stakeStartBlock,
            _stakefactory,
            _stakeType,
            _defiAddr
        );

        proxy.grantRole(ADMIN_ROLE, owner);
        proxy.revokeRole(ADMIN_ROLE, address(this));

        return address(proxy);
    }
}
```
`StakeVaultFactory` interface is as follows.

In case of `StakeUniswapV3Factory`, token0 address is inserted to `paytoken` and token1 address is inserted to `defiAddress` so the pair can be known.

```jsx
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.6;

interface IStakeVaultFactory {
    /// @dev Create a vault that hold reward, _cap is allocated reward amount.
    /// @param _addr the array of [token, paytoken, vault, defiAddr]
    /// @param _intInfo array of [_stakeType, _cap, _saleStartBlock, _stakeStartBlock]
    /// @param owner the owner adderess
    /// @return a vault address
    function create(
        address[4] calldata _addr,
        uint256[4] calldata _intInfo,
        address owner
    ) external returns (address);
}
```

---

### (4) Stake Contract Factory : StakeFactory 

Stake contract is created by `StakeFactory`, depending on its parameter `StakeType`, it can create various types of stake contracts.


![](https://i.imgur.com/KKmASVk.png)



If `StakeType` is 0, stake contract for staking TON is created.

If `StakeType` is 1, stake contract for staking ETH or ERC20 is created by `StakeSimpleFactory`. If `StakeType` is 2, stake contract for staking UniswapV3 LP(NFT) is created by `StakeUniswapV3Factory`. 

`StakeFactory` contract manages Factory addresses according to `StakeType`. In order to support stake contract for other types of assets in the future, after creating new type of factory, you must register factory address that maps to the `StakeType` by function `setFactoryByStakeType(uint256 _stakeType, address _factory)` in `StakeFactory`.

```jsx
    /// StakeType - Factory address
    mapping(uint256 => address) public factory;

    /// @dev Set factory address by StakeType
    /// @param _stakeType the stake type , 0:TON, 1: Simple, 2: UniswapV3LP
    /// @param _factory the factory address
    function setFactoryByStakeType(uint256 _stakeType, address _factory)
        external
        override
        onlyOwner
        nonZero(_factory)
    {
        factory[_stakeType] = _factory;
    }
```

When creating a factory for other types of staking contract, the constructor function must contain the interface of the `IStakeContractFactory`.

In case of StakeUniswapV3 contract, `paytoken` must be token0 address and `defiAddress` must be token 1.

```jsx
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.6;

interface IStakeContractFactory {
    /// @dev Create a stake contract that can stake TON.
    /// @param _addr the array of [token, paytoken, vault, defiAddr]
    /// @param _registry  the registry address
    /// @param _intdata the array of [saleStartBlock, startBlock, periodBlocks]
    /// @param owner  owner address
    /// @return contract address
    function create(
        address[4] calldata _addr,
        address _registry,
        uint256[3] calldata _intdata,
        address owner
    ) external returns (address);
}
```

  

Interface of `StakeFactory` is as follows:

```jsx
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.6;

interface IStakeFactory {
    /// @dev Create a stake contract that calls the desired stake factory according to stakeType
    /// @param stakeType if 0, stakeTONFactory, else if 1 , stakeSimpleFactory , else if 2, stakeDefiFactory
    /// @param _addr array of [token, paytoken, vault, _defiAddr]
    /// @param registry  registry address
    /// @param _intdata array of [saleStartBlock, startBlock, periodBlocks]
    /// @return contract address
    function create(
        uint256 stakeType,
        address[4] calldata _addr,
        address registry,
        uint256[3] calldata _intdata
    ) external returns (address);

    /// @dev Set factory address by StakeType
    /// @param _stakeType the stake type , 0:TON, 1: Simple, 2: UniswapV3LP
    /// @param _factory the factory address
    function setFactoryByStakeType(uint256 _stakeType, address _factory) external;

}
```

---

### PART II. Vaults & StakeContracts

The vault and staking contracts consist of a vault providing TOS rewards and a stake contract using that vault. Each stake contract is created and managed depending on `StakeType`. 

---

### (1) Vault : **Stake1Vault**

In the storage of `Stake1Vault`, there are total TOS reward cap and token address(`paytoken`) that can be staked in that vault. Reward amount per block(`blockTotalReward`) is calculated based on start and end block of staking, which is `stakeStartBlock` and `stakeEndBlock`.

If there is no staking from users, reward amount per block will be re-calcualted after closeSale.

`stakeType` currently has 0, 1, 2. 0 means vaults for staking TON, 1 means vaults for staking ETH and ERC20 tokens, and 2 means vaults for UniswapV3 LP staking.

All stake contract addresses using vaults are stored in `stakeAddresses` storage, states of each contract are managed by `stakeInfos` storage.

```jsx
struct StakeInfo {
        string name;
        uint256 startBlock;
        uint256 endBlock;
        uint256 balance;            
        uint256 totalRewardAmount;  
        uint256 claimRewardAmount;  
    }

/// @dev the information of the stake contract
    mapping(address => LibTokenStake1.StakeInfo) public stakeInfos;

```

After `stakeStartBlock`, `closeSale` is called for allocating reward. Here, it records the end block of staking in ascending order for each stake contract, and the total staking amount in each block. It is important to note that the staking period of the staking contract should be recorded in ascending order. Therefore, when you make a vault and make a stake contract for that vault, it should be made in the order of short to long staking period.

```jsx
    /// @dev the end blocks of the stake contracts, which must be in ascending order
    uint256[] public orderedEndBlocks;

    /// @dev the total staked amount stored at orderedEndBlock’s end block time
    mapping(uint256 => uint256) public stakeEndBlockTotal;
```

**The longer you stake, the more rewards you receive.**    

![](https://i.imgur.com/YHaB0mE.png)


It is easy to understand if you look at the figure above that one reward vault is used in multiple stake contracts.

For example, suppose one vault is used by three stake contracts, and each stake contract has a four-month, eight-month, and twelve-month staking period.

The above Period1, Period2, Period3 are all 4 months. Rewards per block in the vault are set at cap/(number of blocks for 12 months) and the amount of rewards allocated for each period is the same.

 

`TotalStakedAmount_PeriodA = TotalStakedAmount_A +  TotalStakedAmount_B + TotalStakedAmount_C`

`TotalStakedAmount_PeriodB = TotalStakedAmount_B + TotalStakedAmount_C`

`TotalStakedAmount_PeriodC = TotalStakedAmount_C`

The reward for user(a) who stakes on stake contract A is calculated as follows.

`Reward_a = StakedAmount_a / TotalStakedAmount_PeriodA * (Number of blocks in Period1) * (Reward per block)` 

The reward for user(b) who stakes on stake contract B is calculated as follows.

`Reward_b = StakedAmount_b / TotalStakedAmount_PeriodA * (Number of blocks in Period1) * (Reward per block) +  StakedAmount_b / TotalStakedAmount_PeriodB * (Number of blocks in Period2) * (Reward per block) `

The reward for user(c) who stakes on stake contract C is calculated as follows.

`Reward_c = StakedAmount_c / TotalStakedAmount_PeriodA * (Number of blocks in Period1) * (Reward per block) +  StakedAmount_c / TotalStakedAmount_PeriodB * (Number of blocks in Period2) * (Reward per block) + StakedAmount_c / TotalStakedAmount_PeriodC * (Number of blocks in Period3) * (Reward per block)`

This means that even with the same vault you can get more rewards by staking in stake contracts with longer periods.

Stkae1Vault Interface is as follows.

```jsx
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.6;
pragma abicoder v2;
import "../libraries/LibTokenStake1.sol";

interface IStake1Vault {

    /// @dev Sets TOS address
    /// @param _tos  TOS address
    function setTOS(address _tos) external;

    /// @dev Change cap of the vault
    /// @param _cap  allocated reward amount
    function changeCap(uint256 _cap) external;

    /// @dev Set Defi Address
    /// @param _defiAddr DeFi related address
    function setDefiAddr(address _defiAddr) external;

    /// @dev If the vault has more money than the reward to give, the owner can withdraw the remaining amount.
    /// @param _amount the amount of withdrawal
    function withdrawReward(uint256 _amount) external;

    /// @dev  Add stake contract
    /// @param _name stakeContract's name
    /// @param stakeContract stakeContract's address
    /// @param periodBlocks the period that give rewards of stakeContract
    function addSubVaultOfStake(
        string memory _name,
        address stakeContract,
        uint256 periodBlocks
    ) external;

    /// @dev  Close the sale that can stake by user
    function closeSale() external;

    /// @dev claim function.
    /// @dev sender is a staking contract.
    /// @dev A function that pays the amount(_amount) to _to by the staking contract.
    /// @dev A function that _to claim the amount(_amount) from the staking contract and gets the TOS in the vault.
    /// @param _to a user that received reward
    /// @param _amount the receiving amount
    /// @return true
    function claim(address _to, uint256 _amount) external returns (bool);

    /// @dev Whether user(to) can receive a reward amount(_amount)
    /// @param _to  a staking contract.
    /// @param _amount the total reward amount of stakeContract
    /// @return true
    function canClaim(address _to, uint256 _amount)
        external
        view
        returns (bool);

    /// @dev Give the infomation of this vault
    /// @return paytoken, cap, saleStartBlock, stakeStartBlock, stakeEndBlock, blockTotalReward, saleClosed
    function infos()
        external
        view
        returns (
            address[2] memory,
            uint256,
            uint256,
            uint256[3] memory,
            uint256,
            bool
        );

    /// @dev Returns Give the TOS balance stored in the vault
    /// @return the balance of TOS in this vault.
    function balanceTOSAvailableAmount() external view returns (uint256);

    /// @dev Give Total reward amount of stakeContract(_account)
    /// @return Total reward amount of stakeContract(_account)
    function totalRewardAmount(address _account)
        external
        view
        returns (uint256);

    /// @dev Give all stakeContracts's addresses in this vault
    /// @return all stakeContracts's addresses
    function stakeAddressesAll() external view returns (address[] memory);

    /// @dev Give the ordered end blocks of stakeContracts in this vault
    /// @return the ordered end blocks
    function orderedEndBlocksAll() external view returns (uint256[] memory);
}
```

---

### (2) Staking TON : StakeTON

You can create contract for staking TON and mining TOS reward. 

Using a vault address with StakeType set to 0, calling function `Stake1Proxy.createStakeContract` creates `StakeTON` contract from `StakeTONFactory`.

![](https://i.imgur.com/7ZaggbG.png)


All stake contracts inherit `Stake1Storage` which is base storage for staking. 

```jsx
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.6;

import "../libraries/LibTokenStake1.sol";

/// @title The base storage of stakeContract
contract Stake1Storage {

    /// @dev reward token : TOS
    address public token;

    /// @dev registry
    address public stakeRegistry;

    /// @dev paytoken is the token that the user stakes. ( if paytoken is ether, paytoken is address(0) )
    address public paytoken;

    /// @dev A vault that holds TOS rewards.
    address public vault;

    /// @dev the start block for sale.
    uint256 public saleStartBlock;

    /// @dev the staking start block, once staking starts, users can no longer apply for staking.
    uint256 public startBlock;

    /// @dev the staking end block.
    uint256 public endBlock;

    /// @dev the total amount claimed
    uint256 public rewardClaimedTotal;

    /// @dev the total staked amount
    uint256 public totalStakedAmount;

    /// @dev information staked by user
    mapping(address => LibTokenStake1.StakedAmount) public userStaked;

    /// @dev total stakers
    uint256 public totalStakers;

    uint256 internal _lock;

    /// @dev flag for pause proxy
    bool public pauseProxy;

    /// @dev extra address storage
    address public defiAddr;

    /// @dev user's staked information
    function getUserStaked(address user)
        external
        view
        returns (
            uint256 amount,
            uint256 claimedBlock,
            uint256 claimedAmount,
            uint256 releasedBlock,
            uint256 releasedAmount,
            uint256 releasedTOSAmount,
            bool released
        )
    {
        return (
            userStaked[user].amount,
            userStaked[user].claimedBlock,
            userStaked[user].claimedAmount,
            userStaked[user].releasedBlock,
            userStaked[user].releasedAmount,
            userStaked[user].releasedTOSAmount,
            userStaked[user].released
        );
    }
}
```

The `StakeTON` contract additionally has storage for Tokamak or UniswapV3.

```jsx
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.6;

import "./Stake1Storage.sol";

/// @title the storage of StakeTONStorage
contract StakeTONStorage is Stake1Storage {
    /// @dev TON address
    address public ton;

    /// @dev WTON address
    address public wton;

    /// @dev SeigManager address
    address public seigManager;

    /// @dev DepositManager address
    address public depositManager;

    /// @dev swapProxy address
    address public swapProxy;

    /// @dev the layer2 address in Tokamak
    address public tokamakLayer2;

    /// @dev the accumulated TON amount staked into tokamak , in wei unit
    uint256 public toTokamak;

    /// @dev the accumulated WTON amount unstaked from tokamak , in ray unit
    uint256 public fromTokamak;

    /// @dev the accumulated WTON amount swapped using uniswap , in ray unit
    uint256 public toUniswapWTON;

    /// @dev the TOS balance in this contract
    uint256 public swappedAmountTOS;

    /// @dev the TON balance in this contract when withdraw at first
    uint256 public finalBalanceTON;

    /// @dev the WTON balance in this contract when withdraw at first
    uint256 public finalBalanceWTON;

    /// @dev defi status
    uint256 public defiStatus;

    /// @dev the number of requesting unstaking to tokamak , when process unstaking, reset zero.
    uint256 public requestNum;

    /// @dev the withdraw flag, when withdraw at first, set true
    bool public withdrawFlag;
}
```

Simple staking interface is as follows. 

```jsx
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.6;
pragma abicoder v2;

import "../libraries/LibTokenStake1.sol";

interface IStakeTON {
    /// @dev Stake amount
    /// @param amount  the amount of staked
    //function stake(uint256 amount) external payable;

    /// @dev Claim for reward
    function claim() external;

    /// @dev withdraw
    function withdraw() external;

    /// @dev Returns the amount that can be rewarded
    /// @param account  the account that claimed reward
    /// @param specificBlock the block that claimed reward
    /// @return reward the reward amount that can be taken
    function canRewardAmount(address account, uint256 specificBlock)
        external
        view
        returns (uint256);
}
```

Note that there is no stake function. First, it calls function `approveAndCall` of TON so that TON can be used in stake contract, and tokens are staked by calling `onApprove` in `StakeTONProxy` contract. By doing so, users can approve and stake in single transaction without having to approve additionally.

- function approveAndCall of TON

```jsx
contract ERC20OnApprove is ERC20 {
  function approveAndCall(address spender, uint256 amount, bytes memory data) public returns (bool) {
    require(approve(spender, amount));
    _callOnApprove(msg.sender, spender, amount, data);
    return true;
  }

  function _callOnApprove(address owner, address spender, uint256 amount, bytes memory data) internal {
    bytes4 onApproveSelector = OnApprove(spender).onApprove.selector;

    require(ERC165Checker._supportsInterface(spender, onApproveSelector),
      "ERC20OnApprove: spender doesn't support onApprove");

    (bool ok, bytes memory res) = spender.call(
      abi.encodeWithSelector(
        onApproveSelector,
        owner,
        spender,
        amount,
        data
      )
    );

    // check if low-level call reverted or not
    require(ok, string(res));

    assembly {
      ok := mload(add(res, 0x20))
    }

    // check if OnApprove.onApprove returns true or false
    require(ok, "ERC20OnApprove: failed to call onApprove");
  }

}
```

- Stake implementation through onApprove in `StakeTONProxy`

```jsx
    /// @dev Approves function
    /// @dev call by WTON
    /// @param owner  who actually calls
    /// @param spender  Who gives permission to use
    /// @param tonAmount  how much will be available
    /// @param data  Amount data to use with users
    function onApprove(
        address owner,
        address spender,
        uint256 tonAmount,
        bytes calldata data
    ) external override returns (bool) {
        (address _spender, uint256 _amount) = _decodeStakeData(data);
        require(
            tonAmount == _amount && spender == _spender,
            "StakeTONProxy: tonAmount != stakingAmount "
        );
        require(
            stakeOnApprove(msg.sender, owner, _spender, _amount),
            "StakeTONProxy: stakeOnApprove fails "
        );
        return true;
    }

    function _decodeStakeData(bytes calldata input)
        internal
        pure
        returns (address spender, uint256 amount)
    {
        (spender, amount) = abi.decode(input, (address, uint256));
    }

    /// @dev stake with WTON
    /// @param from  WTON
    /// @param _owner  who actually calls
    /// @param _spender  Who gives permission to use
    /// @param _amount  how much will be available
    function stakeOnApprove(
        address from,
        address _owner,
        address _spender,
        uint256 _amount
    ) public returns (bool) {
        require(
            (paytoken == from && _amount > 0 && _spender == address(this)),
            "StakeTONProxy: stakeOnApprove init fail"
        );
        require(
            block.number >= saleStartBlock && block.number < startBlock,
            "StakeTONProxy: period not allowed"
        );

        require(
            !IStakeVaultStorage(vault).saleClosed(),
            "StakeTONProxy: end sale"
        );
        require(
            IIERC20(paytoken).balanceOf(_owner) >= _amount,
            "StakeTONProxy: insuffient"
        );

        LibTokenStake1.StakedAmount storage staked = userStaked[_owner];
        if (staked.amount == 0) totalStakers = totalStakers.add(1);

        staked.amount = staked.amount.add(_amount);
        totalStakedAmount = totalStakedAmount.add(_amount);
        require(
            IIERC20(from).transferFrom(_owner, _spender, _amount),
            "StakeTONProxy: transfer fail"
        );

        emit Staked(_owner, _amount);
        return true;
    }
```

Reward TON from staking TON can be swapped to TOS via UniswapV3. WTON can be swapped to TOS through `exchangeWTONtoTOS`.

Since Uniswap pool is for WTON not TON, reward TON must be changed to WTON first. It can be done with `SwapProxy` as follows. It can get `SwapProxy` contract address from registry.

```jsx
bytes memory data = abi.encode(swapProxy, swapProxy);
uint256 swapTON = _amountIn.sub(_amountWTON).div(10**9);
require(
    ITON(ton).approveAndCall(wton, swapTON, data),
    "TokamakStaker:exchangeWTONtoTOS approveAndCall fail"
);
```

According to number of pairs for swap, it uses different functions for swap in UniswapV3. The `_kind` variable determines which function to use. 

If `_kind` is 0, it swaps WTON for TOS in WTON-TOS pool.

```jsx
            ISwapRouter.ExactInputSingleParams memory params =
                ISwapRouter.ExactInputSingleParams({
                    tokenIn: wton,
                    tokenOut: token,
                    fee: uint24(_fee),
                    recipient: address(this),
                    deadline: _deadline,
                    amountIn: _amountIn,
                    amountOutMinimum: _amountOutMinimum,
                    sqrtPriceLimitX96: _sqrtPriceLimitX96
                });
            amountOut = ISwapRouter(uniswapRouter).exactInputSingle(params);
```

If `_kind` is 1, it uses two pairs for swap. It swaps WTON for TOS using WTON-WETH and WETH-TOS pool.

```jsx
	    ISwapRouter.ExactInputParams memory params =
                ISwapRouter.ExactInputParams({
                    path: abi.encodePacked(
                        wton,
                        uint24(_fee),
                        wethAddress,
                        uint24(_fee),
                        token
                    ),
                    recipient: address(this),
                    amountIn: _amountIn,
                    amountOutMinimum: _amountOutMinimum,
                    deadline: _deadline
                });
            amountOut = ISwapRouter(uniswapRouter).exactInput(params);
```

Implementation of function `exchangeWTONtoTOS` is as follows.

```jsx
    /// @dev exchange holded WTON to TOS using uniswap
    /// @param _amountIn the input amount
    /// @param _amountOutMinimum the minimun output amount
    /// @param _deadline deadline
    /// @param _sqrtPriceLimitX96 sqrtPriceLimitX96
    /// @param _kind the function type, if 0, use exactInputSingle function, else if, use exactInput function
    /// @return amountOut the amount of exchanged out token
    function exchangeWTONtoTOS(
        uint256 _amountIn,
        uint256 _amountOutMinimum,
        uint256 _deadline,
        uint160 _sqrtPriceLimitX96,
        uint256 _kind
    ) external override lock onlyClosed returns (uint256 amountOut) {
        require(block.number <= endBlock, "TokamakStaker: period end");
        require(
            _kind < 2,
            "TokamakStaker: not available kind"
        );
        checkTokamak();

        {
            uint256 _amountWTON = IERC20BASE(wton).balanceOf(address(this));
            uint256 _amountTON = IERC20BASE(ton).balanceOf(address(this));
            uint256 stakeOf = 0;
            if (tokamakLayer2 != address(0)) {
                stakeOf = IISeigManager(seigManager).stakeOf(
                    tokamakLayer2,
                    address(this)
                );
                stakeOf = stakeOf.add(
                    IIDepositManager(depositManager).pendingUnstaked(
                        tokamakLayer2,
                        address(this)
                    )
                );
            }
            uint256 holdAmount = _amountWTON;
            if (_amountTON > 0)
                holdAmount = holdAmount.add(_amountTON.mul(10**9));
            require(
                holdAmount >= _amountIn,
                "TokamakStaker: wton insufficient"
            );

            if (stakeOf > 0) holdAmount = holdAmount.add(stakeOf);

            require(
                holdAmount > totalStakedAmount.mul(10**9) &&
                    holdAmount.sub(totalStakedAmount.mul(10**9)) >= _amountIn,
                "TokamakStaker:insufficient"
            );
            if (_amountWTON < _amountIn) {
                bytes memory data = abi.encode(swapProxy, swapProxy);
                uint256 swapTON = _amountIn.sub(_amountWTON).div(10**9);
                require(
                    ITON(ton).approveAndCall(wton, swapTON, data),
                    "TokamakStaker:exchangeWTONtoTOS approveAndCall fail"
                );
            }
        }

        toUniswapWTON = toUniswapWTON.add(_amountIn);
        (address uniswapRouter, , address wethAddress, uint256 _fee, ) =
            ITokamakRegistry(stakeRegistry).getUniswap();
        require(uniswapRouter != address(0), "TokamakStaker:uniswap zero");
        require(
            IERC20BASE(wton).approve(uniswapRouter, _amountIn),
            "TokamakStaker:can't approve uniswapRouter"
        );

        if (_kind == 0) {
            ISwapRouter.ExactInputSingleParams memory params =
                ISwapRouter.ExactInputSingleParams({
                    tokenIn: wton,
                    tokenOut: token,
                    fee: uint24(_fee),
                    recipient: address(this),
                    deadline: _deadline,
                    amountIn: _amountIn,
                    amountOutMinimum: _amountOutMinimum,
                    sqrtPriceLimitX96: _sqrtPriceLimitX96
                });
            amountOut = ISwapRouter(uniswapRouter).exactInputSingle(params);
        } else if (_kind == 1) {
            ISwapRouter.ExactInputParams memory params =
                ISwapRouter.ExactInputParams({
                    path: abi.encodePacked(
                        wton,
                        uint24(_fee),
                        wethAddress,
                        uint24(_fee),
                        token
                    ),
                    recipient: address(this),
                    amountIn: _amountIn,
                    amountOutMinimum: _amountOutMinimum,
                    deadline: _deadline
                });
            amountOut = ISwapRouter(uniswapRouter).exactInput(params);
        }

        emit ExchangedWTONtoTOS(msg.sender, _amountIn, amountOut);
    }
```

Interface to connect Tokamak and Uniswap is as follows.

```jsx
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.6;

interface ITokamakStaker {
    /// @dev set the tokamak Layer2 address
    /// @param _layer2 new the tokamak Layer2 address
    function setTokamakLayer2(address _layer2) external;

    /// @dev get the addresses yhat used in uniswap interfaces
    /// @return uniswapRouter the address of uniswapV3 Router
    /// @return npm the address of positionManagerAddress
    /// @return ext the address of ext
    /// @return fee the amount of fee
    /// @return uniswapV2Router uniswapV2 router address
    function getUniswapInfo()
        external
        view
        returns (
            address uniswapRouter,
            address npm,
            address ext,
            uint256 fee,
            address uniswapV2Router
        );

    /// @dev Change the TON holded in contract have to WTON, or change WTON to TON.
    /// @param amount the amount to be changed
    /// @param toWTON if it's true, TON->WTON , else WTON->TON
    function swapTONtoWTON(uint256 amount, bool toWTON) external;

    /// @dev  staking the staked TON in layer2 in tokamak
    /// @param _layer2 the layer2 address in tokamak
    /// @param stakeAmount the amount that stake to layer2
    function tokamakStaking(address _layer2, uint256 stakeAmount) external;

    /// @dev  request unstaking the wtonAmount in layer2 in tokamak
    /// @param _layer2 the layer2 address in tokamak
    /// @param wtonAmount the amount requested to unstaking
    function tokamakRequestUnStaking(address _layer2, uint256 wtonAmount)
        external;

    /// @dev  request unstaking the wtonAmount in layer2 in tokamak
    /// @param _layer2 the layer2 address in tokamak
    function tokamakRequestUnStakingAll(address _layer2) external;

    /// @dev process unstaking in layer2 in tokamak
    /// @param _layer2 the layer2 address in tokamak
    function tokamakProcessUnStaking(address _layer2) external;

    /// @dev exchange holded WTON to TOS using uniswap-v3
    /// @param _amountIn the input amount
    /// @param _amountOutMinimum the minimun output amount
    /// @param _deadline deadline
    /// @param _sqrtPriceLimitX96 sqrtPriceLimitX96
    /// @param _kind the function type, if 0, use exactInputSingle function, else if, use exactInput function
    function exchangeWTONtoTOS(
        uint256 _amountIn,
        uint256 _amountOutMinimum,
        uint256 _deadline,
        uint160 _sqrtPriceLimitX96,
        uint256 _kind
    ) external returns (uint256 amountOut);

    /// @dev exchange holded WTON to TOS using uniswap-v2
    /// @param _amountIn the input amount
    /// @param _amountOutMinimum the minimun output amount
    /// @param _deadline deadline
    /// @param _kind the function type, if 0, use exactInputSingle function, else if, use exactInput function
    function exchangeWTONtoTOSv2(
        uint256 _amountIn,
        uint256 _amountOutMinimum,
        uint256 _deadline,
        uint256 _kind
    ) external returns (uint256 amountOut);
}
 

```

---

### (3) Simple Staking : StakeSimple

It is a contract to mine TOS reward through staking ETH or ERC20 tokens.

`StakeSimple` contract can be created from `StakeSimpleFactory` by calling `Stake1Proxy.createStakeContract` with vault address with `StakeType` set to 1.


![](https://i.imgur.com/3gSMi2O.png)


`StakeSimple` contract, like `StakeTON` contract, inherit the default storage(`Stake1Storage`) and support the default staking interface.

```jsx
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.6;

interface IStakeSimple {

    /// @dev Stake amount
    /// @param amount  the amount of staked
    function stake(uint256 amount) external payable;

    /// @dev withdraw
    function withdraw() external;

    /// @dev Claim for reward
    function claim() external;

    /// @dev Returns the amount that can be rewarded
    /// @param account  the account that claimed reward
    /// @param specificBlock the block that claimed reward
    /// @return reward the reward amount that can be taken
    function canRewardAmount(address account, uint256 specificBlock)
        external
        view
        returns (uint256);
}
```

### **stake**

For staking ETH, you can both use `stake` function and `sendTransaction`.

```jsx
    /// @dev receive ether
    /// @dev call stake function with msg.value
    receive() external payable {
        stake(msg.value);
    }
```

### **claim**

Reward amount of target block is calculated through `canRewardAmount` function.

In Stake1Vault, based on the staking end block list(`orderedEndBlocksAll`) and the total amount of staking for each block(`stakeEndBlockTotal`), it can calculate the amount of block rewards within the period according to the ratio of your staking amount for each block period. The rewards that you can receive from a specific block are calculated by adding all the rewards you haven't received yet.

```jsx
    /// @dev Returns the amount that can be rewarded
    /// @param account  the account that claimed reward
    /// @param specificBlock the block that claimed reward
    /// @return reward the reward amount that can be taken
    function canRewardAmount(address account, uint256 specificBlock)
        public
        view
        override
        returns (uint256)
    {
        uint256 reward = 0;
        if (specificBlock > endBlock) specificBlock = endBlock;

        if (
            specificBlock < startBlock ||
            userStaked[account].amount == 0 ||
            userStaked[account].claimedBlock > endBlock ||
            userStaked[account].claimedBlock > specificBlock
        ) {
            reward = 0;
        } else {
            uint256 startR = startBlock;
            uint256 endR = endBlock;
            if (startR < userStaked[account].claimedBlock)
                startR = userStaked[account].claimedBlock;
            if (specificBlock < endR) endR = specificBlock;

            uint256[] memory orderedEndBlocks =
                IIStake1Vault(vault).orderedEndBlocksAll();

            if (orderedEndBlocks.length > 0) {
                uint256 _end = 0;
                uint256 _start = startR;
                uint256 _total = 0;
                uint256 blockTotalReward = 0;
                blockTotalReward = IIStake1Vault(vault).blockTotalReward();

                address user = account;
                uint256 amount = userStaked[user].amount;

                for (uint256 i = 0; i < orderedEndBlocks.length; i++) {
                    _end = orderedEndBlocks[i];
                    _total = IIStake1Vault(vault).stakeEndBlockTotal(_end);

                    if (_start > _end) {} else if (endR <= _end) {
                        if (_total > 0) {
                            uint256 _period1 = endR.sub(startR);
                            reward = reward.add(
                                blockTotalReward.mul(_period1).mul(amount).div(
                                    _total
                                )
                            );
                        }
                        break;
                    } else {
                        if (_total > 0) {
                            uint256 _period2 = _end.sub(startR);
                            reward = reward.add(
                                blockTotalReward.mul(_period2).mul(amount).div(
                                    _total
                                )
                            );
                        }
                        startR = _end;
                    }
                }
            }
        }
        return reward;
    }
```

---

### (4) UniswapV3 LP Staking : **StakeUniswapV3**

It is contract for mining TOS reward by staking LP token of UniswapV3. In phase 2, using vault address with `StakeType` set to 2, `StakeUniswapV3` contract can be created from `StakeUniswapV3Factory` by calling `Stake1Proxy.createStakeContract` function.

In UniswapV2, LP tokens are defined as fungible ERC 20, but now they are changed to non-fungible liquidity position as a form of ERC721. In StakeUniswapV3, you can mine TOS reward by staking UniswapV3 liquidity position token. 

When users stake liquidity position of UniswapV3 in `StakeUniswapV3`, its ownership is transferred to the contract and after staking it will go back to users.