feat: add guarded Base Sepolia encore
This commit is contained in:
+220
-8
@@ -6,6 +6,7 @@ import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
|
||||
import {DemoScript} from "../script/lib/DemoScript.sol";
|
||||
import {DeployV1} from "../script/DeployV1.s.sol";
|
||||
import {SeedV1Demo} from "../script/SeedV1Demo.s.sol";
|
||||
import {SeedBaseSepolia} from "../script/SeedBaseSepolia.s.sol";
|
||||
import {CheckState} from "../script/CheckState.s.sol";
|
||||
import {UpgradeV2} from "../script/UpgradeV2.s.sol";
|
||||
import {TransferV2Demo} from "../script/TransferV2Demo.s.sol";
|
||||
@@ -88,7 +89,8 @@ contract ScriptPreflightTest is Test {
|
||||
}
|
||||
|
||||
function testUnsupportedChainsAreRejectedBeforeBroadcast() public {
|
||||
uint256[3] memory rejected = [uint256(1), uint256(8453), uint256(7777777)];
|
||||
uint256[7] memory rejected =
|
||||
[uint256(1), uint256(10), uint256(56), uint256(137), uint256(8453), uint256(42161), uint256(7777777)];
|
||||
for (uint256 i; i < rejected.length; ++i) {
|
||||
vm.expectRevert(abi.encodeWithSelector(DemoScript.UnsupportedChain.selector, rejected[i]));
|
||||
harness.requireSupportedChain(rejected[i]);
|
||||
@@ -210,7 +212,7 @@ contract ScriptPreflightTest is Test {
|
||||
harness.readManifest(path, true);
|
||||
}
|
||||
|
||||
function testBaseManifestRequiresOnlyOwnerActor() public {
|
||||
function testBaseManifestRequiresExactPresenterRecipientActors() public {
|
||||
string memory path = _writePublicBaseManifest();
|
||||
vm.chainId(84532);
|
||||
_etchManifestContracts();
|
||||
@@ -270,16 +272,18 @@ contract ScriptPreflightTest is Test {
|
||||
harness.readManifest(path, true);
|
||||
}
|
||||
|
||||
function testBaseSepoliaManifestUsesCamelCaseAndOmitsUnavailableUrls() public {
|
||||
function testBaseSepoliaManifestUsesPublicUrlsAndExactActors() public {
|
||||
string memory path = _writePublicBaseManifest();
|
||||
vm.chainId(84532);
|
||||
_etchManifestContracts();
|
||||
DemoScript.Manifest memory manifest = harness.readManifest(path, true);
|
||||
assertEq(manifest.network, "baseSepolia");
|
||||
assertEq(manifest.rpcUrl, "");
|
||||
assertEq(manifest.explorerBaseUrl, "");
|
||||
assertEq(manifest.actors[0].label, "owner");
|
||||
assertEq(manifest.rpcUrl, "https://public.invalid");
|
||||
assertEq(manifest.explorerBaseUrl, "https://sepolia.basescan.org");
|
||||
assertEq(manifest.actors[0].label, "Presenter");
|
||||
assertEq(manifest.actors[0].address_, OWNER);
|
||||
assertEq(manifest.actors[1].label, "Recipient");
|
||||
assertEq(manifest.actors[1].address_, BOB);
|
||||
}
|
||||
|
||||
function testSerializedManifestContainsPublicAddressesAndNoSecrets() public {
|
||||
@@ -332,6 +336,76 @@ contract ScriptPreflightTest is Test {
|
||||
assertEq(manifest.actors[2].address_, 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC);
|
||||
}
|
||||
|
||||
function testBaseDeployRejectsMismatchedConfiguredSenderBeforeBroadcast() public {
|
||||
string memory path = string.concat(fixtureDir, "/base-deploy-mismatch.json");
|
||||
vm.chainId(84532);
|
||||
vm.setEnv("SCRIPT_SENDER", vm.toString(OWNER));
|
||||
vm.setEnv("BASE_SEPOLIA_SENDER", vm.toString(ALICE));
|
||||
vm.setEnv("BASE_SEPOLIA_RECIPIENT", vm.toString(BOB));
|
||||
vm.setEnv("BASE_SEPOLIA_PUBLIC_RPC_URL", "https://public.invalid");
|
||||
vm.setEnv("DEPLOYMENT_MANIFEST_PATH", path);
|
||||
uint256 nonceBefore = vm.getNonce(OWNER);
|
||||
DeployV1 deployer = new DeployV1();
|
||||
|
||||
vm.expectRevert(
|
||||
abi.encodeWithSelector(DemoScript.UnexpectedAddress.selector, "BASE_SEPOLIA_SENDER", OWNER, ALICE)
|
||||
);
|
||||
deployer.run();
|
||||
|
||||
assertEq(vm.getNonce(OWNER), nonceBefore);
|
||||
}
|
||||
|
||||
function testBaseDeployWritesPublicRpcExplorerAndPresenterRecipientActors() public {
|
||||
string memory path = string.concat(fixtureDir, "/base-deployed.json");
|
||||
vm.chainId(84532);
|
||||
_setBaseConfig(OWNER, BOB, "https://public.invalid");
|
||||
vm.setEnv("DEPLOYMENT_MANIFEST_PATH", path);
|
||||
|
||||
(address token, address proxy, address implementation) = new DeployV1().run();
|
||||
|
||||
_etchManifestContracts();
|
||||
DemoScript.Manifest memory manifest = harness.readManifest(path, false);
|
||||
assertEq(manifest.token, token);
|
||||
assertEq(manifest.proxy, proxy);
|
||||
assertEq(manifest.implementation, implementation);
|
||||
assertEq(manifest.owner, OWNER);
|
||||
assertEq(manifest.rpcUrl, "https://public.invalid");
|
||||
assertEq(manifest.explorerBaseUrl, "https://sepolia.basescan.org");
|
||||
assertEq(manifest.actors.length, 2);
|
||||
assertEq(manifest.actors[0].label, "Presenter");
|
||||
assertEq(manifest.actors[0].address_, OWNER);
|
||||
assertEq(manifest.actors[1].label, "Recipient");
|
||||
assertEq(manifest.actors[1].address_, BOB);
|
||||
}
|
||||
|
||||
function testBaseDeployRejectsZeroOrSameRecipientBeforeBroadcast() public {
|
||||
vm.chainId(84532);
|
||||
DeployV1 deployer = new DeployV1();
|
||||
address[2] memory invalidRecipients = [address(0), OWNER];
|
||||
for (uint256 i; i < invalidRecipients.length; ++i) {
|
||||
_setBaseConfig(OWNER, invalidRecipients[i], "https://public.invalid");
|
||||
uint256 nonceBefore = vm.getNonce(OWNER);
|
||||
vm.expectRevert(DemoScript.InvalidActorConfiguration.selector);
|
||||
deployer.run();
|
||||
assertEq(vm.getNonce(OWNER), nonceBefore);
|
||||
}
|
||||
}
|
||||
|
||||
function testBaseDeployRejectsCredentialBearingPublicRpcBeforeBroadcast() public {
|
||||
vm.chainId(84532);
|
||||
DeployV1 deployer = new DeployV1();
|
||||
string[3] memory invalidUrls =
|
||||
["http://public.invalid", "https://user@public.invalid", "https://public.invalid/path?api_key=fixture"];
|
||||
bytes4 invalidPublicRpcUrl = bytes4(keccak256("InvalidPublicRpcUrl()"));
|
||||
for (uint256 i; i < invalidUrls.length; ++i) {
|
||||
_setBaseConfig(OWNER, BOB, invalidUrls[i]);
|
||||
uint256 nonceBefore = vm.getNonce(OWNER);
|
||||
vm.expectRevert(invalidPublicRpcUrl);
|
||||
deployer.run();
|
||||
assertEq(vm.getNonce(OWNER), nonceBefore);
|
||||
}
|
||||
}
|
||||
|
||||
function testSeedV1DemoExecutesExactActOneStateAndCheckStateAcceptsIt() public {
|
||||
(string memory path, DemoScript.Manifest memory manifest) = _deployFixtureNamed("seed-v1");
|
||||
vm.setEnv("DEPLOYMENT_MANIFEST_PATH", path);
|
||||
@@ -350,6 +424,38 @@ contract ScriptPreflightTest is Test {
|
||||
new CheckState().run();
|
||||
}
|
||||
|
||||
function testSeedBaseSepoliaCreatesExactPresenterDepositState() public {
|
||||
(string memory path, DemoScript.Manifest memory manifest) = _deployBaseFixtureNamed("seed-base");
|
||||
_setBaseConfig(OWNER, BOB, "https://public.invalid");
|
||||
vm.setEnv("DEPLOYMENT_MANIFEST_PATH", path);
|
||||
|
||||
new SeedBaseSepolia().run();
|
||||
|
||||
BankV1 bank = BankV1(manifest.proxy);
|
||||
MockUSDC token = MockUSDC(manifest.token);
|
||||
assertEq(bank.balanceOf(OWNER), 1_000e6);
|
||||
assertEq(bank.balanceOf(BOB), 0);
|
||||
assertEq(bank.totalLiabilities(), 1_000e6);
|
||||
assertEq(token.balanceOf(manifest.proxy), 1_000e6);
|
||||
assertEq(token.balanceOf(OWNER), 0);
|
||||
}
|
||||
|
||||
function testSeedBaseSepoliaRejectsMismatchedSenderBeforeMintOrBroadcast() public {
|
||||
(string memory path, DemoScript.Manifest memory manifest) = _deployBaseFixtureNamed("seed-base-mismatch");
|
||||
_setBaseConfig(OWNER, BOB, "https://public.invalid");
|
||||
vm.setEnv("BASE_SEPOLIA_SENDER", vm.toString(ALICE));
|
||||
vm.setEnv("DEPLOYMENT_MANIFEST_PATH", path);
|
||||
SeedBaseSepolia seeder = new SeedBaseSepolia();
|
||||
|
||||
vm.expectRevert(
|
||||
abi.encodeWithSelector(DemoScript.UnexpectedAddress.selector, "BASE_SEPOLIA_SENDER", OWNER, ALICE)
|
||||
);
|
||||
seeder.run();
|
||||
|
||||
assertEq(MockUSDC(manifest.token).totalSupply(), 0);
|
||||
assertEq(BankV1(manifest.proxy).totalLiabilities(), 0);
|
||||
}
|
||||
|
||||
function testCheckStateRejectsManifestImplementationMismatch() public {
|
||||
(string memory path, DemoScript.Manifest memory manifest) = _deployUpgradeFixtureNamed("check-mismatch");
|
||||
vm.writeJson(string.concat('"', vm.toString(manifest.token), '"'), path, ".implementation");
|
||||
@@ -506,6 +612,73 @@ contract ScriptPreflightTest is Test {
|
||||
assertEq(MockUSDC(manifest.token).balanceOf(manifest.proxy), 1_400e6);
|
||||
}
|
||||
|
||||
function testBaseUpgradeAndTransferPreserveAccountingAndMoveExactBalance() public {
|
||||
(string memory path, DemoScript.Manifest memory manifest) = _deployBaseFixtureNamed("transfer-base");
|
||||
_setBaseConfig(OWNER, BOB, "https://public.invalid");
|
||||
vm.setEnv("DEPLOYMENT_MANIFEST_PATH", path);
|
||||
new SeedBaseSepolia().run();
|
||||
|
||||
string memory pending = string.concat(fixtureDir, "/base-upgrade-pending.json");
|
||||
(, address implementation) = new UpgradeV2Harness().runWithPaths(path, pending, OWNER);
|
||||
vm.writeJson(string.concat('"', vm.toString(implementation), '"'), path, ".implementation");
|
||||
|
||||
new TransferV2DemoHarness().runWithPath(path);
|
||||
|
||||
BankV2 bank = BankV2(manifest.proxy);
|
||||
MockUSDC token = MockUSDC(manifest.token);
|
||||
assertEq(bank.balanceOf(OWNER), 750e6);
|
||||
assertEq(bank.balanceOf(BOB), 250e6);
|
||||
assertEq(bank.totalLiabilities(), 1_000e6);
|
||||
assertEq(token.balanceOf(manifest.proxy), 1_000e6);
|
||||
new CheckStateHarness().runWithPath(path, "invariants");
|
||||
}
|
||||
|
||||
function testBaseUpgradeRejectsMismatchedConfiguredSenderBeforeBroadcast() public {
|
||||
(string memory path, DemoScript.Manifest memory manifest) = _deployBaseFixtureNamed("upgrade-base-mismatch");
|
||||
_setBaseConfig(ALICE, BOB, "https://public.invalid");
|
||||
string memory pending = string.concat(fixtureDir, "/base-mismatch-pending.json");
|
||||
UpgradeV2Harness upgrader = new UpgradeV2Harness();
|
||||
|
||||
vm.expectRevert(
|
||||
abi.encodeWithSelector(DemoScript.UnexpectedAddress.selector, "BASE_SEPOLIA_SENDER", OWNER, ALICE)
|
||||
);
|
||||
upgrader.runWithPaths(path, pending, OWNER);
|
||||
|
||||
assertEq(Upgrades.getImplementationAddress(manifest.proxy), manifest.implementation);
|
||||
}
|
||||
|
||||
function testBaseCheckStateRejectsConfiguredActorMismatch() public {
|
||||
(string memory path, DemoScript.Manifest memory manifest) = _deployBaseFixtureNamed("check-base-mismatch");
|
||||
_setBaseConfig(OWNER, ALICE, "https://public.invalid");
|
||||
CheckStateHarness checker = new CheckStateHarness();
|
||||
|
||||
vm.expectRevert(
|
||||
abi.encodeWithSelector(
|
||||
DemoScript.UnexpectedAddress.selector, "Recipient", manifest.actors[1].address_, ALICE
|
||||
)
|
||||
);
|
||||
checker.runWithPath(path, "deployed");
|
||||
}
|
||||
|
||||
function testBaseTransferRejectsInvalidRecipientBeforeBroadcast() public {
|
||||
(string memory path, DemoScript.Manifest memory manifest) = _deployBaseFixtureNamed("transfer-base-invalid");
|
||||
_setBaseConfig(OWNER, BOB, "https://public.invalid");
|
||||
vm.setEnv("DEPLOYMENT_MANIFEST_PATH", path);
|
||||
new SeedBaseSepolia().run();
|
||||
string memory pending = string.concat(fixtureDir, "/base-transfer-upgrade.json");
|
||||
(, address implementation) = new UpgradeV2Harness().runWithPaths(path, pending, OWNER);
|
||||
vm.writeJson(string.concat('"', vm.toString(implementation), '"'), path, ".implementation");
|
||||
_setBaseConfig(OWNER, OWNER, "https://public.invalid");
|
||||
TransferV2DemoHarness transfer = new TransferV2DemoHarness();
|
||||
|
||||
vm.expectRevert(DemoScript.InvalidActorConfiguration.selector);
|
||||
transfer.runWithPath(path);
|
||||
|
||||
BankV2 bank = BankV2(manifest.proxy);
|
||||
assertEq(bank.balanceOf(OWNER), 1_000e6);
|
||||
assertEq(bank.balanceOf(BOB), 0);
|
||||
}
|
||||
|
||||
function _snapshot() internal pure returns (UpgradeV2.Snapshot memory snapshot) {
|
||||
snapshot.proxy = PROXY;
|
||||
snapshot.implementation = IMPLEMENTATION;
|
||||
@@ -593,6 +766,30 @@ contract ScriptPreflightTest is Test {
|
||||
vm.writeFile(path, harness.serializeManifest(manifest));
|
||||
}
|
||||
|
||||
function _deployBaseFixtureNamed(string memory name)
|
||||
internal
|
||||
returns (string memory path, DemoScript.Manifest memory manifest)
|
||||
{
|
||||
vm.chainId(84532);
|
||||
MockUSDC deployedToken = new MockUSDC(OWNER);
|
||||
address deployedProxy = Upgrades.deployUUPSProxy(
|
||||
"BankV1.sol:BankV1", abi.encodeCall(BankV1.initialize, (address(deployedToken), OWNER))
|
||||
);
|
||||
manifest.schemaVersion = 1;
|
||||
manifest.network = "baseSepolia";
|
||||
manifest.chainId = 84532;
|
||||
manifest.deploymentBlock = 1;
|
||||
manifest.rpcUrl = "https://public.invalid";
|
||||
manifest.explorerBaseUrl = "https://sepolia.basescan.org";
|
||||
manifest.token = address(deployedToken);
|
||||
manifest.proxy = deployedProxy;
|
||||
manifest.implementation = Upgrades.getImplementationAddress(deployedProxy);
|
||||
manifest.owner = OWNER;
|
||||
manifest.actors = _baseActors();
|
||||
path = string.concat(fixtureDir, "/", name, ".json");
|
||||
vm.writeFile(path, harness.serializeManifest(manifest));
|
||||
}
|
||||
|
||||
function _seedState(DemoScript.Manifest memory manifest) internal {
|
||||
MockUSDC deployedToken = MockUSDC(manifest.token);
|
||||
BankV1 deployedBank = BankV1(manifest.proxy);
|
||||
@@ -689,7 +886,7 @@ contract ScriptPreflightTest is Test {
|
||||
vm.writeFile(
|
||||
path,
|
||||
string.concat(
|
||||
'{"schemaVersion":1,"network":"baseSepolia","chainId":84532,"deploymentBlock":1,"token":"',
|
||||
'{"schemaVersion":1,"network":"baseSepolia","chainId":84532,"deploymentBlock":1,"rpcUrl":"https://public.invalid","explorerBaseUrl":"https://sepolia.basescan.org","token":"',
|
||||
vm.toString(TOKEN),
|
||||
'","proxy":"',
|
||||
vm.toString(PROXY),
|
||||
@@ -697,8 +894,10 @@ contract ScriptPreflightTest is Test {
|
||||
vm.toString(IMPLEMENTATION),
|
||||
'","owner":"',
|
||||
vm.toString(OWNER),
|
||||
'","actors":[{"label":"owner","address":"',
|
||||
'","actors":[{"label":"Presenter","address":"',
|
||||
vm.toString(OWNER),
|
||||
'"},{"label":"Recipient","address":"',
|
||||
vm.toString(BOB),
|
||||
'"}]}'
|
||||
)
|
||||
);
|
||||
@@ -717,6 +916,12 @@ contract ScriptPreflightTest is Test {
|
||||
actors[2] = DemoScript.Actor({label: "Bob", address_: 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC});
|
||||
}
|
||||
|
||||
function _baseActors() internal pure returns (DemoScript.Actor[] memory actors) {
|
||||
actors = new DemoScript.Actor[](2);
|
||||
actors[0] = DemoScript.Actor({label: "Presenter", address_: OWNER});
|
||||
actors[1] = DemoScript.Actor({label: "Recipient", address_: BOB});
|
||||
}
|
||||
|
||||
function _contains(string memory haystack, string memory needle) internal pure returns (bool) {
|
||||
bytes memory h = bytes(haystack);
|
||||
bytes memory n = bytes(needle);
|
||||
@@ -733,4 +938,11 @@ contract ScriptPreflightTest is Test {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function _setBaseConfig(address sender, address recipient, string memory publicRpcUrl) internal {
|
||||
vm.setEnv("SCRIPT_SENDER", vm.toString(sender));
|
||||
vm.setEnv("BASE_SEPOLIA_SENDER", vm.toString(sender));
|
||||
vm.setEnv("BASE_SEPOLIA_RECIPIENT", vm.toString(recipient));
|
||||
vm.setEnv("BASE_SEPOLIA_PUBLIC_RPC_URL", publicRpcUrl);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user