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

Implement fee estimate #325

Closed
wants to merge 19 commits into from
Closed

Implement fee estimate #325

wants to merge 19 commits into from

Conversation

Nutomic
Copy link
Contributor

@Nutomic Nutomic commented Jan 11, 2023

Very much work in progress

Summary of changes

  • Add API endpoint to calculate fees
  • Estimate transaction fee using etherscan.io API
  • Calculate token exchange rate using coingecko.com

Todo

  • Actually use the calculated fee in relay
  • Support for different blockchains
  • Retrieve actual token names
  • Error handling
  • Fix todos
  • Update API description in readme

Reference issue to close (if applicable)

  • Closes

Code Checklist

  • Tested
  • Documented

@shekohex shekohex marked this pull request as draft January 11, 2023 14:13
@Nutomic
Copy link
Contributor Author

Nutomic commented Jan 12, 2023

Updated. The basic functionality is working according to the spec as far as I can tell. However there are a lot of todos where things are unclear, please have a look.

The main task now is to retrieve the actual wrappedToken and baseToken from the transaction. I looked through the code but couldnt find anything related. Any hints how to do this?

@Nutomic Nutomic force-pushed the fee-estimate branch 4 times, most recently from 88637a6 to eeb8f87 Compare January 12, 2023 14:10
@salman01zp salman01zp added the wip 🚧 Work in-progress label Jan 12, 2023
@dutterbutter
Copy link
Contributor

Updated. The basic functionality is working according to the spec as far as I can tell. However there are a lot of todos where things are unclear, please have a look.

The main task now is to retrieve the actual wrappedToken and baseToken from the transaction. I looked through the code but couldnt find anything related. Any hints how to do this?

@shekohex @drewstone any suggestions?

Copy link
Contributor

@drewstone drewstone left a comment

Choose a reason for hiding this comment

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

Good start, hopefully my answers help give more direction here. Let me know if some things aren't clear.

// TODO: hardcoded
let estimated_gas_amount = U256::from(1_721_713);

// TODO: need to get the actual tokens which are being exchanged (also in handle_vanchor_relay_tx)
Copy link
Contributor

Choose a reason for hiding this comment

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

Correct, currently each "bridge" or "set of anchors" is directly coupled with a specific Webb wrapped token. If a relayer receives a request to submit a transaction against Anchor A on Ethereum then we should be able to directly check A.token().

Copy link
Contributor

Choose a reason for hiding this comment

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

You can organize as you think is best, perhaps with function args or new structs.

