I registered with OpenZeppelin Defender to at least deploy a contract but the tutorial only serves upgradeable contract.
Environment
I'm deploying to the Sepolia network using Foundry.
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.
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;