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

Define Consumer Contracts #33

Merged
merged 10 commits into from
Jun 1, 2023
Merged

Define Consumer Contracts #33

merged 10 commits into from
Jun 1, 2023

Conversation

ethanfrey
Copy link
Collaborator

@ethanfrey ethanfrey commented May 24, 2023

This will start some contract code from the consumer side. The intention is to clarify some interfaces in the documentation by means of sample code. But also, to work towards a minimal working virtual staking contract that can be used for system-level integrations with the virtual staking SDK module.

  • Define new interfaces between these contracts
  • Create minimal gov-set price-feed contract
  • Start native staking contract (no reward withdrawal)
  • Provide stub for the converter contract

@ethanfrey ethanfrey force-pushed the start-consumer-apis branch from c0ec886 to 14c29ae Compare May 24, 2023 19:17
@ethanfrey ethanfrey requested a review from hashedone May 24, 2023 19:33
Copy link
Collaborator

@hashedone hashedone left a comment

Choose a reason for hiding this comment

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

Some comments for current state.

contracts/consumer/converter/src/lib.rs Outdated Show resolved Hide resolved
contracts/consumer/virtual-staking/src/contract.rs Outdated Show resolved Hide resolved

fn handle_epoch(
&self,
deps: DepsMut<VirtualStakeCustomQuery>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ouch, I am not sure if it is properly supported in Sylvia to use custom queries, it was postponed. I will double-check, and if not, we will push for delivering it asap.

@jawoznia

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I looked and the following are all not supported:

  • CustomQueries (Deps)
  • Exposing sudo in multitests (and maybe making another Sylvia method like reply to enable that)

I tried returning custom messages in instantiate and it works (at least the compiler didn't complain once I updated the lib.rs bindings... good to check this works with the auto-generated entry points)

For Custom Queries, note it could vary between T and Empty between calls, but not T and U. I guess all execute variants need the same, as well as all query variants, but eg instantiate may be DepsMut while execute is DepsMut

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good thing is sudo is outside of Sylvia so I can get this to compile and run for system level tests (uploading to a chain), even if I can't for multitest yet

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

NB: I rebased on your vault branch to check new Sylvia and it errors on returning Response<CustomMsg> from instantiate. (something about anyhow::Error so I assume mt related. One more thing to check then

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Created CosmWasm/sylvia#139 and CosmWasm/sylvia#140

CosmWasm/sylvia#16 is the oldest open issue... time to knock it out.

Copy link
Collaborator

@hashedone hashedone May 26, 2023

Choose a reason for hiding this comment

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

Yea, both custom queries and custom messages are not supported. We will prioritize those.

Sudo is easy thing to do, for now we can do it in reply-wise and deprecate it when we properly implement them.

For Custom Queries, note it could vary between T and Empty between calls, but not T and U. I guess all execute variants need the same, as well as all query variants, but eg instantiate may be DepsMut while execute is DepsMut

I am aware, however it might happen we will enforce using T through entire contract due to practical reasons (it should not be a problem - Querier<T> can do everythin Querier<Empty> can do). Simillarly as with error type (you use ContractError even if StdError would be sufficient).

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not 100% sure how to approach it. This is too big PR to leave it not merged, but queries/responses would be in Sylvia this/next week (Jan is working on that right now). Sudo messages I can do in one day (not the way replies go, I have another approach for that which will stay relevant). On the other way, I assume it won't work properly so it is merging invalid code probably.

My take: change the reply to "default" for now, and where you query it just create a dummy default response, leaving todo: "Properly query when CosmWasm/sylvia#16 is merged". Then create issue on GH to fix it (to not forget).

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ahh, ignore it - it is used only in sudo. So you just use sudo this way which is working for now, except not testable at this point. But we will deliver it soon and then change. Good (for now).

contracts/consumer/virtual-staking/src/contract.rs Outdated Show resolved Hide resolved
config: Item<'a, Config>,
// Amount of tokens that have been requested to bond to a validator
// (Sum of bond minus unbond requests). This will only update actual bond on epoch changes.
bond_requests: Map<'a, &'a str, Uint128>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

This key should probably be an &Addr

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The key is a validator address (specifically valoper), which due to some weird architectural decision of the SDK uses a different bech32 prefix than the normal accounts, although it actually works the same way 🤷‍♂️

We have to treat as String without validating.

Comment on lines 74 to 124
let requests: Vec<(String, Uint128)> = self
.bond_requests
.range(deps.storage, None, None, cosmwasm_std::Order::Ascending)
.collect::<Result<_, _>>()?;
let total_request: Uint128 = requests.iter().map(|(_, v)| v).sum();
if total_request > bond.cap.amount {
// TODO: normalize the list of requests to match the cap
todo!();
}

// TODO: Look at existing list and calculate differences for bond/unbond calls

// Save the new values
self.bonded.save(deps.storage, &requests)?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Collecting all the requests, then summing them, then ignoring those out of range, and store after transformation seems waaaay off to me. I assume that normalizing requests is done to prevent wasting gas on transforming all the requests, but probably more gas is wasted by collecting all the requests, and always fetching/storing all the bonded items.

The algorithm should probably go ass follows:

The requests should never be collected, instead they should be transformed fold-like keeping the transformed items, and total request (both as Iterator::scan items). The reason for using scan instead of fold is, that fold is eager, and scan is lazy - after scan we can use take_while, to stop transforming after the cap is reached.

I would go even deeper, and in the scan I would just return an iterator over (request, total), then take_wile to break when total reaches cap, then map to transform items and update them immediately in bonded (which should be Map for that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Happy for better design, but let me finish this, then improve. I need to:

  1. Get all bond requests
  2. Sum the total amount
  3. If the sum <= max_cap then use collected requests as is
  4. If the sum > max_cap,
    a. calculate multiplier Decimal(max_cap / sum)
    b. multiply every element of the collected requests in place.
  5. Find diff between collected (normalized) requests and last bonding amounts (which go up, which down).
  6. Transform diff into unbond and bond requests, sorting so all unbond happen first

The largest optimization I can see is collecting (in 1) into a {Hash,BTree}Map as we will likely need that later.

We don't cut off at the cap, we multiply everyone down. We need to get it all.

The storage load is the most expensive. Highest V is 100-150, so even O(N^2) in memory, which I would like to avoid, may be cheaper than the storage reads

contracts/consumer/virtual-staking/src/types.rs Outdated Show resolved Hide resolved
@ethanfrey ethanfrey force-pushed the start-consumer-apis branch from 4b1f611 to 33e5cb7 Compare May 25, 2023 16:19
@ethanfrey ethanfrey marked this pull request as ready for review May 25, 2023 17:18
@ethanfrey
Copy link
Collaborator Author

The basic code is laid out. There are no tests.

Simple Price Feed is simple enough to be tested, and a good place to learn how, but not really needed.
The Virtual Staking contract, which really could use serious tests, is not testable with current Sylvia framework, as most logic happens in sudo entry point with CustomMsg and CustomQuery... all of which need new Sylvia features to test

However, this is a first step to getting something that @alpe could use in system tests (as the generated wasm should work)

@ethanfrey ethanfrey force-pushed the start-consumer-apis branch from 8ff516c to 25b07cb Compare May 30, 2023 08:24
@ethanfrey
Copy link
Collaborator Author

@hashedone I rebased on vault merge, can you review this (ideally run cargo build and cargo clippy locally).

The only issue I have is a build warning. Code:

use mesh_apis::local_staking_api::{self, LocalStakingApi, MaxSlashResponse};

#[contract]
#[messages(local_staking_api as LocalStakingApi)]
impl LocalStakingApi for NativeStakingContract<'_> {
 // ...
}

Error:

warning: unused import: `self`
 --> contracts/native-staking/src/local_staking_api.rs:6:36
  |
6 | use mesh_apis::local_staking_api::{self, LocalStakingApi, MaxSlashResponse};
  |                                    ^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

I guess the #messages[...] block is unused there? And I should remove it?

Copy link
Collaborator

@hashedone hashedone left a comment

Choose a reason for hiding this comment

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

Couple comments.

First of all - entry points can be generated with a single line from Sylvia - who commented on it twice, but there are more contracts and that apply to all of them.

The warning about unused modules is surprising, but it is not because #[messages(...)] is not used - it is always used, but when mt utils are not generated (so no mt flag enabled on Sylvia), the macro does not use the provided path. Don't remove the #[messages(...)] attribute; it will cause more problems in the future. For now, the best I can think of is adding #[allow(unused_imports)] just before the import block which causes this one. We will fix this warning in Sylvia by artificially using the module even when not generating things using it.

I am leaving the approval as I don't see any serious problem so you can merge when you fix or ignore comments.

contracts/consumer/converter/src/lib.rs Outdated Show resolved Hide resolved
contracts/consumer/converter/src/contract.rs Show resolved Hide resolved
contracts/consumer/simple-price-feed/src/lib.rs Outdated Show resolved Hide resolved

fn handle_epoch(
&self,
deps: DepsMut<VirtualStakeCustomQuery>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not 100% sure how to approach it. This is too big PR to leave it not merged, but queries/responses would be in Sylvia this/next week (Jan is working on that right now). Sudo messages I can do in one day (not the way replies go, I have another approach for that which will stay relevant). On the other way, I assume it won't work properly so it is merging invalid code probably.

My take: change the reply to "default" for now, and where you query it just create a dummy default response, leaving todo: "Properly query when CosmWasm/sylvia#16 is merged". Then create issue on GH to fix it (to not forget).

contracts/consumer/virtual-staking/src/contract.rs Outdated Show resolved Hide resolved

fn handle_epoch(
&self,
deps: DepsMut<VirtualStakeCustomQuery>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ahh, ignore it - it is used only in sudo. So you just use sudo this way which is working for now, except not testable at this point. But we will deliver it soon and then change. Good (for now).

@ethanfrey ethanfrey force-pushed the start-consumer-apis branch from 25b07cb to 53ac9e6 Compare June 1, 2023 07:15
@ethanfrey
Copy link
Collaborator Author

Made requested fixes and rebased on main.
Will merge when this passes CI

@ethanfrey ethanfrey merged commit bf2c73c into main Jun 1, 2023
@ethanfrey ethanfrey deleted the start-consumer-apis branch June 1, 2023 07:19
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.

2 participants