Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ broadcast
**/*/.DS_Store

# Certora
.certora_internal/
.certora_internal/

.DS_Store
1 change: 1 addition & 0 deletions lib/LayerZero-v2
Submodule LayerZero-v2 added at 9c741e
1 change: 1 addition & 0 deletions lib/devtools
Submodule devtools added at 128b69
33 changes: 33 additions & 0 deletions scripts/DeployOneInchSafeImpl.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import { stdJson } from "forge-std/StdJson.sol";
import { console } from "forge-std/console.sol";

import { EtherFiSafe } from "../src/safe/EtherFiSafe.sol";
import { Utils } from "./utils/Utils.sol";

/**
* @title DeployOneInchSafeImpl
* @notice Deploys a new EtherFiSafe implementation with ERC-1271 support
*
* ENV=mainnet forge script scripts/DeployOneInchSafeImpl.s.sol --rpc-url <optimism_rpc> --broadcast
*/
contract DeployOneInchSafeImpl is Utils {
function run() public {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

string memory deployments = readDeploymentFile();

address dataProvider = stdJson.readAddress(deployments, string.concat(".", "addresses", ".", "EtherFiDataProvider"));

vm.startBroadcast(deployerPrivateKey);

EtherFiSafe safeImpl = new EtherFiSafe(dataProvider);

console.log("New EtherFiSafe implementation deployed at:", address(safeImpl));
console.log("Set NEW_SAFE_IMPL=%s for the gnosis upgrade script", address(safeImpl));

vm.stopBroadcast();
}
}
36 changes: 36 additions & 0 deletions scripts/DeployOneInchSwapModule.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import { stdJson } from "forge-std/StdJson.sol";
import { console } from "forge-std/console.sol";

import { OneInchSwapModule } from "../src/modules/oneinch-swap/OneInchSwapModule.sol";
import { Utils } from "./utils/Utils.sol";

/**
* @title DeployOneInchSwapModule
* @notice Deploys the OneInchSwapModule contract
*
* ENV=mainnet forge script scripts/DeployOneInchSwapModule.s.sol --rpc-url <optimism_rpc> --broadcast
*/
contract DeployOneInchSwapModule is Utils {
// 1inch v6 Aggregation Router — canonical address on all EVM chains
address constant AGGREGATION_ROUTER = 0x111111125421cA6dc452d289314280a0f8842A65;

function run() public {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

string memory deployments = readDeploymentFile();

address dataProvider = stdJson.readAddress(deployments, string.concat(".", "addresses", ".", "EtherFiDataProvider"));

vm.startBroadcast(deployerPrivateKey);

OneInchSwapModule oneInchModule = new OneInchSwapModule(AGGREGATION_ROUTER, dataProvider);

console.log("OneInchSwapModule deployed at:", address(oneInchModule));
console.log("Set ONE_INCH_MODULE=%s for the gnosis config script", address(oneInchModule));

vm.stopBroadcast();
}
}
67 changes: 67 additions & 0 deletions scripts/DeployOneInchSwapModuleDev.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {stdJson} from "forge-std/StdJson.sol";
import {console} from "forge-std/console.sol";

import {EtherFiSafe} from "../src/safe/EtherFiSafe.sol";
import {EtherFiSafeFactory} from "../src/safe/EtherFiSafeFactory.sol";
import {EtherFiDataProvider} from "../src/data-provider/EtherFiDataProvider.sol";
import {OneInchSwapModule} from "../src/modules/oneinch-swap/OneInchSwapModule.sol";
import {Utils} from "./utils/Utils.sol";

/**
* @title DeployOneInchSwapModuleDev
* @notice All-in-one dev script: deploys new Safe impl, upgrades beacon, deploys module, configures module
* @dev Runs everything via a single EOA. For dev environment only.
*
* Usage:
* ENV=dev PRIVATE_KEY=0x... forge script scripts/DeployOneInchSwapModuleDev.s.sol --rpc-url <optimism_rpc> --broadcast
*/
contract DeployOneInchSwapModuleDev is Utils {
// 1inch v6 Aggregation Router — canonical address on all EVM chains
address constant AGGREGATION_ROUTER = 0x111111125421cA6dc452d289314280a0f8842A65;

function run() public {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

string memory deployments = readDeploymentFile();

address dataProvider = stdJson.readAddress(
deployments,
string.concat(".", "addresses", ".", "EtherFiDataProvider")
);

address safeFactoryAddr = stdJson.readAddress(
deployments,
string.concat(".", "addresses", ".", "EtherFiSafeFactory")
);

vm.startBroadcast(deployerPrivateKey);

// 1. Deploy new EtherFiSafe implementation with ERC-1271 support
EtherFiSafe safeImpl = new EtherFiSafe(dataProvider);
console.log("New EtherFiSafe impl:", address(safeImpl));

// 2. Upgrade beacon to new implementation
EtherFiSafeFactory safeFactory = EtherFiSafeFactory(safeFactoryAddr);
safeFactory.upgradeBeaconImplementation(address(safeImpl));
console.log("Beacon upgraded");

// 3. Deploy OneInchSwapModule
OneInchSwapModule oneInchModule = new OneInchSwapModule(AGGREGATION_ROUTER, dataProvider);
console.log("OneInchSwapModule:", address(oneInchModule));

// 4. Configure as default module on DataProvider
address[] memory modules = new address[](1);
modules[0] = address(oneInchModule);

bool[] memory shouldWhitelist = new bool[](1);
shouldWhitelist[0] = true;

EtherFiDataProvider(dataProvider).configureDefaultModules(modules, shouldWhitelist);
console.log("Module configured as default");

vm.stopBroadcast();
}
}
61 changes: 61 additions & 0 deletions scripts/gnosis-txs/ConfigureOneInchSwapModule.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {stdJson} from "forge-std/StdJson.sol";
import {console} from "forge-std/console.sol";

import {EtherFiDataProvider} from "../../src/data-provider/EtherFiDataProvider.sol";
import {GnosisHelpers} from "../utils/GnosisHelpers.sol";
import {Utils} from "../utils/Utils.sol";

/**
* @title ConfigureOneInchSwapModule
* @notice Generates a Gnosis Safe transaction to configure the OneInchSwapModule as a default module
*
* ENV=mainnet ONE_INCH_MODULE=0x... forge script scripts/gnosis-txs/ConfigureOneInchSwapModule.s.sol --rpc-url <optimism_rpc>
*/
contract ConfigureOneInchSwapModule is GnosisHelpers, Utils {
// Prod multisig (DATA_PROVIDER_ADMIN_ROLE holder)
address constant multisig = 0xA6cf33124cb342D1c604cAC87986B965F428AAC4;

function run() public {
address oneInchModule = vm.envAddress("ONE_INCH_MODULE");

string memory deployments = readDeploymentFile();
string memory chainId = vm.toString(block.chainid);

address dataProvider = stdJson.readAddress(
deployments,
string.concat(".", "addresses", ".", "EtherFiDataProvider")
);

// Build Gnosis transaction: dataProvider.configureDefaultModules([module], [true])
string memory txs = _getGnosisHeader(chainId, addressToHex(multisig));

address[] memory modules = new address[](1);
modules[0] = oneInchModule;

bool[] memory shouldWhitelist = new bool[](1);
shouldWhitelist[0] = true;

string memory configData = iToHex(
abi.encodeWithSelector(EtherFiDataProvider.configureDefaultModules.selector, modules, shouldWhitelist)
);
txs = string(abi.encodePacked(
txs,
_getGnosisTransaction(addressToHex(dataProvider), configData, "0", true)
));

vm.createDir("./output", true);
string memory path = "./output/ConfigureOneInchSwapModule.json";
vm.writeFile(path, txs);

console.log("Gnosis transaction written to:", path);
console.log("DataProvider:", dataProvider);
console.log("OneInchSwapModule:", oneInchModule);

// Simulate execution
executeGnosisTransactionBundle(path);
console.log("Simulation passed");
}
}
55 changes: 55 additions & 0 deletions scripts/gnosis-txs/UpgradeSafeImplOneInch.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {stdJson} from "forge-std/StdJson.sol";
import {console} from "forge-std/console.sol";

import {EtherFiSafeFactory} from "../../src/safe/EtherFiSafeFactory.sol";
import {GnosisHelpers} from "../utils/GnosisHelpers.sol";
import {Utils} from "../utils/Utils.sol";

/**
* @title UpgradeSafeImplOneInch
* @notice Generates a Gnosis Safe transaction to upgrade the EtherFiSafe beacon implementation
*
* ENV=mainnet NEW_SAFE_IMPL=0x... forge script scripts/gnosis-txs/UpgradeSafeImplOneInch.s.sol --rpc-url <optimism_rpc>
*/
contract UpgradeSafeImplOneInch is GnosisHelpers, Utils {
// Prod multisig (roleRegistry owner + DATA_PROVIDER_ADMIN_ROLE holder)
address constant multisig = 0xA6cf33124cb342D1c604cAC87986B965F428AAC4;

function run() public {
address newSafeImpl = vm.envAddress("NEW_SAFE_IMPL");

string memory deployments = readDeploymentFile();
string memory chainId = vm.toString(block.chainid);

address safeFactory = stdJson.readAddress(
deployments,
string.concat(".", "addresses", ".", "EtherFiSafeFactory")
);

// Build Gnosis transaction: safeFactory.upgradeBeaconImplementation(newSafeImpl)
string memory txs = _getGnosisHeader(chainId, addressToHex(multisig));

string memory upgradeData = iToHex(
abi.encodeWithSelector(EtherFiSafeFactory.upgradeBeaconImplementation.selector, newSafeImpl)
);
txs = string(abi.encodePacked(
txs,
_getGnosisTransaction(addressToHex(safeFactory), upgradeData, "0", true)
));

vm.createDir("./output", true);
string memory path = "./output/UpgradeSafeImplOneInch.json";
vm.writeFile(path, txs);

console.log("Gnosis transaction written to:", path);
console.log("Safe Factory:", safeFactory);
console.log("New Safe Impl:", newSafeImpl);

// Simulate execution
executeGnosisTransactionBundle(path);
console.log("Simulation passed");
}
}
11 changes: 11 additions & 0 deletions src/interfaces/IERC1271.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice EIP-1271 interface for smart contract signature validation
interface IERC1271 {
/// @notice Validates a signature for a given hash
/// @param hash The hash that was signed
/// @param signature The signature bytes
/// @return magicValue 0x1626ba7e if valid, any other value if invalid
function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4);
}
11 changes: 9 additions & 2 deletions src/interfaces/IEtherFiSafe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface IEtherFiSafe {
* @custom:throws DuplicateElementFound If the signers array contains duplicate addresses
* @custom:throws InvalidSigner If a signer is the zero address or not an owner of the safe
*/
function checkSignatures(bytes32 digestHash, address[] calldata signers, bytes[] calldata signatures) external view returns (bool);
function checkSignatures(bytes32 digestHash, address[] memory signers, bytes[] memory signatures) external view returns (bool);

/**
* @notice Executes a transaction from an authorized module
Expand Down Expand Up @@ -50,5 +50,12 @@ interface IEtherFiSafe {
*/
function useNonce() external returns (uint256);

function isAdmin(address account) external view returns (bool);
function isAdmin(address account) external view returns (bool);

/**
* @notice Returns the EIP-712 domain separator used by the Safe
* @dev Modules that verify owner sigs via `checkSignatures` should build digests as
* `keccak256("\x19\x01" || domainSeparator || structHash)` under this domain.
*/
function getDomainSeparator() external view returns (bytes32);
}
Loading
Loading