feat: add guarded Base Sepolia encore

This commit is contained in:
golem
2026-08-21 16:01:35 -06:00
parent 94f2ad6e09
commit 90b0a11b8a
16 changed files with 784 additions and 25 deletions
+6
View File
@@ -20,6 +20,12 @@ contract CheckState is DemoScript {
_printEducationalWarning();
bool deployedStage = keccak256(bytes(stage)) == keccak256("deployed");
Manifest memory manifest = _readManifest(manifestPath, !deployedStage);
if (block.chainid == BASE_SEPOLIA_CHAIN_ID) {
address expectedSender = vm.envAddress("BASE_SEPOLIA_SENDER");
(address presenter, address recipient,) = _requireBaseConfig(expectedSender);
_assertAddress("Presenter", manifest.actors[0].address_, presenter);
_assertAddress("Recipient", manifest.actors[1].address_, recipient);
}
BankV1 bank = BankV1(manifest.proxy);
MockUSDC token = MockUSDC(manifest.token);
+13 -5
View File
@@ -11,6 +11,12 @@ contract DeployV1 is DemoScript {
_requireSupportedChain(block.chainid);
_printEducationalWarning();
address sender = vm.envAddress("SCRIPT_SENDER");
address recipient;
string memory publicRpcUrl;
if (block.chainid == BASE_SEPOLIA_CHAIN_ID) {
(, recipient, publicRpcUrl) = _requireBaseConfig(sender);
}
if (block.chainid == ANVIL_CHAIN_ID) {
(uint256 ownerKey, address derivedOwner) = _deriveLocalActor(block.chainid, 0);
@@ -40,17 +46,18 @@ contract DeployV1 is DemoScript {
manifest.network = block.chainid == ANVIL_CHAIN_ID ? "anvil" : "baseSepolia";
manifest.chainId = block.chainid;
manifest.deploymentBlock = 0;
manifest.rpcUrl = block.chainid == ANVIL_CHAIN_ID ? "http://127.0.0.1:8545" : "";
manifest.rpcUrl = block.chainid == ANVIL_CHAIN_ID ? "http://127.0.0.1:8545" : publicRpcUrl;
manifest.explorerBaseUrl = block.chainid == BASE_SEPOLIA_CHAIN_ID ? "https://sepolia.basescan.org" : "";
manifest.token = tokenAddress;
manifest.proxy = proxy;
manifest.implementation = implementation;
manifest.owner = sender;
manifest.actors = _actors(sender);
manifest.actors = _actors(sender, recipient);
_writeManifest(_manifestPath(PENDING_MANIFEST_PATH), manifest);
}
function _actors(address sender) private view returns (Actor[] memory actors) {
function _actors(address sender, address recipient) private view returns (Actor[] memory actors) {
if (block.chainid == ANVIL_CHAIN_ID) {
actors = new Actor[](3);
actors[0] = Actor({label: "owner", address_: sender});
@@ -59,8 +66,9 @@ contract DeployV1 is DemoScript {
actors[1].label = "Alice";
actors[2].label = "Bob";
} else {
actors = new Actor[](1);
actors[0] = Actor({label: "owner", address_: sender});
actors = new Actor[](2);
actors[0] = Actor({label: "Presenter", address_: sender});
actors[1] = Actor({label: "Recipient", address_: recipient});
}
}
}
+35
View File
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.35;
import {BankV1} from "../src/BankV1.sol";
import {MockUSDC} from "../src/MockUSDC.sol";
import {DemoScript} from "./lib/DemoScript.sol";
contract SeedBaseSepolia is DemoScript {
uint256 internal constant SEED_AMOUNT = 1_000e6;
function run() external {
if (block.chainid != BASE_SEPOLIA_CHAIN_ID) revert UnsupportedChain(block.chainid);
_printEducationalWarning();
address scriptSender = vm.envAddress("SCRIPT_SENDER");
(address presenter, address recipient,) = _requireBaseConfig(scriptSender);
Manifest memory manifest = _readManifest(_manifestPath(ACTIVE_MANIFEST_PATH), true);
_assertAddress("Presenter", manifest.actors[0].address_, presenter);
_assertAddress("Recipient", manifest.actors[1].address_, recipient);
BankV1 bank = BankV1(manifest.proxy);
MockUSDC token = MockUSDC(manifest.token);
_assertDeployedState(manifest);
vm.startBroadcast(presenter);
token.mint(presenter, SEED_AMOUNT);
token.approve(manifest.proxy, SEED_AMOUNT);
bank.deposit(SEED_AMOUNT);
vm.stopBroadcast();
_assertUint("Presenter internal balance", SEED_AMOUNT, bank.balanceOf(presenter));
_assertUint("Recipient internal balance", 0, bank.balanceOf(recipient));
_assertUint("liabilities", SEED_AMOUNT, bank.totalLiabilities());
_assertUint("reserves", SEED_AMOUNT, token.balanceOf(manifest.proxy));
}
}
+37 -1
View File
@@ -11,9 +11,18 @@ contract TransferV2Demo is DemoScript {
}
function _run(string memory manifestPath) internal {
if (block.chainid != ANVIL_CHAIN_ID) revert UnsupportedChain(block.chainid);
_requireSupportedChain(block.chainid);
_printEducationalWarning();
Manifest memory manifest = _readManifest(manifestPath, true);
if (block.chainid == BASE_SEPOLIA_CHAIN_ID) {
_runBase(manifest);
return;
}
_runLocal(manifest);
}
function _runLocal(Manifest memory manifest) private {
(uint256 aliceKey, address alice) = _deriveLocalActor(block.chainid, 1);
address bob = manifest.actors[2].address_;
_assertAddress("Alice actor", manifest.actors[1].address_, alice);
@@ -38,4 +47,31 @@ contract TransferV2Demo is DemoScript {
_assertUint("reserves", reserves, token.balanceOf(manifest.proxy));
_assertUint("surplus", 0, token.balanceOf(manifest.proxy) - bank.totalLiabilities());
}
function _runBase(Manifest memory manifest) private {
address scriptSender = vm.envAddress("SCRIPT_SENDER");
(address presenter, address recipient,) = _requireBaseConfig(scriptSender);
_assertAddress("Presenter", manifest.actors[0].address_, presenter);
_assertAddress("Recipient", manifest.actors[1].address_, recipient);
BankV2 bank = BankV2(manifest.proxy);
MockUSDC token = MockUSDC(manifest.token);
_assertUint("version", 2, bank.contractVersion());
_assertUint("Presenter internal balance", 1_000e6, bank.balanceOf(presenter));
_assertUint("Recipient internal balance", 0, bank.balanceOf(recipient));
uint256 liabilities = bank.totalLiabilities();
uint256 reserves = token.balanceOf(manifest.proxy);
_assertUint("liabilities", 1_000e6, liabilities);
_assertUint("reserves", 1_000e6, reserves);
vm.startBroadcast(presenter);
bank.transferBalance(recipient, 250e6);
vm.stopBroadcast();
_assertUint("Presenter internal balance", 750e6, bank.balanceOf(presenter));
_assertUint("Recipient internal balance", 250e6, bank.balanceOf(recipient));
_assertUint("liabilities", liabilities, bank.totalLiabilities());
_assertUint("reserves", reserves, token.balanceOf(manifest.proxy));
_assertUint("surplus", 0, token.balanceOf(manifest.proxy) - bank.totalLiabilities());
}
}
+8
View File
@@ -37,7 +37,15 @@ contract UpgradeV2 is DemoScript {
internal
returns (bool upgraded, address implementation)
{
address configuredRecipient;
if (block.chainid == BASE_SEPOLIA_CHAIN_ID) {
(, configuredRecipient,) = _requireBaseConfig(sender);
}
Manifest memory manifest = _readManifest(activePath, true);
if (block.chainid == BASE_SEPOLIA_CHAIN_ID) {
_assertAddress("Presenter", manifest.actors[0].address_, sender);
_assertAddress("Recipient", manifest.actors[1].address_, configuredRecipient);
}
BankV1 bank = BankV1(manifest.proxy);
address actualImplementation = Upgrades.getImplementationAddress(manifest.proxy);
+75 -4
View File
@@ -20,6 +20,7 @@ abstract contract DemoScript is Script {
error InvalidDeploymentBlock();
error InvalidManifestSchema(uint256 schemaVersion);
error InvalidActorConfiguration();
error InvalidPublicRpcUrl();
error UnexpectedState(string label, uint256 expected, uint256 actual);
error UnexpectedAddress(string label, address expected, address actual);
@@ -60,6 +61,72 @@ abstract contract DemoScript is Script {
return vm.envOr("DEPLOYMENT_MANIFEST_PATH", defaultPath);
}
function _requireBaseSender(address scriptSender) internal view returns (address sender) {
sender = vm.envAddress("BASE_SEPOLIA_SENDER");
_assertAddress("BASE_SEPOLIA_SENDER", scriptSender, sender);
}
function _requireBaseConfig(address scriptSender)
internal
view
returns (address sender, address recipient, string memory publicRpcUrl)
{
sender = _requireBaseSender(scriptSender);
recipient = vm.envAddress("BASE_SEPOLIA_RECIPIENT");
if (sender == address(0) || recipient == address(0) || sender == recipient) revert InvalidActorConfiguration();
publicRpcUrl = vm.envString("BASE_SEPOLIA_PUBLIC_RPC_URL");
_requirePublicHttpsUrl(publicRpcUrl);
}
function _requirePublicHttpsUrl(string memory value) internal pure {
bytes memory url = bytes(value);
bytes memory prefix = bytes("https://");
if (url.length <= prefix.length) revert InvalidPublicRpcUrl();
for (uint256 i; i < prefix.length; ++i) {
if (url[i] != prefix[i]) revert InvalidPublicRpcUrl();
}
bool query;
uint256 queryStart;
for (uint256 i = prefix.length; i < url.length; ++i) {
bytes1 character = url[i];
if (!query && character == "@") revert InvalidPublicRpcUrl();
if (character == "?") {
query = true;
queryStart = i + 1;
break;
}
if (character == "#") break;
}
if (!query) return;
bytes memory lowered = new bytes(url.length - queryStart);
for (uint256 i; i < lowered.length; ++i) {
uint8 character = uint8(url[queryStart + i]);
lowered[i] = character >= 65 && character <= 90 ? bytes1(character + 32) : bytes1(character);
}
if (
_containsBytes(lowered, bytes("key=")) || _containsBytes(lowered, bytes("token="))
|| _containsBytes(lowered, bytes("secret=")) || _containsBytes(lowered, bytes("password="))
|| _containsBytes(lowered, bytes("credential="))
) revert InvalidPublicRpcUrl();
}
function _containsBytes(bytes memory haystack, bytes memory needle) private pure returns (bool) {
if (needle.length > haystack.length) return false;
for (uint256 i; i + needle.length <= haystack.length; ++i) {
bool matches = true;
for (uint256 j; j < needle.length; ++j) {
if (haystack[i + j] != needle[j]) {
matches = false;
break;
}
}
if (matches) return true;
}
return false;
}
function _readManifest(string memory path, bool active) internal view returns (Manifest memory manifest) {
string memory json = vm.readFile(path);
_assertExactManifestSchema(json);
@@ -112,7 +179,7 @@ abstract contract DemoScript is Script {
}
function _parseActors(string memory json, uint256 chainId) private view returns (Actor[] memory actors) {
uint256 actorCount = chainId == ANVIL_CHAIN_ID ? 3 : 1;
uint256 actorCount = chainId == ANVIL_CHAIN_ID ? 3 : 2;
actors = new Actor[](actorCount);
for (uint256 i; i < actorCount; ++i) {
string memory index = vm.toString(i);
@@ -157,9 +224,13 @@ abstract contract DemoScript is Script {
}
if (
manifest.chainId != BASE_SEPOLIA_CHAIN_ID || manifest.actors.length != 1
|| !_equals(manifest.network, "baseSepolia") || !_equals(manifest.actors[0].label, "owner")
|| manifest.actors[0].address_ != manifest.owner
manifest.chainId != BASE_SEPOLIA_CHAIN_ID || manifest.actors.length != 2
|| !_equals(manifest.network, "baseSepolia") || !_equals(manifest.actors[0].label, "Presenter")
|| !_equals(manifest.actors[1].label, "Recipient") || manifest.actors[0].address_ != manifest.owner
) revert InvalidActorConfiguration();
if (
manifest.actors[0].address_ == address(0) || manifest.actors[1].address_ == address(0)
|| manifest.actors[0].address_ == manifest.actors[1].address_
) revert InvalidActorConfiguration();
}