Quick Guide !!

We have prepared a set of scripts to deploy all contracts in one click. Otherwise, you can look into and customize each contract as you like.

we reference to OPStack 's version of v1.9.4

3.0 : Prerequisites

Make sure you have run the deploy script for superchain layer:

You should have following file with below fields saved at the deployments/31337/.save.json. Otherwise, as specified in .env.<network>.local.

{
"SafeProxyFactory": "<ADDRESS_1>",
"SafeSingleton": "<ADDRESS_2>",
"SystemOwnerSafe": "<ADDRESS_3>",
"AddressManager": "<ADDRESS_4>",
"ProxyAdmin": "<ADDRESS_5>",
"SuperchainConfigProxy": "<ADDRESS_6>",
"SuperchainConfig": "<ADDRESS_7>",
"ProtocolVersionsProxy": "<ADDRESS_8>",
"ProtocolVersions": "<ADDRESS_9>"
}

Without this artifact file, the next deployment scripts can not be run.

One-Click OpAltDA Deployment

Note: This script is not completed yet. It is for one layer only. Check our final script for full deployment.

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

Step 2

Step 3

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

import {DeploySafeProxyScript} from "@scripts/101_DeploySafeProxyScript.s.sol";
import {Script} from "@redprint-forge-std/Script.sol";
import {SetupOpAltDAScript} from "@scripts/300_SetupOpAltDAScript.s.sol";
import {SetupSuperchainScript} from "@scripts/200_SetupSuperchain.s.sol";

contract DeployAllScript is Script {
    function run() public {
        DeploySafeProxyScript safeDeployments = new DeploySafeProxyScript();
        //1) set up Safe Multisig
        safeDeployments.deploy();
        SetupSuperchainScript superchainSetups = new SetupSuperchainScript();
        superchainSetups.run();
        SetupOpAltDAScript opAltDASetups = new SetupOpAltDAScript();
        opAltDASetups.run();
    }
}

        
      

Contract Settings

Step 3

Data Availability Challenge Proxy Data AvailabilityChallenge
Deploy Script:
300_SetupOpAltDAScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeployAndInitializeDataAvailabilityChallengeScript} from "@scripts/301B_DeployAndInitializeDataAvailabilityChallengeScript.s.sol";
import {DeployDataAvailabilityChallengeProxyScript} from "@scripts/301A_DeployDataAvailabilityChallengeProxyScript.s.sol";
import {IDeployer, getDeployer} from "@redprint-deploy/deployer/DeployScript.sol";
import {Script} from "@redprint-forge-std/Script.sol";
import {console} from "@redprint-forge-std/console.sol";

contract SetupOpAltDAScript is Script {
    IDeployer deployerProcedue;

    function run() public {
        deployerProcedue = getDeployer();
        deployerProcedue.setAutoSave(true);

        console.log("Setup Op Alt DA ... ");

        DeployDataAvailabilityChallengeProxyScript dataAvailabilityChallengeProxyDeployments = new DeployDataAvailabilityChallengeProxyScript();
        DeployAndInitializeDataAvailabilityChallengeScript dataAvailabilityChallengeDeployments = new DeployAndInitializeDataAvailabilityChallengeScript();

        dataAvailabilityChallengeProxyDeployments.deploy();
        dataAvailabilityChallengeDeployments.deploy();
        dataAvailabilityChallengeDeployments.initialize();


        console.log("DataAvailabilityChallengeProxy at: ", deployerProcedue.getAddress("DataAvailabilityChallengeProxy"));
        console.log("DataAvailabilityChallenge at: ", deployerProcedue.getAddress("DataAvailabilityChallenge"));
    }
}

        
      

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

3.1A : Deploy DataAvailabilityChallengeProxy Contract

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

forge script script/301A_DeployDataAvailabilityChallengeProxyScript.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

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

