Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add stake-cw20-external-rewards #170

Closed
wants to merge 9 commits into from
Closed

Conversation

ben2x4
Copy link
Contributor

@ben2x4 ben2x4 commented Feb 9, 2022

This contract distributes external staking rewards to stakers in the stake-cw20 contract. External means the rewards is either a native or cw20 that is not the same as the cw20 being staked.

The contract calculates rewards based on blocks but only distributes them at predefined epochs. This is all configurable in the instatiation message.

Copy link
Contributor

@0xekez 0xekez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good :) Just couple minor comments

contracts/stake-cw20-external-rewards/Cargo.toml Outdated Show resolved Hide resolved
if env.block.height > msg.start_block {
return Err(ContractError::StartBlockAlreadyOccurred {});
}
if msg.start_block > msg.end_block {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be >=? Currently could instantiate a contract that starts and ends at the same block and it would be valid so long as that block was a multiple of payment_block_delta.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, this would would result in one payment. Probable not the best practice and a seperate contract should be written for these cases, but nothing invalid about this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a test case for this? If not we should make a TODO / issue. Can be added in a separate PR.

contracts/stake-cw20-external-rewards/src/contract.rs Outdated Show resolved Hide resolved
contracts/stake-cw20-external-rewards/src/contract.rs Outdated Show resolved Hide resolved
contracts/stake-cw20-external-rewards/src/contract.rs Outdated Show resolved Hide resolved
Comment on lines +197 to +202
Ok(match config.clone().denom {
Denom::Native(denom) => BankMsg::Send {
to_address: info.sender.to_string(),
amount: vec![Coin { amount, denom }],
}
.into(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Ok(match config.clone().denom {
Denom::Native(denom) => BankMsg::Send {
to_address: info.sender.to_string(),
amount: vec![Coin { amount, denom }],
}
.into(),
Ok(match &config.denom {
Denom::Native(denom) => BankMsg::Send {
to_address: info.sender.to_string(),
amount: vec![Coin { amount, denom: denom.to_string() }],
}
.into(),

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me like.

contracts/stake-cw20-external-rewards/src/contract.rs Outdated Show resolved Hide resolved
) -> Result<Response, ContractError> {
validate_instantiate_msg(&env, &msg)?;
let config = Config {
start_block: msg.start_block,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the rationale for having the user pass in start_block, end_block, and payment per block? It would be more elegant to accept a duration and an amount, and then calculate payment per unit time within the contract. I do something similar here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that the parameters are explicit and the instatiator is required to double check everything will function as expected.

if config.denom != Denom::Cw20(info.sender) {
return Err(ContractError::IncorrectDenom {});
}
if amount != config.total_amount {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change to amount >= config.total_amount, and refund any extra funds that were sent

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like require the instatiator to be precise with their inputs. Failing the instatiate requires them to revaluate the inputs and ensure they are doing what they intend to do.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, agree with @ben2x4 on this one.

let delta = config.payment_block_delta;
let mut current_block = last_claimed;
let mut amount = Uint128::zero();
while current_block <= min(up_to_block, config.end_block) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see. ClaimUpToBlock can be used to prevent out of gas issue here

amount: Uint128,
) -> Result<Response, ContractError> {
let mut config = CONFIG.load(deps.storage)?;
if config.funded {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't you be able to top up this contract whenever the rewards get depleted. Otherwise, you'd have to instantiate a new contract every time you want to allocate more rewards

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this is a good additional feature

@vernonjohnson
Copy link
Contributor

vernonjohnson commented Feb 9, 2022

This is a solid start. There are a few major issues though:

  1. The contract has to loop over every unclaimed block height to redeem all rewards for an account. So stakers would potentially have to submit several transactions to claim all of their rewards (otherwise their transactions will run out of gas). This UX is not ideal IMO. This is primarily due to the limitations of querying the stake cw20 contract, which indexes staking info based on block height. If you want to accommodate more traditional staking mechanisms (similar to Synthetix staking rewards), which provide better UX, you may want to consider a different staking contract implementation.
  2. The contract can't be topped up, so you can't allocate more funds to issue additional rewards. You'd have to deploy an entirely new rewards contract to do so.

@ben2x4
Copy link
Contributor Author

ben2x4 commented Feb 10, 2022

This is a solid start. There are a few major issues though:

  1. The contract has to loop over every unclaimed block height to redeem all rewards for an account. So stakers would potentially have to submit several transactions to claim all of their rewards (otherwise their transactions will run out of gas). This UX is not ideal IMO. This is primarily due to the limitations of querying the stake cw20 contract, which indexes staking info based on block height. If you want to accommodate more traditional staking mechanisms (similar to Synthetix staking rewards), which provide better UX, you may want to consider a different staking contract implementation.
  2. The contract can't be topped up, so you can't allocate more funds to issue additional rewards. You'd have to deploy an entirely new rewards contract to do so.

I agree that point 2 is a great feature to implement before production deployment.

In regards to point 1, need to look a little more deeply into how they implement this. Would love to chat.

@JakeHartnell
Copy link
Member

Can we please rebase this branch to make it easier to merge in PRs potentially? 🙏

same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update this to be either your name or DAO DAO please. : )

@@ -0,0 +1,108 @@
# CosmWasm Starter Pack
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The most basic README would be nice. Can remove all of this template stuff.

Comment on lines +277 to +283
let (amount, _) = get_amount_owed(
deps,
&address,
&config,
last_claimed.block_height,
env.block.height,
)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could get expensive?

@donkey-donkey
Copy link

have you all checked out the repo from cw20 - reward. its just like anchor on terra and yield protocol on ethereum
https://github.com/CosmWasm/cw-tokens/tree/cw20-reward/contracts/cw20-reward

@vernonjohnson
Copy link
Contributor

have you all checked out the repo from cw20 - reward. its just like anchor on terra and yield protocol on ethereum https://github.com/CosmWasm/cw-tokens/tree/cw20-reward/contracts/cw20-reward

Ben also needs it to support multiple reward tokens for a single staking contract. But I agree, this is a good start.

@ben2x4
Copy link
Contributor Author

ben2x4 commented Mar 6, 2022

This approach is flawed, implementing this with a different method

@ben2x4 ben2x4 closed this Mar 6, 2022
@JakeHartnell JakeHartnell deleted the stake-cw20-external-rewards branch November 21, 2022 23:40
@JakeHartnell JakeHartnell restored the stake-cw20-external-rewards branch November 21, 2022 23:41
@JakeHartnell JakeHartnell deleted the stake-cw20-external-rewards branch August 20, 2023 03:16
@JakeHartnell JakeHartnell restored the stake-cw20-external-rewards branch August 20, 2023 03:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants