|
1 | 1 | import { expect } from "chai"; |
2 | 2 | import { Contract } from "ethers"; |
3 | | - |
4 | 3 | import { ethers } from "hardhat"; |
5 | | -import { ZERO_ADDRESS } from "@uma/common"; |
| 4 | +import { ZERO_ADDRESS, parseAncillaryData } from "@uma/common"; |
6 | 5 | import { getContractFactory, SignerWithAddress, createRandomBytes32, seedWallet } from "./utils"; |
7 | | -import { depositDestinationChainId, bondAmount, refundProposalLiveness } from "./constants"; |
8 | | -import { hubPoolFixture } from "./HubPool.Fixture"; |
| 6 | +import * as consts from "./constants"; |
| 7 | +import { hubPoolFixture, enableTokensForLiquidityProvision } from "./HubPool.Fixture"; |
| 8 | + |
| 9 | +let hubPool: Contract, weth: Contract, optimisticOracle: Contract; |
| 10 | +let owner: SignerWithAddress, dataWorker: SignerWithAddress, liquidityProvider: SignerWithAddress; |
9 | 11 |
|
10 | | -let hubPool: Contract, weth: Contract, usdc: Contract; |
11 | | -let owner: SignerWithAddress, dataWorker: SignerWithAddress; |
| 12 | +const mockBundleEvaluationBlockNumbers = [1, 2, 3]; |
| 13 | +const mockPoolRebalanceLeafCount = 5; |
| 14 | +const mockPoolRebalanceRoot = createRandomBytes32(); |
| 15 | +const mockDestinationDistributionRoot = createRandomBytes32(); |
12 | 16 |
|
13 | 17 | describe("HubPool Relayer Refund", function () { |
14 | | - before(async function () { |
15 | | - [owner, dataWorker] = await ethers.getSigners(); |
16 | | - ({ weth, hubPool, usdc } = await hubPoolFixture()); |
17 | | - await seedWallet(dataWorker, [], weth, bondAmount); |
| 18 | + beforeEach(async function () { |
| 19 | + [owner, dataWorker, liquidityProvider] = await ethers.getSigners(); |
| 20 | + ({ weth, hubPool, optimisticOracle } = await hubPoolFixture()); |
| 21 | + await seedWallet(dataWorker, [], weth, consts.bondAmount); |
| 22 | + await seedWallet(owner, [], weth, consts.bondAmount); |
| 23 | + await seedWallet(dataWorker, [], weth, consts.bondAmount.add(consts.finalFee).mul(2)); |
| 24 | + await seedWallet(liquidityProvider, [], weth, consts.amountToLp); |
| 25 | + |
| 26 | + await enableTokensForLiquidityProvision(owner, hubPool, [weth]); |
| 27 | + await weth.connect(liquidityProvider).approve(hubPool.address, consts.amountToLp); |
| 28 | + await hubPool.connect(liquidityProvider).addLiquidity(weth.address, consts.amountToLp); |
18 | 29 | }); |
19 | 30 |
|
20 | 31 | it("Initialization of a relay correctly stores data, emits events and pulls the bond", async function () { |
21 | | - const bundleEvaluationBlockNumbers = [1, 2, 3]; |
22 | | - const poolRebalanceLeafCount = 5; |
23 | | - const poolRebalanceProof = createRandomBytes32(); |
24 | | - const destinationDistributionProof = createRandomBytes32(); |
| 32 | + const expectedRequestExpirationTimestamp = Number(await hubPool.getCurrentTime()) + consts.refundProposalLiveness; |
| 33 | + await weth.connect(dataWorker).approve(hubPool.address, consts.bondAmount); |
| 34 | + const dataWorkerWethBalancerBefore = await weth.callStatic.balanceOf(dataWorker.address); |
25 | 35 |
|
26 | | - const expectedRequestExpirationTimestamp = Number(await hubPool.getCurrentTime()) + refundProposalLiveness; |
27 | | - await weth.connect(dataWorker).approve(hubPool.address, bondAmount); |
28 | 36 | await expect( |
29 | 37 | hubPool |
30 | 38 | .connect(dataWorker) |
31 | 39 | .initiateRelayerRefund( |
32 | | - bundleEvaluationBlockNumbers, |
33 | | - poolRebalanceLeafCount, |
34 | | - poolRebalanceProof, |
35 | | - destinationDistributionProof |
| 40 | + mockBundleEvaluationBlockNumbers, |
| 41 | + mockPoolRebalanceLeafCount, |
| 42 | + mockPoolRebalanceRoot, |
| 43 | + mockDestinationDistributionRoot |
36 | 44 | ) |
37 | 45 | ) |
38 | 46 | .to.emit(hubPool, "InitiateRefundRequested") |
39 | 47 | .withArgs( |
40 | | - 0, |
41 | 48 | expectedRequestExpirationTimestamp, |
42 | | - poolRebalanceLeafCount, |
43 | | - bundleEvaluationBlockNumbers, |
44 | | - poolRebalanceProof, |
45 | | - destinationDistributionProof, |
| 49 | + mockPoolRebalanceLeafCount, |
| 50 | + mockBundleEvaluationBlockNumbers, |
| 51 | + mockPoolRebalanceRoot, |
| 52 | + mockDestinationDistributionRoot, |
46 | 53 | dataWorker.address |
47 | 54 | ); |
48 | 55 | // Balances of the hubPool should have incremented by the bond and the dataWorker should have decremented by the bond. |
49 | | - expect(await weth.balanceOf(hubPool.address)).to.equal(bondAmount); |
50 | | - expect(await weth.balanceOf(dataWorker.address)).to.equal(0); |
| 56 | + expect(await weth.balanceOf(hubPool.address)).to.equal(consts.bondAmount.add(consts.amountToLp)); |
| 57 | + expect(await weth.balanceOf(dataWorker.address)).to.equal(dataWorkerWethBalancerBefore.sub(consts.bondAmount)); |
| 58 | + |
| 59 | + const refundRequest = await hubPool.refundRequest(); |
| 60 | + expect(refundRequest.requestExpirationTimestamp).to.equal(expectedRequestExpirationTimestamp); |
| 61 | + expect(refundRequest.unclaimedPoolRebalanceLeafCount).to.equal(mockPoolRebalanceLeafCount); |
| 62 | + expect(refundRequest.poolRebalanceRoot).to.equal(mockPoolRebalanceRoot); |
| 63 | + expect(refundRequest.destinationDistributionRoot).to.equal(mockDestinationDistributionRoot); |
| 64 | + expect(refundRequest.claimedBitMap).to.equal(0); // no claims yet so everything should be marked at 0. |
| 65 | + expect(refundRequest.proposer).to.equal(dataWorker.address); |
| 66 | + expect(refundRequest.proposerBondRepaid).to.equal(false); |
51 | 67 |
|
52 | 68 | // Can not re-initialize if the previous bundle has unclaimed leaves. |
53 | 69 | await expect( |
54 | 70 | hubPool |
55 | 71 | .connect(dataWorker) |
56 | 72 | .initiateRelayerRefund( |
57 | | - bundleEvaluationBlockNumbers, |
58 | | - poolRebalanceLeafCount, |
59 | | - poolRebalanceProof, |
60 | | - destinationDistributionProof |
| 73 | + mockBundleEvaluationBlockNumbers, |
| 74 | + mockPoolRebalanceLeafCount, |
| 75 | + mockPoolRebalanceRoot, |
| 76 | + mockDestinationDistributionRoot |
61 | 77 | ) |
62 | | - ).to.be.revertedWith("Last bundle has unclaimed leafs"); |
| 78 | + ).to.be.revertedWith("Active request has unclaimed leafs"); |
| 79 | + }); |
| 80 | + it("Dispute relayer refund correctly deletes the active request and enqueues a price request with the OO", async function () { |
| 81 | + await weth.connect(dataWorker).approve(hubPool.address, consts.bondAmount.mul(10)); |
| 82 | + await hubPool |
| 83 | + .connect(dataWorker) |
| 84 | + .initiateRelayerRefund( |
| 85 | + mockBundleEvaluationBlockNumbers, |
| 86 | + mockPoolRebalanceLeafCount, |
| 87 | + mockPoolRebalanceRoot, |
| 88 | + mockDestinationDistributionRoot |
| 89 | + ); |
| 90 | + |
| 91 | + const preCallAncillaryData = await hubPool._getRefundProposalAncillaryData(); |
| 92 | + |
| 93 | + await hubPool.connect(dataWorker).disputeRelayerRefund(); |
| 94 | + |
| 95 | + // Data should be deleted from the contracts refundRequest struct. |
| 96 | + const refundRequest = await hubPool.refundRequest(); |
| 97 | + expect(refundRequest.requestExpirationTimestamp).to.equal(0); |
| 98 | + expect(refundRequest.unclaimedPoolRebalanceLeafCount).to.equal(0); |
| 99 | + expect(refundRequest.poolRebalanceRoot).to.equal(consts.zeroBytes32); |
| 100 | + expect(refundRequest.destinationDistributionRoot).to.equal(consts.zeroBytes32); |
| 101 | + expect(refundRequest.claimedBitMap).to.equal(0); // no claims yet so everything should be marked at 0. |
| 102 | + expect(refundRequest.proposer).to.equal(consts.zeroAddress); |
| 103 | + expect(refundRequest.proposerBondRepaid).to.equal(false); |
| 104 | + |
| 105 | + const priceProposalEvent = (await optimisticOracle.queryFilter(optimisticOracle.filters.ProposePrice()))[0].args; |
| 106 | + |
| 107 | + expect(priceProposalEvent?.requester).to.equal(hubPool.address); |
| 108 | + expect(priceProposalEvent?.identifier).to.equal(consts.identifier); |
| 109 | + expect(priceProposalEvent?.ancillaryData).to.equal(preCallAncillaryData); |
| 110 | + |
| 111 | + const parsedAncillaryData = parseAncillaryData(priceProposalEvent?.ancillaryData); |
| 112 | + expect(parsedAncillaryData?.requestExpirationTimestamp).to.equal( |
| 113 | + Number(await hubPool.getCurrentTime()) + consts.refundProposalLiveness |
| 114 | + ); |
| 115 | + expect(parsedAncillaryData?.unclaimedPoolRebalanceLeafCount).to.equal(mockPoolRebalanceLeafCount); |
| 116 | + expect("0x" + parsedAncillaryData?.poolRebalanceRoot).to.equal(mockPoolRebalanceRoot); |
| 117 | + expect("0x" + parsedAncillaryData?.destinationDistributionRoot).to.equal(mockDestinationDistributionRoot); |
| 118 | + expect(parsedAncillaryData?.claimedBitMap).to.equal(0); |
| 119 | + expect(ethers.utils.getAddress("0x" + parsedAncillaryData?.proposer)).to.equal(dataWorker.address); |
| 120 | + }); |
| 121 | + it("Can not dispute after proposal liveness", async function () { |
| 122 | + await weth.connect(dataWorker).approve(hubPool.address, consts.bondAmount.mul(10)); |
| 123 | + await hubPool |
| 124 | + .connect(dataWorker) |
| 125 | + .initiateRelayerRefund( |
| 126 | + mockBundleEvaluationBlockNumbers, |
| 127 | + mockPoolRebalanceLeafCount, |
| 128 | + mockPoolRebalanceRoot, |
| 129 | + mockDestinationDistributionRoot |
| 130 | + ); |
| 131 | + |
| 132 | + await hubPool.setCurrentTime(Number(await hubPool.getCurrentTime()) + consts.refundProposalLiveness + 1); |
| 133 | + |
| 134 | + await expect(hubPool.connect(dataWorker).disputeRelayerRefund()).to.be.revertedWith("Request passed liveness"); |
63 | 135 | }); |
64 | 136 | }); |
0 commit comments