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.

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;