CHIP-0050 & CHIP-0051: Action Layer and Slots + Reward Distributor
So putting my fantasy hat on with a low understanding to dream of things for the pool/farm reward mechanism...
is these concepts something that could be used for farming on pools (or solos) when it comes to claiming and distributing rewards with fewer transactions, as well as flexibility in how much of the initial reward portions are committed to the pool and/or farmer?
Fantasy 1: would this allow a fancy puzzle for farmers to commit 100% (or varying amounts) of the pool/farm reward portions to a pool rather than the 87.5% / 12.5% split today?
If transaction fees in the farmer portion become the largest portion of the reward, some users will want that shared with the whole pool. So if there was an "official protocol" instead of a centralized pool making blocks for them or trusting people won't cheat a regular pool which often requires them to build up credit owed before payout, that might be neat.
Fantasy 2: would this allow stacking of multiple pool rewards and a pool can inject their (by block, by day, by fixed amount) oracle data for payouts when claiming those rewards, turning it into a single transaction? rather than claiming rewards and multiple distribution transactions after?
If the fantasy thinking is possible, would it just take a change to consensus in the upcoming hard fork to open the door to these ideas and more with flexibility going forward while we're doing the hard fork?
CHIPs 50 and 51 are now drafts. (Both CHIPs can be handled in this PR.) Please leave your reviews here, and feel free to discuss in the #chips channel of our Discord.
Left a comment PR on the reference repository: https://github.com/Yakuhito/slot-machine/pull/13
Left a comment PR on the reference repository: Yakuhito/slot-machine#13
Replied. The slot puzzle no longer assumes a singleton amount of 1 and is roughly ~25% smaller.
After thinking about your ideas @OverActiveBladderSystem here's my comments:
is these concepts something that could be used for farming on pools (or solos) when it comes to claiming and distributing rewards with fewer transactions, as well as flexibility in how much of the initial reward portions are committed to the pool and/or farmer?
Fantasy 1: would this allow a fancy puzzle for farmers to commit 100% (or varying amounts) of the pool/farm reward portions to a pool rather than the 87.5% / 12.5% split today?
To change the split, you'd probably want to change consensus to allow custom or even configurable percentages. I think the same mechanism used to switch pools could work. I'm not an expert on that topic, so not the best person to ask 😅
If transaction fees in the farmer portion become the largest portion of the reward, some users will want that shared with the whole pool. So if there was an "official protocol" instead of a centralized pool making blocks for them or trusting people won't cheat a regular pool which often requires them to build up credit owed before payout, that might be neat.
This would, more likely, require a custom pooling singleton (and that to be supported at the consensus level - not sure if it is). I'm pro-official-protocol-pools, but I don't think these puzzles can necessarily help here.
Fantasy 2: would this allow stacking of multiple pool rewards and a pool can inject their (by block, by day, by fixed amount) oracle data for payouts when claiming those rewards, turning it into a single transaction? rather than claiming rewards and multiple distribution transactions after?
The distributors work on a per-second basis. The difficult 'balance' here is that pools will likely frequently update the list of active farmers and their shares of the rewards, which might also cause a lot of on-chain transactions. This contrasts the ideal case of the rewards distributor, where updates from the manager are not that frequent. The NFT reward distributor doesn't have this problem because users update it themselves when they stake/unstake an NFT, but you unfortunately can't trust farmers to update their own space. Is there an aspect of the current farming rewards distribution mechanism you'd like to see improved?
If the fantasy thinking is possible, would it just take a change to consensus in the upcoming hard fork to open the door to these ideas and more with flexibility going forward while we're doing the hard fork?
Generally no, as the CHIPs cover Chialisp puzzles that can run on the current version of the blockchain. I think a hard fork would be needed for the split idea, for example. Probably best to ask over Discord, where people with better knowledge in that area can answer.
CHIP call slides (call in 8h13m from now)
As suggested in the call, please find the benchmarks produced by the current test from chia-wallet-sdk below. You can compare the values against the ones provided in the Chia Documentation, although I found the docs to be (sometimes severely) underreporting some costs.
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
| Cost statistics for Reward Distributor (Manager) | | | | | |
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
| label | avg | n | min | max | median |
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
| launch | 151730455.0 | 1 | 151730455 | 151730455 | 151730455 |
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
| add_entry | 104729921.5 | 2 | 104444449 | 105015394 | 104729921.5 |
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
| commit_incentives | 141736519.0 | 4 | 137560099 | 146701662 | 141342157.5 |
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
| withdraw_incentives | 113997442.0 | 1 | 113997442 | 113997442 | 113997442 |
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
| new_epoch | 101502745.0 | 1 | 101502745 | 101502745 | 101502745 |
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
| sync | 93926023.0 | 6 | 80309510 | 107598629 | 93918000.5 |
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
| add_incentives | 115397867.0 | 1 | 115397867 | 115397867 | 115397867 |
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
| remove_entry | 115405084.0 | 1 | 115405084 | 115405084 | 115405084 |
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
| sync_and_new_epoch | 108136537.5 | 6 | 108112428 | 108160714 | 108136531 |
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
| initiate_payout | 105309683.0 | 1 | 105309683 | 105309683 | 105309683 |
+--------------------------------------------------+-------------+---+-----------+-----------+-------------+
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| Cost statistics for Reward Distributor (NFT) | | | | | |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| label | avg | n | min | max | median |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| launch | 151730455.0 | 1 | 151730455 | 151730455 | 151730455 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| mint_nft | 80563807.0 | 1 | 80563807 | 80563807 | 80563807 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| stake_nft | 220668339.0 | 1 | 220668339 | 220668339 | 220668339 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| commit_incentives | 141736519.0 | 4 | 137560099 | 146701662 | 141342157.5 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| withdraw_incentives | 113997442.0 | 1 | 113997442 | 113997442 | 113997442 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| new_epoch | 101502745.0 | 1 | 101502745 | 101502745 | 101502745 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| sync | 93926023.0 | 6 | 80309510 | 107598629 | 93918000.5 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| add_incentives | 115397867.0 | 1 | 115397867 | 115397867 | 115397867 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| mint_2_nfts | 144588923.0 | 1 | 144588923 | 144588923 | 144588923 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| stake_2_nfts | 345962778.0 | 1 | 345962778 | 345962778 | 345962778 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| unstake_2_nfts | 257211607.0 | 1 | 257211607 | 257211607 | 257211607 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| sync_and_new_epoch | 108136537.5 | 6 | 108112428 | 108160714 | 108136531 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
| initiate_payout | 105309683.0 | 1 | 105309683 | 105309683 | 105309683 |
+----------------------------------------------+-------------+---+-----------+-----------+-------------+
The recording of our Zoom call for these CHIPs is now available: https://youtu.be/4RvTy3kZdQU
I think, there is a small code optimization possible in withdraw_incentives.clsp
replace
(if (= withdrawal_share (/ (* WITHDRAWAL_SHARE_BPS committed_value) 10000)) withdrawal_share (x))
with
(if (= (* withdrawal_share 10000) (* WITHDRAWAL_SHARE_BPS committed_value) ) withdrawal_share (x))
Alternatively, instead of carrying in WITHDRAWAL_SHARE_BPS carry in WITHDRAWAL_SHARE_BPS divided by 10000 and update the above line of code accordingly.
Similar optimizations can be applied to
add_incentives.clsp line 26 and the parameter FEE_BPS
new_epoch.clsp line 40 and the parameter FEE_BPS
I think, there is a small code optimization possible in
withdraw_incentives.clspreplace(if (= withdrawal_share (/ (* WITHDRAWAL_SHARE_BPS committed_value) 10000)) withdrawal_share (x))with(if (= (* withdrawal_share 10000) (* WITHDRAWAL_SHARE_BPS committed_value) ) withdrawal_share (x))Alternatively, instead of carrying in WITHDRAWAL_SHARE_BPS carry in WITHDRAWAL_SHARE_BPS divided by 10000 and update the above line of code accordingly.
Similar optimizations can be applied to
add_incentives.clspline 26 and the parameter FEE_BPSnew_epoch.clspline 40 and the parameter FEE_BPS
Are you definitely sure the * operation is less expensive than the / operation?
I think, there is a small code optimization possible in
withdraw_incentives.clspreplace(if (= withdrawal_share (/ (* WITHDRAWAL_SHARE_BPS committed_value) 10000)) withdrawal_share (x))with(if (= (* withdrawal_share 10000) (* WITHDRAWAL_SHARE_BPS committed_value) ) withdrawal_share (x))Alternatively, instead of carrying in WITHDRAWAL_SHARE_BPS carry in WITHDRAWAL_SHARE_BPS divided by 10000 and update the above line of code accordingly. Similar optimizations can be applied toadd_incentives.clspline 26 and the parameter FEE_BPSnew_epoch.clspline 40 and the parameter FEE_BPSAre you definitely sure the
*operation is less expensive than the/operation?
Based on the cost table https://chialisp.com/costs/ it seems that it is slightly cheaper. However, I did not perform any tests. IMO, more importantly, / produces rounding error while * does not. Whether this rounding error can be exploited or not, I do not know.
The second proposed optimization. At this point a curried in parameter is divided by a constant 10000, both are known at compile time, so why not just curry in the quotient. If only integers can be curried in, this optimization cannot be applied.
I think, there is a small code optimization possible in
withdraw_incentives.clspreplace(if (= withdrawal_share (/ (* WITHDRAWAL_SHARE_BPS committed_value) 10000)) withdrawal_share (x))with(if (= (* withdrawal_share 10000) (* WITHDRAWAL_SHARE_BPS committed_value) ) withdrawal_share (x))Alternatively, instead of carrying in WITHDRAWAL_SHARE_BPS carry in WITHDRAWAL_SHARE_BPS divided by 10000 and update the above line of code accordingly. Similar optimizations can be applied toadd_incentives.clspline 26 and the parameter FEE_BPSnew_epoch.clspline 40 and the parameter FEE_BPSAre you definitely sure the
*operation is less expensive than the/operation?Based on the cost table https://chialisp.com/costs/ it seems that it is slightly cheaper. However, I did not perform any tests. IMO, more importantly, / produces rounding error while * does not. Whether this rounding error can be exploited or not, I do not know.
The second proposed optimization. At this point a curried in parameter is divided by a constant 10000, both are known at compile time, so why not just curry in the quotient. If only integers can be curried in, this optimization cannot be applied.
The static cost difference between * and / is 11, but multiplication may have bigger dynamic cost.
Chialisp has no floats to my knowledge, which is why the puzzles use BPS and divide by 10000 on the go. The order of operations was specifically chosen to preserve as much precision as possible in the calculations. Rounding is always done in the dApp's favor (to the user's disadvantage) to prevent potential exploits.
CHIPs 50 and 51 have a working implementation, and all comments so far have been addressed. These CHIPs are now in Review status. Please leave any final reviews here.
The NFT mode of the reward distributor has begun mainnet testing as part of the DIG Network community alpha: X post / site
Puzzles will be updated to use better precision for rewards: blog.fireacademy.io/p/too-discrete-to-handle-how-low-cat
Hi Yakuhito, just wanted to share some notes and feedback on your CHIP-51.
First and foremost, thanks for building this! Even in its first form this a great thing to have in the CHIA ecosystem.
I've been testing with your https://rewards.fireacademy.io/distributor/datalayer-minions
I have 106 minions staked.
After claiming, I received 75 DIG coins, each of amount 0.016, and there are more to claim. Claiming a second time... I received 31 more coins, also of 0.016 DIG each.
It appears the rewards batch per NFT. In the single-NFT case this is good, as I receive one reward coin per claim. In the multi-nft case, it'd be nicer if I received one coin overall. At present, following a claim, I need to do a sweep operation in my wallet in order to keep the rewards manageable.
- Would it be possible to bundle the rewards into one coin, no matter how many NFTs are staked?
- Is it viable to adjust the claim mechanism to gather all of the rewards in one operation (instead of one-per-75)?
Next, thinking about staking mechanisms that are common in other blockchains (e.g. ETH and Solana), there are a few very common patterns that I would love to see CHIA support so that we have feature parity. Maybe those are better as future CHIPS, but to lay out the wishlist I would look for CHIA's staking mechanisms to facilitate:
-
Additional rewards of varying types to be added flexibly on a per-epoch basis. E.g. both the Jupiter (https://vote.jup.ag/) and Aethir (https://user.aethir.com/stake) staking systems use this mechanism to both accept sponsorship and cross-promote with other projects. The way it works (from a user perspective) is that in a given epoch, rewards from an ecosystem project (e.g. in Jupiter, one example is CLOUD) are seamlessly included alongside their existing staking rewards claim.
- e.g. It'd be neat if Tang Gang could contribute HOA to a given DataLayer epoch if they wanted to. DataLayer would need to 'approve' this (or add the HOA on themselves) the so that an epoch's rewards don't get dusted or include un-desired cross-promotions.
-
The ability to stake CATs as well as NFTs. Perhaps your spec already permits this, but I would say broadly that in other ecosystems it's usually tokens that are staked rather than NFTs. This is broadly used to drive up the price of a token in the short term (/fundraise) in exchange for promising an APR on that token once staked, with the hopes that the token's project will in time become more valuable as the project succeeds.... or it's used as a sort of pump-and-dump by the creator. That said, lots of non-scam popular projects have this and used it to bootstrap their funding / reward early investors.
-
Flexibility in staking durations. I've seen a few common patterns here, including:
- There's a fixed withdraw cooldown; Your stake is indefinite, but you can trigger an unstaking event and then your locked assets become available to you again over time. (e.g. the https://vote.sanctum.so/ staking mechanism)
- When staking, you declare how long to stake for, and your stake's rewards receive some multiplier. For example there might be a minimum stake of 1 month which returns rewards at a 1x multiplier, or a maximum stake duration of 4 years which returns a 100x reward multiplier. ( e.g. https://docs.helium.com/governance/staking-with-helium-vote/#lockup-period )
-
Staking and rewards coupled into a DAO mechanism, where a staked position (including relevant multipliers) grants voting power in proportion to the stake and its multiplier, and epoch rewards can be scaled depending on the user's level of participation in the DAO's votes (e.g. Helium, Sanctum, and Jupiter, above)
-
[Edit: One more... ] Liquid staking tokens. (See https://learn.sanctum.so/docs/introduction-to-lsts/from-native-to-liquid-staking ). These have become popular in Solana and I'm seeing them in ETH projects now as well. The basic idea is that (translated to Chia) when I stake a CAT, I receive a different token (sCAT) as a receipt. These tokens can then trade on secondary markets, or can convert from a sCAT back to a CAT by performing the un-stake operation. If you want to liquidate your stake immediately (and the stake has a cooldown period involved), you could go on secondary market and trade sCATs for CATs directly and pay a premium. Depending on the implementation, the sCAT tokens can be used either to determine the rewards you receive when you claim, or instead claiming can be done away with entirely and instead the exchange rate of CAT <-> sCAT changes over time as the staked assets grow. (much like how your existing liquidity provider tokens work on tibetSwap).
I imagine many of these would be large efforts deserving separate CHIPs, but since you've thought through the Chialisp on this, perhaps some of these items are viable to bring into scope for this CHIP.