-
Notifications
You must be signed in to change notification settings - Fork 75
feat(hubpool): Add Initial initiateRelayerRefund implementation #10
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
Changes from all commits
fc7df12
df84090
7eb63c7
68005b4
6fac5b2
888b9e9
4eb788b
8770d3b
2236c03
3d7ab5f
cf74acd
17f2121
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // SPDX-License-Identifier: GPL-3.0-only | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| import "./interfaces/BridgeAdminInterface.sol"; | ||
|
|
||
| contract BridgeAdmin is BridgeAdminInterface { | ||
| // Finder used to point to latest OptimisticOracle and other DVM contracts. | ||
| address public finder; | ||
|
|
||
| constructor(address _finder) { | ||
| finder = _finder; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,9 @@ import "@uma/core/contracts/common/implementation/Testable.sol"; | |
| import "@uma/core/contracts/common/implementation/Lockable.sol"; | ||
| import "@uma/core/contracts/common/implementation/MultiCaller.sol"; | ||
| import "@uma/core/contracts/common/implementation/ExpandedERC20.sol"; | ||
| import "@uma/core/contracts/oracle/interfaces/FinderInterface.sol"; | ||
| import "@uma/core/contracts/oracle/interfaces/StoreInterface.sol"; | ||
| import "@uma/core/contracts/oracle/implementation/Constants.sol"; | ||
|
|
||
| import "@openzeppelin/contracts/access/Ownable.sol"; | ||
| import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; | ||
|
|
@@ -26,13 +29,41 @@ contract HubPool is Testable, Lockable, MultiCaller, Ownable { | |
| bool isEnabled; | ||
| } | ||
|
|
||
| WETH9Like public l1Weth; | ||
| enum RefundRequestStatus { | ||
| Pending, | ||
| Finalized, | ||
| Disputed | ||
| } | ||
|
|
||
| struct RelayerRefundRequest { | ||
| bytes32 poolRebalanceProof; | ||
| bytes32 destinationDistributionProof; | ||
| RefundRequestStatus status; | ||
| } | ||
|
|
||
| RelayerRefundRequest[] public relayerRefundRequests; | ||
|
|
||
| // Whitelist of origin token to destination token routings to be used by off-chain agents. | ||
| mapping(address => mapping(uint256 => address)) public whitelistedRoutes; | ||
|
|
||
| mapping(address => LPToken) public lpTokens; // Mapping of L1TokenAddress to the associated LPToken. | ||
|
|
||
| // Address of L1Weth. Enable LPs to deposit/receive ETH, if they choose, when adding/removing liquidity. | ||
| WETH9Like public l1Weth; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in general do we prefer storing contracts as addresses or as interfaces? I stored
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ye, good point. my main question which should inform this decision is is this more expensive to store? is this more expensive to use? my main reason for doing it this way is it means you dont need to do any type casting when using the interface which makes things a bit easier. |
||
|
|
||
| // Token used to bond the data worker for proposing relayer refund bundles. | ||
| IERC20 public bondToken; | ||
|
|
||
| // The computed bond amount as the UMA Store's final fee multiplied by the bondTokenFinalFeeMultiplier. | ||
| uint256 public bondAmount; | ||
|
|
||
| // There should only ever be one refund proposal pending at any point in time. This bool toggles accordingly. | ||
| bool public currentPendingRefundProposal = false; | ||
|
|
||
| event BondAmountSet(uint64 newBondMultiplier); | ||
|
|
||
| event BondTokenSet(address newBondMultiplier); | ||
|
|
||
| event LiquidityAdded( | ||
| address indexed l1Token, | ||
| uint256 amount, | ||
|
|
@@ -47,14 +78,39 @@ contract HubPool is Testable, Lockable, MultiCaller, Ownable { | |
| ); | ||
| event WhitelistRoute(address originToken, uint256 destinationChainId, address destinationToken); | ||
|
|
||
| constructor(address _l1Weth, address _timerAddress) Testable(_timerAddress) { | ||
| event RelayerRefundRequested( | ||
| uint64 relayerRefundId, | ||
| uint256[] bundleEvaluationBlockNumbers, | ||
| bytes32 indexed poolRebalanceProof, | ||
| bytes32 indexed destinationDistributionProof, | ||
| address proposer | ||
| ); | ||
|
|
||
| constructor( | ||
| uint256 _bondAmount, | ||
| address _bondToken, | ||
| address _l1Weth, | ||
| address _timerAddress | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: group similar types together |
||
| ) Testable(_timerAddress) { | ||
| bondAmount = _bondAmount; | ||
| bondToken = IERC20(_bondToken); | ||
| l1Weth = WETH9Like(_l1Weth); | ||
| } | ||
|
|
||
| /************************************************* | ||
| * ADMIN FUNCTIONS * | ||
| *************************************************/ | ||
|
|
||
| function setBondToken(address newBondToken) public onlyOwner { | ||
| bondToken = IERC20(newBondToken); | ||
| emit BondTokenSet(newBondToken); | ||
| } | ||
|
|
||
| function setBondTokenFinalFeeMultiplier(uint64 newBondAmount) public onlyOwner { | ||
| bondAmount = newBondAmount; | ||
| emit BondAmountSet(newBondAmount); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Whitelist an origin token <-> destination token route. | ||
| */ | ||
|
|
@@ -139,11 +195,36 @@ contract HubPool is Testable, Lockable, MultiCaller, Ownable { | |
|
|
||
| function liquidityUtilizationPostRelay(address token, uint256 relayedAmount) public returns (uint256) {} | ||
|
|
||
| /************************************************* | ||
| * DATA WORKER FUNCTIONS * | ||
| *************************************************/ | ||
|
|
||
| function initiateRelayerRefund( | ||
| uint256[] memory bundleEvaluationBlockNumberForChain, | ||
| bytes32 chainBatchRepaymentProof, | ||
| bytes32 relayerRepaymentDistributionProof | ||
| ) public {} | ||
| uint256[] memory bundleEvaluationBlockNumbers, | ||
| bytes32 poolRebalanceProof, | ||
| bytes32 destinationDistributionProof | ||
| ) public { | ||
| require(currentPendingRefundProposal == false, "Only one proposal at a time"); | ||
| currentPendingRefundProposal = true; | ||
| relayerRefundRequests.push( | ||
| RelayerRefundRequest({ | ||
| poolRebalanceProof: poolRebalanceProof, | ||
| destinationDistributionProof: destinationDistributionProof, | ||
| status: RefundRequestStatus.Pending | ||
| }) | ||
| ); | ||
|
|
||
| // Pull bondAmount of bondToken from the caller. | ||
| bondToken.safeTransferFrom(msg.sender, address(this), bondAmount); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is annoying, but how do we deal with a changing bond amount mid-refund? I know this code is to pull the bond, but do you think we need to store the bond amount for each refund request?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just dont change it if there is a pending refund I guess. if we set the bond as a variable directly (not using the store) this becomes easier. |
||
|
|
||
| emit RelayerRefundRequested( | ||
| uint64(relayerRefundRequests.length - 1), // Index of the relayerRefundRequest within the array. | ||
| bundleEvaluationBlockNumbers, | ||
| poolRebalanceProof, | ||
| destinationDistributionProof, | ||
| msg.sender | ||
| ); | ||
| } | ||
|
|
||
| function executeRelayerRefund( | ||
| uint256 relayerRefundRequestId, | ||
|
|
@@ -155,6 +236,12 @@ contract HubPool is Testable, Lockable, MultiCaller, Ownable { | |
| bytes32[] memory inclusionProof | ||
| ) public {} | ||
|
|
||
| function disputeRelayerRefund() public {} | ||
|
|
||
| /************************************************* | ||
| * INTERNAL FUNCTIONS * | ||
| *************************************************/ | ||
|
|
||
| function _exchangeRateCurrent() internal pure returns (uint256) { | ||
| return 1e18; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| // SPDX-License-Identifier: AGPL-3.0-only | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| interface BridgeAdminInterface { | ||
| function finder() external view returns (address); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { expect } from "chai"; | ||
| import { Contract } from "ethers"; | ||
|
|
||
| import { ethers } from "hardhat"; | ||
| import { ZERO_ADDRESS } from "@uma/common"; | ||
| import { getContractFactory, SignerWithAddress, createRandomBytes32, seedWallet } from "./utils"; | ||
| import { depositDestinationChainId, bondAmount } from "./constants"; | ||
| import { deployHubPoolTestHelperContracts } from "./HubPool.Fixture"; | ||
|
|
||
| let hubPool: Contract, weth: Contract, usdc: Contract; | ||
| let owner: SignerWithAddress, dataWorker: SignerWithAddress; | ||
|
|
||
| describe("HubPool Relayer Refund", function () { | ||
| before(async function () { | ||
| [owner, dataWorker] = await ethers.getSigners(); | ||
| ({ weth, hubPool, usdc } = await deployHubPoolTestHelperContracts(owner)); | ||
| await seedWallet(dataWorker, [], weth, bondAmount); | ||
| }); | ||
|
|
||
| it("Initialization of a relay correctly stores data, emits events and pulls the bond", async function () { | ||
| const bundleEvaluationBlockNumbers = [1, 2, 3]; | ||
| const poolRebalanceProof = createRandomBytes32(); | ||
| const destinationDistributionProof = createRandomBytes32(); | ||
|
|
||
| await weth.connect(dataWorker).approve(hubPool.address, bondAmount); | ||
| await expect( | ||
| hubPool | ||
| .connect(dataWorker) | ||
| .initiateRelayerRefund(bundleEvaluationBlockNumbers, poolRebalanceProof, destinationDistributionProof) | ||
| ) | ||
| .to.emit(hubPool, "RelayerRefundRequested") | ||
| .withArgs(0, bundleEvaluationBlockNumbers, poolRebalanceProof, destinationDistributionProof, dataWorker.address); | ||
| // Balances of the hubPool should have incremented by the bond and the dataWorker should have decremented by the bond. | ||
| expect(await weth.balanceOf(hubPool.address)).to.equal(bondAmount); | ||
| expect(await weth.balanceOf(dataWorker.address)).to.equal(0); | ||
|
|
||
| // Can only have one pending proposal at a time. | ||
| await expect( | ||
| hubPool | ||
| .connect(dataWorker) | ||
| .initiateRelayerRefund(bundleEvaluationBlockNumbers, poolRebalanceProof, destinationDistributionProof) | ||
| ).to.be.revertedWith("Only one proposal at a time"); | ||
| }); | ||
| }); |
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 not just import this from uma/core?
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.
Ah I see that this would be a brand new contract