Skip to content

Commit

Permalink
Update Faucet contract to use bridging on drip function (#10621)
Browse files Browse the repository at this point in the history
* adding bridging to the faucet drip call

* updating the logic based on suggesstions

* adding changeset

* fixing ci issue

* update auth module tests

* final fix

* lint fix

* update snapshots

* update changeset

* change to patch

---------

Co-authored-by: Tarun Khasnavis <tarunkhasnavis@Taruns-MacBook-Pro.local>
Co-authored-by: tre <tremarkley@gmail.com>
  • Loading branch information
3 people committed May 28, 2024
1 parent 1c22d5f commit eb454ac
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/witty-pants-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@eth-optimism/contracts-bedrock': patch
---

Add data field to faucet contract drip parameters
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,20 @@
"name": "recipient",
"type": "address"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "nonce",
"type": "bytes32"
},
{
"internalType": "uint32",
"name": "gasLimit",
"type": "uint32"
}
],
"internalType": "struct Faucet.DripParameters",
Expand Down
10 changes: 10 additions & 0 deletions packages/contracts-bedrock/snapshots/abi/Faucet.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,20 @@
"name": "recipient",
"type": "address"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "nonce",
"type": "bytes32"
},
{
"internalType": "uint32",
"name": "gasLimit",
"type": "uint32"
}
],
"internalType": "struct Faucet.DripParameters",
Expand Down
10 changes: 8 additions & 2 deletions packages/contracts-bedrock/src/periphery/faucet/Faucet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.15;

import { IFaucetAuthModule } from "./authmodules/IFaucetAuthModule.sol";
import { SafeCall } from "../../libraries/SafeCall.sol";

/// @title SafeSend
/// @notice Sends ETH to a recipient account without triggering any code.
Expand All @@ -25,7 +26,9 @@ contract Faucet {
/// @notice Parameters for a drip.
struct DripParameters {
address payable recipient;
bytes data;
bytes32 nonce;
uint32 gasLimit;
}

/// @notice Parameters for authentication.
Expand Down Expand Up @@ -113,14 +116,17 @@ contract Faucet {
"Faucet: drip parameters could not be verified by security module"
);

// Verify recepient is not the faucet address.
require(_params.recipient != address(this), "Faucet: cannot drip to itself");

// Set the next timestamp at which this auth id can be used.
timeouts[_auth.module][_auth.id] = block.timestamp + config.ttl;

// Mark the nonce as used.
nonces[_auth.id][_params.nonce] = true;

// Execute a safe transfer of ETH to the recipient account.
new SafeSend{ value: config.amount }(_params.recipient);
// Execute transfer of ETH to the recipient account.
SafeCall.call(_params.recipient, _params.gasLimit, config.amount, _params.data);

emit Drip(config.name, _auth.id, config.amount, _params.recipient);
}
Expand Down
44 changes: 31 additions & 13 deletions packages/contracts-bedrock/test/periphery/faucet/Faucet.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ contract FaucetTest is Faucet_Initializer {
function test_authAdmin_drip_succeeds() external {
_enableFaucetAuthModules();
bytes32 nonce = faucetHelper.consumeNonce();
bytes memory data = "0x";
uint32 gasLimit = 200000;
bytes memory signature = issueProofWithEIP712Domain(
faucetAuthAdminKey,
bytes(optimistNftFamName),
Expand All @@ -119,14 +121,16 @@ contract FaucetTest is Faucet_Initializer {

vm.prank(nonAdmin);
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce),
Faucet.DripParameters(payable(fundsReceiver), data, nonce, gasLimit),
Faucet.AuthParameters(optimistNftFam, keccak256(abi.encodePacked(fundsReceiver)), signature)
);
}

function test_nonAdmin_drip_fails() external {
_enableFaucetAuthModules();
bytes32 nonce = faucetHelper.consumeNonce();
bytes memory data = "0x";
uint32 gasLimit = 200000;
bytes memory signature = issueProofWithEIP712Domain(
nonAdminKey,
bytes(optimistNftFamName),
Expand All @@ -141,14 +145,16 @@ contract FaucetTest is Faucet_Initializer {
vm.prank(nonAdmin);
vm.expectRevert("Faucet: drip parameters could not be verified by security module");
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce),
Faucet.DripParameters(payable(fundsReceiver), data, nonce, gasLimit),
Faucet.AuthParameters(optimistNftFam, keccak256(abi.encodePacked(fundsReceiver)), signature)
);
}

function test_drip_optimistNftSendsCorrectAmount_succeeds() external {
_enableFaucetAuthModules();
bytes32 nonce = faucetHelper.consumeNonce();
bytes memory data = "0x";
uint32 gasLimit = 200000;
bytes memory signature = issueProofWithEIP712Domain(
faucetAuthAdminKey,
bytes(optimistNftFamName),
Expand All @@ -163,7 +169,7 @@ contract FaucetTest is Faucet_Initializer {
uint256 recipientBalanceBefore = address(fundsReceiver).balance;
vm.prank(nonAdmin);
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce),
Faucet.DripParameters(payable(fundsReceiver), data, nonce, gasLimit),
Faucet.AuthParameters(optimistNftFam, keccak256(abi.encodePacked(fundsReceiver)), signature)
);
uint256 recipientBalanceAfter = address(fundsReceiver).balance;
Expand All @@ -173,6 +179,8 @@ contract FaucetTest is Faucet_Initializer {
function test_drip_githubSendsCorrectAmount_succeeds() external {
_enableFaucetAuthModules();
bytes32 nonce = faucetHelper.consumeNonce();
bytes memory data = "0x";
uint32 gasLimit = 200000;
bytes memory signature = issueProofWithEIP712Domain(
faucetAuthAdminKey,
bytes(githubFamName),
Expand All @@ -187,7 +195,7 @@ contract FaucetTest is Faucet_Initializer {
uint256 recipientBalanceBefore = address(fundsReceiver).balance;
vm.prank(nonAdmin);
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce),
Faucet.DripParameters(payable(fundsReceiver), data, nonce, gasLimit),
Faucet.AuthParameters(githubFam, keccak256(abi.encodePacked(fundsReceiver)), signature)
);
uint256 recipientBalanceAfter = address(fundsReceiver).balance;
Expand All @@ -197,6 +205,8 @@ contract FaucetTest is Faucet_Initializer {
function test_drip_emitsEvent_succeeds() external {
_enableFaucetAuthModules();
bytes32 nonce = faucetHelper.consumeNonce();
bytes memory data = "0x";
uint32 gasLimit = 200000;
bytes memory signature = issueProofWithEIP712Domain(
faucetAuthAdminKey,
bytes(githubFamName),
Expand All @@ -213,14 +223,16 @@ contract FaucetTest is Faucet_Initializer {

vm.prank(nonAdmin);
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce),
Faucet.DripParameters(payable(fundsReceiver), data, nonce, gasLimit),
Faucet.AuthParameters(githubFam, keccak256(abi.encodePacked(fundsReceiver)), signature)
);
}

function test_drip_disabledModule_reverts() external {
_enableFaucetAuthModules();
bytes32 nonce = faucetHelper.consumeNonce();
bytes memory data = "0x";
uint32 gasLimit = 200000;
bytes memory signature = issueProofWithEIP712Domain(
faucetAuthAdminKey,
bytes(githubFamName),
Expand All @@ -234,15 +246,15 @@ contract FaucetTest is Faucet_Initializer {

vm.startPrank(faucetContractAdmin);
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce),
Faucet.DripParameters(payable(fundsReceiver), data, nonce, gasLimit),
Faucet.AuthParameters(githubFam, keccak256(abi.encodePacked(fundsReceiver)), signature)
);

faucet.configure(githubFam, Faucet.ModuleConfig("GithubModule", false, 1 days, 0.05 ether));

vm.expectRevert("Faucet: provided auth module is not supported by this faucet");
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce),
Faucet.DripParameters(payable(fundsReceiver), data, nonce, gasLimit),
Faucet.AuthParameters(githubFam, keccak256(abi.encodePacked(fundsReceiver)), signature)
);
vm.stopPrank();
Expand All @@ -251,6 +263,8 @@ contract FaucetTest is Faucet_Initializer {
function test_drip_preventsReplayAttacks_succeeds() external {
_enableFaucetAuthModules();
bytes32 nonce = faucetHelper.consumeNonce();
bytes memory data = "0x";
uint32 gasLimit = 200000;
bytes memory signature = issueProofWithEIP712Domain(
faucetAuthAdminKey,
bytes(githubFamName),
Expand All @@ -264,13 +278,13 @@ contract FaucetTest is Faucet_Initializer {

vm.startPrank(faucetContractAdmin);
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce),
Faucet.DripParameters(payable(fundsReceiver), data, nonce, gasLimit),
Faucet.AuthParameters(githubFam, keccak256(abi.encodePacked(fundsReceiver)), signature)
);

vm.expectRevert("Faucet: nonce has already been used");
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce),
Faucet.DripParameters(payable(fundsReceiver), data, nonce, gasLimit),
Faucet.AuthParameters(githubFam, keccak256(abi.encodePacked(fundsReceiver)), signature)
);
vm.stopPrank();
Expand All @@ -279,6 +293,8 @@ contract FaucetTest is Faucet_Initializer {
function test_drip_beforeTimeout_reverts() external {
_enableFaucetAuthModules();
bytes32 nonce0 = faucetHelper.consumeNonce();
bytes memory data = "0x";
uint32 gasLimit = 200000;
bytes memory signature0 = issueProofWithEIP712Domain(
faucetAuthAdminKey,
bytes(githubFamName),
Expand All @@ -292,7 +308,7 @@ contract FaucetTest is Faucet_Initializer {

vm.startPrank(faucetContractAdmin);
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce0),
Faucet.DripParameters(payable(fundsReceiver), data, nonce0, gasLimit),
Faucet.AuthParameters(githubFam, keccak256(abi.encodePacked(fundsReceiver)), signature0)
);

Expand All @@ -310,7 +326,7 @@ contract FaucetTest is Faucet_Initializer {

vm.expectRevert("Faucet: auth cannot be used yet because timeout has not elapsed");
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce1),
Faucet.DripParameters(payable(fundsReceiver), data, nonce1, gasLimit),
Faucet.AuthParameters(githubFam, keccak256(abi.encodePacked(fundsReceiver)), signature1)
);
vm.stopPrank();
Expand All @@ -319,6 +335,8 @@ contract FaucetTest is Faucet_Initializer {
function test_drip_afterTimeout_succeeds() external {
_enableFaucetAuthModules();
bytes32 nonce0 = faucetHelper.consumeNonce();
bytes memory data = "0x";
uint32 gasLimit = 200000;
bytes memory signature0 = issueProofWithEIP712Domain(
faucetAuthAdminKey,
bytes(githubFamName),
Expand All @@ -332,7 +350,7 @@ contract FaucetTest is Faucet_Initializer {

vm.startPrank(faucetContractAdmin);
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce0),
Faucet.DripParameters(payable(fundsReceiver), data, nonce0, gasLimit),
Faucet.AuthParameters(githubFam, keccak256(abi.encodePacked(fundsReceiver)), signature0)
);

Expand All @@ -350,7 +368,7 @@ contract FaucetTest is Faucet_Initializer {

vm.warp(startingTimestamp + 1 days + 1 seconds);
faucet.drip(
Faucet.DripParameters(payable(fundsReceiver), nonce1),
Faucet.DripParameters(payable(fundsReceiver), data, nonce1, gasLimit),
Faucet.AuthParameters(githubFam, keccak256(abi.encodePacked(fundsReceiver)), signature1)
);
vm.stopPrank();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ contract AdminFaucetAuthModuleTest is Test {
/// @notice Assert that verify returns true for valid proofs signed by admins.
function test_adminProof_verify_succeeds() external {
bytes32 nonce = faucetHelper.consumeNonce();
bytes memory data = "0x";
uint32 gasLimit = 200000;
address fundsReceiver = makeAddr("fundsReceiver");
bytes memory proof = issueProofWithEIP712Domain(
adminKey,
Expand All @@ -96,7 +98,9 @@ contract AdminFaucetAuthModuleTest is Test {
vm.prank(nonAdmin);
assertEq(
adminFam.verify(
Faucet.DripParameters(payable(fundsReceiver), nonce), keccak256(abi.encodePacked(fundsReceiver)), proof
Faucet.DripParameters(payable(fundsReceiver), data, nonce, gasLimit),
keccak256(abi.encodePacked(fundsReceiver)),
proof
),
true
);
Expand All @@ -105,6 +109,8 @@ contract AdminFaucetAuthModuleTest is Test {
/// @notice Assert that verify returns false for proofs signed by nonadmins.
function test_nonAdminProof_verify_succeeds() external {
bytes32 nonce = faucetHelper.consumeNonce();
bytes memory data = "0x";
uint32 gasLimit = 200000;
address fundsReceiver = makeAddr("fundsReceiver");
bytes memory proof = issueProofWithEIP712Domain(
nonAdminKey,
Expand All @@ -120,7 +126,9 @@ contract AdminFaucetAuthModuleTest is Test {
vm.prank(admin);
assertEq(
adminFam.verify(
Faucet.DripParameters(payable(fundsReceiver), nonce), keccak256(abi.encodePacked(fundsReceiver)), proof
Faucet.DripParameters(payable(fundsReceiver), data, nonce, gasLimit),
keccak256(abi.encodePacked(fundsReceiver)),
proof
),
false
);
Expand All @@ -130,6 +138,8 @@ contract AdminFaucetAuthModuleTest is Test {
/// than the id in the call to verify.
function test_proofWithWrongId_verify_succeeds() external {
bytes32 nonce = faucetHelper.consumeNonce();
bytes memory data = "0x";
uint32 gasLimit = 200000;
address fundsReceiver = makeAddr("fundsReceiver");
address randomAddress = makeAddr("randomAddress");
bytes memory proof = issueProofWithEIP712Domain(
Expand All @@ -146,7 +156,9 @@ contract AdminFaucetAuthModuleTest is Test {
vm.prank(admin);
assertEq(
adminFam.verify(
Faucet.DripParameters(payable(fundsReceiver), nonce), keccak256(abi.encodePacked(randomAddress)), proof
Faucet.DripParameters(payable(fundsReceiver), data, nonce, gasLimit),
keccak256(abi.encodePacked(randomAddress)),
proof
),
false
);
Expand Down

0 comments on commit eb454ac

Please sign in to comment.