1.0 : Prerequisites

Add the redprint-forge using your favorite package manager, e.g., with npm:

npm install -D redprint-forge

Find out about our lib more on github

Add remappings.txt to the root directory with following:

@redprint-core/=node_modules/redprint-forge/src
@redprint-deploy/=node_modules/redprint-forge/script
@redprint-forge-std/=node_modules/redprint-forge/lib/forge-std/src
@redprint-openzeppelin/=node_modules/redprint-forge/lib/openzeppelin-4_9_4/contracts
@redprint-openzeppelin-upgradable/=node_modules/redprint-forge/lib/openzeppelin-upgradable-4_9_4/contracts
@redprint-safe-contracts/=node_modules/redprint-forge/lib/safe-smart-account/contracts

We use @redprint-/ as a convention to avoid any naming conflicts with your previously installed libararies ( i.e. @redprint-forge-std/ vs @forge-std/)

Add the deploy config file< network >.json to your desired directory ( i.e script/deploy-config/hardhat.json ) with following:

Also make sure that you enable permissions in foundry.toml as following:

[profile.default]
// ...
fs_permissions = [
  { access = 'read-write', path = './' },
]

Add .env.<network>.localand modify as required. For example, this is a file .env.optimism.local for optimism network.

In our example, we use ldenv as convention guide for environment variable management.This will helps to manage deployment artifacts when deploying to different networks. Check our example !!

1.1 : Deploy Governance Contract

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/101_DeploySafeProxyScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Deploy Script Settings

Chains

OpSec Management

Owner
Smart Contract:
SafeProxy.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IProxy} from "@redprint-safe-contracts/contracts/proxies/SafeProxy.sol";

/// @custom:security-contact Consult full code at https://github.com/safe-global/safe-smart-account/blob/a9e3385bb38c29d45b3901ff7180b59fcee86ac9/contracts/proxies/SafeProxy.sol
contract SafeProxy {
    address internal singleton;

    constructor(address _singleton) {
        require(_singleton != address(0), "Invalid singleton address provided");
        singleton = _singleton;
    }

    fallback() external payable {
        // solhint-disable-next-line no-inline-assembly
        assembly {
          let _singleton := sload(0)
          // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
          if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
              mstore(0, shr(12, shl(12, _singleton)))
              return(0, 0x20)
          }
          calldatacopy(0, 0, calldatasize())
          let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
          returndatacopy(0, 0, returndatasize())
          if eq(success, 0) {
              revert(0, returndatasize())
          }
          return(0, returndatasize())
        }
    }
}

        
      
Deploy Script:
101_DeploySafeProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {SafeProxyFactory} from "@redprint-safe-contracts/contracts/proxies/SafeProxyFactory.sol";
import {Safe} from "@redprint-safe-contracts/contracts/Safe.sol";
import {SafeProxy} from "@redprint-safe-contracts/contracts/proxies/SafeProxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeploySafeProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    string mnemonic = vm.envString("MNEMONIC");
    uint256 ownerPrivateKey = vm.deriveKey(mnemonic, "m/44'/60'/0'/0/", 1);
    address owner = vm.envOr("DEPLOYER_ADDRESS", vm.addr(ownerPrivateKey));

    function deploy()
        external
        returns (SafeProxyFactory safeProxyFactory_, Safe safeSingleton_, SafeProxy safeProxy_)
    {
        address safeProxyFactory = 0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2;
        address safeSingleton = 0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552;
        safeProxyFactory.code.length == 0
            ? safeSingleton_ = Safe(deployer.deploy_Safe("SafeSingleton"))
            : safeSingleton_ = Safe(payable(safeSingleton));
        safeProxy_ = SafeProxy(deployer.deploy_SystemOwnerSafe("SystemOwnerSafe", "SafeProxyFactory", "SafeSingleton", address(owner)));
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

(Alternative) : Deploy All

In your terminal, copy below contracts' codes and run deployment scripts to your prefered network:

forge script script/000_DeployAllScript.s.sol --trezor --sender <DEPLOYER_ADDRESS> --rpc-url <RPC_URL> --broadcast

(Optional), you can specify your derivation path:

--mnemonic-derivation-paths "m/44'/60'/0'/0/0"

Contract Settings

Step 1

Owner
Deploy Script:
000_DeployAllScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Script} from "@redprint-forge-std/Script.sol";
import {IDeployer, getDeployer} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeploySafeProxyScript} from "@script/101_DeploySafeProxyScript.s.sol";

contract DeployAllScript is Script {
    IDeployer deployerProcedue;

    function run() public {
        deployerProcedue = getDeployer();
        deployerProcedue.setAutoSave(true);
        DeploySafeProxyScript safeDeployments = new DeploySafeProxyScript();
        //1) set up Safe Multisig
        safeDeployments.deploy();
    }
}

        
      

After running the deploy script, the address deployed is saved at deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.