## Gear 合约开发案例解析 - Staking
<p align="center">
<a href="https://gitpod.io/#https://github.com/gear-dapps/staking">
<img src="https://gitpod.io/button/open-in-gitpod.svg" width="240" alt="GEAR">
</a>
</p>
代码仓库: https://github.com/gear-dapps/staking
本 PPT 链接: https://hackmd.io/@btwiuse/staking
<!--
- https://github.com/gear-dapps/sharded-fungible-token
-->
---
## 什么是 Staking
> Staking is a way of earning rewards for holding certain cryptocurrencies.
-- [Coinbase: What is staking](https://www.coinbase.com/learn/crypto-basics/what-is-staking)
---
## 常见的 Staking 应用: PoS
- Proof of Stake (PoS) Consensus Mechanisms: In PoS-based blockchains, validators are chosen to create new blocks based on the number of tokens they hold and are willing to 'stake' as collateral. This use case is a little different, as it's not typically something a smart contract developer would implement, but is rather part of the underlying blockchain protocol.
---
## 常见的 Staking 应用: DeFi - Lending
- Decentralized Finance (DeFi): This is the most common use of staking. Users stake their tokens in smart contracts to earn interest. The staked tokens are used by the protocol to lend to other users, and the interest earned is distributed among the stakers. Examples of DeFi protocols that use staking are Compound and Aave.
---
## 常见的 Staking 应用: DeFi - Liquidity Provision
- Liquidity Provision: In decentralized exchanges like Uniswap or SushiSwap, users can stake their tokens in liquidity pools. This helps maintain a balance of tokens in the exchange and provide liquidity for traders. In return, stakers earn fees from the trading activity.
---

---

---

---
在时间段 {a, b} 内,某用户的质押收益公式:
$$
r(a, b) = R\sum_{t=a}^{t=b} \frac {l(t)} {L(t)}
$$
其中:
- r(a, b): 用户在时间段 {a, b} 内获得的奖励;
- R: 单位时间内产生的奖励;
- L(t): t 时刻的质押总量;
- l(t): t 时刻用户的质押量;
---
此公式在链下进行计算并不困难
但合约中,计算只能在用户与合约交互时发生
- 添加质押 Stake
- 解除质押 Unstake / Withdraw
- 提取收益 Claim
需要避免 For 循环 / 保持 O(1) 空间复杂度
---
Synthetix Staking Rewards Algorithm
<p align="center">
<a href="https://synthetix.io">
<img src="https://hackmd.io/_uploads/SkhIP4v8n.png" width="240" alt="Synthetix">
</a>
</p>
<small>
> 第一个基于链上计算奖励的流动性挖矿计划是 SNX 的 sETH Uniswap 池的[激励计划](https://sips.synthetix.io/sips/sip-31)。这是由 Anton Bukov 于2019年编写的 [Unipool](https://github.com/k06a/Unipool/commit/e4bdb0a978fd498a1480e3d1bc4b4c1682c74c12#diff-0d1e350796b5338e3c326be95f9a9ad147d4695746306a50a9fdccf8dbbfd708) sETH 合约实现的。我认为这是有史以来最有影响力的智能合约之一。
-- [Liquidity Mining on Uniswap v3](https://www.paradigm.xyz/2021/05/liquidity-mining-on-uniswap-v3)
</small>
---
假设该用户质押量 l(t) 在时间段 {a, b} 内为常量 k, 则:
$$
r(a, b) = R\sum_{t=a}^{t=b} \frac {l(t)} {L(t)} = Rk\sum_{t=a}^{t=b} \frac {1} {L(t)}
$$
---
公式右侧的 Sum 可进一步化简:
$$
\sum_{t=a}^{t=b} \frac {1} {L(t)} = \frac {1} {L(a)} + \frac {1} {L(a + 1)} + ... + \frac {1} {L(b)} =
$$
<small>
$$
\frac {1} {L(0)} + \frac {1} {L(1)} + ... + \frac {1} {L(b)} -
(\frac {1} {L(0)} + \frac {1} {L(1)} + ... + \frac {1} {L(a - 1)}) =
$$
</small>
$$
\sum_{t=0}^{t=b} \frac {1} {L(t)} - \sum_{t=0}^{t=a-1} \frac {1} {L(t)}
$$
---
因此,在用户质押量 k=l(t) 不变的前提下,用户在时间段 {a, b} 内总收益为:
$$
Rk\sum_{t=a}^{t=b} \frac {1} {L(t)} = Rk(\sum_{t=0}^{t=b} \frac {1} {L(t)} - \sum_{t=0}^{t=a-1} \frac {1} {L(t)})
$$
---
公式可用代码表示
```rust
(staker.balance * self.tokens_per_stake) / DECIMALS_FACTOR + staker.reward_allowed - staker.reward_debt - staker.distributed
```
<p align="center">
<a href="https://gitpod.io/#https://github.com/gear-dapps/fungible-token">
<img src="https://gitpod.io/button/open-in-gitpod.svg" width="240" alt="GEAR">
</a>
</p>
---
## build
```
$ make init
$ make build
```
## download
```
./target/wasm32-unknown-unknown/release/staking_state.meta.wasm
./target/wasm32-unknown-unknown/release/staking.opt.wasm
./staking.meta.txt
```
## deploy
https://idea.gear-tech.io/
---
初始化合约

---
发送消息与合约交互

---
合约初始状态

---
添加第一笔质押后的合约状态

---
## State
./io/src/contract.rs
```
pub struct IoStaking {
pub owner: ActorId,
pub staking_token_address: ActorId,
pub reward_token_address: ActorId,
pub tokens_per_stake: u128,
pub total_staked: u128,
pub distribution_time: u64,
pub produced_time: u64,
pub reward_total: u128,
pub all_produced: u128,
pub reward_produced: u128,
pub stakers: Vec<(ActorId, Staker)>,
pub transactions: BTreeMap<ActorId, Transaction<StakingAction>>,
pub current_tid: TransactionId,
}
```
---
## Metadata
./io/src/lib.rs
```
pub struct StakingMetadata;
impl Metadata for StakingMetadata {
type Init = In<InitStaking>;
type Handle = InOut<StakingAction, Result<StakingEvent, Error>>;
...
}
```
---
## Init
./io/src/lib.rs
```
pub struct InitStaking {
pub staking_token_address: ActorId,
pub reward_token_address: ActorId,
pub distribution_time: u64,
pub reward_total: u128,
}
```
---
./src/contract.rs
```
#[no_mangle]
extern "C" fn init() {
let mut staking = Staking {
owner: msg::source(),
..Default::default()
};
let result = staking.update_staking(config);
/* omitted ... */
unsafe { STAKING = Some(staking) };
}
```
---
## Handle
./io/src/lib.rs
```
pub enum StakingAction {
Stake(u128),
Withdraw(u128),
UpdateStaking(InitStaking),
GetReward,
}
```
---
./io/src/lib.rs
```
pub enum StakingEvent {
StakeAccepted(u128),
Updated,
Reward(u128),
Withdrawn(u128),
}
```
---
./src/contract.rs
```
#[no_mangle]
extern "C" fn handle() {
...
let result = match action {
StakingAction::Stake(amount) => {
let result = staking.stake(amount).await;
...
}
StakingAction::Withdraw(amount) => {
let result = staking.withdraw(amount).await;
...
}
StakingAction::UpdateStaking(config) => {
let result = staking.update_staking(config);
...
}
StakingAction::GetReward => {
let result = staking.send_reward().await;
...
}
};
...
}
```
---
## Contract
./src/contract.rs
```
impl FungibleToken {
...
fn update_reward(&mut self) { ... }
fn update_staking(&mut self, config: InitStaking) -> Result<StakingEvent, Error> { ... }
async fn stake(&mut self, amount: u128) -> Result<StakingEvent, Error> { ... }
async fn send_reward(&mut self) -> Result<StakingEvent, Error> { ... }
async fn withdraw(&mut self, amount: u128) -> Result<StakingEvent, Error> { ... }
}
```
---
## 课后作业
编译 https://github.com/gear-dapps/staking
并在 [Gear IDEA](https://idea.gear-tech.io/) 上部署你的合约,并质押一定数量的代币
提交合约访问链接
作业提交/课后提问频道: https://t.me/Gear_CN
---
## 参考链接
- https://wiki.gear-tech.io/docs/examples/staking/
- https://www.paradigm.xyz/2021/05/liquidity-mining-on-uniswap-v3
- https://www.youtube.com/watch?v=iNZWMj4USUM
- https://github.com/stakewithus/notes
- https://sips.synthetix.io/sips/sip-31/
本 PPT 链接: https://hackmd.io/@btwiuse/staking
{"metaMigratedAt":"2023-06-18T06:06:59.381Z","metaMigratedFrom":"YAML","title":"Untitled","breaks":true,"slideOptions":"{\"theme\":\"moon\",\"spotlight\":{\"enabled\":false}}","contributors":"[{\"id\":\"94262fbf-81ae-4ed7-933c-561a41bd977a\",\"add\":13554,\"del\":6142}]"}