Is it possible to deploy non-upgradeable contracts?

I registered with OpenZeppelin Defender to at least deploy a contract but the tutorial only serves upgradeable contract.

:computer: Environment

I'm deploying to the Sepolia network using Foundry.

:memo:Details

I'm running into a blocker here.

Is there any code to deal with non-upgradeable contracts? I don't want to deploy an upgradeable contract for the time being.

:1234: Code to reproduce

// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.20;

import {Initializable} from  "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

/// @title Box
/// @notice A box with objects inside.
contract Box is Initializable, UUPSUpgradeable, OwnableUpgradeable {
    /*//////////////////////////////////////////////////////////////
                                VARIABLES
    //////////////////////////////////////////////////////////////*/

    /// @notice Number of objects inside the box.
    uint256 public numberOfObjects;

    /*//////////////////////////////////////////////////////////////
                                FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /// @notice No constructor in upgradable contracts, so initialized with this function.
    function initialize(uint256 objects, address multisig) public initializer {
        __UUPSUpgradeable_init();
        __Ownable_init(multisig);

        numberOfObjects = objects;
    }

    /// @notice Remove an object from the box.
    function removeObject() external {
        require(numberOfObjects > 1, "Nothing inside");
        numberOfObjects -= 1;
    }

    /// @dev Upgrades the implementation of the proxy to new address.
    function _authorizeUpgrade(address) internal override onlyOwner {}
}

Yes. You can use the Defender.deployContract function to deploy non-upgradeable contracts. See https://docs.openzeppelin.com/upgrades-plugins/1.x/foundry-defender#non_upgradeable_contracts

Thank you, I've tried what you suggested but ran into an issue below. Are you familiar with the error message?

% forge script script/Deploy.s.sol --force --rpc-url https://ethereum-sepolia.publicnode.com
[⠊] Compiling...
[β ‘] Compiling 39 files with Solc 0.8.26
[⠊] Solc 0.8.26 finished in 2.68s
Compiler run successful!
Traces:
  [3062865] β†’ new DefenderScript@0x5b73C5498c1E3b4dbA84de0F1833c4a029d90519
    └─ ← [Return] 15187 bytes of code

  [98] DefenderScript::setUp()
    └─ ← [Stop] 

  [9197837] DefenderScript::run()
    β”œβ”€ [0] VM::envOr("FOUNDRY_OUT", "out") [staticcall]
    β”‚   └─ ← [Return] <env var value>
    β”œβ”€ [0] VM::projectRoot() [staticcall]
    β”‚   └─ ← [Return] "(user directory, this is redacted)"
    β”œβ”€ [0] VM::readFile("/Users/MyContract.json") [staticcall]
    β”‚   └─ ← [Return] <file>
    β”œβ”€ [0] VM::keyExistsJson("<JSON file>", ".ast") [staticcall]
    β”‚   └─ ← [Return] true
    β”œβ”€ [0] VM::parseJsonString("<stringified JSON>", ".ast.absolutePath") [staticcall]
    β”‚   └─ ← [Return] "contracts/MyContract.sol"
    β”œβ”€ [0] VM::keyExistsJson("<JSON file>", ".ast.license") [staticcall]
    β”‚   └─ ← [Return] true
    β”œβ”€ [0] VM::parseJsonString("<stringified JSON>", ".ast.license") [staticcall]
    β”‚   └─ ← [Return] "MIT"
    β”œβ”€ [0] VM::parseJsonString("<stringified JSON>", ".metadata.sources.['contracts/MyContract.sol'].keccak256") [staticcall]
    β”‚   └─ ← [Return] "0x9804f20589c170dfadf473b3ad6691e58894aab213d027950223cb92ac5993c5"
    β”œβ”€ [0] VM::envOr("OPENZEPPELIN_BASH_PATH", "bash") [staticcall]
    β”‚   └─ ← [Return] <env var value>
    β”œβ”€ [0] VM::tryFfi(["bash", "-c", "grep -rl \"0x9804f20589c170dfadf473b3ad6691e58894aab213d027950223cb92ac5993c5\" out/build-info"])
    β”‚   └─ ← [Return] (0, 0x6f75742f6275696c642d696e666f2f37656137626232303435366362363638663539323532363734393133393838312e6a736f6e, 0x)
    β”œβ”€ [0] VM::toString(0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000033238330000000000000000000000000000000000000000000000000000000000) [staticcall]
    β”‚   └─ ← [Return] "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000033238330000000000000000000000000000000000000000000000000000000000"
    β”œβ”€ [0] VM::envOr("OPENZEPPELIN_BASH_PATH", "bash") [staticcall]
    β”‚   └─ ← [Return] <env var value>
    β”œβ”€ [0] VM::tryFfi(["bash", "-c", "npx @openzeppelin/defender-deploy-client-cli@0.0.1-alpha.7 deploy --contractName MyContract --contractPath contracts/MyContract.sol --chainId 11155111 --buildInfoFile out/build-info/7ea7bb20456cb668f592526749139881.json --constructorBytecode 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000033238330000000000000000000000000000000000000000000000000000000000 --licenseType \"MIT\""])
    β”‚   └─ ← [Return] (1, 0x, 0x6e706d207761726e20657865632054686520666f6c6c6f77696e67207061636b61676520776173206e6f7420666f756e6420616e642077696c6c20626520696e7374616c6c65643a20406f70656e7a657070656c696e2f646566656e6465722d6465706c6f792d636c69656e742d636c6940302e302e312d616c7068612e370a6e6f64653a696e7465726e616c2f70726f636573732f70726f6d697365733a3238390a20202020202020202020202074726967676572556e636175676874457863657074696f6e286572722c2074727565202f2a2066726f6d50726f6d697365202a2f293b0a2020202020202020202020205e0a0a446566656e646572417069526573706f6e73654572726f723a2052657175657374206661696c656420776974682073746174757320636f6465203430300a2020202061742072656a65637457697468446566656e6465724170694572726f7220282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d73646b2d626173652d636c69656e742f6c69622f6170692f6170692e6a733a31303a3237290a2020202061742070726f636573732e70726f636573735469636b73416e6452656a656374696f6e7320286e6f64653a696e7465726e616c2f70726f636573732f7461736b5f7175657565733a39353a35290a202020206174206173796e63204178696f732e7265717565737420282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f6178696f732f646973742f6e6f64652f6178696f732e636a733a343231393a3134290a202020206174206173796e63204465706c6f79436c69656e742e77697468526574727920282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d73646b2d626173652d636c69656e742f6c69622f6170692f636c69656e742e6a733a38363a3230290a202020206174206173796e63204465706c6f79436c69656e742e77697468526574727920282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d73646b2d626173652d636c69656e742f6c69622f6170692f636c69656e742e6a733a3131313a3137290a202020206174206173796e63204465706c6f79436c69656e742e77697468526574727920282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d73646b2d626173652d636c69656e742f6c69622f6170692f636c69656e742e6a733a3131313a3137290a202020206174206173796e63204465706c6f79436c69656e742e77697468526574727920282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d73646b2d626173652d636c69656e742f6c69622f6170692f636c69656e742e6a733a3131313a3137290a202020206174206173796e63206465706c6f79436f6e747261637420282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d6465706c6f792d636c69656e742d636c692f646973742f696e7465726e616c2f6465706c6f792d636f6e74726163742e6a733a32323a3232290a202020206174206173796e63206465706c6f7920282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d6465706c6f792d636c69656e742d636c692f646973742f636f6d6d616e64732f6465706c6f792e6a733a33383a3235290a202020206174206173796e63206d61696e20282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d6465706c6f792d636c69656e742d636c692f646973742f636f6d6d616e642d73656c6563746f722e6a733a33313a3133290a202020206174204178696f732e7265717565737420282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f6178696f732f646973742f6e6f64652f6178696f732e636a733a343232343a3431290a2020202061742070726f636573732e70726f636573735469636b73416e6452656a656374696f6e7320286e6f64653a696e7465726e616c2f70726f636573732f7461736b5f7175657565733a39353a35290a202020206174206173796e63204465706c6f79436c69656e742e77697468526574727920282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d73646b2d626173652d636c69656e742f6c69622f6170692f636c69656e742e6a733a38363a3230290a202020206174206173796e63204465706c6f79436c69656e742e77697468526574727920282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d73646b2d626173652d636c69656e742f6c69622f6170692f636c69656e742e6a733a3131313a3137290a202020206174206173796e63204465706c6f79436c69656e742e77697468526574727920282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d73646b2d626173652d636c69656e742f6c69622f6170692f636c69656e742e6a733a3131313a3137290a202020206174206173796e63204465706c6f79436c69656e742e77697468526574727920282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d73646b2d626173652d636c69656e742f6c69622f6170692f636c69656e742e6a733a3131313a3137290a202020206174206173796e63206465706c6f79436f6e747261637420282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d6465706c6f792d636c69656e742d636c692f646973742f696e7465726e616c2f6465706c6f792d636f6e74726163742e6a733a32323a3232290a202020206174206173796e63206465706c6f7920282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d6465706c6f792d636c69656e742d636c692f646973742f636f6d6d616e64732f6465706c6f792e6a733a33383a3235290a202020206174206173796e63206d61696e20282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d6465706c6f792d636c69656e742d636c692f646973742f636f6d6d616e642d73656c6563746f722e6a733a33313a3133290a202020206174206173796e632072756e20282f55736572732f616e61725f656e6873616968616e2f2e6e706d2f5f6e70782f353766373431326362316331643731382f6e6f64655f6d6f64756c65732f406f70656e7a657070656c696e2f646566656e6465722d6465706c6f792d636c69656e742d636c692f646973742f636c692e6a733a363a3529207b0a2020726571756573743a207b20706174683a20272f76322f6465706c6f796d656e7473272c206d6574686f643a2027504f535427207d2c0a2020726573706f6e73653a207b0a202020207374617475733a203430302c0a20202020737461747573546578743a20274261642052657175657374272c0a20202020646174613a207b0a2020202020206d6573736167653a202753616665206465706c6f796d656e742061726520646f6e65207669612061204352454154453220636f6e747261637420616e64207265717569726520612073616c74270a202020207d0a20207d0a7d0a0a4e6f64652e6a73207632302e31302e300a)
    └─ ← [Revert] revert: Failed to deploy contract MyContract.sol: npm warn exec The following package was not found and will be installed: @openzeppelin/defender-deploy-client-cli@0.0.1-alpha.7
node:internal/process/promises:289
            triggerUncaughtException(err, true /* fromPromise */);
            ^

DefenderApiResponseError: Request failed with status code 400
    at rejectWithDefenderApiError (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/api.js:10:27)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Axios.request (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/axios/dist/node/axios.cjs:4219:14)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:86:20)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:111:17)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:111:17)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:111:17)
    at async deployContract (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/internal/deploy-contract.js:22:22)
    at async deploy (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/commands/deploy.js:38:25)
    at async main (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/command-selector.js:31:13)
    at Axios.request (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/axios/dist/node/axios.cjs:4224:41)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:86:20)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:111:17)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:111:17)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:111:17)
    at async deployContract (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/internal/deploy-contract.js:22:22)
    at async deploy (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/commands/deploy.js:38:25)
    at async main (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/command-selector.js:31:13)
    at async run (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/cli.js:6:5) {
  request: { path: '/v2/deployments', method: 'POST' },
  response: {
    status: 400,
    statusText: 'Bad Request',
    data: {
      message: 'Safe deployment are done via a CREATE2 contract and require a salt'
    }
  }
}

Node.js v20.10.0



Error: 
script failed: revert: Failed to deploy contract MyContract.sol: npm warn exec The following package was not found and will be installed: @openzeppelin/defender-deploy-client-cli@0.0.1-alpha.7
node:internal/process/promises:289
            triggerUncaughtException(err, true /* fromPromise */);
            ^

DefenderApiResponseError: Request failed with status code 400
    at rejectWithDefenderApiError (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/api.js:10:27)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Axios.request (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/axios/dist/node/axios.cjs:4219:14)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:86:20)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:111:17)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:111:17)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:111:17)
    at async deployContract (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/internal/deploy-contract.js:22:22)
    at async deploy (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/commands/deploy.js:38:25)
    at async main (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/command-selector.js:31:13)
    at Axios.request (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/axios/dist/node/axios.cjs:4224:41)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:86:20)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:111:17)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:111:17)
    at async DeployClient.withRetry (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-sdk-base-client/lib/api/client.js:111:17)
    at async deployContract (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/internal/deploy-contract.js:22:22)
    at async deploy (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/commands/deploy.js:38:25)
    at async main (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/command-selector.js:31:13)
    at async run (/Users/.npm/_npx/57f7412cb1c1d718/node_modules/@openzeppelin/defender-deploy-client-cli/dist/cli.js:6:5) {
  request: { path: '/v2/deployments', method: 'POST' },
  response: {
    status: 400,
    statusText: 'Bad Request',
    data: {
      message: 'Safe deployment are done via a CREATE2 contract and require a salt'
    }
  }
}

Node.js v20.10.0

I think I've figured it out with the code below:

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

import {MyContract} from "contracts/MyContract.sol";

import {Defender} from "openzeppelin-foundry-upgrades/Defender.sol";
import {DefenderOptions, TxOverrides} from "openzeppelin-foundry-upgrades/Options.sol";

contract DefenderScript is Script {
    function setUp() public {}

    function run() public {
        // Using a salt for CREATE2 deployment
        bytes32 salt = keccak256(abi.encodePacked(block.timestamp, msg.sender));

        // Define Defender deployment options
        DefenderOptions memory defenderOpts = DefenderOptions({
            useDefenderDeploy: true,
            skipVerifySourceCode: false, // Change to true if you want to skip source code verification
            relayerId: "", // Set your relayer ID
            salt: salt, // Use a specific salt for CREATE2 or set to 0 for CREATE
            upgradeApprovalProcessId: "", // Optional, for upgradeable contracts
            licenseType: "", // Set the license type if required
            skipLicenseType: false,
            txOverrides: TxOverrides({
                gasLimit: 3000000, // Adjust gas limit if necessary
                gasPrice: 0, // Set gas price if using legacy transactions
                maxFeePerGas: 0, // Set for EIP-1559 transactions
                maxPriorityFeePerGas: 0 // Set for EIP-1559 transactions
            })
        });

        address deployed = Defender.deployContract(
            "MyContract.sol",
            abi.encode("parameter"),
            defenderOpts
        );
        console.log("Deployed contract to address", deployed);
    }
}

I hope this can serve to help others. Maybe it could even be used in an upcoming tutorial on these kind of deployments.

1 Like

If you only want to set one specific option such as the salt, you could simplify the options declaration like this:

        // Define Defender deployment options
        DefenderOptions memory defenderOpts;
        defenderOpts.salt = salt;
1 Like