<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: slw.eth</title>
    <description>The latest articles on DEV Community by slw.eth (@slw).</description>
    <link>https://dev.to/slw</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F839687%2F9d564aa5-e76e-461f-9286-a469224aa26d.jpg</url>
      <title>DEV Community: slw.eth</title>
      <link>https://dev.to/slw</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/slw"/>
    <language>en</language>
    <item>
      <title>Tutorial: Get Started with Autotasks in OpenZeppelin Defender</title>
      <dc:creator>slw.eth</dc:creator>
      <pubDate>Tue, 11 Oct 2022 20:40:36 +0000</pubDate>
      <link>https://dev.to/slw/tutorial-get-started-with-autotasks-in-openzeppelin-defender-8l6</link>
      <guid>https://dev.to/slw/tutorial-get-started-with-autotasks-in-openzeppelin-defender-8l6</guid>
      <description>&lt;p&gt;This tutorial provides an introduction into Defender Autotasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is An Autotask?
&lt;/h2&gt;

&lt;p&gt;An Autotask is an environment available via Defender where you can execute a bit of Javascript code based on a schedule or triggered by some set of conditions. Check out the first section of the &lt;a href="https://docs.openzeppelin.com/defender/autotasks"&gt;documentation&lt;/a&gt; for an overview on Autotasks and how they relate to other Defender components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.openzeppelin.com/defender/autotasks#environment"&gt;Behind the Scenes of the Autotask environment&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Example Autotasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.openzeppelin.com/defender/guide-pauseguardian"&gt;Pause Guardian Security Automation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.openzeppelin.com/defender/guide-balance-automation-forta-sentinel"&gt;Balance Maintenance Automation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create a Hello World Autotask
&lt;/h2&gt;

&lt;p&gt;This walkthrough provides a hands-on introduction into the moving parts involved in setting up a Defender Autotask.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;The Autotask you'll be creating will make use of a &lt;a href="https://docs.openzeppelin.com/defender/relay"&gt;Relayer&lt;/a&gt; to make &lt;a href="https://docs.openzeppelin.com/defender/relay#using-ethers.js"&gt;ethers.js&lt;/a&gt; JSON-RPC calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a new Relayer&lt;/strong&gt;&lt;br&gt;
Select Relay from the left and then select Create Relayer. Give the Relayer a name and select Goerli for the network.&lt;/p&gt;
&lt;h3&gt;
  
  
  Hello World - (Using Web Interface)
&lt;/h3&gt;

&lt;p&gt;In Defender, each of the main components are listed on the left of the screen.&lt;/p&gt;

&lt;p&gt;Select Autotask -&amp;gt; New Autotask&lt;/p&gt;

&lt;p&gt;Give the Autotask a name and select Webhook so that it can be triggered via a request sent to the Autotask's endpoint. &lt;/p&gt;

&lt;p&gt;Select the Relayer you just created so that the Autotask can connect to it as a provider and make network queries using Ethers.js.&lt;/p&gt;

&lt;p&gt;An Autotask is a bit of serverless code that needs to export a &lt;code&gt;handler&lt;/code&gt;. The handler is what gets invoked each time the Autotask is triggered. &lt;/p&gt;

&lt;p&gt;Supply the following code in the Autotask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { KeyValueStoreClient } = require('defender-kvstore-client')
const ethers = require('ethers')
const {
  DefenderRelaySigner,
  DefenderRelayProvider,
} = require('defender-relay-client/lib/ethers')

async function handler(event) {
  const store = new KeyValueStoreClient(event)
  const provider = new DefenderRelayProvider(event)
  const {userName} = event.request.body;
  const block = await provider.getBlockNumber();
  const lastRunBlockNumber = await store.get('lastRunBlockNumber')
  let msg = (lastRunBlockNumber) ? `This Autotask was last triggered on block ${lastRunBlockNumber}` : 'This is the first time this Autotask has been triggered'

  await store.put('lastRunBlockNumber', block.toString())

  return `Hello ${userName}! Current block: ${block}. ${msg}`;
}

module.exports = {
  handler,
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.openzeppelin.com/defender/autotasks#kvstore"&gt;Key-value data store package&lt;/a&gt; allows you to persist data storage across different Autotask runs.&lt;/p&gt;

&lt;p&gt;On the next screen, copy the Webhook URI.&lt;/p&gt;

&lt;p&gt;Open &lt;a href="https://www.postman.com/"&gt;Postman&lt;/a&gt; (or similar) and create a new POST request. Paste the Autotask's endpoint. Under the Body tab, send the following as raw/JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "name": "Satoshi"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the request! You'll see the greeting in the body's result.&lt;/p&gt;

&lt;p&gt;Congratulations on successfully creating and triggering an Autotask! Next, you'll use the client package (rather than the web interface) to create an Autotask.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hello World - (Using &lt;code&gt;defender-autotask-client&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The Defender client packages allows you to create and manage the various Defender components via API using your favorite code editor. &lt;/p&gt;

&lt;p&gt;Create a new folder and initialize a &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir autotasks &amp;amp;&amp;amp; cd autotasks &amp;amp;&amp;amp; yarn init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the Defender Autotask Client and necessary npm packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ yarn add defender-autotask-client defender-relay-client defender-kvstore-client dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;defender-client&lt;/code&gt; package allows you full CRUD functionality with Autotasks.&lt;/p&gt;

&lt;p&gt;Create a gitignored file to store environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Supply your Defender Team API key and secret in this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API_KEY=
API_SECRET=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new &lt;code&gt;index.js&lt;/code&gt; file for your Autotask code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir autotasks &amp;amp;&amp;amp; touch autotasks/index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using the client package, Defender expects the Autotask code to be in an &lt;code&gt;index.js&lt;/code&gt; file. &lt;/p&gt;

&lt;p&gt;In &lt;code&gt;index.js&lt;/code&gt;, supply the same Autotask code as before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get the relayer's ID&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Autotask knows which relayer is connected to it by having the Relayer ID specified.&lt;/p&gt;

&lt;p&gt;Using the web UI, it was only a matter of selecting the Relayer. Using defender-client package, you'll need to specify the Relayer using its ID.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;defender-relay-client&lt;/code&gt; package you can run a simple query to list all relayers associated with your Defender account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir scripts &amp;amp;&amp;amp; touch scripts/getRelayerId.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following code and run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { RelayClient } = require('defender-relay-client');

async function run() {
  const { API_KEY: apiKey, API_SECRET: apiSecret } = process.env;
  const relayClient = new RelayClient({ apiKey, apiSecret });
  console.log(await relayClient.list());
}

run().catch((error) =&amp;gt; {
  console.error(error);
  process.exitCode = 1;
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the &lt;code&gt;relayerId&lt;/code&gt; value of whichever Relayer you would like the Autotask to be connected to. You'll supply this value in the Autotask creation script.&lt;/p&gt;

&lt;p&gt;Next, create the script to deploy the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir scripts &amp;amp;&amp;amp; touch scripts/createAutotask.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { AutotaskClient } = require('defender-autotask-client')

async function main() {
  require('dotenv').config()
  const credentials = {
    apiKey: process.env.API_KEY,
    apiSecret: process.env.API_SECRET,
  }
  const autotaskClient = new AutotaskClient(credentials)

  const params = {
    name: 'Hello World 2',
    encodedZippedCode: await autotaskClient.getEncodedZippedCodeFromFolder(
      './autotasks'
    ),
    trigger: {
      type: 'webhook',
    },
    paused: false,
    relayerId: '3f12191a-26a3-44ca-a3c9-ab7b57a97b8e',
  }

  const createdAutotask = await autotaskClient.create(params)
  console.log('Created Autotask with ID: ', createdAutotask.autotaskId)

}

if (require.main === module) {
  main()
    .then(() =&amp;gt; process.exit(0))
    .catch((error) =&amp;gt; {
      console.error(error)
      process.exit(1)
    })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with &lt;code&gt;node scripts/createAutotask.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Open Defender and refresh the Autotasks dashboard. Copy this Autotask's webhook, supply it to Postman and trigger the Autotask as before.&lt;/p&gt;

&lt;p&gt;Note that because Autotasks use the same key-value store, you will see the stored value from the Autotask created previously via the UI.  &lt;/p&gt;

&lt;p&gt;Congratulations on taking your first steps into the world of Defender Autotasks!&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>ethereum</category>
      <category>web3</category>
    </item>
    <item>
      <title>Declare an OpenZeppelin Defender Environment Configuration Via Serverless Plugin</title>
      <dc:creator>slw.eth</dc:creator>
      <pubDate>Sat, 24 Sep 2022 00:17:58 +0000</pubDate>
      <link>https://dev.to/slw/declare-an-openzeppelin-defender-environment-configuration-via-serverless-plugin-44kp</link>
      <guid>https://dev.to/slw/declare-an-openzeppelin-defender-environment-configuration-via-serverless-plugin-44kp</guid>
      <description>&lt;p&gt;This is a tutorial-style getting started guide for the OpenZeppelin Defender Serverless plugin. &lt;/p&gt;

&lt;h2&gt;
  
  
  Install Serverless
&lt;/h2&gt;

&lt;p&gt;This step installs the Serverless NPM package globally and walks you through setting up AWS credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm i -g serverless
$ serverless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select &lt;code&gt;AWS - Node.js - Starter&lt;/code&gt; and accept the remaining defaults. You will be walked through the creation of the relevant AWS credentials.&lt;/p&gt;

&lt;p&gt;When the AWS setup is complete, &lt;code&gt;cd&lt;/code&gt; into the project directory and run &lt;code&gt;serverless deploy&lt;/code&gt; to try out the service.&lt;/p&gt;

&lt;p&gt;Run the example function with &lt;code&gt;serverless invoke --function hello&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;serverless remove&lt;/code&gt; will remove the example project from deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Download Defender-Serverless Template
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.openzeppelin.com/defender/serverless-plugin"&gt;&lt;code&gt;Defender-serverless&lt;/code&gt;&lt;/a&gt; comes with an example configuration you can use as a template for further development.&lt;/p&gt;

&lt;p&gt;Initialize the example project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sls install --url https://github.com/OpenZeppelin/defender-serverless/tree/main/template -n hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you'll need to supply your Defender Team API credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ yarn add dotenv
$ touch .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save your Defender API key and secret in &lt;code&gt;.env&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TEAM_API_KEY=
TEAM_API_SECRET=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Review the code in &lt;code&gt;serverless.yml&lt;/code&gt;. Note the syntax for creating an Autotask (code supplied in &lt;code&gt;autotasks/index.js&lt;/code&gt;) and a Relayer.&lt;/p&gt;

&lt;p&gt;Add the following line to indicate where the plugin should look to find your environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useDotenv: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IMPORTANT:&lt;br&gt;
Ensure that you leave &lt;code&gt;ssot: false&lt;/code&gt;. Setting &lt;code&gt;ssot: true&lt;/code&gt; will overwrite your existing Defender configuration with that which is defined in the &lt;code&gt;.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Deploy with &lt;code&gt;serverless deploy&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ sls deploy
Running "serverless" from node_modules
========================================================
Secrets
Contracts
Relayers
✔ Created API Key (mystack.relayer-1.key1) for Relayer (41d99bcc-8457-4615-9e18-1d7068c48e06)
✔ Created mystack.relayer-1 (41d99bcc-8457-4615-9e18-1d7068c48e06)
Autotasks
✔ Created mystack.autotask-example-1 (207c958e-699b-46c0-b9c9-550278ae1b65)
Notifications
Sentinels
========================================================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this command adds the declared configuration to your Defender instance.&lt;/p&gt;

&lt;p&gt;You can view the Hello World Autotask by selecting &lt;code&gt;Hello world from serverless&lt;/code&gt; from the Defender Autotask dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qkJDj_Qm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8l806kjaryktb1jr96we.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qkJDj_Qm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8l806kjaryktb1jr96we.png" alt="screenshot" width="880" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! With this plugin, you can quickly declare Defender configurations that you would like to reproduce or iterate on in multiple environments.&lt;/p&gt;

&lt;p&gt;If you have developed configurations you'd like to share, feel free to post your setup on the &lt;a href="//forum.openzeppelin.com"&gt;OpenZeppelin community forum&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Deploy Smart Contracts via Relayer</title>
      <dc:creator>slw.eth</dc:creator>
      <pubDate>Mon, 25 Jul 2022 20:58:00 +0000</pubDate>
      <link>https://dev.to/slw/how-to-deploy-smart-contracts-via-relayer-3876</link>
      <guid>https://dev.to/slw/how-to-deploy-smart-contracts-via-relayer-3876</guid>
      <description>&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=UN30F6ZaoMo"&gt;Video Walkthrough&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This guide shows you how to deploy smart contracts without risk of exposing a private key by using &lt;a href="https://docs.openzeppelin.com/defender"&gt;OpenZeppelin Defender&lt;/a&gt;'s  &lt;a href="https://www.npmjs.com/package/defender-relay-client"&gt;Relayer API&lt;/a&gt;. &lt;a href="https://docs.openzeppelin.com/defender/relay"&gt;What is a relayer&lt;/a&gt;?&lt;/p&gt;

&lt;h2&gt;
  
  
  Copy Your Defender Team API Key and Secret
&lt;/h2&gt;

&lt;p&gt;From &lt;a href="https://defender.openzeppelin.com"&gt;Defender&lt;/a&gt;, select the hamburger menu at the top right and then Team API Keys. Here you will see any existing API keys as well as what can be done with them. From this dashboard you can also delete API keys by hovering over it and selecting the trash can from the right.&lt;/p&gt;

&lt;p&gt;Select Create Team API Key, then leave everything checked unless you wish to restrict the rights of what can be performed using this API key. Hit Save. You will be prompted to copy the API key and secret. Store these in a secure location. They will not be displayed again.&lt;/p&gt;

&lt;p&gt;Select the checkmark to indicate that you have copied the keys, and select Save.&lt;/p&gt;

&lt;p&gt;Copy your team API key and secret from Defender.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create A Relayer
&lt;/h2&gt;

&lt;p&gt;First, install the necessary package:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ npm install defender-relay-client&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Create a script such as the following, supplying your Defender API key and secret in your .env file and adjusting variable names as appropriate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { RelayClient } = require('defender-relay-client');
const { appendFileSync, writeFileSync} = require('fs');

async function run() {
  require('dotenv').config();
  const { DEFENDER_API_KEY: apiKey, DEFENDER_API_SECRET: apiSecret } = process.env;
  const relayClient = new RelayClient({ apiKey, apiSecret });

  // create relay using defender client
  const requestParams = {
    name: 'MyRelayer',
    network: 'goerli',
    minBalance: BigInt(1e17).toString(),
  };
  const relayer = await relayClient.create(requestParams);

  // store relayer info in file (optional)
  writeFileSync('relay.json', JSON.stringify({
    relayer
  }, null, 2));
  console.log('Relayer ID: ', relayer);

  // create and save the api key to .env - needed for sending tx
  const {apiKey: relayerKey, secretKey: relayerSecret} = await relayClient.createKey(relayer.relayerId);
  appendFileSync('.env', `\nRELAYER_KEY=${relayerKey}\nRELAYER_SECRET=${relayerSecret}`);
}

run().catch((error) =&amp;gt; {
  console.error(error);
  process.exitCode = 1;
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: To send a transaction directly via the Relayer using the API, you need the key and secret (which were appended in the above code to your &lt;code&gt;.env&lt;/code&gt; file). If, however, you wish to send a transaction by way of an Autotask, the only credentials you need are the team API key and secret along with the &lt;code&gt;relayerId&lt;/code&gt; (which in the above step was saved to the &lt;code&gt;relay.json&lt;/code&gt; file). &lt;/p&gt;

&lt;h2&gt;
  
  
  Compile and Deploy the Contract
&lt;/h2&gt;

&lt;p&gt;Run &lt;code&gt;npx hardhat compile&lt;/code&gt; (or the equivalent if using a different compiler) to get your smart contract ready for deployment.&lt;/p&gt;

&lt;p&gt;Use the following script to deploy the contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { DefenderRelayProvider, DefenderRelaySigner } = require('defender-relay-client/lib/ethers');
const { ethers } = require('hardhat');
const {writeFileSync} = require('fs');

async function main() {
  require('dotenv').config();
  const credentials = {apiKey: process.env.RELAYER_KEY, apiSecret: process.env.RELAYER_SECRET};
  const provider = new DefenderRelayProvider(credentials);
  const relaySigner = new DefenderRelaySigner(credentials, provider, { speed: 'fast' });

  const MyContract = await ethers.getContractFactory("SimpleRegistry");
  const myContract = await MyContract.connect(relaySigner).deploy().then(f =&amp;gt; f.deployed());

  writeFileSync('deploy.json', JSON.stringify({
    MyContract: myContract.address,
  }, null, 2));

  console.log(`MyContract: ${myContract.address}\n`);
}

if (require.main === module) {
  main().then(() =&amp;gt; process.exit(0))
    .catch(error =&amp;gt; { console.error(error); process.exit(1); });
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code pulls the Relayer's credentials from the local file along with the artifacts for the contract and uses ethers.js to deploy. The relevant address of the contract is saved to the local file &lt;code&gt;deploy.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Congratulations on successfully deploying your contract via Relayer!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Deploy and Manage an Upgradeable NFT Smart Contract</title>
      <dc:creator>slw.eth</dc:creator>
      <pubDate>Mon, 25 Apr 2022 20:59:36 +0000</pubDate>
      <link>https://dev.to/slw/create-and-upgrade-a-multisig-owned-uups-upgradeable-erc721-nft-contract-using-openzeppelin-defender-and-hardhat-1mn9</link>
      <guid>https://dev.to/slw/create-and-upgrade-a-multisig-owned-uups-upgradeable-erc721-nft-contract-using-openzeppelin-defender-and-hardhat-1mn9</guid>
      <description>&lt;p&gt;Immutability—the characteristic of being unable to be changed or erased—is one of the key features of smart contracts. It is a major part of the security of Web3. But as every developer knows, sometimes a protocol needs an upgrade or a new feature. And—on rare occasion, of course—a developer might make a mistake. In such cases, it can be handy to be able to perform upgrades. This tutorial explains how to create an upgradeable NFT contract in accordance with EIP-1967 - Standard Proxy Storage Slots Universal Upgradeable Proxy Standard (UUPS).&lt;/p&gt;

&lt;h1&gt;
  
  
  Types of Upgradeable Smart Contracts
&lt;/h1&gt;

&lt;p&gt;Smart contracts can be upgraded by separating a given smart contract into essentially two components: a proxy and an implementation. The diagram below symbolizes an upgrade from the contract ERC20 to the contract ERC20v2. &lt;/p&gt;

&lt;p&gt;OpenZeppelin Contracts provide two options for proxy-based contract upgradeability: transparent proxies and universal upgradeable proxies. These are discussed briefly below. For a deep dive on the subject of smart contract upgradeability, see this blog post by Santiago Palladino. &lt;/p&gt;

&lt;h2&gt;
  
  
  Transparent
&lt;/h2&gt;

&lt;p&gt;One type of smart contract upgrade follows what is called the transparent pattern. This pattern places both the state and the ability to upgrade in a proxy contract. The proxy points to a given implementation contract which holds the logic. When any non-admin address calls the contract, the calls are delegated to the implementation contract. When the admin calls the contract, the proxy reveals the upgradeTo function, giving the administrator the ability to point to a new implementation contract and thereby upgrade.&lt;/p&gt;

&lt;p&gt;One downside to this proxy pattern is that interacting with it costs a lot of gas, because doing so requires two storage accesses to check if the caller’s address is the admin.&lt;/p&gt;

&lt;h2&gt;
  
  
  UUPS
&lt;/h2&gt;

&lt;p&gt;The UUPS upgradeability pattern, on the other hand, is more efficient. This pattern places the upgrade function in the implementation contract. The proxy contract is minimal; it uses delegatecall to direct the implementation contract to execute transactions.&lt;/p&gt;

&lt;p&gt;With this proxy pattern, it is vital to ensure that the upgrade function remains functional and present with each upgrade. Otherwise, the ability to upgrade will be lost forever.&lt;/p&gt;

&lt;p&gt;This tutorial will follow the UUPS pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgradeable Contract Deployment
&lt;/h2&gt;

&lt;p&gt;This guide shows you how to deploy a barebones UUPS upgradeable ERC721 contract for minting gas-efficient NFTs. Ownership will be transferred to a Gnosis Safe multisig account and contract administration will be managed using OpenZeppelin Defender.&lt;/p&gt;

&lt;p&gt;Development Environment Setup&lt;br&gt;
For this tutorial, you will use Hardhat as a local development environment using node package manager. To get started, run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir uupsNFT
npm init -y
npm i dotenv
npm i --save-dev hardhat @nomiclabs/hardhat-etherscan
npx hardhat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select Create a basic sample project and accept the default arguments.&lt;/p&gt;

&lt;p&gt;You will need to install the OpenZeppelin Upgradeable Contracts library as well as the Hardhat Defender npm package for integrating upgrades with OpenZeppelin Defender. The Upgradeable Contracts package replicates the structure of the main OpenZeppelin Contracts, but with the addition of the Upgradeable suffix for every file and contract.&lt;/p&gt;

&lt;p&gt;The nft.storage package allows for easy deployment of IPFS-hosted NFT metadata.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i --save-dev @openzeppelin/contracts-upgradeable @openzeppelin/hardhat-defender @openzeppelin/hardhat-upgrades nft.storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sample project creates a few example files that are safe to delete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rm scripts/sample-script.js test/sample-test.js contracts/Greeter.sol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dotenv package allows you to access environment variables stored in a local file, but that file needs to be created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other Setup
&lt;/h2&gt;

&lt;p&gt;You will need to obtain a few important keys to be stored in your .env file. (Double-check that this file is listed in your .gitignore so that your private keys remain private.)&lt;/p&gt;

&lt;p&gt;In Alchemy, create an app on Rinkeby and copy the http key. Add it to your .env file.&lt;/p&gt;

&lt;p&gt;In Metamask, click Account Details -&amp;gt; Export Private Key to copy the private key you will use to deploy contracts.&lt;/p&gt;

&lt;p&gt;Important Security Note: Use an entirely different browser and a different Metamask account than one you might use for other purposes. That way, if you accidentally reveal your private key, the security of your personal funds will not be compromised.&lt;/p&gt;

&lt;p&gt;Get an API key from nft.storage and add it to your .env file.&lt;/p&gt;

&lt;p&gt;The contract will initially be deployed with a single EOA. After an initial upgrade, ownership will be transferred to a Gnosis Safe multisig.&lt;/p&gt;

&lt;p&gt;To create a new Gnosis Safe in OpenZeppelin Defender, navigate to Admin, select Contracts → Create Gnosis Safe. Provide the addresses of three owners and set the threshold at two for the multisig. &lt;/p&gt;

&lt;p&gt;You can create a new API key and secret in Defender by navigating to the hamburger menu at the top right and selecting Team API Keys. You can select yes for each of the default options and click Save.&lt;/p&gt;

&lt;p&gt;Your .env will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PRIVATE_KEY=
ALCHEMY_URL=
ETHERSCAN_API=
DEFENDER_KEY=
DEFENDER_SECRET=
NFT_API=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace your hardhat.config.js with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require("@openzeppelin/hardhat-upgrades");
require('@openzeppelin/hardhat-defender');
require("@nomiclabs/hardhat-etherscan");
require('@nomiclabs/hardhat-waffle');
require('dotenv').config();

module.exports = {
  solidity: "0.8.4",
  networks: {
    rinkeby: {
      url: `${process.env.ALCHEMY_URL}`,
      accounts: [`0x${process.env.PRIVATE_KEY}`],
    } 
  },
  etherscan: {
    apiKey: process.env.ETHERSCAN_API
  },
  defender: 
  {
    "apiKey": process.env.DEFENDER_KEY,
    "apiSecret": process.env.DEFENDER_SECRET
  }

};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Upload NFT Metadata
&lt;/h2&gt;

&lt;p&gt;This NFT token will consist of an image. The nft.storage npm package gives developers a straightforward way of uploading the .json metadata as well as the image asset.&lt;/p&gt;

&lt;p&gt;From the base project directory, create a folder to store the image asset:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir assets

touch scripts/uploadNFTData.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include the following code in this file, updating the code as necessary to use your image asset, name, and description:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NFTStorage, File } from "nft.storage"
import fs from 'fs'
import dotenv from 'dotenv'
dotenv.config()

async function storeAsset() {
   const client = new NFTStorage({ token: process.env.NFT_KEY })
   const metadata = await client.store({
       name: 'MyToken',
       description: 'This is a two-dimensional representation of a four-dimensional cube',
       image: new File(
           [await fs.promises.readFile('assets/cube.gif')],
           'cube.gif',
           { type: 'image/gif' }
       ),
   })
   console.log("Metadata stored on Filecoin and IPFS with URL:", metadata.url)
}

storeAsset()
   .then(() =&amp;gt; process.exit(0))
   .catch((error) =&amp;gt; {
       console.error(error);
       process.exit(1);
   });

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; node scripts/uploadNFTData.mjs
Metadata stored on Filecoin and IPFS with URL: ipfs://bafyreidb6v2ilmlhg2sznfb4cxdd5urdmxhks3bu4yqqmvbzdkatopr3nq/metadata.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success! Now your image and metadata are ready to be linked to your NFT contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Smart Contract
&lt;/h2&gt;

&lt;p&gt;Go to wizard.openzeppelin.com and select ERC721.&lt;/p&gt;

&lt;p&gt;Give your token whatever features you would like. Be sure to check the box for Upgradeability and select UUPS.&lt;/p&gt;

&lt;p&gt;Select Download → As Single File. Save this in your project’s /contracts folder.&lt;/p&gt;

&lt;p&gt;Note the Solidity version in the contract and edit hardhat.config.js to either match this version or be more recent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Deployment
&lt;/h2&gt;

&lt;p&gt;Create a file and supply the following code, adjusting as necessary based on your contract and token name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch scripts/deploy.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { ethers, upgrades } = require("hardhat");

async function main() {

  const CubeToken = await ethers.getContractFactory("CubeToken");
  const cubeToken = await upgrades.deployProxy(CubeToken);
  await cubeToken.deployed();

  console.log("Token address:", cubeToken.address);
}

main()
  .then(() =&amp;gt; process.exit(0))
  .catch((error) =&amp;gt; {
    console.error(error);
    process.exit(1);
  });


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat run scripts/deploy.js --network rinkeby

Token address: 0x12a9ba92b3B2746f41AcC45Af36c44ac00E107b0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mint NFT
&lt;/h2&gt;

&lt;p&gt;Now that the contract is deployed, you can call the safeMint function to mint the NFT using the data uploaded to IFPS earlier.&lt;/p&gt;

&lt;p&gt;Create a new file to include the following commands, substituting the address of your proxy contract and metadata URL in the relevant sections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch scripts/mintToken.mjs

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const CONTRACT_ADDRESS = "0x12a9ba92b3B2746f41AcC45Af36c44ac00E107b0"
const META_DATA_URL = "ipfs://bafyreidb6v2ilmlhg2sznfb4cxdd5urdmxhks3bu4yqqmvbzdkatopr3nq/metadata.json"

async function mintNFT(contractAddress, metaDataURL) {
   const ExampleNFT = await ethers.getContractFactory("CubeToken")
   const [owner] = await ethers.getSigners()
   await ExampleNFT.attach(contractAddress).safeMint(owner.address, metaDataURL)
   console.log("NFT minted to: ", owner.address)
}

mintNFT(CONTRACT_ADDRESS, META_DATA_URL)
   .then(() =&amp;gt; process.exit(0))
   .catch((error) =&amp;gt; {
       console.error(error);
       process.exit(1);
   });

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat run scripts/mintToken.mjs

NFT minted to:  0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verify Contract
&lt;/h2&gt;

&lt;p&gt;Until this point, we have been able to run functions on the deployed contract because we have the Application Binary Interface (ABI). Verifying the source code makes the complete contract code and ABI available publicly. This is good practice and it also makes it easier to interact with the contract.&lt;/p&gt;

&lt;p&gt;Go to the contract’s address in Etherscan and select Read as Proxy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rinkeby.etherscan.io/address/%7BPROXY_ADDRESS%7D"&gt;https://rinkeby.etherscan.io/address/{PROXY_ADDRESS}&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click Verify.&lt;/p&gt;

&lt;p&gt;Copy the implementation address.&lt;/p&gt;

&lt;p&gt;Use this address from the command line to verify the smart contract of the implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat verify --network rinkeby 0xf92d88cbfac9e20ab3cf05f6064d213a3468cf77
Nothing to compile
Successfully submitted source code for contract
contracts/MyToken.sol:MyToken at 0xf92d88cbfac9e20ab3cf05f6064d213a3468cf77
for verification on the block explorer. Waiting for verification result...

Successfully verified contract MyToken on Etherscan.
https://rinkeby.etherscan.io/address/0xf92d88cbfac9e20ab3cf05f6064d213a3468cf77#code

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defender Contract Admin
&lt;/h2&gt;

&lt;p&gt;Defender’s Admin feature makes it easy to manage contract administration and call contract functions. To do this, the contract needs to be imported into Defender. Importing a contract does not affect contract ownership in any way—and it can be done just as easily with a contract that you did not deploy as with one you did. For a simple example, you could add the Art Blocks NFT contract to your Admin dashboard and easily monitor contract state as well as do more interesting things such as firing a Sentinel notification each time a new NFT is minted.&lt;/p&gt;

&lt;p&gt;In Defender, navigate to Admin --&amp;gt; Add Contract --&amp;gt; Import Contract.&lt;/p&gt;

&lt;p&gt;Give it a name, select Rinkeby, and paste your contract’s proxy address from Etherscan. &lt;/p&gt;

&lt;p&gt;Defender will detect that the contract is Upgradable and Pausable.&lt;/p&gt;

&lt;p&gt;Select Add.&lt;/p&gt;

&lt;p&gt;Deploy Version 2 using Hardhat&lt;br&gt;
Edit the smart contract’s code, adding the following at the very end of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract MyTokenUpgraded is MyToken {

    function version() pure public returns(uint){
        return 2;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because this contract inherits the previously deployed one, it contains the existing functionality plus this function just added.&lt;/p&gt;

&lt;p&gt;Next, create a script to deploy the new implementation contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch scripts/upgrade.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following, substituting the proxy address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { ethers, upgrades } = require("hardhat");

async function main() {

  const CubeTokenUpg = await ethers.getContractFactory("CubeTokenUpgraded");
  const cubeTokenUpg = await upgrades.prepareUpgrade("{{YOUR_PROXY_ADDRESS}}", CubeTokenUpg);
  console.log("Upgrade Implementation address:", cubeTokenUpg);
}

main()
  .then(() =&amp;gt; process.exit(0))
  .catch((error) =&amp;gt; {
    console.error(error);
    process.exit(1);
  });


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prepareUpgrade function both validates and deploys the implementation contract. &lt;/p&gt;

&lt;p&gt;Run the script to deploy the upgraded contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat run scripts/upgrade.js --network rinkeby

0xB463054DDa7a0BD059f1Ba59Fa07Ebd7f531E9d7

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Upgrade Proxy via Defender
&lt;/h2&gt;

&lt;p&gt;The upgraded implementation has been deployed, but the proxy currently still points to the initial version. &lt;/p&gt;

&lt;p&gt;You can upgrade the contract using Defender by selecting New Proposal --&amp;gt; Upgrade.&lt;/p&gt;

&lt;p&gt;Paste in the address you just got for the new implementation. Since the contract is still owned by your Metamask account, and that account is connected to Defender, you will leave the admin contract and admin address blank.&lt;/p&gt;

&lt;p&gt;Give the upgrade a friendly name and (optionally) a description, then select Create Upgrade Proposal. &lt;/p&gt;

&lt;p&gt;On the next screen, review the details and execute the upgrade.&lt;/p&gt;

&lt;p&gt;Now, under the Admin dashboard for the contract, you will see the upgrade listed. Selecting it from the dashboard will take you back to the account details for that transaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transfer Ownership to Multisig
&lt;/h2&gt;

&lt;p&gt;It is not very secure for the owner of a smart contract to be a single account in Metamask. As a next step, transfer ownership to the multisig created earlier.&lt;/p&gt;

&lt;p&gt;Under Admin, select New Proposal → Admin Action.&lt;/p&gt;

&lt;p&gt;One function of the Ownable contract is transferOwnership, which does what you would expect.To execute it via Defender Admin, simply select the it from the Function dropdown. The function takes the parameter of the new owner’s address. For that, select the name of the Gnosis Safe Multisig you created earlier.&lt;/p&gt;

&lt;p&gt;For this function, the Execution Strategy is still EOA. Give the admin action proposal a name, select Create Admin Action, and then execute the transaction using your connected Metamask account.&lt;/p&gt;

&lt;p&gt;After completing this step, the contract’s new owner is the multisig, so future transactions will require two approvals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Propose a New Upgrade using Hardhat
&lt;/h2&gt;

&lt;p&gt;There are many possible reasons why it can be beneficial for a smart contract to be upgradeable. What if your auditor (or a malicious hacker or bot) discovers a bug in your contract? A non-upgradeable contract would be without an elegant solution. Another example is more common to iterative development generally – contract upgradeability gives the contract owner the option to add, modify, or remove functionality as desired. There are other caveats to be aware of with respect to upgrading a contract. Existing storage variables cannot be changed, and any new variables that you declare must be added after the existing variables. With respect to functions, you have more flexibility. Functions can be added, modified or removed in a  new upgrade. Be particularly attentive that you do not accidentally impede with the contract’s upgrade function, or else the contract will forever lose its ability to be upgraded.&lt;/p&gt;

&lt;p&gt;Using hardhat-defender, you can propose an upgrade to a contract owned by another account. This creates a proposal for review in Defender.&lt;/p&gt;

&lt;p&gt;You will need to create a script similar to before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch propose-upgrade.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { defender } = require("hardhat");

async function main() {
  const proxyAddress = '{{INSERT_YOUR_PROXY_ADDRESS}}';
  const CubeTokenV3 = await ethers.getContractFactory("CubeTokenV3");
  console.log("Preparing proposal...");
  const proposal = await defender.proposeUpgrade(proxyAddress, CubeTokenV3, {title: 'Propose Upgrade to V3', multisig: '{{YOUR MULTISIG ADDRESS}}' });
  console.log("Upgrade proposal created at:", proposal.url);
}

main()
  .then(() =&amp;gt; process.exit(0))
  .catch(error =&amp;gt; {
    console.error(error);
    process.exit(1);
  })

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, run the proposal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat run scripts/propose-upgrade.js --network rinkeby                    
Compiled 1 Solidity file successfully
Preparing proposal...
Upgrade proposal created at: https://defender.openzeppelin.com/#/admin/contracts/rinkeby-0x98A28EdD77Ba4249D85cbe9C902c92b037C6b977/proposals/2a577119-ab7b-4ab8-837d-b81acccc2684
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clicking the link will take you to the proposal review page in Defender where you can choose to Approve and Execute, if desired.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check Out the NFT
&lt;/h2&gt;

&lt;p&gt;You minted an NFT token in an earlier step. By now, it should be ready to view in OpenSea’s explorer. Head to testnets.opensea.io, select the dropdown, and enter the proxy contract address. Rather than hitting enter, it is necessary to click the link to the proxy contract address in the dropdown.&lt;/p&gt;

&lt;p&gt;Congratulations! You have successfully used OpenZeppelin Defender and Hardhat to deploy an UUPS-upgradeable ERC721 NFT contract, transferred ownership to a Gnosis Safe Multisig, and deployed an upgraded implementation contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;OpenZeppelin Defender&lt;/li&gt;
&lt;li&gt;OpenZeppelin Contracts&lt;/li&gt;
&lt;li&gt;Hardhat Upgrades NPM Package&lt;/li&gt;
&lt;li&gt;Hardhat Defender NPM Package&lt;/li&gt;
&lt;li&gt;nft.storage NPM Package&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>solidity</category>
      <category>ethereum</category>
      <category>web3</category>
      <category>nft</category>
    </item>
    <item>
      <title>Deploy a Secure NFT Without Writing Code</title>
      <dc:creator>slw.eth</dc:creator>
      <pubDate>Fri, 08 Apr 2022 16:44:56 +0000</pubDate>
      <link>https://dev.to/slw/deploy-a-secure-mutisig-owned-nft-using-openzeppelin-contracts-and-defender-13o1</link>
      <guid>https://dev.to/slw/deploy-a-secure-mutisig-owned-nft-using-openzeppelin-contracts-and-defender-13o1</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/yZvr5tEpRE8"&gt;
&lt;/iframe&gt;
&lt;br&gt;
There’s a lot of excitement around NFTs, and with good reason. With a few lines of Solidity code, you can create a token that represents ownership of a unique artwork (Twin Flames). You can even encode that artwork within the code itself (Art Blocks). NFTs can be used to register your own .eth name or signal membership in a community (BAYC). The future is bright for NFTs, and the technology holds immense potential for value creation. Whether that value endures, however, depends on the security of the underlying contract.&lt;/p&gt;

&lt;p&gt;In this tutorial, I’ll show you how you can create and deploy an NFT contract — without needing to touch a single line of Solidity code or JavaScript — using the &lt;a href="//wizard.openzeppelin.com"&gt;OpenZeppelin Contracts Wizard&lt;/a&gt;. You’ll be able to manage your token via a multisig, using &lt;a href="//defender.openzeppelin.com"&gt;OpenZeppelin Defender&lt;/a&gt; to mint tokens and to pause the contract’s functionality in the event of an emergency.&lt;/p&gt;

&lt;p&gt;By the end of this tutorial, you’ll have your own deployed NFT contract. More importantly, you’ll also have some valuable experience with important concepts in secure contract deployment and administration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Three Accounts
&lt;/h2&gt;

&lt;p&gt;First, let’s take a look at the issue of contract ownership. A simple NFT contract can be deployed with any externally owned account (EOA). For example, you can use your Metamask account to deploy a contract, and then your account will own that contract. As long as no one steals your private keys and you never lose access to that account, you will be the sole owner of that contract and you can run whatever admin functions you assign to it. However, it is not secure for a single EOA to act as contract administrator.&lt;/p&gt;

&lt;p&gt;A more secure practice is for your smart contract to be owned by a multisig. Think of a multisig as a team wallet that consists of more than one account. Often, ownership of a multisig would be distributed to three to five different individuals using hardware wallets in different locations, but in this case, for simplicity, you’ll create three Metamask accounts and remain sole owner of all of them. A three-account multisig provides an additional layer of security when you specify that two out of the three accounts in your multisig must approve a given transaction.&lt;/p&gt;

&lt;p&gt;For a three-account multisig, you will need to have three separate accounts. It’s easy to create a new account in Metamask by clicking your account image then selecting Create Account. Once you have done this three times, you’re ready to proceed.&lt;/p&gt;

&lt;p&gt;Important security note: It’s a good practice during development to use a different wallet and even a different browser than the one you might normally use to make transactions on Ethereum. Even though Rinkeby is a different network from mainnet, your Rinkeby private key is the same as your mainnet private key. If you accidentally leaked your private key while developing, you would likely lose all of the assets owned by that wallet, so using a different browser — and different accounts entirely — is a worthwhile thing to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Testnet ETH
&lt;/h2&gt;

&lt;p&gt;It costs gas to store data to the blockchain. You will need to pay for that in ETH. Fortunately, since you will be using a testnet, you can use testnet ETH, which can be freely obtained from a faucet.&lt;/p&gt;

&lt;p&gt;A faucet is a smart contract that sends a predetermined amount of ETH to any contract that calls it. Only one of your three accounts will need to be funded with ETH. Once you have obtained your ETH from a faucet, you’re ready for the next step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://forum.openzeppelin.com/t/rinkeby-testnet-faucets/25866"&gt;I’ve assembled a list of Rinkeby faucets here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Gnosis Safe with Defender Admin
&lt;/h2&gt;

&lt;p&gt;OpenZeppelin Defender is a security operations (SecOps) platform for Ethereum development. In this tutorial, you will use it to manage your multisig account and to run admin functions on your deployed contract.&lt;/p&gt;

&lt;p&gt;Head over to &lt;a href="//defender.openzeppelin.com"&gt;defender.openzeppelin.com&lt;/a&gt;. If you don’t have an account already, you’ll need to sign up. The free account will give you loads of functionality, which we will explore more in future tutorials.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;    Once signed in, you’ll see a few options on the left nav. Select Admin → Add Contract → Create Gnosis Safe.&lt;/li&gt;
&lt;li&gt;    In Metamask, select the Rinkeby network.&lt;/li&gt;
&lt;li&gt;    Click Connect Wallet and select an account that holds the testnet ETH that you obtained from the faucet.&lt;/li&gt;
&lt;li&gt;    Give your multisig whatever name you’d like and select the Rinkeby network.&lt;/li&gt;
&lt;li&gt;    Under Owners, you’ll need to add the address of each of your three Metamask accounts. To do this, you select an account in Metamask, copy the address, paste it into Defender, and then select Add Owner. Repeat this step for each Metamask account.&lt;/li&gt;
&lt;li&gt;    Set your Threshold to 2.&lt;/li&gt;
&lt;li&gt;    Click Create Gnosis Safe.&lt;/li&gt;
&lt;li&gt;    Each account connected to Defender will need to confirm the transaction.
When you see the “Transaction Confirmed” message, you can click Go to Safe to see the details of your multisig. Later in this tutorial, you will transfer ownership of your NFT contract to this account.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important security note: In this tutorial, the three-account multisig has a threshold of 2, meaning that 2 out of the three accounts must approve any transaction for the transaction to go through. A 2-of-3 multisig is more secure than an EOA. Setting a threshold of 1 would be less secure than an EOA, since if any one of the accounts were compromised, a transaction could be approved on behalf of the multisig.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LSUadIr---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cfn3711phl1orkzkzz9m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LSUadIr---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cfn3711phl1orkzkzz9m.png" alt="Create Gnosis Safe" width="880" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Consult the Wizard
&lt;/h2&gt;

&lt;p&gt;The OpenZeppelin Contracts Wizard allows you to jump-start development by giving you pre-built Solidity code from the latest release of secure OpenZeppelin contracts based on the selections you make. You can take this code and export it to Remix or download it for working locally.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="//wizard.openzeppelin.com"&gt;wizard.openzeppelin.com&lt;/a&gt; to proceed.&lt;/p&gt;

&lt;p&gt;With this tool, you can create ERC20 fungible token contracts, ERC721 NFTs, ERC1155 multi-token contracts, and Governor contracts for managing a DAO. Since you’re making an NFT, you could select either ERC1155 or ERC721. For this tutorial, we will select ERC721.&lt;/p&gt;

&lt;p&gt;The Contracts Wizard gives you several key options specific to your chosen contract standard so that you can get up and running simply by making your desired selections on the left-hand panel.&lt;/p&gt;

&lt;p&gt;Under Settings, give your token whatever Name and Symbol you would like.&lt;/p&gt;

&lt;p&gt;You can ignore Base URI for the time being, since you won’t be using your token to point to any metadata in this tutorial.&lt;/p&gt;

&lt;p&gt;About Metadata: Token metadata is a whole topic unto itself, so we will devote a future tutorial to that aspect of NFT development. For this tutorial, the focus is on secure contract ownership and administration. This is a very important building block for dealing responsibly with immutable code on a decentralized public blockchain.&lt;/p&gt;

&lt;p&gt;Under Features, select Mintable, so that you can create new tokens by calling a mint function.&lt;/p&gt;

&lt;p&gt;Next, let’s consider the token’s access control. The Wizard’s tooltips are very helpful for explaining the main idea here. You need some form of access control to answer the very important question of who gets to own and call which function. Ownable is a straightforward rule for access control.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sla6MMuW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pl6cwyq27urv3begzgl1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sla6MMuW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pl6cwyq27urv3begzgl1.png" alt="safeMint using OnlyOwner" width="880" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, Access Control is set to Ownable and our contract is set to import Ownable.sol. You’ll note in the code there is an onlyOwner modifier on public functions, which means only the address of the contract’s deployer can call it. The deployer’s address gets stored as the contract owner by the constructor when the contract gets created and written to the blockchain. Later, you will update the contract’s ownership by running a function to transfer ownership to your multisig account.&lt;/p&gt;

&lt;p&gt;In addition to the onlyOwner modifier, making the contract Ownable gives you other functions you can use, such as renounceOwnership and transferOwnership. These functions can be called by the existing owner to permanently change which account the contract regards as its owner.&lt;/p&gt;

&lt;p&gt;Why would a user want to renounce ownership of a contract? Depending on the contract, renouncing ownership could be done as a sign of good faith that the contract is not at risk of being tampered with by any meddlesome administrator. The contract would still exist, tokens could still be freely transferred, and the contract’s public or external state variables could still be queried. In short, any external or public functions not restricted by the onlyOwner modifier could be called.&lt;/p&gt;

&lt;p&gt;Select Auto Increment Ids so that each time a token is successfully minted, the contract keeps track of the number of total tokens minted. You can see that happening with the Counters.sol import and with the code added to the safeMint function.&lt;/p&gt;

&lt;p&gt;Next, let’s look at the issue of pausability. Selecting Pausable adds the functions pause and unpause as well as a check that happens when using the whenNotPaused modifier on token transfer. By default, paused is set to false. Pausing a contract affects token transfer because the added _beforeTokenTransfer function runs when someone wants to transfer a token.&lt;/p&gt;

&lt;p&gt;Why consider implementing this? As mentioned, if something catastrophic were to occur, pause allows you to disallow token transfer functionality until the contract gets unpaused.&lt;/p&gt;

&lt;p&gt;Of course, for transparency’s sake, it would be a good idea to alert your users about the issue, but that is another matter.&lt;/p&gt;

&lt;p&gt;Backing up a step, contract pausability gets us to consider what really happens during the transfer of a token. Your digital wallet isn’t like a physical wallet with fiat money. When you own an NFT, that NFT doesn’t ever get transported into your wallet. It’s more accurate to say that your wallet contains your private key and its ability to sign transactions. Ownership of an NFT happens by virtue of your public key getting stored on the smart contract to which the NFT belongs in a mapping of address to token id.&lt;/p&gt;

&lt;p&gt;Feel free to experiment with various functionalities to see what gets added or modified in the code supplied by the Contracts Wizard.&lt;/p&gt;

&lt;p&gt;On upgradeability: Remix currently doesn’t work for deploying upgradeable contracts. Contract upgradeability really opens things up, so in a future tutorial, you’ll make an upgradable contract, working locally using VSCode and Hardhat.&lt;/p&gt;

&lt;p&gt;You’ll notice there’s a space for a Security Contact. It is a good idea to include this now, since you won’t be able to edit your contract once it’s deployed. In the event that you want to add any monitoring or alerting service later, this allows your contract to be registered with that service once your code is verified.&lt;/p&gt;

&lt;p&gt;At this point, you should take a step back and look at what you have here. It’s secure, it’s awesome, and it only took a bit of thought about what functionality we’d like your NFT contract to have.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZI-IEUT3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dj3mglj3wwmhx7l8tjey.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZI-IEUT3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dj3mglj3wwmhx7l8tjey.png" alt="Contracts Wizard" width="880" height="835"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Import to Remix
&lt;/h2&gt;

&lt;p&gt;When you’re ready, simply click Open in Remix, and everything will be imported into Remix. Remix is a bonafide Solidity IDE, so once your code is imported here, you could add to it or change things around. However, since the Wizard left everything in good shape, don’t change anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compile
&lt;/h2&gt;

&lt;p&gt;Compiling your code makes it possible to deploy it to the blockchain.&lt;/p&gt;

&lt;p&gt;On the left, ensure that the compiler either matches the version of Solidity specified by your &lt;code&gt;pragma&lt;/code&gt; or is a more recent version and click Compile contract-{someAlphanumeric}.sol.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VuWJg_j6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r1i01o07onrphk7z07yy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VuWJg_j6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r1i01o07onrphk7z07yy.png" alt="0.8.7" width="880" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code will compile and you’ll see some new options on the left. Down below, you’ll notice a link to copy the ABI and bytecode. We won’t be using it here, but if you are building a frontend to interact with your smart contract, you’ll need the contract’s ABI, which is essentially an API used by a library such as ethers.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy
&lt;/h2&gt;

&lt;p&gt;Deploying our code sends a transaction to write it to the blockchain.&lt;/p&gt;

&lt;p&gt;From Remix’s toolbar, select Deploy &amp;amp; Run Transactions.&lt;/p&gt;

&lt;p&gt;Let’s consider the different environments for a moment. If we leave it as JavaScript VM, it won’t cost us anything to deploy, but it also won’t exist outside of Remix. This option is great for initial testing and experimenting, but we are ready to deploy our code to a public testnet. A public testnet is a bonafide blockchain with miners and gas fees, with a key difference: testnet ETH has no real-world value. If you make a mistake on a testnet and need to redeploy, it won’t set you back financially.&lt;/p&gt;

&lt;p&gt;First, double-check that you are using a Metamask wallet used only for development. This should be the same wallet you filled with some Rinkeby ETH earlier. In your Metamask wallet, ensure that you are on the Rinkeby network.&lt;/p&gt;

&lt;p&gt;Under Environment, select Injected Web3. Doing so will cause Metamask to connect to the site.&lt;/p&gt;

&lt;p&gt;Once connected, you will see your account’s public key displayed under Account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RazJe5kN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/31x9xyi516xrbf2rlydv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RazJe5kN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/31x9xyi516xrbf2rlydv.png" alt="Injected Web3" width="737" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Things are getting exciting! Let’s take this opportunity to dig deeper into what’s happening behind the scenes when it comes to import statements in contracts. Although all that we see at first is the additional line of code at the top, there is more going on here, and it explains where the additional functionality comes from.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HitpogPe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hz0kkmtvmz73gddu04ct.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HitpogPe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hz0kkmtvmz73gddu04ct.png" alt="import statements" width="880" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under Contract, click the dropdown. At the very bottom, you’ll see your ERC721 token contract. But where did all these other contracts come from? These are the imports specified at the top of the contract along with all their necessary dependencies. In effect, you can think of your contract as including each of these contracts first, all in one long mega-contract.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QnmoZEu7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sn3zqqzng5izy9zq71ge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QnmoZEu7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sn3zqqzng5izy9zq71ge.png" alt="List of contracts" width="880" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll notice that Ownable shows at the top of the list, but we want to deploy our token, not just Ownable, so be sure to select your token contract. When you deploy it, all the imports and dependencies will get deployed in the same transaction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tS4zwYU6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cpqvp7zp9xqzhbhxqlnf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tS4zwYU6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cpqvp7zp9xqzhbhxqlnf.png" alt="" width="588" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Selecting Deploy will prompt you to confirm the transaction in Metamask and pay for gas.&lt;/p&gt;

&lt;p&gt;At the bottom of the screen, Remix lets you know that the transaction is being mined and when it gets confirmed. It includes details of which block, the address deployed from, etc. You can select view on etherscan to see all the details of that specific transaction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jzlXzA17--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d128uvxkimnop6fvl8ul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jzlXzA17--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d128uvxkimnop6fvl8ul.png" alt="View on etherscan" width="880" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Etherscan, the From address shows the address you just used to deploy the contract. Selecting that address will show you any previous transactions made by that address, indexed by Etherscan.&lt;/p&gt;

&lt;p&gt;The To address is the address of the deployed contract.&lt;/p&gt;

&lt;p&gt;You can also view the contract by going back to Remix, clicking the button to copy the address where it says Deployed Contracts, and entering that in the search box in &lt;a href="//rinkeby.etherscan.io"&gt;Rinkeby Etherscan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FoxGoP8P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m6os4j8rcbu7qqgl2ayy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FoxGoP8P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m6os4j8rcbu7qqgl2ayy.png" alt="Contract address" width="618" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Contract Verification
&lt;/h2&gt;

&lt;p&gt;Verified contracts are those that make their source code easily accessible to anyone looking them up on Etherscan. Verifying a contract also allows users to run any publicly available read/write functions directly from Etherscan. While verifying your source code is not necessary, it is definitely a good practice.&lt;/p&gt;

&lt;p&gt;To do this in Remix, you’ll need to first add the Etherscan Contract Verification plugin by clicking the plug at the bottom left. Search for Etherscan and install it. Once it’s installed, select the Etherscan Contract Verification plugin from the left navigation bar.&lt;/p&gt;

&lt;p&gt;You need an Etherscan API key, which you can get by going to etherscan.io and creating an account. Once logged in, you’ll see in the dropdown under your user ID a listing for API KEYS.&lt;/p&gt;

&lt;p&gt;Once you have your API key created and copied, return to Remix and paste it in, then select your token contract from the dropdown. (You don’t need to include any constructor arguments.) Paste the address of the deployed contract. You can find this in Etherscan in the To: field from the transaction earlier. You can also find it in Remix in the Deploy section under Deployed Contracts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GO_M1hgd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/79a6cf9ty0igee4yblgw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GO_M1hgd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/79a6cf9ty0igee4yblgw.png" alt="Contract Verification" width="624" height="900"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Interacting with Your Verified Contract on Etherscan
&lt;/h2&gt;

&lt;p&gt;Return to Etherscan and select Contract. You will see the complete source code of your contract along with all of the imported contracts. Because your contract has been verified, you also gain the ability to read and write to the contract. Selecting Read Contract shows functions you can call on your contract that don’t cost gas.&lt;/p&gt;

&lt;p&gt;Selecting Write Transactions shows you the functions that require you to connect your wallet and pay for the transaction because they change the state of the blockchain.&lt;/p&gt;

&lt;p&gt;Etherscan gives verified contracts this very minimal frontend for transacting with deployed and verified contracts. It’s minimal, but good enough to run the same transactions for which you would use ethers.js when developing a full web app.&lt;/p&gt;

&lt;p&gt;Right now, querying these won’t tell us anything very interesting. We haven’t minted any tokens. Let’s turn back to Defender for that part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing the Contract into Defender
&lt;/h2&gt;

&lt;p&gt;Defender is an awesome tool for working with deployed contracts. To begin the process, we need to import the contract into Defender. “Importing a contract” makes no change to the contract itself. Rather, just think of Defender as a tool for calling a contract’s functions. In fact, it’s equally possible to import any verified contract into Defender, whether it is owned by you or not. It is a public blockchain, after all!&lt;/p&gt;

&lt;p&gt;Head back to Defender and select Admin. You’ll see the multisig created earlier. We will make use of that in a bit.&lt;/p&gt;

&lt;p&gt;Select Add Contract → Import a New Contract from the top right-hand corner.&lt;/p&gt;

&lt;p&gt;You can name it anything. For simplicity’s sake, it’s a good idea to give it the same name as the deployed contract.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6hlH5Wdw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/in1b5mu7v88jb5chouvo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6hlH5Wdw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/in1b5mu7v88jb5chouvo.png" alt="Import Contract" width="714" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select Rinkeby and paste in the deployed contract address. Remember: You can find the contract address via Etherscan as the To: field of your wallet’s most recent transaction or in Remix under Deployed Contracts.&lt;/p&gt;

&lt;p&gt;Because the contract has been verified, Defender loads the ABI automatically, and it detects that your contract is Pausable, which means you can use Defender to pause our contract if you want.&lt;/p&gt;

&lt;p&gt;Click Add.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transferring Ownership to the Multisig
&lt;/h2&gt;

&lt;p&gt;Right now, your NFT contract has the wallet address you used to deploy it set as its current owner.&lt;/p&gt;

&lt;p&gt;Multisig ownership is more secure, so we will transfer ownership to the multisig created earlier. To do this, select New Proposal then Admin Action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wdxtsuvv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/92899cc2fvbjj9ugjwol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wdxtsuvv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/92899cc2fvbjj9ugjwol.png" alt="Admin action" width="880" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, on the right side of the screen, you can see the current state of the contract. It’s not paused, the owner is your wallet address, and the totalSupply is zero since we haven’t minted any tokens.&lt;/p&gt;

&lt;p&gt;Click the Function dropdown and select transferOwnership. Under newOwner, select the name you gave the multisig.&lt;/p&gt;

&lt;p&gt;Below this, you’ll see Execution Strategy. What’s that, you ask? Defender is just asking you how you would like to make this transaction happen.&lt;/p&gt;

&lt;p&gt;If your contract was owned by a governance contract, as with a DAO, you would select Governor, so that transactions would run if they were passed in a vote. Typically, that would be done in tandem with a timelock, which applies a preset delay in transaction execution. To do this, you would select Timelock.&lt;/p&gt;

&lt;p&gt;We aren’t using a timelock or a governor, so our execution strategy is rather straightforward.&lt;/p&gt;

&lt;p&gt;In general, if you ever come to something that has you scratching your head, search the documentation and the answer is probably there. In Defender, you will find a link to it at the bottom left.&lt;/p&gt;

&lt;p&gt;Select EOA for externally owned accounts. Ensure that the same wallet used to deploy the contract is currently connected to Defender and set to be on the Rinkeby network. Since this account owns the contract, we have the ability to run this onlyOwner function. After transferring ownership, only the multisig can run such functions.&lt;/p&gt;

&lt;p&gt;Under the proposal title, give a friendly description to make it clear what you intend to accomplish here. “Transfer ownership to multisig” would suffice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wGjWH_tr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zum4gxymdrcm7ui79kjj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wGjWH_tr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zum4gxymdrcm7ui79kjj.png" alt="New Admin Action" width="630" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select Create admin action and you will see that this transaction (with the friendly title you just supplied) is pending approval.&lt;/p&gt;

&lt;p&gt;This screen is a bit like viewing your shopping cart before you actually make a purchase. That is good, since transferring ownership is a significant decision.&lt;/p&gt;

&lt;p&gt;Select Approve and Execute. You’ll have to sign (and pay for) the transaction in Metamask and then it gets executed. In the future, when running transactions for this contract you will still need to use Metamask, but the process will work a bit differently since the contract is now owned by a multisig.&lt;br&gt;
Mint a Token&lt;/p&gt;

&lt;p&gt;To mint a token using Defender, you’ll run another Admin action.&lt;/p&gt;

&lt;p&gt;Select Admin → {Your NFT Contract}.&lt;/p&gt;

&lt;p&gt;Under Proposals, you’ll see the transfer ownership transaction just executed.&lt;/p&gt;

&lt;p&gt;Select New Proposal → Admin action.&lt;/p&gt;

&lt;p&gt;Under Function, select safeMint. You’ll mint a token to the contract’s previous owner to say thanks for deploying the contract.&lt;/p&gt;

&lt;p&gt;For execution strategy, select Multisig along with the multisig managed by Defender that is the current owner.&lt;/p&gt;

&lt;p&gt;Defender shows us that your multisig has three addresses and that you need at least two to approve the transaction for it to go through.&lt;/p&gt;

&lt;p&gt;Describe the proposal as your first mint and select Create admin action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jYsarwGP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8mvwf4vckdm5gztpw59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jYsarwGP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8mvwf4vckdm5gztpw59.png" alt="Create admin action" width="576" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next screen, review the admin action and select Approve.&lt;/p&gt;

&lt;p&gt;To actually make this multisig transaction happen, you’ll need to sign the transaction with two of the three accounts belonging to the multisig. Accounts belonging to a given multisig could be wallets anywhere across the world. It just happens that you are managing them all in one place here.&lt;/p&gt;

&lt;p&gt;In Metamask, switch to an account in the multisig, select Approve, sign, switch to the second account, sign, then select Approve and Execute. (Once the transaction has been signed by two, it can be executed by anyone, including the account that just signed.)&lt;/p&gt;

&lt;p&gt;You have successfully minted your first token. To see this on the blockchain, select the contract’s address and paste it in Rinkeby Etherscan (or simply hover over the contract address and select View in Block Explorer) .&lt;/p&gt;

&lt;p&gt;In Transactions, note the transfer of ownership.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s_Oi8NIf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6qztjm81hj3aphuxyl5b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s_Oi8NIf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6qztjm81hj3aphuxyl5b.png" alt="Transactions" width="880" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Internal Txns, you’ll see the mint transaction. Selecting the parent transaction takes you to the transaction details, where you can see that Tokens Transferred was the token ID zero to the same address that deployed the contract.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AwYT_Qpm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rn8s5fyonj3q7q7iikwh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AwYT_Qpm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rn8s5fyonj3q7q7iikwh.png" alt="Token transfer" width="880" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! You have successfully deployed an ERC721 contract, transferred ownership to a multisig, and minted a token to an externally-owned account. From here, feel free to experiment further with Defender. See what happens if you pause your function and try to mint another token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;    OpenZeppelin &lt;a href="//docs.openzeppelin.com/defender"&gt;Defender&lt;/a&gt; documentation&lt;/li&gt;
&lt;li&gt;    &lt;a href="https://docs.openzeppelin.com/defender/admin#gnosis-safe"&gt;Gnosis Safe&lt;/a&gt; multi-signature wallets&lt;/li&gt;
&lt;li&gt;    &lt;a href="https://remix-ide.readthedocs.io/en/latest/"&gt;Remix&lt;/a&gt; documentation&lt;/li&gt;
&lt;li&gt;    Ethereum EIP-721 &lt;a href="https://eips.ethereum.org/EIPS/eip-721"&gt;Non-Fungible Token Standard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;    OpenZeppelin Contracts &lt;a href="https://docs.openzeppelin.com/contracts/4.x/"&gt;ERC-721 documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>solidity</category>
      <category>nft</category>
      <category>ethereum</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
