Commit a9f23c2c authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into ctb/fix-factory-create2

parents ae54c97f e24c4041
......@@ -57,26 +57,26 @@ DisputeGameFactory_SetImplementation_Test:test_setImplementation_notOwner_revert
DisputeGameFactory_SetImplementation_Test:test_setImplementation_succeeds() (gas: 44301)
DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_notOwner_reverts() (gas: 15950)
DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_succeeds() (gas: 18642)
Drippie_Test:test_create_calledTwice_reverts() (gas: 168931)
Drippie_Test:test_create_succeeds() (gas: 183380)
Drippie_Test:test_drip_amount_succeeds() (gas: 285294)
Drippie_Test:test_drip_notExist_reverts() (gas: 14876)
Drippie_Test:test_drip_reentrant_reverts() (gas: 18853)
Drippie_Test:test_name_notExist_reverts() (gas: 16012)
Drippie_Test:test_notReentrant_zeroInterval_reverts() (gas: 18845)
Drippie_Test:test_not_active_reverts() (gas: 171074)
Drippie_Test:test_reentrant_succeeds() (gas: 180159)
Drippie_Test:test_set_statusNone_reverts() (gas: 168743)
Drippie_Test:test_set_statusSame_reverts() (gas: 169129)
Drippie_Test:test_set_status_succeeds() (gas: 198449)
Drippie_Test:test_shouldArchive_ifPaused_succeeds() (gas: 177260)
Drippie_Test:test_shouldNotAllowActive_ifArchived_reverts() (gas: 174581)
Drippie_Test:test_shouldNotAllowPaused_ifArchived_reverts() (gas: 174604)
Drippie_Test:test_shouldNotArchive_ifActive_reverts() (gas: 175622)
Drippie_Test:test_status_unauthorized_reverts() (gas: 167344)
Drippie_Test:test_trigger_oneFunction_succeeds() (gas: 338143)
Drippie_Test:test_trigger_twoFunctions_succeeds() (gas: 491870)
Drippie_Test:test_twice_inOneInterval_reverts() (gas: 303767)
Drippie_Test:test_create_calledTwice_reverts() (gas: 168953)
Drippie_Test:test_create_succeeds() (gas: 183401)
Drippie_Test:test_drip_amount_succeeds() (gas: 285353)
Drippie_Test:test_drip_notExist_reverts() (gas: 14920)
Drippie_Test:test_drip_reentrant_reverts() (gas: 18875)
Drippie_Test:test_name_notExist_reverts() (gas: 16056)
Drippie_Test:test_notReentrant_zeroInterval_reverts() (gas: 18867)
Drippie_Test:test_not_active_reverts() (gas: 171162)
Drippie_Test:test_reentrant_succeeds() (gas: 180158)
Drippie_Test:test_set_statusNone_reverts() (gas: 168809)
Drippie_Test:test_set_statusSame_reverts() (gas: 169195)
Drippie_Test:test_set_status_succeeds() (gas: 198603)
Drippie_Test:test_shouldArchive_ifPaused_succeeds() (gas: 177348)
Drippie_Test:test_shouldNotAllowActive_ifArchived_reverts() (gas: 174669)
Drippie_Test:test_shouldNotAllowPaused_ifArchived_reverts() (gas: 174692)
Drippie_Test:test_shouldNotArchive_ifActive_reverts() (gas: 175732)
Drippie_Test:test_status_unauthorized_reverts() (gas: 167388)
Drippie_Test:test_trigger_oneFunction_succeeds() (gas: 338226)
Drippie_Test:test_trigger_twoFunctions_succeeds() (gas: 491907)
Drippie_Test:test_twice_inOneInterval_reverts() (gas: 303933)
EASUpgrader:test_script_succeeds() (gas: 3078)
FaucetTest:test_authAdmin_drip_succeeds() (gas: 366107)
FaucetTest:test_drip_afterTimeout_succeeds() (gas: 447891)
......
{
"faucetAdmin": "0xf2C22a95bBA6F35545269183D8d1751a27F047F6"
"faucetAdmin": "0xf2C22a95bBA6F35545269183D8d1751a27F047F6",
"faucetDrippieOwner": "0xEa193Fd9565284E7534dDDA15b07B119e7792644",
"faucetDripV1Value": 20000000000000000000,
"faucetDripV1Interval": 3600,
"faucetDripV1Threshold": 100000000000000000000,
"faucetDripV2Interval": 604800,
"faucetDripV2Threshold": 20000000000000000000,
"faucetDripV2Value": 500000000000000000000,
"faucetAdminDripV1Interval": 86400,
"faucetAdminDripV1Threshold": 100000000000000000,
"faucetAdminDripV1Value": 1000000000000000000,
"faucetGelatoTreasury": "0x644CB00854EDC55FE8CCC9c1967BABb22F08Ad2f",
"faucetGelatoRecipient": "0x789e58a4B08A23a7f60141959C6ABbdC0D0C4Aba",
"faucetGelatoBalanceV1DripInterval": 86400,
"faucetGelatoBalanceV1Value": 1000000000000000000,
"faucetGelatoThreshold": 100000000000000000
}
......@@ -97,7 +97,7 @@ contract Deploy is Deployer {
/// Using this helps to reduce config across networks as the implementation
/// addresses will be the same across networks when deployed with create2.
function implSalt() public returns (bytes32) {
return keccak256(bytes(vm.envOr("IMPL_SALT", string("ether's phoenix"))));
return keccak256(bytes(vm.envOr("IMPL_SALT", string("ethers phoenix"))));
}
/// @notice Modifier that wraps a function in broadcasting.
......
......@@ -10,6 +10,11 @@ import { ProxyAdmin } from "src/universal/ProxyAdmin.sol";
import { Proxy } from "src/universal/Proxy.sol";
import { Faucet } from "src/periphery/faucet/Faucet.sol";
import { Drippie } from "src/periphery/drippie/Drippie.sol";
import { CheckGelatoLow } from "src/periphery/drippie/dripchecks/CheckGelatoLow.sol";
import { CheckBalanceHigh } from "src/periphery/drippie/dripchecks/CheckBalanceHigh.sol";
import { CheckBalanceLow } from "src/periphery/drippie/dripchecks/CheckBalanceLow.sol";
import { CheckTrue } from "src/periphery/drippie/dripchecks/CheckTrue.sol";
/// @title DeployPeriphery
/// @notice Script used to deploy periphery contracts.
......@@ -52,6 +57,11 @@ contract DeployPeriphery is Deployer {
/// @notice Deploy all of the implementations
function deployImplementations() public {
deployFaucet();
deployFaucetDrippie();
deployCheckTrue();
deployCheckBalanceLow();
deployCheckBalanceHigh();
deployCheckGelatoLow();
}
/// @notice Modifier that wraps a function in broadcasting.
......@@ -127,6 +137,102 @@ contract DeployPeriphery is Deployer {
}
}
/// @notice Deploy drippie contract.
function deployFaucetDrippie() public broadcast returns (address addr_) {
bytes32 salt = keccak256(bytes("FaucetDrippie"));
bytes32 initCodeHash =
keccak256(abi.encodePacked(type(Drippie).creationCode, abi.encode(cfg.faucetDrippieOwner())));
address preComputedAddress = computeCreate2Address(salt, initCodeHash);
if (preComputedAddress.code.length > 0) {
console.log("FaucetDrippie already deployed at %s", preComputedAddress);
save("FaucetDrippie", preComputedAddress);
addr_ = preComputedAddress;
} else {
Drippie drippie = new Drippie{ salt: salt }(cfg.faucetDrippieOwner());
save("FaucetDrippie", address(drippie));
console.log("FaucetDrippie deployed at %s", address(drippie));
addr_ = address(drippie);
}
}
/// @notice Deploy CheckTrue contract.
function deployCheckTrue() public broadcast returns (address addr_) {
bytes32 salt = keccak256(bytes("CheckTrue"));
bytes32 initCodeHash = keccak256(abi.encodePacked(type(CheckTrue).creationCode));
address preComputedAddress = computeCreate2Address(salt, initCodeHash);
if (preComputedAddress.code.length > 0) {
console.log("CheckTrue already deployed at %s", preComputedAddress);
save("CheckTrue", preComputedAddress);
addr_ = preComputedAddress;
} else {
CheckTrue checkTrue = new CheckTrue{ salt: salt }();
save("CheckTrue", address(checkTrue));
console.log("CheckTrue deployed at %s", address(checkTrue));
addr_ = address(checkTrue);
}
}
/// @notice Deploy CheckBalanceLow contract.
function deployCheckBalanceLow() public broadcast returns (address addr_) {
bytes32 salt = keccak256(bytes("CheckBalanceLow"));
bytes32 initCodeHash = keccak256(abi.encodePacked(type(CheckBalanceLow).creationCode));
address preComputedAddress = computeCreate2Address(salt, initCodeHash);
if (preComputedAddress.code.length > 0) {
console.log("CheckBalanceLow already deployed at %s", preComputedAddress);
save("CheckBalanceLow", preComputedAddress);
addr_ = preComputedAddress;
} else {
CheckBalanceLow checkBalanceLow = new CheckBalanceLow{ salt: salt }();
save("CheckBalanceLow", address(checkBalanceLow));
console.log("CheckBalanceLow deployed at %s", address(checkBalanceLow));
addr_ = address(checkBalanceLow);
}
}
/// @notice Deploy CheckBalanceHigh contract.
function deployCheckBalanceHigh() public broadcast returns (address addr_) {
bytes32 salt = keccak256(bytes("CheckBalanceHigh"));
bytes32 initCodeHash = keccak256(abi.encodePacked(type(CheckBalanceHigh).creationCode));
address preComputedAddress = computeCreate2Address(salt, initCodeHash);
if (preComputedAddress.code.length > 0) {
console.log("CheckBalanceHigh already deployed at %s", preComputedAddress);
save("CheckBalanceHigh", preComputedAddress);
addr_ = preComputedAddress;
} else {
CheckBalanceHigh checkBalanceHigh = new CheckBalanceHigh{ salt: salt }();
save("CheckBalanceHigh", address(checkBalanceHigh));
console.log("CheckBalanceHigh deployed at %s", address(checkBalanceHigh));
addr_ = address(checkBalanceHigh);
}
}
/// @notice Deploy CheckGelatoLow contract.
function deployCheckGelatoLow() public broadcast returns (address addr_) {
bytes32 salt = keccak256(bytes("CheckGelatoLow"));
bytes32 initCodeHash = keccak256(abi.encodePacked(type(CheckGelatoLow).creationCode));
address preComputedAddress = computeCreate2Address(salt, initCodeHash);
if (preComputedAddress.code.length > 0) {
console.log("CheckGelatoLow already deployed at %s", preComputedAddress);
save("CheckGelatoLow", preComputedAddress);
addr_ = preComputedAddress;
} else {
CheckGelatoLow checkGelatoLow = new CheckGelatoLow{ salt: salt }();
save("CheckGelatoLow", address(checkGelatoLow));
console.log("CheckGelatoLow deployed at %s", address(checkGelatoLow));
addr_ = address(checkGelatoLow);
}
}
/// @notice Initialize the Faucet
function initializeFaucet() public broadcast {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
......@@ -141,4 +247,160 @@ contract DeployPeriphery is Deployer {
require(Faucet(payable(faucetProxy)).ADMIN() == Faucet(payable(faucet)).ADMIN());
}
/// @notice installs the drip configs in the faucet drippie contract.
function installFaucetDrippieConfigs() public {
Drippie drippie = Drippie(mustGetAddress("FaucetDrippie"));
console.log("Installing faucet drips at %s", address(drippie));
installFaucetDripV1();
installFaucetDripV2();
installFaucetAdminDripV1();
installFaucetGelatoBalanceV1();
console.log("Faucet drip configs successfully installed");
}
/// @notice installs the FaucetDripV1 drip on the faucet drippie contract.
function installFaucetDripV1() public broadcast {
Drippie drippie = Drippie(mustGetAddress("FaucetDrippie"));
string memory dripName = "FaucetDripV1";
if (drippie.getDripStatus(dripName) == Drippie.DripStatus.NONE) {
console.log("installing %s", dripName);
Drippie.DripAction[] memory actions = new Drippie.DripAction[](1);
actions[0] =
Drippie.DripAction({ target: mustGetAddress("FaucetProxy"), data: "", value: cfg.faucetDripV1Value() });
drippie.create({
_name: dripName,
_config: Drippie.DripConfig({
reentrant: false,
interval: cfg.faucetDripV1Interval(),
dripcheck: CheckBalanceLow(mustGetAddress("CheckBalanceLow")),
checkparams: abi.encode(
CheckBalanceLow.Params({ target: mustGetAddress("FaucetProxy"), threshold: cfg.faucetDripV1Threshold() })
),
actions: actions
})
});
console.log("%s installed successfully", dripName);
} else {
console.log("%s already installed.", dripName);
}
_activateIfPausedDrip(drippie, dripName);
}
/// @notice installs the FaucetDripV2 drip on the faucet drippie contract.
function installFaucetDripV2() public broadcast {
Drippie drippie = Drippie(mustGetAddress("FaucetDrippie"));
string memory dripName = "FaucetDripV2";
if (drippie.getDripStatus(dripName) == Drippie.DripStatus.NONE) {
console.log("installing %s", dripName);
Drippie.DripAction[] memory actions = new Drippie.DripAction[](1);
actions[0] =
Drippie.DripAction({ target: mustGetAddress("FaucetProxy"), data: "", value: cfg.faucetDripV2Value() });
drippie.create({
_name: dripName,
_config: Drippie.DripConfig({
reentrant: false,
interval: cfg.faucetDripV2Interval(),
dripcheck: CheckBalanceLow(mustGetAddress("CheckBalanceLow")),
checkparams: abi.encode(
CheckBalanceLow.Params({ target: mustGetAddress("FaucetProxy"), threshold: cfg.faucetDripV2Threshold() })
),
actions: actions
})
});
console.log("%s installed successfully", dripName);
} else {
console.log("%s already installed.", dripName);
}
_activateIfPausedDrip(drippie, dripName);
}
/// @notice installs the FaucetAdminDripV1 drip on the faucet drippie contract.
function installFaucetAdminDripV1() public broadcast {
Drippie drippie = Drippie(mustGetAddress("FaucetDrippie"));
string memory dripName = "FaucetAdminDripV1";
if (drippie.getDripStatus(dripName) == Drippie.DripStatus.NONE) {
console.log("installing %s", dripName);
Drippie.DripAction[] memory actions = new Drippie.DripAction[](1);
actions[0] = Drippie.DripAction({
target: mustGetAddress("FaucetProxy"),
data: "",
value: cfg.faucetAdminDripV1Value()
});
drippie.create({
_name: dripName,
_config: Drippie.DripConfig({
reentrant: false,
interval: cfg.faucetAdminDripV1Interval(),
dripcheck: CheckBalanceLow(mustGetAddress("CheckBalanceLow")),
checkparams: abi.encode(
CheckBalanceLow.Params({
target: mustGetAddress("FaucetProxy"),
threshold: cfg.faucetAdminDripV1Threshold()
})
),
actions: actions
})
});
console.log("%s installed successfully", dripName);
} else {
console.log("%s already installed.", dripName);
}
_activateIfPausedDrip(drippie, dripName);
}
/// @notice installs the GelatoBalanceV1 drip on the faucet drippie contract.
function installFaucetGelatoBalanceV1() public broadcast {
Drippie drippie = Drippie(mustGetAddress("FaucetDrippie"));
string memory dripName = "GelatoBalanceV2";
if (drippie.getDripStatus(dripName) == Drippie.DripStatus.NONE) {
console.log("installing %s", dripName);
Drippie.DripAction[] memory actions = new Drippie.DripAction[](1);
actions[0] = Drippie.DripAction({
target: payable(cfg.faucetGelatoTreasury()),
data: abi.encodeWithSignature(
"depositFunds(address,address,uint256)",
cfg.faucetGelatoRecipient(),
// Gelato represents ETH as 0xeeeee....eeeee
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,
cfg.faucetGelatoBalanceV1Value()
),
value: cfg.faucetGelatoBalanceV1Value()
});
drippie.create({
_name: dripName,
_config: Drippie.DripConfig({
reentrant: false,
interval: cfg.faucetGelatoBalanceV1DripInterval(),
dripcheck: CheckGelatoLow(mustGetAddress("CheckGelatoLow")),
checkparams: abi.encode(
CheckGelatoLow.Params({
recipient: cfg.faucetGelatoRecipient(),
threshold: cfg.faucetGelatoThreshold(),
treasury: cfg.faucetGelatoTreasury()
})
),
actions: actions
})
});
console.log("%s installed successfully", dripName);
} else {
console.log("%s already installed.", dripName);
}
_activateIfPausedDrip(drippie, dripName);
}
function _activateIfPausedDrip(Drippie drippie, string memory dripName) internal {
if (drippie.getDripStatus(dripName) == Drippie.DripStatus.PAUSED) {
console.log("%s is paused, activating", dripName);
drippie.status(dripName, Drippie.DripStatus.ACTIVE);
console.log("%s activated", dripName);
require(drippie.getDripStatus(dripName) == Drippie.DripStatus.ACTIVE);
}
}
}
......@@ -13,6 +13,21 @@ contract PeripheryDeployConfig is Script {
string internal _json;
address public faucetAdmin;
address public faucetDrippieOwner;
uint256 public faucetDripV1Value;
uint256 public faucetDripV1Interval;
uint256 public faucetDripV1Threshold;
uint256 public faucetDripV2Value;
uint256 public faucetDripV2Interval;
uint256 public faucetDripV2Threshold;
uint256 public faucetAdminDripV1Value;
uint256 public faucetAdminDripV1Interval;
uint256 public faucetAdminDripV1Threshold;
address public faucetGelatoTreasury;
address public faucetGelatoRecipient;
uint256 public faucetGelatoBalanceV1DripInterval;
uint256 public faucetGelatoBalanceV1Value;
uint256 public faucetGelatoThreshold;
constructor(string memory _path) {
console.log("PeripheryDeployConfig: reading file %s", _path);
......@@ -24,5 +39,20 @@ contract PeripheryDeployConfig is Script {
}
faucetAdmin = stdJson.readAddress(_json, "$.faucetAdmin");
faucetDrippieOwner = stdJson.readAddress(_json, "$.faucetDrippieOwner");
faucetDripV1Value = stdJson.readUint(_json, "$.faucetDripV1Value");
faucetDripV1Interval = stdJson.readUint(_json, "$.faucetDripV1Interval");
faucetDripV1Threshold = stdJson.readUint(_json, "$.faucetDripV1Threshold");
faucetDripV2Value = stdJson.readUint(_json, "$.faucetDripV2Value");
faucetDripV2Interval = stdJson.readUint(_json, "$.faucetDripV2Interval");
faucetDripV2Threshold = stdJson.readUint(_json, "$.faucetDripV2Threshold");
faucetAdminDripV1Value = stdJson.readUint(_json, "$.faucetAdminDripV1Value");
faucetAdminDripV1Interval = stdJson.readUint(_json, "$.faucetAdminDripV1Interval");
faucetAdminDripV1Threshold = stdJson.readUint(_json, "$.faucetAdminDripV1Threshold");
faucetGelatoTreasury = stdJson.readAddress(_json, "$.faucetGelatoTreasury");
faucetGelatoRecipient = stdJson.readAddress(_json, "$.faucetGelatoRecipient");
faucetGelatoBalanceV1DripInterval = stdJson.readUint(_json, "$.faucetGelatoBalanceV1DripInterval");
faucetGelatoBalanceV1Value = stdJson.readUint(_json, "$.faucetGelatoBalanceV1Value");
faucetGelatoThreshold = stdJson.readUint(_json, "$.faucetGelatoThreshold");
}
}
......@@ -244,4 +244,11 @@ contract Drippie is AssetReceiver {
emit DripExecuted(_name, _name, msg.sender, block.timestamp);
}
/// @notice Returns the status of a given drip.
/// @param _name Drip to check.
/// @return DripStatus of the given drip.
function getDripStatus(string calldata _name) public view returns (DripStatus) {
return drips[_name].status;
}
}
......@@ -246,12 +246,122 @@ A move against a particular claim is no longer possible once the parent of the d
### Resolution
Resolving the FDG determines which team won.
This is done by examining the left-most, uncontested (or undisputed) claim.
If the depth of this claim is odd then Challengers win, otherwise Defenders win.
Resolution is only possible once the Clock of the left-most, uncontested claim has expired.
Given these rules, players are motivated to move quickly to challenge dishonest claims.
Resolving the FDG determines which team won the game. To do this, we use the internal sub game structure.
Each claim within the game is the root of its own sub game. These subgames are modeled as nested DAGs, each with a max
depth of 1. In order for a claim to be considered countered, only one of its children must be uncountered. Subgames
can also not be resolved until all of their children, which are subgames themselves, have been resolved and
the potential opponent's chess clock has run out. Because each claim is the root of its own sub-game,
truth percolates upwards towards the root claim by resolving each individual sub-game bottom-up.
In a game like the one below, we can resolve up from the deepest subgames. Here, we'd resolve `b0`
to uncountered and `a0` to countered by walking up from their deepest children, and now that all children of the
root game are resolved, we can resolve the root to countered due to `b0` remaining uncountered.
<!--
digraph G {
rankdir=LR
newrank=true
node [shape=plaintext]
subgraph cluster_01 {
label = "Legend";
key [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
<tr><td align="right" port="i1">bisection</td></tr>
<tr><td align="right" port="i2">resolution</td></tr>
</table>>]
key2 [label=<<table border="0" cellpadding="" cellspacing="0" cellborder="0">
<tr><td port="i1">&nbsp;</td></tr>
<tr><td port="i2">&nbsp;</td></tr>
</table>>]
key:i1:e -> key2:i1:w [color=green]
key:i2:e -> key2:i2:w [color=coral1, style=dotted]
}
subgraph cluster_0 {
color=cornflowerblue;
node [style=filled];
a0 -> a1 [color=green];
a1 -> a0 [color=coral1, style=dotted];
subgraph cluster_0_0 {
label = "subgame #5";
color=purple;
a1 -> a2 [color=green];
a2 -> a1 [color=coral1, style=dotted];
subgraph cluster_0_1 {
label = "subgame #6";
color=magenta;
a2 -> a3 [color=green];
a3 -> a2 [color=coral1, style=dotted];
a2 -> a4 [color=green];
a4 -> a2 [color=coral1, style=dotted];
subgraph cluster_0_2 {
label = "subgame #7";
color=lightpink;
a3
}
subgraph cluster_0_3 {
label = "subgame #8";
color=lightpink;
a4 -> a5 [color=green];
a5 -> a4 [color=coral1, style=dotted];
subgraph cluster_0_4 {
label = "subgame #9";
color=palegreen;
a5
}
}
}
}
label = "subgame #4";
}
subgraph cluster_1 {
node [style=filled];
label = "subgame #1";
color=cornflowerblue
b0 -> b1 [color=green];
b1 -> b0 [color=coral1, style=dotted];
subgraph cluster_1_0 {
label = "subgame #2";
color=purple;
b1 -> b2 [color=green];
b2 -> b1 [color=coral1, style=dotted];
subgraph cluster_1_1 {
label = "subgame #3";
edge [style=invis]
color=magenta;
b2
}
}
}
Root -> a0 [color=green];
Root -> b0 [color=green];
a0 -> Root [color=coral1, style=dotted];
b0 -> Root [color=coral1, style=dotted];
Root [shape=Mdiamond];
}
-->
<!-- markdownlint-disable no-inline-html -->
<p align="center">
<img src="https://github.com/ethereum-optimism/optimism/assets/8406232/9b20ba8d-0b64-47b3-9962-5533f7eb4ef7" width=60%>
</p>
Given these rules, players are motivated to move quickly to challenge all dishonest claims.
Each move bisects the execution trace and eventually, `MAX_GAME_DEPTH` is reached where disputes
can be settled conclusively. Dishonest players are disincentivized to participate, via backwards induction,
as an invalid claim won't remain uncontested. Further incentives can be added to the game by requiring
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment