11// SPDX-License-Identifier: GPL-3.0-only
22pragma solidity ^ 0.8.0 ;
33
4+ import "./MerkleLib.sol " ;
5+
46import "@uma/core/contracts/common/implementation/Testable.sol " ;
57import "@uma/core/contracts/common/implementation/Lockable.sol " ;
68import "@uma/core/contracts/common/implementation/MultiCaller.sol " ;
@@ -29,16 +31,14 @@ contract HubPool is Testable, Lockable, MultiCaller, Ownable {
2931 bool isEnabled;
3032 }
3133
32- enum RefundRequestStatus {
33- Pending,
34- Finalized,
35- Disputed
36- }
37-
3834 struct RelayerRefundRequest {
39- bytes32 poolRebalanceProof;
40- bytes32 destinationDistributionProof;
41- RefundRequestStatus status;
35+ uint64 requestExpirationTimestamp;
36+ uint64 unclaimedPoolRebalanceLeafs;
37+ bytes32 poolRebalanceRoot;
38+ bytes32 destinationDistributionRoot;
39+ mapping (uint256 => uint256 ) claimedBitMap;
40+ address proposer;
41+ bool proposerBondRepaid;
4242 }
4343
4444 RelayerRefundRequest[] public relayerRefundRequests;
@@ -57,8 +57,9 @@ contract HubPool is Testable, Lockable, MultiCaller, Ownable {
5757 // The computed bond amount as the UMA Store's final fee multiplied by the bondTokenFinalFeeMultiplier.
5858 uint256 public bondAmount;
5959
60- // There should only ever be one refund proposal pending at any point in time. This bool toggles accordingly.
61- bool public currentPendingRefundProposal = false ;
60+ // Each refund proposal must stay in liveness for this period of time before it can be considered finalized. It can
61+ // be disputed only during this period of time.
62+ uint64 public refundProposalLiveness;
6263
6364 event BondAmountSet (uint64 newBondMultiplier );
6465
@@ -78,21 +79,28 @@ contract HubPool is Testable, Lockable, MultiCaller, Ownable {
7879 );
7980 event WhitelistRoute (address originToken , uint256 destinationChainId , address destinationToken );
8081
81- event RelayerRefundRequested (
82- uint64 relayerRefundId ,
82+ event InitiateRefundRequested (
83+ uint64 indexed relayerRefundId ,
84+ uint64 requestExpirationTimestamp ,
85+ uint64 poolRebalanceLeafCount ,
8386 uint256 [] bundleEvaluationBlockNumbers ,
84- bytes32 indexed poolRebalanceProof ,
85- bytes32 indexed destinationDistributionProof ,
86- address proposer
87+ bytes32 poolRebalanceRoot ,
88+ bytes32 destinationDistributionRoot ,
89+ address indexed proposer
8790 );
91+ event RelayerRefundExecuted (uint256 relayerRefundId , MerkleLib.PoolRebalance poolRebalance , address caller );
92+
93+ event RelayerRefundDisputed (uint256 relayerRefundId , address disputer );
8894
8995 constructor (
9096 uint256 _bondAmount ,
97+ uint64 _refundProposalLiveness ,
9198 address _bondToken ,
9299 address _l1Weth ,
93100 address _timerAddress
94101 ) Testable (_timerAddress) {
95102 bondAmount = _bondAmount;
103+ refundProposalLiveness = _refundProposalLiveness;
96104 bondToken = IERC20 (_bondToken);
97105 l1Weth = WETH9Like (_l1Weth);
98106 }
@@ -201,48 +209,101 @@ contract HubPool is Testable, Lockable, MultiCaller, Ownable {
201209
202210 function initiateRelayerRefund (
203211 uint256 [] memory bundleEvaluationBlockNumbers ,
204- bytes32 poolRebalanceProof ,
205- bytes32 destinationDistributionProof
212+ uint64 poolRebalanceLeafCount ,
213+ bytes32 poolRebalanceRoot ,
214+ bytes32 destinationDistributionRoot
206215 ) public {
207- require (currentPendingRefundProposal == false , "Only one proposal at a time " );
208- currentPendingRefundProposal = true ;
209- relayerRefundRequests.push (
210- RelayerRefundRequest ({
211- poolRebalanceProof: poolRebalanceProof,
212- destinationDistributionProof: destinationDistributionProof,
213- status: RefundRequestStatus.Pending
214- })
216+ // The most recent refund proposal must be fully claimed before the next relayer refund bundle is initiated.
217+ require (
218+ relayerRefundRequests.length == 0 || // If this is the first initiated relay refund.
219+ relayerRefundRequests[getNumberOfRelayRefunds ()].unclaimedPoolRebalanceLeafs == 0 ,
220+ "Last bundle has unclaimed leafs "
215221 );
216222
223+ uint64 requestExpirationTimestamp = uint64 (getCurrentTime () + refundProposalLiveness);
224+
225+ RelayerRefundRequest storage relayerRefundRequest = relayerRefundRequests.push ();
226+ relayerRefundRequest.requestExpirationTimestamp = requestExpirationTimestamp;
227+ relayerRefundRequest.unclaimedPoolRebalanceLeafs = poolRebalanceLeafCount;
228+ relayerRefundRequest.poolRebalanceRoot = poolRebalanceRoot;
229+ relayerRefundRequest.destinationDistributionRoot = destinationDistributionRoot;
230+ relayerRefundRequest.proposer = msg .sender ;
231+
217232 // Pull bondAmount of bondToken from the caller.
218233 bondToken.safeTransferFrom (msg .sender , address (this ), bondAmount);
219234
220- emit RelayerRefundRequested (
221- uint64 (relayerRefundRequests.length - 1 ), // Index of the relayerRefundRequest within the array.
235+ emit InitiateRefundRequested (
236+ uint64 (relayerRefundRequests.length - 1 ),
237+ requestExpirationTimestamp,
238+ poolRebalanceLeafCount,
222239 bundleEvaluationBlockNumbers,
223- poolRebalanceProof ,
224- destinationDistributionProof ,
240+ poolRebalanceRoot ,
241+ destinationDistributionRoot ,
225242 msg .sender
226243 );
227244 }
228245
229246 function executeRelayerRefund (
230247 uint256 relayerRefundRequestId ,
231- uint256 leafId ,
232- uint256 repaymentChainId ,
233- address [] memory l1TokenAddress ,
234- uint256 [] memory accumulatedLpFees ,
235- uint256 [] memory netSendAmounts ,
236- bytes32 [] memory inclusionProof
237- ) public {}
248+ MerkleLib.PoolRebalance memory poolRebalance ,
249+ bytes32 [] memory proof
250+ ) public {
251+ RelayerRefundRequest storage relayerRefund = relayerRefundRequests[relayerRefundRequestId];
252+ require (getCurrentTime () > relayerRefund.requestExpirationTimestamp, "Not passed liveness " );
253+
254+ // Verify the leafId in the poolRebalance has not yet been claimed.
255+ require (! MerkleLib.isClaimed (relayerRefund.claimedBitMap, poolRebalance.leafId), "Already claimed " );
256+
257+ // Verify the props provided generate a leaf that, along with the proof, are included in the merkle root.
258+ require (MerkleLib.verifyPoolRebalance (relayerRefund.poolRebalanceRoot, poolRebalance, proof), "Bad Proof " );
238259
239- function disputeRelayerRefund () public {}
260+ // Set the leafId in the claimed bitmap.
261+ MerkleLib.setClaimed (relayerRefund.claimedBitMap, poolRebalance.leafId);
262+
263+ // Decrement the unclaimedPoolRebalanceLeafs.
264+ relayerRefund.unclaimedPoolRebalanceLeafs-- ;
265+
266+ // Transfer the bondAmount to back to the proposer, if this was not done before for this refund bundle.
267+ if (! relayerRefund.proposerBondRepaid) {
268+ relayerRefund.proposerBondRepaid = true ;
269+ bondToken.safeTransfer (relayerRefund.proposer, bondAmount);
270+ }
271+
272+ // TODO call into canonical bridge to send PoolRebalance.netSendAmount for the associated
273+ // PoolRebalance.tokenAddresses, to the target PoolRebalance.chainId. this will likely happen within a
274+ // x_Messenger contract for each chain. these messengers will be registered in a separate process that will follow
275+ // in a later PR.
276+ // TODO: modify the associated utilized and pending reserves for each token sent.
277+
278+ emit RelayerRefundExecuted (relayerRefundRequestId, poolRebalance, msg .sender );
279+ }
280+
281+ function disputeRelayerRefund (uint256 relayerRefundRequestId ) public {
282+ RelayerRefundRequest storage relayerRefund = relayerRefundRequests[relayerRefundRequestId];
283+ require (
284+ getCurrentTime () > relayerRefundRequests[relayerRefundRequestId].requestExpirationTimestamp,
285+ "Passed liveness "
286+ );
287+
288+ // Delete the last element in the relayerRefundRequests array. This acts to throw out the request.
289+ emit RelayerRefundDisputed (relayerRefundRequestId, msg .sender );
290+
291+ relayerRefundRequests.pop ();
292+
293+ // TODO: pull bonds. request price from OO.
294+ }
295+
296+ function getNumberOfRelayRefunds () public view returns (uint256 ) {
297+ if (relayerRefundRequests.length == 0 ) return 0 ;
298+ return relayerRefundRequests.length - 1 ;
299+ }
240300
241301 /*************************************************
242302 * INTERNAL FUNCTIONS *
243303 *************************************************/
244304
245305 function _exchangeRateCurrent () internal pure returns (uint256 ) {
306+ // TODO: implement this method to consider utilization.
246307 return 1e18 ;
247308 }
248309
0 commit comments