Sishiswapのマスターシェフを理解する
41650 ワード
👋 導入
Web 2からWEB 3の開発に移行する私の目標の一部として、私はゼロからDefiアプリケーションを構築し、学習して、粘り強さを実践しています.
Web devからWeb 3.0への私の旅-パート1
マークコップ・ 月30日・ 6分読む
#web3
#blockchain
#smartcontracts
#devjournal
私はstakingの実装から始めて、私が最近使用していたdefiからスマートリファレンスを参照として使用します.
大部分のstaking契約がからのコピーであるとわかりますSushiSwap's MasterChef contract .
契約書を読んでいる間、私は実際に計算された方法を理解することができました.
function pendingSushi(uint256 _pid, address _user)
external
view
returns (uint256)
{
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][_user];
uint256 accSushiPerShare = pool.accSushiPerShare;
uint256 lpSupply = pool.lpToken.balanceOf(address(this));
if (block.number > pool.lastRewardBlock && lpSupply != 0) {
uint256 multiplier =
getMultiplier(pool.lastRewardBlock, block.number);
uint256 sushiReward =
multiplier.mul(sushiPerBlock).mul(pool.allocPoint).div(
totalAllocPoint
);
accSushiPerShare = accSushiPerShare.add(
sushiReward.mul(1e12).div(lpSupply)
);
}
return user.amount.mul(accSushiPerShare).div(1e12).sub(user.rewardDebt);
}
つまり、トークンが各ブロックごとに鋳造され、プールに参加することによってすべてのステッカーの間で分配されるのを見るのは簡単です.しかし、どのような役割がどのような変数であるかは不明です
accSushiPerShare
and rewardDebt
この計算で遊ぶ.このブログ記事では、このマスターシェフ契約の背後にあるロジックをどのように理解しているかを共有したいと思います.
まず最初にステッカーのための公正な報酬となる自分自身を考え出すことから始めましょう.
🧠 単純報酬シミュレーション
仮定しましょう
RewardsPerBlock = $1
On block 0, Staker A deposits $100
On block 10, Staker B deposits $400
On block 15, Staker A harvests all rewards
On block 25, Staker B harvests all rewards
On block 30, both stakers harvests all rewards.
スティッカーAはブロック0に10ドル、10ブロック後にステッカーBは400ドルを預けます.最初の10ブロックでは、Staker Aは100 %の報酬を持っていました.
From block 0 to 10:
BlocksPassed: 10
BlockRewards = BlocksPassed * RewardsPerBlock
BlockRewards = $10
StakerATokens: $100
TotalTokens: $100
StakerAShare = StakerATokens / TotalTokens
StakerAShare = 1
StakerAAccumulatedRewards = BlockRewards * StakerAShare
StakerAAccumulatedRewards = $10
ブロック10では、スタッカBは400ドルを預けている.現在、ブロック15のステッカーAは、その報酬を収穫しています.
彼らはブロック0から10まで、10から15に100 %の報酬を得た一方、彼らは20 %(1/5)を取得しています
From Block 10 to 15:
BlocksPassed: 5
BlockRewards = BlocksPassed * RewardsPerBlock
BlockRewards = $5
StakerATokens: $100
StakerBTokens: $400
TotalTokens: $500
StakerAShare = StakerATokens / TotalTokens
StakerAShare = 1/5
StakerAAccumulatedRewards = (BlockRewards * StakerAShare) + StakerAAccumulatedRewards
StakerAAccumulatedRewards = $1 + $10
StakerBShare = StakerBTokens / TotalTokens
StakerBShare = 4/5
StakerBAccumulatedRewards = BlockRewards * StakerBShare
StakerBAccumulatedRewards = $4
ステッカーA収穫11ドルと11StakerAAccumulatedRewards
0にリセットします.ステッカーBはこれらの最後の5ブロックのために$ 4を蓄積しました.
その後、さらに多くのブロックを通過し、同様に収穫を決定するB.
From Block 15 to 25:
BlocksPassed: 10
BlockRewards: $10
StakerATokens: $100
StakerBTokens: $400
TotalTokens: $500
StakerAAccumulatedRewards: $2
StakerBAccumulatedRewards: $8 + $4
ステッカーBは、12ドルと12ドルを収穫しますStakerBAccumulatedRewards
0にリセットします.最後に、両方のステッカーは、ブロック30で彼らの報酬を収穫します.
From Block 25 to 30:
BlocksPassed: 5
BlockRewards: $5
StakerATokens: $100
StakerBTokens: $400
TotalTokens: $500
StakerAAccumulatedRewards: $1 + $2
StakerBAccumulatedRewards: $4
ステッカーA収穫3ドルとB収穫4ドル.ステッカーは合計14ドルとB $ 16で収穫しました
📝 実装
このように、それぞれの行動(預金や収穫)のために、我々はすべてのステッカーを通過し、その蓄積報酬を計算する必要がありました.
以下に、この実装について簡単な手順を示します.
< div >
< p >
updateStakersRewards
すべてのステッカーの上でループして、誰かが預金して、彼らの収益を収穫するか、収穫するたびに、彼らの蓄積された報酬を更新する責任がありますp >しかし、私たちがこのループを避けることができるならば、どうですかp >
📐 いくつかの数学操作の適用
私たちがstakerを見るならば、ブロックの各グループの彼らの報酬の合計として報酬
<> P >
クラスをハイライト表示する
StakerARewards =
StakerA0to10Rewards +
StakerA10to15Rewards +
StakerA15to25Rewards +
StakerA25to30Rewards
< div >そして、私たちがブロックNからMまで彼らの報酬を同じ範囲で分配される報酬の間の掛け算と同じくらいの範囲で彼らの報酬を見るならば
<> P >
クラスをハイライト表示する
StakerANtoMRewards = BlockRewardsOnNtoM * StakerAShareOnNtoM
< div >それから、私たちは、ステッカー報酬を報酬と彼らの分け前との間の掛け算の合計として、最後の<br>>までの範囲まで得ます
<> P >
クラスをハイライト表示する
StakerARewards =
(BlockRewardsOn0to10 * StakerAShareOn0to10) +
(BlockRewardsOn10to15 * StakerAShareOn10to15) +
(BlockRewardsOn15to25 * StakerAShareOn15to25) +
(BlockRewardsOn25to30 * StakerAShareOn25to30)
< div >そして、以下の式を使用して、それらのトークンが、プール< br/>内の全トークンによって分割されたトークンとして表現される
<> P >
クラスをハイライト表示する
StakerAShareOnNtoM = StakerATokensOnNtoM / TotalTokensOnNtoM
< div >< br/> < br/>
<> P >
クラスをハイライト表示する
StakerARewards =
(BlockRewardsOn0to10 * StakerATokensOn0to10 / TotalTokensOn0to10) +
(BlockRewardsOn10to15 * StakerATokensOn10to15 / TotalTokensOn10to15) +
(BlockRewardsOn15to25 * StakerATokensOn15to25 / TotalTokensOn15to25) +
(BlockRewardsOn25to30 * StakerATokensOn25to30 / TotalTokensOn25to30)
< div >しかし、この場合、stakerは同じ範囲のトークンを持っていた
<> P >
クラスをハイライト表示する
StakerATokensOn0to10 =
StakerATokensOn10to15 =
StakerATokensOn15to25 =
StakerATokensOn25to30 =
StakerATokens
< div >それから、我々は我々を単純にすることができます
StakerARewards
フォーミュラ・B<> P >
クラスをハイライト表示する
StakerARewards =
(BlockRewardsOn0to10 * StakerATokens / TotalTokensOn0to10) +
(BlockRewardsOn10to15 * StakerATokens / TotalTokensOn10to15) +
(BlockRewardsOn15to25 * StakerATokens / TotalTokensOn15to25) +
(BlockRewardsOn25to30 * StakerATokens / TotalTokensOn25to30)
< div >< p >とパッティング
StakerATokens
証拠に我々はこの< br/>を持っている<> P >
クラスをハイライト表示する
StakerARewards = StakerATokens * (
(BlockRewardsOn0to10 / TotalTokensOn0to10) +
(BlockRewardsOn10to15 / TotalTokensOn10to15) +
(BlockRewardsOn15to25 / TotalTokensOn15to25) +
(BlockRewardsOn25to30 / TotalTokensOn25to30)
)
< div >私たちは、これらの大きな単語を数字で置き換えて、Stacker A < br/>の合計報酬を得ることで、我々のシナリオで動作することを確認することができます
<> P >
クラスをハイライト表示する
StakerARewards = 100 * (
(10 / 100) +
(5 / 500) +
(10 / 500) +
(5 / 500)
)
< div >クラスをハイライト表示する
StakerARewards = 14
< div >< p >は< tt/
staker b < br/>を使いましょう
<> P >
クラスをハイライト表示する
StakerBRewards =
(BlockRewardsOn10to15 * StakerBTokens / TotalTokensOn10to15) +
(BlockRewardsOn15to25 * StakerBTokens / TotalTokensOn15to25) +
(BlockRewardsOn25to30 * StakerBTokens / TotalTokensOn25to30)
< div >クラスをハイライト表示する
StakerBRewards = StakerBTokens * (
(BlockRewardsOn10to15 / TotalTokensOn10to15) +
(BlockRewardsOn15to25 / TotalTokensOn15to25) +
(BlockRewardsOn25to30 / TotalTokensOn25to30)
)
< div >クラスをハイライト表示する
StakerBRewards = 400 * (
(5 / 500) +
(10 / 500) +
(5 / 500)
)
< div >クラスをハイライト表示する
StakerBRewards = 16
< div >現在、両方のスターカーの報酬が前に見たものと一致しているので、両方の報酬計算で再利用できるものをチェックしましょうp >
あなたが見ることができるように、両方のスタッカーは公式の報酬の共通総和
<> P >
クラスをハイライト表示する
(5 / 500) + (10 / 500) + (5 / 500)
< div >Sishiswapの契約コールこの合計
accSushiPerShare
, では、各課を呼びましょうRewardsPerShare
<> P >
クラスをハイライト表示する
RewardsPerShareOn0to10 = (10 / 100)
RewardsPerShareOn10to15 = (5 / 500)
RewardsPerShareOn15to25 = (10 / 500)
RewardsPerShareOn25to30 = (5 / 500)
< div >の代わりに
accSushiPerShare
我々は彼らの合計を電話しますAccumulatedRewardsPerShare
<> P >
クラスをハイライト表示する
AccumulatedRewardsPerShare =
RewardsPerShareOn0to10 +
RewardsPerShareOn10to15 +
RewardsPerShareOn15to25 +
RewardsPerShareOn25to30
< div >それから、我々はそれを言うことができます
StakerARewards
はStakerATokens
そばAccumulatedRewardsPerShare
<> P >
クラスをハイライト表示する
StakerARewards = StakerATokens *
AccumulatedRewardsPerShare
< div >以降
AccumulatedRewardsPerShare
すべてのステッカーのために同じです、我々は言うことができますStakerBRewards
その値は、ブロック0 to 10 < br/>から得られなかった報酬を引いています<> P >
クラスをハイライト表示する
StakerBRewards = StakerBTokens *
(AccumulatedRewardsPerShare - RewardsPerShareOn0to10)
< div >< p >これは重要です、なぜなら私たちが利用できるとしても
AccumulatedRewardsPerShare
すべてのステッカー報酬計算のためにRewardsPerShare
それは彼らの預金/収穫行為の前に起こりましたp >ここで見つけたものを使って、最初の収穫で収穫したステーキAの量を調べましょうp >
💸 Reward債務を見つける
私たちは、ステッカーAが得た報酬は、最初の収穫と最後の収穫の合計であることを知っています.br/>
また、我々は同じ値を得ることができることを知っている
StakerARewards
我々が上記の< br/>を使った公式<> P >
クラスをハイライト表示する
StakerARewards = StakerARewardsOn0to15 + StakerARewardsOn15to30
StakerARewards = StakerATokens * AccumulatedRewardsPerShare
< div >分離するなら
StakerARewardsOn15to30
最初の式で置換StakerATokens
2番目のもの< br/><> P >
クラスをハイライト表示する
StakerARewardsOn15to30 = StakerARewards - StakerARewardsOn0to15
StakerARewards = StakerATokens * AccumulatedRewardsPerShare
< div >< tag > < br/>
<> P >
クラスをハイライト表示する
StakerARewardsOn15to30 = StakerATokens *
AccumulatedRewardsPerShare - StakerARewardsOn0to15
< div >現在、私たちはブロック0 to 15 < br/>のために以下の式を使用できます
<> P >
クラスをハイライト表示する
StakerARewardsOn0to15 = StakerATokens *
AccumulatedRewardsPerShareOn0to15
< div >< p >と置換
StakerARewardsOn0to15
前の< br/><> P >
クラスをハイライト表示する
StakerARewardsOn15to30 =
StakerATokens * AccumulatedRewardsPerShare -
StakerATokens * AccumulatedRewardsPerShareOn0to15
< div >あなたは、我々が孤立することができると気がつきました
StakerATokens
再び< br/><> P >
クラスをハイライト表示する
StakerARewardsOn15to30 = StakerATokens *
(AccumulatedRewardsPerShare - AccumulatedRewardsPerShareOn0to15)
< div >そして、それは我々が得た公式にとても似ています
StakerBRewards
以前は< br/><> P >
クラスをハイライト表示する
StakerBRewards = StakerBTokens *
(AccumulatedRewardsPerShare - RewardsPerShareOn0to10)
< div ><高橋潤子>
<> P >
クラスをハイライト表示する
StakerATokens = 100
AccumulatedRewardsPerShare = (10 / 100) + (5 / 500) + (10 / 500) + (5 / 500)
AccumulatedRewardsPerShare = (10 / 100) + (5 / 500)
StakerARewardsOn15to30 = StakerATokens *
(AccumulatedRewardsPerShare - AccumulatedRewardsPerShareOn0to15)
StakerARewardsOn15to30 = 100 * ((10 / 500) + (5 / 500))
StakerARewardsOn15to30 = 3
< div >< em >そうですねp >
これは、我々が保存するなら
AccumulatedRewardsPerShare
値は、彼らの預金または撤回をするたびに、ステッカートークン量によって乗算されます.p >これは呼ばれます
rewardDebt
マスターシェフの契約についてp >それはブロック0からステッカー合計報酬を計算するようなものですが、すでに収穫された報酬を削除するか、報酬を取り除くことは、彼らがまだstakingしていなかったので、巧みに主張することでありませんでしたp >
📝 累積されたrewardspershareの実装
前の契約をベースとして、単純に計算できます
accumulatedRewardsPerShare
on updatePoolRewards
関数名updateStakersRewards
) そして、ステッカーを得るrewardsDebt
毎回動作します.p >あなたはdiffコードを見ることができますthis commit .
< div class ="LagagCount - gig - Link - tag "
"スクリプトのID "https://gist.github.com/Markkop/24503047d199121651f4b75545ddbc86.js//>
< div >
⛽ ガス節約
私たちがループを避けている理由は主にガスを節約することです.ご存知のように、我々が持っているより多くのステーキ、より高価な
updateStakersRewards
関数が取得するp >私たちは、両方のガス費をhardhatテストと比較することができます
<> P >
クラスをハイライト表示する
it.only("Harvest rewards according with the staker pool's share", async function () {
// Arrange Pool
const stakeToken = rewardToken;
await stakeToken.transfer(
account2.address,
ethers.utils.parseEther("200000") // 200.000
);
await createStakingPool(stakingManager, stakeToken.address);
const amount1 = ethers.utils.parseEther("80");
const amount2 = ethers.utils.parseEther("20");
// Arrange Account1 staking
await stakeToken.approve(stakingManager.address, amount1);
await stakingManager.deposit(0, amount1);
// Arrange Account 2 staking
await stakeToken.connect(account2).approve(stakingManager.address, amount2);
await stakingManager.connect(account2).deposit(0, amount2);
// Act
const acc1HarvestTransaction = await stakingManager.harvestRewards(0);
const acc2HarvestTransaction = await stakingManager
.connect(account2)
.harvestRewards(0);
// Assert
// 2 blocks with 100% participation = 4 reward tokens * 2 blocks = 8
// 1 block with 80% participation = 3.2 reward tokens * 1 block = 3.2
// Account1 Total = 8 + 3.2 = 11.2 reward tokens
const expectedAccount1Rewards = ethers.utils.parseEther("11.2");
await expect(acc1HarvestTransaction)
.to.emit(stakingManager, "HarvestRewards")
.withArgs(account1.address, 0, expectedAccount1Rewards);
// 2 block with 20% participation = 0.8 reward tokens * 2 block
// Account 1 Total = 1.6 reward tokens
const expectedAccount2Rewards = ethers.utils.parseEther("1.6");
await expect(acc2HarvestTransaction)
.to.emit(stakingManager, "HarvestRewards")
.withArgs(account2.address, 0, expectedAccount2Rewards);
});
< div ><ップ>hardhat-gas-reporter それぞれの実装がどれだけ高価かを見ることができます.p >
最初の1つ(ループのすべてのステッカー):<br/>
(最後の1つについては< urlurl ="http ://www . linux . or . jp/">を参照ください).
それは、2台のストーカーだけでさえ、全体の20 %のガス節約ですp >
したがって、Susiiswapのマスターシェフ契約は、私が示した最後のものに類似していますbr/>
実際には、より効率的です
harvestRewards
関数.収穫が起こるとdeposit
関数は量0p >❓ どのような1 E 12のmulとdiv?
以降
accSushiPerShare
小数点以下の数値を持つことができます.sushiReward
大きな数字で1e12
それを計算して、それを使用するとき、同じ数でそれを分割するときp >🏁 結論
< p >私のプロジェクトではほとんどのDefisが報酬を計算していて、Susiiswapの契約がどのように機能しているかを理解している最新の日々を過ごしました.p >
私はいくつかのマスターシェフ変数(特にaccsusipershareとrewarddec)の意味を理解することができました.p >
私は契約書を説明する資料を見つけましたが、それらのすべてはあまりに浅薄でした.それで私はそれを自分で説明することにしましたp >
<高橋潤子>p >
Reference
この問題について(Sishiswapのマスターシェフを理解する), 我々は、より多くの情報をここで見つけました https://dev.to/heymarkkop/understanding-sushiswaps-masterchef-staking-rewards-1m6fテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol