-
Notifications
You must be signed in to change notification settings - Fork 1
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
Auto return stake in tg4 contract #124
Conversation
|
||
use crate::error::ContractError; | ||
use crate::msg::{ | ||
ExecuteMsg, InstantiateMsg, PreauthResponse, QueryMsg, StakedResponse, UnbondingPeriodResponse, | ||
}; | ||
use crate::state::{members, Config, ADMIN, CLAIMS, CONFIG, HOOKS, PREAUTH, STAKE, TOTAL}; | ||
|
||
pub type Response = cosmwasm_std::Response<TgradeMsg>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few minor hints (that you probably know, but just writing it down)
let config = CONFIG.load(deps.storage)?; | ||
|
||
if config.auto_return_limit > 0 { | ||
let msgs = request_privileges(&[Privilege::EndBlocker]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice to request this - and only if there is a reason to do so.
contracts/tg4-stake/src/contract.rs
Outdated
} | ||
|
||
fn end_block(deps: DepsMut) -> Result<Response, ContractError> { | ||
// TODO: Auto return claims |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And yes, this requires copying over Claims from cw-plus and modifying it with another index... tricky one to be sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI I'm doing copy of Claims in #139
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to be merged first, so basically I would align to your changes with this.
contracts/tg4-stake/src/claim.rs
Outdated
} | ||
|
||
impl<'a> Claims<'a> { | ||
pub fn new(storage_key: &'a str) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍🏼
8b4ce43
to
74e9373
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work. This looks mostly there.
The big issues I would raise are:
- Using ADMIN directly from cw-plus v0.9.0 (which can be left as a TODO for another PR... your copy works fine for now, but let's remove code we don't need)
- Better use of primary key. Indexes are awesome, you can make it even better 😄 ping me on discord if what I wrote didn't make sense
- Merging in the work from Jakub from Store creation height for undelegate #139, so it doesn't get lost/overwritten
Happy to review tomorrow morning. This looks quite close to done
contracts/tg4-stake/src/claim.rs
Outdated
@@ -0,0 +1,162 @@ | |||
//! This utility is based on `cw_controllers::Claim`, but tweaked to be more compliant to what is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm... this is quite some diff... maybe you cannot rebase, but need to merge it in and resolve conflicts manually
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At morning I succesfully rebased it, but if there would be a problem I would just rebase manually, it should not be this much a pain (at least comments, other stuff may be).
contracts/tg4-stake/src/claim.rs
Outdated
} | ||
|
||
struct ClaimIndexes<'a> { | ||
pub addr: MultiIndex<'a, (Addr, Vec<u8>), Claim>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be the Primary Key, we do not need a secondary index for this.
Just release_at as a secondary index
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was old code, not including my todays changes.
contracts/tg4-stake/src/claim.rs
Outdated
|
||
pub struct Claims<'a> { | ||
/// Claims are indexed by arbitrary numeric index, so every claim has its own entry | ||
claims: IndexedMap<'a, U64Key, Claim, ClaimIndexes<'a>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than an arbitrary U64Key, please use (&Addr, Timestamp)
or such here.
Each secondary index has a rather non-trivial cost to read and write, let's make use of the primary key for an index when we can, rather than a meaningless counter here.
storage_key, | ||
&format!("{}__addr", storage_key), | ||
), | ||
release_at: MultiIndex::new( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice job constructing this.
contracts/tg4-stake/src/claim.rs
Outdated
}; | ||
|
||
let claims = IndexedMap::new(storage_key, indexes); | ||
let next_key = Item::new(&format!("{}__key", storage_key)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I would remove the next_key and just use the indexed map with a meaningful primary key
contracts/tg4-stake/src/claim.rs
Outdated
(Some(cap), Ok((_, sum))) => cap <= (*sum).into(), | ||
_ => true, | ||
}) | ||
// need to collet intermediately, cannot remove from map while iterating as it borrows |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very nice chain here.
// take all claims for the addr | ||
.range(storage, None, None, Order::Ascending) | ||
// filter out non-expired claims (leaving errors to stop on first | ||
.filter(|claim| match claim { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With compound index (Addr, U64Key)
, you can use the range query to only get expired Claims, and not every iterate over the others. more efficient
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code sample above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is still inefficient. Using a .range(storage, None, end, Order::Ascending)
where end
is the current block time would be better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, I missed it here.
contracts/tg4-stake/src/claim.rs
Outdated
// removes item from storage and returns accumulated sum | ||
.try_fold(0, |_, (idx, sum)| -> StdResult<_> { | ||
self.claims.remove(storage, idx.into())?; | ||
Ok(sum) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
huh, don't you need to use sum + _
here?
actually, that scan confused me a bit above, maybe you don't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, scan already accumulated things, I would definetely simplify it.
contracts/tg4-stake/src/contract.rs
Outdated
@@ -73,7 +77,21 @@ pub fn execute( | |||
let api = deps.api; | |||
match msg { | |||
ExecuteMsg::UpdateAdmin { admin } => { | |||
Ok(ADMIN.execute_update_admin(deps, info, maybe_addr(api, admin)?)?) | |||
let resp = ADMIN.execute_update_admin(deps, info, maybe_addr(api, admin)?)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you upgrade deps to cw-plus v0.9.0, you can use the ADMIN from cw-plus unmodified, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed this comment before, would apply it at evening/tomorrow, but it is not critical for logic
ffd1ea2
to
d4fabbe
Compare
…s of passing regression)
d4fabbe
to
2d0ac50
Compare
contracts/tg4-stake/src/claim.rs
Outdated
Ok((_, claim)) => claim.release_at <= block.time, | ||
Ok((_, claim)) => claim.release_at.is_expired(&block), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
contracts/tg4-group/src/state.rs
Outdated
use tg_controllers::Admin; | ||
use tg_controllers::{Hooks, Preauth}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Join those.
contracts/tg4-mixer/src/contract.rs
Outdated
use cosmwasm_std::testing::{mock_env, MockApi, MockStorage}; | ||
use cosmwasm_std::{coins, Addr, Empty, Uint128}; | ||
use cosmwasm_std::{coins, Addr, Uint128}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Join those as well? I actually don't know what format are we using here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cosmwasm_std and cosmwasm_std::testing are usually kept separately (test and non-test).
The other comment above I agree with
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hah, I suspected such.
contracts/tg4-stake/src/claim.rs
Outdated
creation_height: u64, | ||
creation_heigh: u64, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was correct (also in couple more places).
const fn default_auto_return_limit() -> u64 { | ||
20 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why function instead of just variable? Won't compiler just inline it in the end anyway?
packages/controllers/src/admin.rs
Outdated
let control = Admin::new("foo"); | ||
|
||
// initialize and check | ||
let admin = Some(Addr::unchecked("admin")); | ||
control.set(deps.as_mut(), admin.clone()).unwrap(); | ||
let got = control.get(deps.as_ref()).unwrap(); | ||
assert_eq!(admin, got); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpicking: I'd rename control
to just admin
and admin
to hungarian (wink) admin_addr
for better clarity
Then you could have
assert_eq!(admin_addr, admin.get(deps.as_ref()).unwrap());
(this got
doesn't "speak" to me)
Also I have some issue here, without reading implementation it's not clear that those get
/set
methods are for reading/assigning addresses - maybe there's room for improvement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is just admin copied from cw-plus, then let's make an issue there and let this continue as is.
Most likely, this file will be removed and we can just import the cw-controllers::Admin from cw-plus v0.9.0, which has support for generic Response
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
8e9bec0
to
743302e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks quite good.
There are a number of comments for a follow up PR to improve the efficiency or code style.
I would just ask for 2 now, so we can merge it (and merge others on top).
heigh
->height
properly named.- undo changes to tg4-group and tg4-mixer non-test code (keep them with Response)
when you finish those, I am happy to merge. And then you can start a new PR for the other issues.
@@ -21,10 +21,11 @@ library = [] | |||
[dependencies] | |||
cw0 = { version = "0.8.0" } | |||
cw2 = { version = "0.8.0" } | |||
cw-controllers = { version = "0.8.0" } | |||
cw-controllers = { version = "0.9.0" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update: I merged this in, worked fine. Now everything is up to v0.9.0
@@ -14,6 +12,10 @@ use tg4::{ | |||
use crate::error::ContractError; | |||
use crate::msg::{ExecuteMsg, InstantiateMsg, PreauthResponse, QueryMsg, SudoMsg}; | |||
use crate::state::{members, ADMIN, HOOKS, PREAUTH, TOTAL}; | |||
use tg_bindings::TgradeMsg; | |||
|
|||
pub type Response = cosmwasm_std::Response<TgradeMsg>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no need to use these types here.
I would use Response unless we make use of Tgrade features.
I think you only add them to tg4-stake.
@@ -346,7 +349,7 @@ mod tests { | |||
} | |||
} | |||
|
|||
pub fn contract_mixer() -> Box<dyn Contract<Empty>> { | |||
pub fn contract_mixer() -> Box<dyn Contract<TgradeMsg>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to change the contracts to make the types match.
There is ContractWrapper::new_with_empty
, which takes a contract that returns Response and converts it to Contract for any T.
I'd prefer this over artificially adding the Tgrade Response types to tg4-group and tg4-mixer.
But yeah, in the multitest that uses them all (this file), we need to use adaptors so the App supports TgradeMsg (as I describe on the first line).
@@ -382,7 +385,7 @@ mod tests { | |||
} | |||
|
|||
// uploads code and returns address of group contract | |||
fn instantiate_group(app: &mut App, members: Vec<Member>) -> Addr { | |||
fn instantiate_group(app: &mut App<TgradeMsg>, members: Vec<Member>) -> Addr { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is needed. (and all App changes)
contracts/tg4-stake/src/claim.rs
Outdated
pub release_at: Expiration, | ||
pub creation_height: u64, | ||
/// Height of a blockchain in a moment of creation of this claim | ||
pub creation_heigh: u64, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please revert this change. We want the ending t
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
update: I fixed the spelling
QueryMsg::Claims { address } => { | ||
to_binary(&CLAIMS.query_claims(deps, &deps.api.addr_validate(&address)?)?) | ||
} | ||
QueryMsg::Claims { address } => to_binary(&ClaimsResponse { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will need to become a proper paginated list query... in a follow-up PR
@@ -454,7 +454,7 @@ mod test { | |||
} | |||
|
|||
fn contract_group() -> Box<dyn Contract<TgradeMsg>> { | |||
let contract = ContractWrapper::new_with_empty( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a nice way to do it without forcing the upgrade to tg4-group.
I would undo any changes to that contract and just use new_with_empty
here and in the mixer test.
.range( | ||
storage, | ||
None, | ||
Some(Bound::inclusive(self.claims.idx.release_at.index_key(( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am pretty sure ExpirationKey::new(Expiration::now(block)).0
will work (or U64Key::from(block.time)
)
Can you make the change and see if tests pass.
Order::Ascending, | ||
); | ||
|
||
let mut claims = self.filter_claims(claims, None, limit.into())?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can remove this filter_claims, in this case, we always have the cash to cover them.
let mut claims = self.filter_claims(claims, None, limit.into())?; | ||
claims.sort_by_key(|claim| claim.addr.clone()); | ||
|
||
let releases = claims |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice grouping 👍
cw + 0.9.0 upgrade
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I made #148 for the type cleanup I want.
If you import TgradeMsg
, you trigger another export that limits where the contract can deploy. I try to do that sparingly. But maybe you think it is better to make everything tied to Tgrade, and maybe you are right.
Take a look at it. You can merge #148 into this, and then this to main. Or just this into main as is.
I would like to get this in main. Follow up parts to finalise it are tracked in #147 and you can add to that.
Reflecting some more... you are right using the TgradeMsg in all these contracts. This repo is designed to run on Tgrade and nowhere else. I am still in the generic cw-plus mindset. I will close #148 and merge this. |
// take all claims for the addr | ||
.range(storage, None, None, Order::Ascending) | ||
// filter out non-expired claims (leaving errors to stop on first | ||
.filter(|claim| match claim { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is still inefficient. Using a .range(storage, None, end, Order::Ascending)
where end
is the current block time would be better.
Err(_) => true, | ||
}); | ||
|
||
let claims = self.filter_claims(claims, cap.map(u128::from), None)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need a cap
at all? A cap
and a limit
sound like too much / unnecessary to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cap came from my old code. I commented to remove this.
} | ||
|
||
/// Releases given claims by removing them from storage | ||
fn release_claims( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would call this remove_claims
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good idea. or maybe delete_claims
Closes #113
Closes #142 (via #146 merged in)