Skip to content

Commit 31b60c3

Browse files
improve(insured-bridge): Settle resolved optimistic price requests that passed dispute period (#3326)
* Update BridgePool.sol * fix tests * Update BridgePool.sol * Update BridgePool.sol * Change settlement logic * Update packages/core/contracts-ovm/insured-bridge/implementation/BridgePool.sol Co-authored-by: Chris Maree <[email protected]> Co-authored-by: Chris Maree <[email protected]>
1 parent 19ad6b9 commit 31b60c3

File tree

3 files changed

+76
-103
lines changed

3 files changed

+76
-103
lines changed

packages/common/src/HardhatConfig.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export function getHardhatConfig(
5353
"contracts/financial-templates/expiring-multiparty/Liquidatable.sol": LARGE_CONTRACT_COMPILER_SETTINGS,
5454
"contracts/oracle/implementation/Voting.sol": LARGE_CONTRACT_COMPILER_SETTINGS,
5555
"contracts/oracle/implementation/test/VotingTest.sol": LARGE_CONTRACT_COMPILER_SETTINGS,
56+
"contracts-ovm/insured-bridge/implementation/BridgePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS,
5657
},
5758
},
5859
ovm: { solcVersion: "0.7.6" },

packages/core/contracts-ovm/insured-bridge/implementation/BridgePool.sol

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,15 @@ contract BridgePool is Testable, BridgePoolInterface, ExpandedERC20 {
291291
}
292292

293293
/**
294-
* @notice Instantly relay a deposit amount minus fees. Instant relayer earns a reward following the pending relay
295-
* challenge period.
294+
* @notice Instantly relay a deposit amount minus fees to the recipient. Instant relayer earns a reward following
295+
* the pending relay challenge period.
296+
* @dev We assume that the caller has performed an off-chain check that the deposit data they are attempting to
297+
* relay is valid. If the deposit data is invalid, then the instant relayer has no recourse
298+
* to receive their funds back after the invalid deposit data is disputed. Moreover, no one will be able to
299+
* resubmit a relay for the invalid deposit data because they know it will get disputed again. On the other hand,
300+
* if the deposit data is valid, then even if it is falsely disputed, the instant relayer will eventually get
301+
* reimbursed because someone else will be incentivized to resubmit the relay to earn slow relayer rewards. Once the
302+
* valid relay is finalized, the instant relayer will be reimbursed.
296303
* @dev Caller must have approved this contract to spend the deposit amount of L1 tokens to relay. There can only
297304
* be one instant relayer per relay attempt and disputed relays cannot be sped up.
298305
* @param _depositData Unique set of L2 deposit data that caller is trying to instantly relay.
@@ -326,25 +333,21 @@ contract BridgePool is Testable, BridgePoolInterface, ExpandedERC20 {
326333
bytes32 depositHash = _getDepositHash(_depositData);
327334
RelayData storage relay = relays[depositHash];
328335

329-
// Note `hasPrice` will return false if liveness has not been passed in the optimistic oracle.
336+
// If relay was disputed, then parties in dispute need to go through OptimisticOracle to resolve dispute.
337+
require(relays[depositHash].relayState == RelayState.Pending, "Relay not settleable");
338+
339+
// Attempt to settle OptimisticOracle price as a convenience for the slow relayer who will receive their
340+
// dispute bond back. If the price is not settleable, then this call will revert. If the price has already
341+
// been settled, then this will not revert and still return the price.
330342
require(
331-
relays[depositHash].relayState == RelayState.Pending &&
332-
_getOptimisticOracle().hasPrice(
333-
address(this),
334-
bridgeAdmin.identifier(),
335-
relay.priceRequestTime,
336-
getRelayAncillaryData(_depositData, relay)
337-
),
338-
"Relay not settleable"
343+
_getOptimisticOracle().settleAndGetPrice(
344+
bridgeAdmin.identifier(),
345+
relay.priceRequestTime,
346+
getRelayAncillaryData(_depositData, relay)
347+
) == int256(1e18), // Canonical value representing "True"; i.e. the proposed relay is valid.
348+
"Relay request was not valid"
339349
);
340350

341-
// Note: Why don't we have to check the value of the price?
342-
// - If the OptimisticOracle has a price and the relayState is PENDING, then we can safely assume that the relay
343-
// was validated. This is because this contract proposes a price of 1e18, or "YES" to the identifier posing the
344-
// question "Is this relay valid for the associated deposit?". If the proposal is disputed, then the relayState
345-
// will be reset to UNINITIALIZED. If the proposal is not disputed, and there is a price available, then the
346-
// proposal must have passed the dispute period, assuming the proposal passed optimistic oracle liveness.
347-
348351
// Update the relay state to Finalized. This prevents any re-settling of a relay.
349352
relay.relayState = RelayState.Finalized;
350353

packages/core/test/insured-bridge/BridgePool.js

Lines changed: 54 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,16 @@ describe("BridgePool", () => {
894894
.send({ from: disputer });
895895

896896
assert(await didContractThrow(bridgePool.methods.settleRelay(depositData).send({ from: relayer })));
897+
898+
// Cannot settle even if disputed price was resolved.
899+
const price = toWei("1");
900+
const stampedDisputeAncillaryData = await optimisticOracle.methods
901+
.stampAncillaryData(relayAncillaryData, bridgePool.options.address)
902+
.call();
903+
await mockOracle.methods
904+
.pushPrice(defaultIdentifier, relayStatus.priceRequestTime.toString(), stampedDisputeAncillaryData, price)
905+
.send({ from: owner });
906+
assert(await didContractThrow(bridgePool.methods.settleRelay(depositData).send({ from: relayer })));
897907
});
898908
it("Can settle pending relays that passed challenge period", async () => {
899909
// Cache price request timestamp.
@@ -931,41 +941,8 @@ describe("BridgePool", () => {
931941
);
932942
assert(await didContractThrow(bridgePool.methods.settleRelay(depositData).send({ from: relayer })));
933943

934-
// Expire and settle proposal on the OptimisticOracle.
944+
// Set time such that optimistic price request is settleable.
935945
await timer.methods.setCurrentTime(expectedExpirationTimestamp).send({ from: owner });
936-
await optimisticOracle.methods
937-
.settle(
938-
bridgePool.options.address,
939-
defaultIdentifier,
940-
relayStatus.priceRequestTime.toString(),
941-
relayAncillaryData
942-
)
943-
.send({ from: relayer });
944-
945-
// Verify price is available.
946-
assert.equal(
947-
await optimisticOracle.methods
948-
.hasPrice(
949-
bridgePool.options.address,
950-
defaultIdentifier,
951-
relayStatus.priceRequestTime.toString(),
952-
relayAncillaryData
953-
)
954-
.call(),
955-
true
956-
);
957-
958-
// If the relay status is PENDING and the price is available, then the value must match the original proposal:
959-
// 1e18.
960-
assert.equal(relayStatus.relayState, InsuredBridgeRelayStateEnum.PENDING);
961-
assert.equal(
962-
(
963-
await optimisticOracle.methods
964-
.settleAndGetPrice(defaultIdentifier, relayStatus.priceRequestTime.toString(), relayAncillaryData)
965-
.call({ from: bridgePool.options.address })
966-
).toString(),
967-
toWei("1")
968-
);
969946

970947
// Settle relay and check event logs.
971948
const settleTxn = await bridgePool.methods.settleRelay(depositData).send({ from: rando });
@@ -1029,22 +1006,13 @@ describe("BridgePool", () => {
10291006
relayData.realizedLpFeePct
10301007
)
10311008
.send({ from: relayer });
1032-
const relayStatus = await bridgePool.methods.relays(depositHash).call();
10331009

10341010
// Speed up relay.
10351011
await l1Token.methods.approve(bridgePool.options.address, slowRelayAmountSubFee).send({ from: instantRelayer });
10361012
await bridgePool.methods.speedUpRelay(depositData).send({ from: instantRelayer });
10371013

1038-
// Expire and settle proposal on the OptimisticOracle.
1014+
// Set time such that optimistic price request is settleable.
10391015
await timer.methods.setCurrentTime(expectedExpirationTimestamp).send({ from: owner });
1040-
await optimisticOracle.methods
1041-
.settle(
1042-
bridgePool.options.address,
1043-
defaultIdentifier,
1044-
relayStatus.priceRequestTime.toString(),
1045-
relayAncillaryData
1046-
)
1047-
.send({ from: relayer });
10481016

10491017
const relayerBalanceBefore = await l1Token.methods.balanceOf(relayer).call();
10501018
const instantRelayerBalanceBefore = await l1Token.methods.balanceOf(instantRelayer).call();
@@ -1059,8 +1027,8 @@ describe("BridgePool", () => {
10591027
toBN(await l1Token.methods.balanceOf(relayer).call())
10601028
.sub(toBN(relayerBalanceBefore))
10611029
.toString(),
1062-
realizedSlowRelayFeeAmount.toString(),
1063-
"Slow relayer should receive proposal slow relay reward"
1030+
toBN(totalRelayBond).add(realizedSlowRelayFeeAmount).toString(),
1031+
"Slow relayer should receive proposal bond + slow relay reward"
10641032
);
10651033
assert.equal(
10661034
toBN(await l1Token.methods.balanceOf(instantRelayer).call())
@@ -1082,6 +1050,42 @@ describe("BridgePool", () => {
10821050
"BridgePool should have balance reduced by relayed amount to recipient"
10831051
);
10841052
});
1053+
it("Does not revert if price was already settled on OptimisticOracle", async () => {
1054+
// Cache price request timestamp.
1055+
const requestTimestamp = (await bridgePool.methods.getCurrentTime().call()).toString();
1056+
const expectedExpirationTimestamp = (Number(requestTimestamp) + defaultLiveness).toString();
1057+
1058+
// Proposer approves pool to withdraw total bond.
1059+
await l1Token.methods.approve(bridgePool.options.address, totalRelayBond).send({ from: relayer });
1060+
await bridgePool.methods
1061+
.relayDeposit(
1062+
depositData.depositId,
1063+
depositData.depositTimestamp,
1064+
depositData.recipient,
1065+
depositData.l2Sender,
1066+
depositData.amount,
1067+
depositData.slowRelayFeePct,
1068+
depositData.instantRelayFeePct,
1069+
depositData.quoteTimestamp,
1070+
relayData.realizedLpFeePct
1071+
)
1072+
.send({ from: relayer });
1073+
const relayStatus = await bridgePool.methods.relays(depositHash).call();
1074+
1075+
// Settle price directly via optimistic oracle
1076+
await timer.methods.setCurrentTime(expectedExpirationTimestamp).send({ from: owner });
1077+
await optimisticOracle.methods
1078+
.settle(
1079+
bridgePool.options.address,
1080+
defaultIdentifier,
1081+
relayStatus.priceRequestTime.toString(),
1082+
relayAncillaryData
1083+
)
1084+
.send({ from: rando });
1085+
1086+
// Settle relay and check event logs.
1087+
assert.ok(await bridgePool.methods.settleRelay(depositData).send({ from: rando }), "SettleRelay reverted");
1088+
});
10851089
});
10861090
describe("Liquidity provision", () => {
10871091
beforeEach(async function () {
@@ -1182,20 +1186,11 @@ describe("BridgePool", () => {
11821186

11831187
// Next, finalize the bridging action. This should reset the pendingReserves to 0 and increment the utilizedReserves.
11841188
// Expire and settle proposal on the OptimisticOracle.
1185-
const relayStatus = await bridgePool.methods.relays(depositHash).call();
1186-
11871189
await timer.methods
11881190
.setCurrentTime(Number(await bridgePool.methods.getCurrentTime().call()) + defaultLiveness)
11891191
.send({ from: owner });
1190-
await optimisticOracle.methods
1191-
.settle(
1192-
bridgePool.options.address,
1193-
defaultIdentifier,
1194-
relayStatus.priceRequestTime.toString(),
1195-
relayAncillaryData
1196-
)
1197-
.send({ from: relayer });
11981192
await bridgePool.methods.settleRelay(depositData).send({ from: rando });
1193+
11991194
// Check the balances have updated correctly. Pending should be 0 (funds are actually utilized now), liquid should
12001195
// be equal to the LP fee of 10 and the utilized should equal the total bridged amount (100).
12011196
assert.equal(await bridgePool.methods.pendingReserves().call(), "0");
@@ -1247,20 +1242,10 @@ describe("BridgePool", () => {
12471242
.send({ from: relayer });
12481243

12491244
// Expire and settle proposal on the OptimisticOracle.
1250-
const relayStatus = await bridgePool.methods.relays(depositHash).call();
12511245
await timer.methods.setCurrentTime(expectedExpirationTimestamp).send({ from: owner });
1252-
await optimisticOracle.methods
1253-
.settle(
1254-
bridgePool.options.address,
1255-
defaultIdentifier,
1256-
relayStatus.priceRequestTime.toString(),
1257-
relayAncillaryData
1258-
)
1259-
.send({ from: relayer });
1246+
await bridgePool.methods.settleRelay(depositData).send({ from: rando });
12601247
});
12611248
it("SettleRelay modifies virtual balances", async () => {
1262-
await bridgePool.methods.settleRelay(depositData).send({ from: rando });
1263-
12641249
// Exchange rate should still be 1, no fees accumulated yet as no time has passed from the settlement. Pool
12651250
// balance, liquidReserves and utilizedReserves should update accordingly. Calculate the bridge tokens used as
12661251
// the amount sent to the receiver+the bond.
@@ -1282,8 +1267,6 @@ describe("BridgePool", () => {
12821267
);
12831268
});
12841269
it("Fees correctly accumulate to LPs over the one week loan interval", async () => {
1285-
await bridgePool.methods.settleRelay(depositData).send({ from: rando });
1286-
12871270
await bridgePool.methods.exchangeRateCurrent().send({ from: rando }); // force sync
12881271

12891272
// As no time has elapsed, no fees should be earned and the exchange rate should still be 1.
@@ -1340,8 +1323,6 @@ describe("BridgePool", () => {
13401323
assert.equal((await bridgePool.methods.undistributedLpFees().call()).toString(), toWei("2.607616"));
13411324
});
13421325
it("Fees correctly accumulate with multiple relays", async () => {
1343-
await bridgePool.methods.settleRelay(depositData).send({ from: rando });
1344-
13451326
// Advance time by a 2 days (172800s) and check that the exchange rate increments accordingly. Expected exchange rate is (910+90+10-(10-0.0000015*172800*10))/1000=1.002592.
13461327
await advanceTime(172800);
13471328

@@ -1391,18 +1372,9 @@ describe("BridgePool", () => {
13911372
depositHash = soliditySha3(depositDataAbiEncoded);
13921373

13931374
// Expire and settle proposal on the OptimisticOracle.
1394-
const relayStatus = await bridgePool.methods.relays(depositHash).call();
13951375
await advanceTime(defaultLiveness);
13961376

13971377
relayAncillaryData = await bridgePool.methods.getRelayAncillaryData(depositData, relayData).call();
1398-
await optimisticOracle.methods
1399-
.settle(
1400-
bridgePool.options.address,
1401-
defaultIdentifier,
1402-
relayStatus.priceRequestTime.toString(),
1403-
relayAncillaryData
1404-
)
1405-
.send({ from: relayer });
14061378

14071379
// Settle the relay action.
14081380
await bridgePool.methods.settleRelay(depositData).send({ from: rando });
@@ -1477,8 +1449,6 @@ describe("BridgePool", () => {
14771449
assert.equal((await bridgePool.methods.undistributedLpFees().call()).toString(), toWei("0"));
14781450
});
14791451
it("Adding/removing impact exchange rate accumulation as expected", async () => {
1480-
await bridgePool.methods.settleRelay(depositData).send({ from: rando });
1481-
14821452
// Advance time by a 2 days (172800s). Exchange rate should be (1000+10*172800*0.0000015)/1000=1.002592
14831453
await advanceTime(172800);
14841454
assert.equal((await bridgePool.methods.exchangeRateCurrent().call()).toString(), toWei("1.002592"));
@@ -1522,9 +1492,8 @@ describe("BridgePool", () => {
15221492
});
15231493
it("Fees & Exchange rate can correctly handel gifted tokens", async () => {
15241494
// We cant control when funds are sent to the contract, just like we cant control when the bridging action
1525-
// concludes. If someone was to randomly send the contract tokens the exchange rate should ignore this. The contract should ignore the exchange rate if someone was to randomly send tokens.
1526-
1527-
await bridgePool.methods.settleRelay(depositData).send({ from: rando });
1495+
// concludes. If someone was to randomly send the contract tokens the exchange rate should ignore this. The
1496+
// contract should ignore the exchange rate if someone was to randomly send tokens.
15281497

15291498
// Advance time by a 2 days (172800s). Exchange rate should be (1000+10*172800*0.0000015)/1000=1.002592
15301499
await advanceTime(172800);

0 commit comments

Comments
 (0)