-
Notifications
You must be signed in to change notification settings - Fork 75
feat: add slow relay functionality #28
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
Conversation
Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
contracts/HubPool.sol
Outdated
| uint64 unclaimedPoolRebalanceLeafCount; | ||
| bytes32 poolRebalanceRoot; | ||
| bytes32 destinationDistributionRoot; | ||
| bytes32 slowRelayDataRoot; |
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'll need to add this to initiateRelayerRefund. I can do that in a separate PR though, If you'd like as this will break a bunch of existing tests.
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 dont like this variable name. can it be SlowRelayFulfilmentRoot?
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.
Oh, I thought I added it everywhere, will do another pass to better understand where I missed.
On the naming, 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.
Done on both.
contracts/MerkleLib.sol
Outdated
| } | ||
|
|
||
| // TODO: should these structs be in this file? | ||
| struct RelayData { |
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.
would a better name be SlowRelayFulfilment? then use this through out, such as verifySlowRelayFufilment
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.
Well, it's tricky here because this is the exact data structure the SpokePool uses internally to represent a relay. I don't know if we want to rename that struct.
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.
move this to SpokePool interface?
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.
hhhmm. ye I see your point. not ideal.
if we move it to the SpokePool interface then we'll get the MerkleLib to import from this interface, yes? equally, we can pull the Hub's Merkle structs to a HubPool interface and import them into the MerkleLib in the same way.
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.
Renamed!
| address depositor, | ||
| address recipient, | ||
| address destinationToken, | ||
| uint64 realizedLpFeePct, | ||
| uint64 relayerFeePct, | ||
| uint64 depositId, | ||
| uint256 originChainId, | ||
| uint256 totalRelayAmount, |
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.
all these props can be taken directly from the MerkleLib.RelayData, no? would it be cleaner to pass in one struct and then the extra fields that are not in the struct?
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 was trying to mirror the fill method, as I think we will effectively have 3 fill methods that will have mostly the same parameters. Would like these to be as consistent as possible, so if we use a struct here, maybe it makes sense to use a struct in the others?
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.
ye, Ideally we pass around structs in all these methods methinks
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.
The only issue is the interpretability on etherscan :/
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 do this on v1 and you can decode them I think.
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.
we might need to re-evaluate the interface in the hub pool as well to spread the props, like you do it 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.
Yeah, this definitely used to work :((
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 spread props in other external spokepool methods for thi very reason
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'll make this change in the hub :(((((
nicholaspai
left a comment
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 skeleton makes total sense +1
chrismaree
left a comment
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 is awesome! good work on re-factoring the interfaces like this.
contracts/HubPool.sol
Outdated
| uint64 unclaimedPoolRebalanceLeafCount; | ||
| bytes32 poolRebalanceRoot; | ||
| bytes32 destinationDistributionRoot; | ||
| bytes32 slowRelayFulfilmentRoot; |
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.
nit: I hate to be that grammar guy but fulfillment has two L's in the "fill" part
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.
Thank you!
I don't mean to throw @chrismaree under the bus, but I just copy-pasted this from his suggestion haha
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.
Will fix.
contracts/HubPoolInterface.sol
Outdated
| import "./chain-adapters/AdapterInterface.sol"; | ||
|
|
||
| interface HubPoolInterface { | ||
| struct PoolRebalance { |
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.
nit: Should we add "Leaf" to the end of PoolRebalance and DestiationDistribution to make it clear that they are to be used as merkle root structures? @chrismaree wdyt, i'm not strongly opinionated and its not a big deal but i think its more readable
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.
Agree, will try to make this change. Hopefully will just be a simple find and replace.
| * @notice Library to help with merkle roots, proofs, and claims. | ||
| */ | ||
| library MerkleLib { | ||
| // TODO: some of these data structures can be moved out if convenient. |
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.
+1 moving these to the spoke/hub pool interface i think that makes a lot of sense
| // If relay token is weth then unwrap and send eth. | ||
| if (relayData.destinationToken == address(weth)) { | ||
| IERC20(relayData.destinationToken).safeTransferFrom(msg.sender, address(this), amountToSend); | ||
| if (!isSlowRelay) |
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.
Can you add a comment explaining that the source of funds to pay the recipient is either the caller+relayer or this contract depending on whether this is a slow relay
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.
yep!
contracts/SpokePool.sol
Outdated
| ); | ||
|
|
||
| bytes32 relayHash = _getRelayHash(relayData); | ||
| uint256 fillAmountPreFees = _fillRelay(relayHash, relayData, relayerFeePct, type(uint256).max / 1e18, true); |
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 could also set maxAmountToSend == totalRelayAmount which might make it more readable?
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.
Interesting idea -- what you're saying is that relayAmount >= total send, so this will always result in the relay being completely filled. I like this. Will add an explanatory comment too. Always in favor of less math.
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.
exactly yeah, if you try to relay the full relayAmount you'll always end up sending the max
contracts/SpokePoolInterface.sol
Outdated
|
|
||
| interface SpokePoolInterface { | ||
| // This leaf is meant to be decoded in the SpokePool in order to pay out individual relayers for this bundle. | ||
| struct DestinationDistribution { |
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.
nit: same as PoolRebalance, maybe add "Leaf" to end of this name
nicholaspai
left a comment
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.
Thanks for merging in my SpokePool changes! I left mostly nits on naming + comments and one suggestion for which variable to pass into the internal _fillRelay method. I also appreciate you not adding tests yet so we can re-review them in a second pass or a follow up PR. I'll be sure to follow the same commit pattern on my PR's!
Co-authored-by: nicholaspai <9457025+nicholaspai@users.noreply.github.com>
Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
contracts/MerkleLib.sol
Outdated
| * @param proof the merkle proof. | ||
| */ | ||
| function verifyPoolRebalance( | ||
| function verifyPoolRebalanceLeaf( |
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 rename this function to end with "Leaf"? I think its not neccessary since you take in a param called "leaf", but if you do go this route then I think you should end verifyRelayData and verifyRelayerDistribution also with "Leaf"
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.
Perhaps verifyRelayData is more appropriately named verifyRelayDataHashLeaf or verifyRelayHashLeaf
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, fixed on both! I reverted the Leaf change here (it was a find-and-replace issue I think)
|
This LGTM but just be consistent on using "leaf" in the "verify-" function names |
contracts/SpokePool.sol
Outdated
| import "@uma/core/contracts/common/implementation/MultiCaller.sol"; | ||
| import "./MerkleLib.sol"; | ||
| import "./SpokePoolInterface.sol"; | ||
| import "hardhat/console.sol"; |
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.
@mrice32 nit
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.
Thanks, will remove!
| public | ||
| override | ||
| { | ||
| _initializeRelayerRefund(relayerRepaymentDistributionRoot, slowRelayFulfillmentRoot); |
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 know you've changed this through the play but do we think that changing these variable names would make our linting a bit easier? relayerRepaymentDistributionRoot is a very long variable name. would relayerRepaymentRoot be better? and slowRelayFulfillmentRoot to slowRelayRoot? My goal is that we have a lot of functions and variables that are wrapping onto multiple lines and would perhaps not wrap if they were a bit shorter.
dont worry about this for now but just something to think about. IMHO as much as posible having code on one line will make things a lot easier to read and generally makes the code look much cleaner.
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.
Yeah, I'll leave for a follow-up. We've had a lot of back and forth on naming in this PR, and just to avoid doing a bunch more find-and-replace, we can do this in one shot in a follow up if that's cool.
package.json
Outdated
| }, | ||
| "engines": { | ||
| "node": ">=14.0.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.
why?
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 ran some command that failed saying it was incompatible with node version 8, and adding this fixed it. I can't remember what command it was, and I can't seem to find it in my local history, so I'll remove.
| import { TokenRolesEnum } from "@uma/common"; | ||
| import { getContractFactory, randomAddress, toBN, fromWei } from "./utils"; | ||
|
|
||
| import { bondAmount, refundProposalLiveness, finalFee, identifier, repaymentChainId } from "./constants"; |
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 should lint for unused variables.
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.
Agree! We should turn on eslint, as it will catch stuff like this. I think these are all used, though.
test/HubPool.Fixture.ts
Outdated
| import { Contract, Signer } from "ethers"; | ||
| import hre from "hardhat"; |
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.
both of these lines can be imported from utils. dont worry about it now, though. I'll do a clean up PR later.
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.
Fixed for this file. Haven't checked other files tho.
| await hubPool.connect(dataWorker).initiateRelayerRefund([3117], 1, tree.getHexRoot(), consts.mockTreeRoot); | ||
| await hubPool | ||
| .connect(dataWorker) | ||
| .initiateRelayerRefund([3117], 1, tree.getHexRoot(), consts.mockTreeRoot, consts.mockSlowRelayFulfillmentRoot); |
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.
here is another example of the long ass variable names and how it makes the code ugly.
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.
Yeah, we can update these when we update the other names. I agree that shorter names would be better! So let's decide offline what these should be called and do a big naming PR.
chrismaree
left a comment
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 is great! I left a few nits but overall this is excellent work! Feel free to merge once you've implemented the nits.
Restoring this for backwards compatibility with the v2 ABI. Co-authored-by: nicholaspai <9457025+nicholaspai@users.noreply.github.com>

This doesn't include tests or some of the meat of the verification and fill logic just to allow us to agree on the interfaces before moving forward.