import {Constants} from "@redprint-core/libraries/Constants.sol";

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/4/packages/contracts-bedrock/src/universal/Proxy.sol
contract Proxy {
    /**
     * @notice An event that is emitted each time the implementation is changed. This event is part
     *         of the EIP-1967 specification.
     *
     * @param implementation The address of the implementation contract
     */
    event Upgraded(address indexed implementation);
    /**
     * @notice An event that is emitted each time the owner is upgraded. This event is part of the
     *         EIP-1967 specification.
     *
     * @param previousAdmin The previous owner of the contract
     * @param newAdmin      The new owner of the contract
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    modifier proxyCallIfNotAdmin() {
        if (msg.sender == _getAdmin() || msg.sender == address(0)) {
            _;
        } else {
            // This WILL halt the call frame on completion.
            _doProxyCall();
        }
    }

    constructor(address _admin) {
        _changeAdmin(_admin);
    }

    receive() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    fallback() external payable {
        // Proxy call by default.
        _doProxyCall();
    }

    function upgradeTo(address _implementation)
        public
        virtual
        returns (proxyCallIfNotAdmin)
    {
        _setImplementation(_implementation);
    }

    function upgradeToAndCall(address _implementation, bytes calldata _data)
        public
        payable virtual proxyCallIfNotAdmin
        returns (bytes memory)
    {
        _setImplementation(_implementation);
        (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
        require(success, "Proxy: delegatecall to new implementation contract failed");
        return returndata;
    }

    function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
        _changeAdmin(_admin);
    }

    function admin() public virtual proxyCallIfNotAdmin returns (address) {
        return _getAdmin();
    }

    function implementation()
        public
        virtual proxyCallIfNotAdmin
        returns (address)
    {
        return _getImplementation();
    }

    function _setImplementation(address _implementation) internal {
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            sstore(proxyImplementation, _implementation)
        }
        emit Upgraded(_implementation);
    }

    function _changeAdmin(address _admin) internal {
        address previous = _getAdmin();
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            sstore(proxyOwner, _admin)
        }
        emit AdminChanged(previous, _admin);
    }

    function _doProxyCall() internal {
        address impl = _getImplementation();
        require(impl != address(0), "Proxy: implementation not initialized");
  
        assembly {
            // Copy calldata into memory at 0x0....calldatasize.
            calldatacopy(0x0, 0x0, calldatasize())
  
            // Perform the delegatecall, make sure to pass all available gas.
            let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
  
            // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
            // overwrite the calldata that we just copied into memory but that doesn't really
            // matter because we'll be returning in a second anyway.
            returndatacopy(0x0, 0x0, returndatasize())
  
            // Success == 0 means a revert. We'll revert too and pass the data up.
            if iszero(success) { revert(0x0, returndatasize()) }
  
            // Otherwise we'll just return and pass the data up.
            return(0x0, returndatasize())
        }
    }

    function _getImplementation() internal view returns (address) {
        address impl;
        bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
        assembly {
            impl := sload(proxyImplementation)
        }
        return impl;
    }

    function _getAdmin() internal view returns (address) {
        address owner;
        bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
        assembly {
            owner := sload(proxyOwner)
        }
        return owner;
    }
}

        
      
Deploy Script:
301A_DeployDataAvailabilityChallengeProxyScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DeployScript} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, IDeployer} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {Proxy} from "@redprint-core/universal/Proxy.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployDataAvailabilityChallengeProxyScript is DeployScript {
    using DeployerFunctions for IDeployer ;
    function deploy() external returns (Proxy) {
        address proxyOwner = deployer.mustGetAddress("ProxyAdmin");

        return Proxy(deployer.deploy_ERC1967Proxy("DataAvailabilityChallengeProxy", proxyOwner));
    }
}

        
      

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

3.1B : Deploy and Initialize DataAvailabilityChallenge Contract

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

forge script script/301B_DeployAndInitializeDataAvailabilityChallengeScript.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

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

import {ISemver} from "@redprint-core/universal/interfaces/ISemver.sol";
import {OwnableUpgradeable} from "@redprint-openzeppelin-upgradeable/access/OwnableUpgradeable.sol";
import {SafeCall} from "@redprint-core/libraries/SafeCall.sol";

/// @dev An enum representing the status of a DA challenge.
enum ChallengeStatus {
    Uninitialized,
    Active,
    Resolved,
    Expired
}
/// @dev An enum representing known commitment types.
enum CommitmentType {
    Keccak256
}
/// @dev A struct representing a single DA challenge.
/// @custom:field status The status of the challenge.
/// @custom:field challenger The address that initiated the challenge.
/// @custom:field startBlock The block number at which the challenge was initiated.
struct Challenge {
    address challenger;
    uint256 lockedBond;
    uint256 startBlock;
    uint256 resolvedBlock;
}
/// @notice Compute the expected commitment for a given blob of data.
/// @param _data The blob of data to compute a commitment for.
/// @return The commitment for the given blob of data.
function computeCommitmentKeccak256(bytes memory _data) pure returns (bytes memory) {
    return bytes.concat(bytes1(uint8(CommitmentType.Keccak256)), keccak256(_data));
}

/// @custom:security-contact Consult full code at https://github.com/ethereum-optimism/optimism/blob/v1.9.4/packages/contracts-bedrock/src/L1/DataAvailabilityChallenge.sol
contract DataAvailabilityChallenge is OwnableUpgradeable {
    /// @notice Error for when the provided resolver refund percentage exceeds 100%.
    error InvalidResolverRefundPercentage(uint256 invalidResolverRefundPercentage);
    /// @notice Error for when the challenger's bond is too low.
    error BondTooLow(uint256 balance, uint256 required);
    /// @notice Error for when attempting to challenge a commitment that already has a challenge.
    error ChallengeExists();
    /// @notice Error for when attempting to resolve a challenge that is not active.
    error ChallengeNotActive();
    /// @notice Error for when attempting to unlock a bond from a challenge that is not expired.
    error ChallengeNotExpired();
    /// @notice Error for when attempting to challenge a commitment that is not in the challenge window.
    error ChallengeWindowNotOpen();
    /// @notice Error for when the provided input data doesn't match the commitment.
    error InvalidInputData(bytes providedDataCommitment, bytes expectedCommitment);
    /// @notice Error for when the call to withdraw a bond failed.
    error WithdrawalFailed();
    /// @notice Error for when a the type of a given commitment is unknown
    error UnknownCommitmentType(uint8 commitmentType);
    /// @notice Error for when the commitment length does not match the commitment type
    error InvalidCommitmentLength(uint8 commitmentType, uint256 expectedLength, uint256 actualLength);
    /// @notice An event that is emitted when the status of a challenge changes.
    /// @param challengedCommitment The commitment that is being challenged.
    /// @param challengedBlockNumber The block number at which the commitment was made.
    /// @param status The new status of the challenge.
    event ChallengeStatusChanged(
        uint256 indexed challengedBlockNumber, bytes challengedCommitment, ChallengeStatus status
    );

    /// @notice An event that is emitted when the bond size required to initiate a challenge changes.
    event RequiredBondSizeChanged(uint256 challengeWindow);

    /// @notice An event that is emitted when the percentage of the resolving cost to be refunded to the resolver
    /// changes.
    event ResolverRefundPercentageChanged(uint256 resolverRefundPercentage);

    /// @notice An event that is emitted when a user's bond balance changes.
    event BalanceChanged(address account, uint256 balance);
    /// @notice Semantic version.
    /// @custom:semver 1.0.1-beta.2
    string public constant version = "1.0.1-beta.2";

    /// @notice The fixed cost of resolving a challenge.
    /// @dev The value is estimated by measuring the cost of resolving with 'bytes(0)'
    uint256 public constant fixedResolutionCost = 72925;

    /// @notice The variable cost of resolving a callenge per byte scaled by the variableResolutionCostPrecision.
    /// @dev upper limit; The value is estimated by measuring the cost of resolving with variable size data where each
    /// byte is non-zero.
    uint256 public constant variableResolutionCost = 16640;

    /// @dev The precision of the variable resolution cost.
    uint256 public constant variableResolutionCostPrecision = 1000;

    /// @notice The block interval during which a commitment can be challenged.
    uint256 public challengeWindow;

    /// @notice The block interval during which a challenge can be resolved.
    uint256 public resolveWindow;

    /// @notice The amount required to post a challenge.
    uint256 public bondSize;

    /// @notice The percentage of the resolving cost to be refunded to the resolver.
    /// @dev There are no decimals, ie a value of 50 corresponds to 50%.
    uint256 public resolverRefundPercentage;

    /// @notice A mapping from addresses to their bond balance in the contract.
    mapping(address => uint256) public balances;

    /// @notice A mapping from challenged block numbers to challenged commitments to challenges.
    mapping(uint256 => mapping(bytes => Challenge)) internal challenges;

    constructor() OwnableUpgradeable() {
        initialize({
            _owner: address(0xdEaD),
            _challengeWindow: 0,
            _resolveWindow: 0,
            _bondSize: 0,
            _resolverRefundPercentage: 0
        });
    }

    receive() external payable {
        deposit();
    }

    function initialize(address _owner, uint256 _challengeWindow, uint256 _resolveWindow, uint256 _bondSize, uint256 _resolverRefundPercentage)
        public
        initializer
    {
        __Ownable_init();
        challengeWindow = _challengeWindow;
        resolveWindow = _resolveWindow;
        setBondSize(_bondSize);
        setResolverRefundPercentage(_resolverRefundPercentage);
        _transferOwnership(_owner);
    }

    function setBondSize(uint256 _bondSize) public onlyOwner {
        bondSize = _bondSize;
        emit RequiredBondSizeChanged(_bondSize);
    }

    function setResolverRefundPercentage(uint256 _resolverRefundPercentage)
        public
        onlyOwner
    {
        if (_resolverRefundPercentage > 100) {
            revert InvalidResolverRefundPercentage(_resolverRefundPercentage);
        }
        resolverRefundPercentage = _resolverRefundPercentage;
    }

    function deposit() public payable {
        balances[msg.sender] += msg.value;
        emit BalanceChanged(msg.sender, balances[msg.sender]);
    }

    function withdraw() external {
        // get caller's balance
        uint256 balance = balances[msg.sender];

        // set caller's balance to 0
        balances[msg.sender] = 0;
        emit BalanceChanged(msg.sender, 0);

        // send caller's balance to caller
        bool success = SafeCall.send(msg.sender, gasleft(), balance);
        if (!success) {
            revert WithdrawalFailed();
        }
    }

    function _isInChallengeWindow(uint256 _challengedBlockNumber)
        internal
        view
        returns (bool)
    {
        return (block.number >= _challengedBlockNumber && block.number <= _challengedBlockNumber + challengeWindow);
    }

    function _isInResolveWindow(uint256 _challengeStartBlockNumber)
        internal
        view
        returns (bool)
    {
        return block.number <= _challengeStartBlockNumber + resolveWindow;
    }

    function getChallenge(uint256 _challengedBlockNumber, bytes calldata _challengedCommitment)
        public
        view
        returns (Challenge memory)
    {
        return challenges[_challengedBlockNumber][_challengedCommitment];
    }

    function getChallengeStatus(uint256 _challengedBlockNumber, bytes calldata _challengedCommitment)
        public
        view
        returns (ChallengeStatus)
    {
        Challenge memory _challenge = challenges[_challengedBlockNumber][_challengedCommitment];
        // if the address is 0, the challenge is uninitialized
        if (_challenge.challenger == address(0)) return ChallengeStatus.Uninitialized;

        // if the challenge has a resolved block, it is resolved
        if (_challenge.resolvedBlock != 0) return ChallengeStatus.Resolved;

        // if the challenge's start block is in the resolve window, it is active
        if (_isInResolveWindow(_challenge.startBlock)) return ChallengeStatus.Active;

        // if the challenge's start block is not in the resolve window, it is expired
        return ChallengeStatus.Expired;
    }

    function _getCommitmentType(bytes calldata _commitment)
        internal
        pure
        returns (uint8)
    {
        return uint8(bytes1(_commitment));
    }

    function validateCommitment(bytes calldata _commitment) public pure {
        uint8 commitmentType = _getCommitmentType(_commitment);
        if (commitmentType == uint8(CommitmentType.Keccak256)) {
            if (_commitment.length != 33) {
                revert InvalidCommitmentLength(uint8(CommitmentType.Keccak256), 33, _commitment.length);
            }
            return;
        }

        revert UnknownCommitmentType(commitmentType);
    }

    function challenge(uint256 _challengedBlockNumber, bytes calldata _challengedCommitment)
        external payable
    {
        // require the commitment type to be known
        validateCommitment(_challengedCommitment);

        // deposit value sent with the transaction as bond
        deposit();

        // require the caller to have a bond
        if (balances[msg.sender] < bondSize) {
            revert BondTooLow(balances[msg.sender], bondSize);
        }

        // require the challenge status to be uninitialized
        if (getChallengeStatus(_challengedBlockNumber, _challengedCommitment) != ChallengeStatus.Uninitialized) {
            revert ChallengeExists();
        }

        // require the current block to be in the challenge window
        if (!_isInChallengeWindow(_challengedBlockNumber)) {
            revert ChallengeWindowNotOpen();
        }

        // reduce the caller's balance
        balances[msg.sender] -= bondSize;

        // store the challenger's address, bond size, and start block of the challenge
        challenges[_challengedBlockNumber][_challengedCommitment] =
            Challenge({ challenger: msg.sender, lockedBond: bondSize, startBlock: block.number, resolvedBlock: 0 });

        // emit an event to notify that the challenge status is now active
        emit ChallengeStatusChanged(_challengedBlockNumber, _challengedCommitment, ChallengeStatus.Active);
    }

    function resolve(uint256 _challengedBlockNumber, bytes calldata _challengedCommitment, bytes calldata _resolveData)
        external
    {
        // require the commitment type to be known
        validateCommitment(_challengedCommitment);

        // require the challenge to be active (started, not resolved, and resolve window still open)
        if (getChallengeStatus(_challengedBlockNumber, _challengedCommitment) != ChallengeStatus.Active) {
            revert ChallengeNotActive();
        }

        // compute the commitment corresponding to the given resolveData
        uint8 commitmentType = _getCommitmentType(_challengedCommitment);
        bytes memory computedCommitment;
        if (commitmentType == uint8(CommitmentType.Keccak256)) {
            computedCommitment = computeCommitmentKeccak256(_resolveData);
        }

        // require the provided input data to correspond to the challenged commitment
        if (keccak256(computedCommitment) != keccak256(_challengedCommitment)) {
            revert InvalidInputData(computedCommitment, _challengedCommitment);
        }

        // store the block number at which the challenge was resolved
        Challenge storage activeChallenge = challenges[_challengedBlockNumber][_challengedCommitment];
        activeChallenge.resolvedBlock = block.number;

        // emit an event to notify that the challenge status is now resolved
        emit ChallengeStatusChanged(_challengedBlockNumber, _challengedCommitment, ChallengeStatus.Resolved);

        // distribute the bond among challenger, resolver and address(0)
        _distributeBond(activeChallenge, _resolveData.length, msg.sender);
    }

    function _distributeBond(Challenge storage _resolvedChallenge, uint256 _preImageLength, address _resolver)
        internal
    {
        uint256 lockedBond = _resolvedChallenge.lockedBond;
        address challenger = _resolvedChallenge.challenger;

        // approximate the cost of resolving a challenge with the provided pre-image size
        uint256 resolutionCost = (
            fixedResolutionCost + _preImageLength * variableResolutionCost / variableResolutionCostPrecision
        ) * block.basefee;

        // refund bond exceeding the resolution cost to the challenger
        if (lockedBond > resolutionCost) {
            balances[challenger] += lockedBond - resolutionCost;
            lockedBond = resolutionCost;
            emit BalanceChanged(challenger, balances[challenger]);
        }

        // refund a percentage of the resolution cost to the resolver (but not more than the locked bond)
        uint256 resolverRefund = resolutionCost * resolverRefundPercentage / 100;
        if (resolverRefund > lockedBond) {
            resolverRefund = lockedBond;
        }
        if (resolverRefund > 0) {
            balances[_resolver] += resolverRefund;
            lockedBond -= resolverRefund;
            emit BalanceChanged(_resolver, balances[_resolver]);
        }

        // burn the remaining bond
        if (lockedBond > 0) {
            payable(address(0)).transfer(lockedBond);
        }
        _resolvedChallenge.lockedBond = 0;
    }

    function unlockBond(uint256 _challengedBlockNumber, bytes calldata _challengedCommitment)
        external
    {
        // require the challenge to be active (started, not resolved, and in the resolve window)
        if (getChallengeStatus(_challengedBlockNumber, _challengedCommitment) != ChallengeStatus.Expired) {
            revert ChallengeNotExpired();
        }

        // Unlock the bond associated with the challenge
        Challenge storage expiredChallenge = challenges[_challengedBlockNumber][_challengedCommitment];
        balances[expiredChallenge.challenger] += expiredChallenge.lockedBond;
        expiredChallenge.lockedBond = 0;

        // Emit balance update event
        emit BalanceChanged(expiredChallenge.challenger, balances[expiredChallenge.challenger]);
    }
}

        
      
Deploy Script:
301B_DeployAndInitializeDataAvailabilityChallengeScript.s.sol
        
          // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {DataAvailabilityChallenge} from "@redprint-core/L1/DataAvailabilityChallenge.sol";
import {DeployConfig} from "@redprint-deploy/deployer/DeployConfig.s.sol";
import {DeployScript, IDeployer} from "@redprint-deploy/deployer/DeployScript.sol";
import {DeployerFunctions, DeployOptions} from "@redprint-deploy/deployer/DeployerFunctions.sol";
import {SafeScript} from "@redprint-deploy/safe-management/SafeScript.sol";
import {Vm, VmSafe} from "@redprint-forge-std/Vm.sol";
import {console} from "@redprint-forge-std/console.sol";

/// @custom:security-contact Consult full internal deploy script at https://github.com/Ratimon/redprint-forge
contract DeployAndInitializeDataAvailabilityChallengeScript is DeployScript, SafeScript {
    using DeployerFunctions for IDeployer ;
    DataAvailabilityChallenge dataAvailabilityChallenge;
    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 (DataAvailabilityChallenge) {
        bytes32 _salt = DeployScript.implSalt();
        DeployOptions memory options = DeployOptions({salt:_salt});

        dataAvailabilityChallenge = deployer.deploy_DataAvailabilityChallenge("DataAvailabilityChallenge", options);

        return dataAvailabilityChallenge;
    }

    function initialize() external {
        (VmSafe.CallerMode mode ,address msgSender, ) = vm.readCallers();
        if(mode != VmSafe.CallerMode.Broadcast && msgSender != owner) {
            console.log("Pranking owner ...");
            vm.startPrank(owner);
            initializeDataAvailabilityChallenge();
            vm.stopPrank();
        } else {
            console.log("Broadcasting ...");
            vm.startBroadcast(owner);

            initializeDataAvailabilityChallenge();
            console.log("DataAvailabilityChallenge setted to : %s", address(dataAvailabilityChallenge));

            vm.stopBroadcast();
        }
    }

    function initializeDataAvailabilityChallenge() public {
        console.log("Upgrading and initializing DataAvailabilityChallenge proxy");

        address proxyAdmin = deployer.mustGetAddress("ProxyAdmin");
        address safe = deployer.mustGetAddress("SystemOwnerSafe");

        address dataAvailabilityChallengeProxy = deployer.mustGetAddress("DataAvailabilityChallengeProxy");

        DeployConfig cfg = deployer.getConfig();

        address finalSystemOwner = cfg.finalSystemOwner();
        uint256 daChallengeWindow = cfg.daChallengeWindow();
        uint256 daResolveWindow = cfg.daResolveWindow();
        uint256 daBondSize = cfg.daBondSize();
        uint256 daResolverRefundPercentage = cfg.daResolverRefundPercentage();

        _upgradeAndCallViaSafe({
            _proxyAdmin: proxyAdmin,
            _safe: safe,
            _owner: owner,
            _proxy: payable(dataAvailabilityChallengeProxy),
            _implementation: address(dataAvailabilityChallenge),
            _innerCallData: abi.encodeCall(
                DataAvailabilityChallenge.initialize,
                (finalSystemOwner, daChallengeWindow, daResolveWindow, daBondSize, daResolverRefundPercentage)
            )
        });

        DataAvailabilityChallenge dac = DataAvailabilityChallenge(payable(dataAvailabilityChallengeProxy));
        string memory version = dac.version();
        console.log("DataAvailabilityChallenge version: %s", version);

        require(dac.owner() == finalSystemOwner);
        require(dac.challengeWindow() == daChallengeWindow);
        require(dac.resolveWindow() == daResolveWindow);
        require(dac.bondSize() == daBondSize);
        require(dac.resolverRefundPercentage() == daResolverRefundPercentage);
    }
}

        
      

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