wrapped_token: &str,
base_token: &str,
) -> f64 {
let tokens = &[wrapped_token, base_token];
Copy link
Contributor

Choose a reason for hiding this comment

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

At launch the wrapped_token is likely not going to be listed on any exchanges or markets and therefore no prices. The division at the end seems problematic. Let's just use the base_token here.


/// Estimate gas price using etherscan.io. Note that this functionality is only available
/// on mainnet.
async fn estimate_gas_price() -> crate::Result<u64> {
Copy link
Contributor

Choose a reason for hiding this comment

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

To your previous question about support for different blockchains, here is a perfect function (as many of the others also are) to parameterize this by the chain. Currently, we have the notion of a TypedChainId that we use to identify chains and ecosystems. You can find this in webb-rs and in Typescript.

The idea with our TypedChainId is that it identifies a blockchain ecosystem and a chain in that ecosystem. We needed a unique identifier that could work for different ecosystems where chains shared the same chain identifier. It fits into a u48 (or 6-bytes), but we use u64 more commonly and reserve 8-bytes for this identifier in our protocol messages. An example of an 8-byte (or 6-byte.. drop the 0x0000) TypedChainId is 0x0000010000000001. The last 4-bytes represent the u32 valued chain identifier of all EVM and Substrate based chains.

We could match on different ecosystems/chain identifiers and switch where and how we query for gas. The only two relevant ecosystems atm are EVMs and Substrate chains.

Copy link
Contributor

Choose a reason for hiding this comment

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

For future consideration, we have in the past tried to integrate this protocol with CosmWasm (Cosmos based smart contract system) chains. In Cosmos, people use very long chain identifiers. So while we have this u64 based identifier which supports nearly every ecosystem, it does not support Cosmos chains.

Example: https://github.com/cosmos/chain-registry/blob/master/gravitybridge/chain.json#L8 (I think this exceeds a u64)

I suppose as we start to consider v2 of the protocol we really do need to ensure our protocol messages support the variation in chain identifiers, addresses, etc.

Copy link
Contributor Author

@Nutomic Nutomic Jan 13, 2023

Choose a reason for hiding this comment

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

In Rust the normal solution for such different data types is something like:

enum ChainTypes {
    Ethereum(u64),
    Cosmos(String),
}

Serde has a couple of different ways to serialize this

Edit: So it this would be a change to TypedChainId.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Remember we need a binary format that so small and so efficient, easy to implement and test. Not just that, but also it should be fairly easy to implement in different languages and platforms.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In that case we could take a hash of the chain id string, and store the first 64 bits of that hash. That would be enough to check that the chain id matches another id.

Copy link
Contributor

Choose a reason for hiding this comment

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

We could do hashing yes, in that case it might fit into our currently allotted 6 bytes. This is actually what we had already planned for the cosmos work.

Nonetheless, the main focus initially is support for EVM and Substrate chains (where we have our current implementations). I would also please ensure you try and leverage the tools we already have built rather than create new ones, specifically the TypedChainIds for identifying chains.

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 mentioned that in my other pull request:

I tried to use TypedChainId but thats not possible because Typescript tests use randomly generated chain ids, and converting them to TypedChainId fails. At the same time I noticed that there are many different types used for chain id (string, u32, u64). It would be good to refactor this in the future for more consistency.

/// Handler for fee estimation
///
/// TODO: do we need to add another endpoint for substrate?
pub async fn handle_fee_info() -> Result<impl warp::Reply, Infallible> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Coming back to this now at the end of my review after understanding the flows more..

I think we need to parameterize these functions or wrap them in some parameterizable struct by the

  • TypedChainId
  • Transaction type (2 input transfer, 16 input transfer, 2 input withdrawal, 16 input withdrawal)
  • VAnchor address in question, after all the honest user is requesting info since they're about to relay a transaction spending some funds whose destination chain is the TypedChainId.

This will tell us the type of chain under consideration so we can switch APIs and gas estimation issues.

@drewstone
Copy link
Contributor

For dealing with the Webb wrapped token in case I didn't add enough details, I would recommend checking out the API for submitting a relayable transaction. In that logic, there should be a clear VAnchor contract address that is being called. On that contract there's a getter function token(): address. If we can grab an instance of the VAnchor contract then we can get its token and from there its base tokens, etc.

@Nutomic
Copy link
Contributor Author

Nutomic commented Jan 13, 2023

Im currently working through the comments and added the caching of FeeInfo (single instance, valid for one minute).

I also added fee info to the test case should relay private transaction. After adding the fee to signatureVBridge.transact() call, it throws an error UNPREDICTABLE_GAS_LIMIT. The suggested solution is to manually set a gas limit, but transact() has no such method. So its probably necessary to add this param in webb-tools library, or maybe there is another solution.

Felix Ableitner added 14 commits January 19, 2023 15:33
- remove static var
- add comments
- add fee logic for handle_vanchor_relay_tx
- better number types
- fixed refund of 1 usd which gets converted to wrapped token
- validate refund amount
- remove params for fee_info endpoint
- Automatically compile relayer when tests are executed
- Add refundWallet for test
@Nutomic
Copy link
Contributor Author

Nutomic commented Jan 19, 2023

Salman suggested to merge this PR first and finish the remaining tasks in a separate PR, so that its easier to review. We can do this if you want, but the code is still not fully working, except in a very specific scenario (certainly not ready for deployment). I will list the main changes and remaining tasks here:

Main changes:

  • Added fee_info API endpoint /api/v1/fee_info:
    {
        "estimatedFee": "0x476b26e0f",
        "gasPrice": "0x11",
        "refundExchangeRate": "0x28f",
        "maxRefund": "0xf3e59",
        "timestamp": "2023-01-19T06:29:49.556114073Z"
    }
  • Removed WithdrawConfig which is now unused
  • Updated test should relay private transaction to use fee info
  • Tests now use cargo run instead of directly executing binary in target folder, so it is automatically recompiled if anything changed in the Rust code

Todo now:

  • Relaying currently tends to fail fee check, because it estimates gas amount in a different way which usually higher than the hardcoded gas amount used in fee_info API endpoint. Need to decide how to handle this.
  • There is no error handling, everything is unwrapped
  • Fix tests

Todo potentially in separate PRs:

  • Token names are still hardcoded to USDC and Ethereum, need to get actual tokens
  • Checking the refund amount in test "should relay private transaction" is currently not working (Salman said he would debug this)
  • fee info needs param for transaction type (number of inputs/outputs), and use different gas amount estimate accordingly
  • fee info is cached globally, should be separate cache entries per user/token/chain
  • Relayer should make a profit, but this is currently not the case. Probably need to increase exchange rate by 1% or something similar.
  • Need to add fee estimation endpoint for substrate

@Nutomic Nutomic requested a review from salman01zp January 20, 2023 01:50
@Nutomic Nutomic marked this pull request as ready for review January 24, 2023 01:28
@Nutomic
Copy link
Contributor Author

Nutomic commented Jan 24, 2023

This is ready to review. After review is done, I we can keep the PR open and wait until I finish the remaining tasks in a second PR, then merge both of them together. This way we avoid breaking the main branch, as the code currently only handles fees for relaying USDC on Ethereum.

@Nutomic Nutomic changed the title WIP: Implement fee estimate Implement fee estimate Jan 24, 2023
@Nutomic Nutomic mentioned this pull request Jan 24, 2023
2 tasks
Copy link
Collaborator

@shekohex shekohex left a comment

Choose a reason for hiding this comment

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

LGTM! Good job @Nutomic

Looking forward for the new PRs!

@drewstone
Copy link
Contributor

This looks great too! Great work @Nutomic

@drewstone
Copy link
Contributor

drewstone commented Jan 31, 2023 via email

@drewstone
Copy link
Contributor

drewstone commented Jan 31, 2023 via email

@Nutomic
Copy link
Contributor Author

Nutomic commented Jan 31, 2023

@drewstone TypedChainId doesnt support the chain id which is used in Typescript tests which is 3334. Calling TypedChainId::from(3334) returns None. So passing the id this way doesnt work. A possible solution would be to allow arbitrary chain ids only in debug mode.

@drewstone
Copy link
Contributor

@Nutomic please refer to: https://github.com/webb-tools/webb-rs/blob/main/proposals/src/header.rs.

There is a method to get it from a u64. But really I want you to read how we construct it from bytes as that is the encoding that we want. As far as you've mentioned, 3334 fits within a u32. If it's an EVM chain the prefixed 2 bytes should reflect that.

@Nutomic
Copy link
Contributor Author

Nutomic commented Jan 31, 2023

Closing this one, we can merge #330 only.

@Nutomic Nutomic closed this Jan 31, 2023
@shekohex shekohex deleted the fee-estimate branch March 8, 2023 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wip 🚧 Work in-progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants