Skip to content

Commit

Permalink
prove w/ tx id [SLT-181] (#3169)
Browse files Browse the repository at this point in the history
* prove w/ tx id SLT-181

* +proveOther tests, forge fmt

* fmt

* fmt
  • Loading branch information
parodime authored Sep 26, 2024
1 parent 2709e85 commit 9742f8f
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 5 deletions.
6 changes: 3 additions & 3 deletions packages/contracts-rfq/contracts/FastBridgeV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,12 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors {

/// @inheritdoc IFastBridge
function prove(bytes memory request, bytes32 destTxHash) external {
prove(request, destTxHash, msg.sender);
bytes32 transactionId = keccak256(request);
prove(transactionId, destTxHash, msg.sender);
}

/// @inheritdoc IFastBridgeV2
function prove(bytes memory request, bytes32 destTxHash, address relayer) public onlyRole(RELAYER_ROLE) {
bytes32 transactionId = keccak256(request);
function prove(bytes32 transactionId, bytes32 destTxHash, address relayer) public onlyRole(RELAYER_ROLE) {
// update bridge tx status given proof provided
if (bridgeStatuses[transactionId] != BridgeStatus.REQUESTED) revert StatusIncorrect();
bridgeStatuses[transactionId] = BridgeStatus.RELAYER_PROVED;
Expand Down
4 changes: 2 additions & 2 deletions packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ interface IFastBridgeV2 is IFastBridge {
function relay(bytes memory request, address relayer) external payable;

/// @notice Provides proof on origin side that relayer provided funds on destination side of bridge transaction
/// @param request The encoded bridge transaction to prove on origin chain
/// @param transactionId The transaction id associated with the encoded bridge transaction to prove
/// @param destTxHash The destination tx hash proving bridge transaction was relayed
/// @param relayer The address of the relaying entity which should have control of the origin funds when claimed
function prove(bytes memory request, bytes32 destTxHash, address relayer) external;
function prove(bytes32 transactionId, bytes32 destTxHash, address relayer) external;

/// @notice Completes bridge transaction on origin chain by claiming originally deposited capital.
/// @notice Can only send funds to the relayer address on the proof.
Expand Down
130 changes: 130 additions & 0 deletions packages/contracts-rfq/test/FastBridgeV2.Src.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ contract FastBridgeV2SrcTest is FastBridgeV2Test {
fastBridge.bridge{value: msgValue}(params);
}

function prove(address caller, bytes32 transactionId, bytes32 destTxHash, address relayer) public {
vm.prank(caller);
fastBridge.prove(transactionId, destTxHash, relayer);
}

function prove(address caller, IFastBridge.BridgeTransaction memory bridgeTx, bytes32 destTxHash) public {
vm.prank(caller);
fastBridge.prove(abi.encode(bridgeTx), destTxHash);
Expand Down Expand Up @@ -353,6 +358,131 @@ contract FastBridgeV2SrcTest is FastBridgeV2Test {
prove({caller: caller, bridgeTx: tokenTx, destTxHash: hex"01"});
}

// ════════════════════════════════════════ PROVE OTHER RELAYER ════════════════════════════════════════════

function test_proveOther_token() public {
bytes32 txId = getTxId(tokenTx);
bridge({caller: userA, msgValue: 0, params: tokenParams});
expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"});
prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA});
(uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId);
assertEq(timestamp, block.timestamp);
assertEq(relayer, relayerA);
assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount);
assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN);
}

function test_proveOther_eth() public {
// bridge token first to match the nonce
bridge({caller: userA, msgValue: 0, params: tokenParams});
bytes32 txId = getTxId(ethTx);
bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams});
expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"});
prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA});
(uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId);
assertEq(timestamp, block.timestamp);
assertEq(relayer, relayerA);
assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount);
assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH);
}

// relayer self-proving using tx id, which is capable of proving for another & most tests focus on that angle.
function test_proveOther_self() public {
bytes32 txId = getTxId(tokenTx);
bridge({caller: userA, msgValue: 0, params: tokenParams});
expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"});
prove({caller: relayerA, transactionId: txId, destTxHash: hex"01", relayer: relayerA});
(uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId);
assertEq(timestamp, block.timestamp);
assertEq(relayer, relayerA);
assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount);
assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN);
}

// arbitrary non-privileged address can be asserted as the relayer
function test_proveOther_permless() public {
bytes32 txId = getTxId(tokenTx);

bridge({caller: userA, msgValue: 0, params: tokenParams});
expectBridgeProofProvided({txId: txId, relayer: address(0x1234), destTxHash: hex"01"});
prove({caller: relayerA, transactionId: txId, destTxHash: hex"01", relayer: address(0x1234)});
(uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId);
assertEq(timestamp, block.timestamp);
assertEq(relayer, address(0x1234));
assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount);
assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN);
}

function test_proveOther_reProveAfterDispute() public {
bridge({caller: userA, msgValue: 0, params: tokenParams});
bytes32 txId = getTxId(ethTx);
bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams});
expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"});
prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA});
expectBridgeProofDisputed(txId, guard);
dispute(guard, txId);
expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"02"});
prove({caller: relayerB, transactionId: txId, destTxHash: hex"02", relayer: relayerA});
expectBridgeProofDisputed(txId, guard);
dispute(guard, txId);
expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"03"});
prove({caller: relayerB, transactionId: txId, destTxHash: hex"03", relayer: relayerA});
(uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId);
assertEq(timestamp, block.timestamp);
assertEq(relayer, relayerA);
assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount);
assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN);
}

// can prove long after relaying as long as status is still good
function test_proveOther_longDelay() public {
bytes32 txId = getTxId(tokenTx);
bridge({caller: userA, msgValue: 0, params: tokenParams});
skip(10 days);
expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"});
prove({caller: relayerA, transactionId: txId, destTxHash: hex"01", relayer: relayerA});
(uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId);
assertEq(timestamp, block.timestamp);
assertEq(relayer, relayerA);
assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount);
assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN);
}

function test_proveOther_revert_statusProved() public {
bytes32 txId = getTxId(tokenTx);
bridge({caller: userA, msgValue: 0, params: tokenParams});
prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA});
vm.expectRevert(StatusIncorrect.selector);
prove({caller: relayerB, transactionId: txId, destTxHash: hex"02", relayer: relayerA});
}

function test_proveOther_revert_statusClaimed() public {
bytes32 txId = getTxId(tokenTx);
bridge({caller: userA, msgValue: 0, params: tokenParams});
prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA});
skip(CLAIM_DELAY + 1);
claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA});
vm.expectRevert(StatusIncorrect.selector);
prove({caller: relayerB, transactionId: txId, destTxHash: hex"02", relayer: relayerA});
}

function test_proveOther_revert_statusRefunded() public {
bytes32 txId = getTxId(tokenTx);
bridge({caller: userA, msgValue: 0, params: tokenParams});
skip(DEADLINE + 1);
refund({caller: refunder, bridgeTx: tokenTx});
vm.expectRevert(StatusIncorrect.selector);
prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA});
}

function test_proveOther_revert_callerNotAuthed(address caller) public {
bytes32 txId = getTxId(tokenTx);
vm.assume(caller != relayerA && caller != relayerB);
bridge({caller: userA, msgValue: 0, params: tokenParams});
expectUnauthorized(caller, fastBridge.RELAYER_ROLE());
prove({caller: caller, transactionId: txId, destTxHash: hex"01", relayer: relayerA});
}

// ═══════════════════════════════════════════════════ CLAIM ═══════════════════════════════════════════════════════

function checkTokenBalancesAfterClaim(address relayer) public view {
Expand Down

0 comments on commit 9742f8f

Please sign in to comment.