Reddit

NFTs are eating the world. A step-by-step Solidity tutorial for beginners to launch your first NFT collection


Hey r/NFT,

Launching an NFT collection for your brand, but don’t want to pay Opensea?

Love investing in NFTs, but have no idea how they actually work?

Want to make the career jump from web2 –> web3?

​

Sweet, this guide is for you. Our goal is to get you smart contract deployment ready in under 90 minutes.

**Formatting code on Reddit is hard, so it’s also on Medium** [**here**](https://medium.com/@brydge/how-to-launch-an-nft-collection-with-solidity-and-brownie-1f56ef2e2149)**.**

## Before we continue

There are a couple of concepts that we should cover before actually writing any code. I will cover each of them super briefly, however, if you are looking to further your comfortability with these topics I will also attach some external resources that I strongly encourage you to explore on your own.

## The essentials

For the sake of conciseness, I am going to assume that if you are reading this, you already have some working knowledge of what a blockchain is as well as some basic familiarity with programming languages such as Python (that’s what we’ll be using today!). If not, I suggest you take a look at the following resources before going any further as it will greatly reduce your confusion as we proceed today:

[Learn Python – Full Course for Beginners \[Tutorial\]](https://www.youtube.com/watch?v=rfscVS0vtbw&ab_channel=freeCodeCamp.org)

[How does a blockchain work – Simply Explained](https://www.youtube.com/watch?v=SSo_EIwHSd4&ab_channel=SimplyExplained)

## Ethereum and smart contracts

If these words mean nothing to you, don’t worry! In short, Ethereum is a blockchain that supports the execution of smart contracts; these are programs that reside at a unique address on the Ethereum blockchain. These contracts are actually types of Ethereum accounts and they can receive and send funds just like any other account, however, they are controlled by the program logic that was specified at the time of them being deployed to the blockchain.

Note that the native token to the Ethereum blockchain is called ether (denoted ETH) and having this ether will be required to facilitate transactions.

For more, check out the following:

[Intro to Ethereum | ethereum.org](https://ethereum.org/en/developers/docs/intro-to-ethereum/)

[Introduction to smart contracts | ethereum.org](https://ethereum.org/en/developers/docs/smart-contracts/)

## ERC-721: the Non-Fungible Token standard

An NFT, defined by the ERC-721 standard is a unique token that resides on the blockchain and is associated with a specific smart contract that complies with the standard. Each NFT, belonging to a smart contract has a unique token ID within that contract such that it can be differentiated from other tokens in the collection. Each NFT can be associated with some further data beyond its contract address and token ID. For our purposes, this data will be a reference to some digital artwork (we’ll come back to this later), however, it could be many other pieces of data too.

Check out these resources if you would like to learn more:

[ERC-721 Non-Fungible Token Standard | ethereum.org](https://ethereum.org/en/developers/docs/standards/tokens/erc-721/)

## Creating our first crypto wallet with MetaMask

In order to participate in the world of crypto and interact with these blockchains, we need some sort of interface. One such interface that many choose to use is a crypto wallet such as MetaMask.

To get started follow the instructions here:

[How to create a MetaMask Wallet](https://myterablock.medium.com/how-to-create-or-import-a-metamask-wallet-a551fc2f5a6b)

Be sure to carefully follow their instructions on keeping track of your seed phrase. This is very important as losing access may lock you out of your wallet or allow someone else to control your funds.

## Getting started with some test currency

Working with real ETH can be really expensive and when we’re learning, experimentation on the Ethereum main network can add up quickly. Even on layer-2 networks like Polygon that attempt to curb the expensive transaction fees of Ethereum, we need to spend real tokens each time we want to change the state of the blockchain. Luckily, Ethereum has some test networks that only require test tokens.

First, let’s make sure that our MetaMask lets us interact with these test networks. In MetaMask, click your account icon, then click settings → Advanced → Toggle “Show test networks” to on. Great! We can now see the test networks on our MetaMask. **We’re going to continue with the Rinkeby test network from this point on.**

Now let’s get some test currency in our account. Navigate to [https://faucets.chain.link/rinkeby](https://faucets.chain.link/rinkeby). You might have to connect your MetaMask to the site; just follow the steps provided there. Then make sure the network is set to **Ethereum Rinkeby**, select 10 test LINK, 0.1 test ETH, and confirm that you are not in fact a robot. Finally, send the request and you should soon see the funds in your account. We can now spend this test currency to change the state of the blockchain!

# Setting up our project with the Python Brownie SDK and Infura

To get started with blockchain development, we will use Brownie, a great framework for doing so. Brownie will help us get up and running with our NFT projects with agility by using Python scripts to deploy and interact with our smart contracts. Alongside Brownie, we will use Infura, an infrastructure-as-a-service product that allows us to easily interact with blockchains.

## Installing Brownie

Go ahead and follow the instructions listed here:

[eth-brownie](https://pypi.org/project/eth-brownie/)

Note that the creators of Brownie recommend using pipx, however, pip can also be used.

## Creating a Brownie project

Now that we have Brownie installed, let’s get started with our first project.

First, open up the command line and navigate to a location from where you would like to create a new project directory. From here create the project directory. We’ll call ours “NFT-demo”.

mkdir NFT-demo cd NFT-demo

Now we can initialize our new project with the following command:

brownie init

Now, in our NFT-demo directory we should see the following subdirectories:

​

* contracts/: Contract sources
* interfaces/: Interface sources
* scripts/: Scripts for deployment and interaction
* tests/: Scripts for testing the project
* build/: Project data such as compiler artifacts and unit test results
* reports/: JSON report files for use in the Brownie GUI

## Configuring the project

In addition to the above subdirectories, we’ll also need two additional files in the NFT-demo project-level directory: an environment variables file to hide our sensitive variables and a brownie-config file to tell Brownie where it can find these variables as well as configure any dependencies.

## .env

Beginning with the environment variable file, create a new file called .envin the NFT-demo directory. To start, include the following code:

PRIVATE_KEY=” WEB3_INFURA_PROJECT_ID=” PINATA_API_KEY=” PINATA_API_SECRET=” ETHERSCAN_TOKEN=”

For now, we will leave everything blank with the exception of our PRIVATE\_KEYvariable. For this, head to your MetaMask account → Menu → Account details → Export private key. From here input your MetaMask password and replace the first line so that it now reads PRIVATE\_KEY=<YOUR\_PRIVATE\_KEY>. We’ll fill in the rest as we go.

For more on environment variables check out the resources below:

[An Introduction to Environment Variables and How to Use Them](https://medium.com/chingu/an-introduction-to-environment-variables-and-how-to-use-them-f602f66d15fa)

## brownie-config.yaml

Now let’s create our Brownie configuration file. In a file called brownie-config.yaml(again, in the NFT-demo directory) input the following code:

dotenv: .env dependencies: – smartcontractkit/chainlink-brownie-contracts@0.4.0 – OpenZeppelin/openzeppelin-contracts@4.5.0 compiler: solc: remappings: – ‘@chainlink=smartcontractkit/chainlink-brownie-contracts@0.4.0’ – ‘@openzeppelin=OpenZeppelin/openzeppelin-contracts@4.5.0’ wallets: from_key: ${PRIVATE_KEY}

A few important points:

&#x200B;

* The dotenventry tells Brownie where to find our environment variables
* At a high level, the dependenciesand compilermappings allow us to easily interact with external libraries (for more info see the resource below)
* The walletsentry gives us an easy way to access our private key programmatically so that we can interact with the blockchain as ourselves

[The Configuration File – Brownie 1.18.1 documentation](https://eth-brownie.readthedocs.io/en/stable/config.html)

## Connecting to the blockchain with Infura

Before writing a contract that we can deploy to the blockchain, we need a way for us to easily interface with them without having to run our own node. To get started with Infura follow the steps provided here:

[How To Get Infura API Key](https://medium.com/jelly-market/how-to-get-infura-api-key-e7d552dd396f)

Once we have our Infura project setup, grab the project ID and add it to our .envfile so that the second line now reads WEB3\_INFURA\_PROJECT\_ID=<YOUR\_PROJECT\_ID>. Brownie will use this behind the scenes to connect us to blockchain networks so we don’t have to worry about this too much from here on.

We’re now ready to begin writing our first NFT smart contract!

# Writing our first smart contract

Let’s jump right in by creating a new file in the contracts subdirectory called WaterCollection.sol. This will be the contract for our new NFT collection.

Our project directory structure should now look like this:

– NFT-demo | – build | – contracts | – WaterCollection.sol | – interfaces | – reports | – scripts | – tests

Note that Solidity is a popular programming language for smart contract development. For a deeper dive check out their docs:

[Solidity – Solidity 0.8.13 documentation](https://docs.soliditylang.org/en/v0.8.13/)

To start let’s add the following lines:

// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import “@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol”; import “@openzeppelin/contracts/token/ERC721/ERC721.sol”;

Here we’re doing a few things. Firstly, we define a license. Don’t worry too much about this, for now, just know that the MIT license essentially means that we’re open-sourcing our contract.

Secondly, we’re defining our solidity version. Again, don’t worry too much, but if you’re curious about these versions, check out the docs above.

Finally, we’re importing contracts from OpenZeppelin, which can be thought of as a set of trusted smart contracts. We’ll inherit some properties of these contracts for our own contract.

## Inheriting the OpenZeppelin implementation

To leverage existing implementations provided by OpenZeppelin, we’ll create our contract in such a way that it takes on the functionality of OpenZeppelin contracts. Specifically, we’ll be using their ERC721URIStorage module which is like their base ERC721 module, with the added ability to attach data to the NFT with a reference called a token URI. This will allow us to associate our NFTs with our artwork. Be sure to read more about the module here:

[ERC 721 – OpenZeppelin Docs](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#ERC721URIStorage)

Let’s update our WaterCollection.solfile:

// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import “@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol”; import “@openzeppelin/contracts/token/ERC721/ERC721.sol”; contract WaterCollection is ERC721URIStorage { uint256 public tokenCounter; }

We now have an outline for our new WaterCollectioncontract that inherits the OpenZeppelin contract.

Note that we have also added a contract variable tokenCounterthat will allow us to keep track of the number of NFTs that have been created by our contract.

## Defining a contract constructor

A constructor method allows us to define the behavior of our contract upon deployment.

Let’s update our WaterCollection.solfile again:

// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import “@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol”; import “@openzeppelin/contracts/token/ERC721/ERC721.sol”; contract WaterCollection is ERC721URIStorage { uint256 public tokenCounter; constructor() public ERC721(“Water Collection”, “DRIP”) { tokenCounter = 0; } }

Here, we call the OpenZeppelin ERC721 constructor, defining that its name is “Water Collection” and its token symbol is “DRIP”. Additionally, we set the token counter of our contract to 0 as at the time of deployment, we will have yet to create an NFT.

## A method to create an NFT

Let’s now define a method that allows us to actually create an NFT with our contract.

We’ll update the contact again:

// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import “@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol”; import “@openzeppelin/contracts/token/ERC721/ERC721.sol”; contract WaterCollection is ERC721URIStorage { uint256 public tokenCounter; constructor() public ERC721(“Water Collection”, “DRIP”) { tokenCounter = 0; } function createToken(string memory tokenURI) public returns (bytes32){ require(tokenCounter < 100, “Max number of tokens reached”); uint256 tokenId = tokenCounter; _safeMint(msg.sender, tokenId); _setTokenURI(tokenId, tokenURI); tokenCounter++; } }

We’ve written a method that takes a token URI string as an argument. Let’s review the logic.

To begin, we’ve chosen to require that a maximum of 100 NFTs may be created with this contract. This is a design choice and does not necessarily need to be done, however, in our case, if someone were to attempt creating a 101st NFT, they would receive an error message and it would not be created.

Next, we set the token ID to the current tokenCounterso that we can call the ERC721 \_safeMintmethod and the ERC721URIStorage \_setTokenURImethod.

The \_safeMintmethod creates or “mints” a new token to our contract and sets its owner to whoever called the createTokenmethod with a token ID of tokenCounter.

Then, the \_setTokenURImethod sets the token URI of that token to the string passed to our function. We’ll discuss what this should be soon.

Finally, we increment our token counter to update the number of tokens in our collection.

Our contract is now done and ready to be deployed!

Let’s run brownie compileto make sure everything is working. We should see a message asserting that our project has been compiled.

## A script to deploy our contract to a testnet

Now that our contract is complete, we can write ourselves a Python script to deploy it to the blockchain of our choosing. Go ahead and open a new file in the scriptssubdirectory of our project called deploy\_water.pywith the following code:

from brownie import WaterCollection, accounts, config def main(): dev = accounts.add(config[‘wallets’][‘from_key’]) WaterCollection.deploy( {‘from’: dev} )

Here we are storing the information about our account that we obtain via the private key we referenced in our brownie-config.yamlfile to the devvariable.

With this account information, we are asking Brownie to deploy our contract to the blockchain and signing the transaction with our information with the {‘from’: dev}snippet so that the blockchain can identify us as the sender of this state change.

Our project directory should now look like this:

– NFT-demo | – build | – contracts | – WaterCollection.sol | – interfaces | – reports | – scripts | – deploy_water.py | – tests

Let’s run this script with Brownie so that it deploys to the Ethereum Rinkeby test network. From our NFT-demo directory run:

brownie run scripts/deploy_water.py –network rinkeby

We should now see something similar to the following:

Running ‘scripts/WaterCollection/deploy_water.py::main’… Transaction sent: 0xade52b4a0bbabdb02aeda6ef4151184116a4c6429462c28d4ccedf90eae0d36d Gas price: 1.033999909 gwei Gas limit: 1544657 Nonce: 236 WaterCollection.constructor confirmed Block: 10423624 Gas used: 1404234 (90.91%) WaterCollection deployed at: 0xE013b913Ca4dAD36584D3cBFCaB6ae687c5B26c5

To make sure everything went as expected, we can go to [https://rinkeby.etherscan.io/](https://rinkeby.etherscan.io/). This is a service that allows us to explore the blockchain. Go ahead and copy the address that your WaterCollection was deployed at and paste it into the Etherscan Rinkeby search bar. Keep this address ready for later!

We should see a single transaction under the contract that represents our contract creation.

Great! We’re deployed to Rinkeby and ready to learn about decentralized storage.

# Blockchain storage and IPFS

As we touched on earlier, we’re going to need a way to associate our artwork with our NFTs. Since we’re aiming to ensure that the future owners of our tokens have invariant access and ownership of the artwork associated with their token so long as they own said token, we would ideally like to have our NFT directly contain the binary data of its artwork. However, we must recognize that storing large amounts of data on the blockchain can be very expensive and a high-resolution image or even set of frames can require a great deal of storage. This motivates us to associate the data indirectly through the token URI that we mentioned earlier. This URI is a link to an external resource wherein our data is stored.

## Why decentralized storage?

Since we’re going to be using an external link, our intuition might be to simply use a link to an address at some cloud storage provider such as Google Drive or AWS S3, however, upon further reflection, we see that this is conceptually problematic.

Since one of the great things about NFTs is that they are decentrally managed thus, we do not have to rely on any single organization to ensure that they continue to exist. So, if we stored our artwork in one of these cloud providers, we would effectively be defeating one of the core purposes of NFTs by relying on a central body to persist our artwork. Admittedly, it is unlikely that Google or AWS suddenly cease to exist, however, to preserve the decentralized properties of our NFTs we will seek out a decentralized method of storage.

## IPFS

Luckily, we have the InterPlanetary File System (IPFS), which is a peer-to-peer distributed file system that we can use to store our data. For more on IPFS, take a look at their website:

[IPFS Powers the Distributed Web](https://ipfs.io/)

## Pinning data to IPFS with Pinata

A great way to interact with and persist data to IPFS is through a service called Pinata. Go ahead and create a free Pinata account. The following guide will walk you through getting your API key and secret (both of which we will need), as well as explain a little more about pinning data with Pinata:

[How to pin to IPFS effortlessly](https://medium.com/pinata/how-to-pin-to-ipfs-effortlessly-ba3437b33885)

Once you have your API key and secret, let’s go back to our .envfile and fill them in. It should now resemble:

PRIVATE_KEY=<YOUR_PRIVATE_KEY> WEB3_INFURA_PROJECT_ID=<YOUR_PROJECT_ID> PINATA_API_KEY=<YOUR_PINATA_KEY> PINATA_API_SECRET=<YOUR_PINATA_SECRET> ETHERSCAN_TOKEN=”

## Preparing our artwork for IPFS

Now that we’re almost ready to upload our artwork to IPFS, we should consider what it is that we want to include. For this tutorial, I’ve chosen to use 100 pictures of water like this one:

📷

You can choose whatever you like for your art!

Now for simplicity’s sake, I have named my images 1.jpg, 2.jpg, …, 100.jpg, but if you want to get more creative, awesome; you’ll just have to make sure to map their names to numbers somehow so that our script that we’ll soon write can find each of them.

Let’s create a new imagessubdirectory and upload our artwork there. The project directory should now look something like this:

– NFT-demo | – build | – contracts | – WaterCollection.sol | – images | – 1.jpg | – … | – 100.jpg | – interfaces | – reports | – scripts | – deploy_water.py | – tests

Now we have a place for our script (that we’re about to write) to find our artwork.

## A script to save our artwork and metadata to IPFS

So we have Pinata set up and our artwork is ready. Let’s write another script to interact with this data. Open up a new file in the scriptssubdirectory called create\_metadata.py.

Before we start writing our code, we should quickly review what we’re trying to do. Due to the specification of the ERC721 (NFT) standard, our data (that we are referencing through the token URI) is not going to be the artwork itself, but a metadata file in which the actual artwork is referenced. Its gonna look something like this:

📷

So, we are going to have to upload 2 files to IPFS for each NFT: 1 for the artwork itself, and 1 for the metadata file that references the artwork.

Now that we’ve covered that, let’s write our script in create\_metadata.py:

import requests import os import json metadata_template = { “name”: “”, “description”: “”, “image”: “” } def main(): write_metadata(100) def write_metadata(num_tokens): # We’ll use this array to store the hashes of the metadata meta_data_hashes = [] for token_id in range(num_tokens): collectible_metadata = metadata_template.copy() # The filename where we’re going to locally store the metadata meta_data_filename = f”metadata/{token_id + 1}.json” # Name of the collectible set to its token id collectible_metadata[“name”] = str(token_id) # Description of the collectible set to be “Wata” collectible_metadata[“description”] = “Wata” # Path of the artwork to be uploaded to IPFS img_path = f”images/{token_id + 1}.jpg” with open(img_path, “rb”) as f: img_binary = f.read() # Upload the image to IPFS and get the storage address image = upload_to_ipfs(img_binary) # Add the image URI to the metadata image_path = f”<https://ipfs.io/ipfs/{image}>” collectible_metadata[“image”] = image_path with open(meta_data_filename, “w”) as f: # Write the metadata locally json.dump(collectible_metadata, f) # Upload our metadata to IPFS meta_data_hash = upload_to_ipfs(collectible_metadata) meta_data_path = f”<https://ipfs.io/ipfs/{meta_data_hash}>” # Add the metadata URI to the array meta_data_hashes.append(meta_data_path) with open(‘metadata/data.json’, ‘w’) as f: # Finally, we’ll write the array of metadata URIs to a file json.dump(meta_data_hashes, f) return meta_data_hashes def upload_to_ipfs(data): # Get our Pinata credentials from our .env file pinata_api_key = os.environ[“PINATA_API_KEY”] pinata_api_secret = os.environ[“PINATA_API_SECRET”] endpoint = “<https://api.pinata.cloud/pinning/pinFileToIPFS>” headers = { ‘pinata_api_key’: pinata_api_key, ‘pinata_secret_api_key’: pinata_api_secret } body = { ‘file’: data } # Make the pin request to Pinata response = requests.post(endpoint, headers=headers, files=body) # Return the IPFS hash where the data is stored return response.json()[“IpfsHash”]

I’ll let you verify the code for yourself, but in short, it will save all of our artwork to IPFS, create a metadata file in the following format:

{ “name”: “<TOKEN_ID>”, “description”: “Wata”, “image”: “<https://ipfs.io/ipfs/><ARTWORK_IPFS_HASH>” }

And will write this file both locally under a metadatasubdirectory (that we will create momentarily) to a file called <TOKEN\_ID>.json, and to IPFS. Finally, it will save a list of the IPFS metadata hashes to a file called data.jsonin that same subdirectory.

Go ahead and run the following commands in your command line from the NFT-demo directory:

mkdir metadata brownie run scripts/create_metadata.py –network rinkeby

If all goes as expected, we will now have the following project structure:

– NFT-demo | – build | – contracts | – WaterCollection.sol | – images | – 1.jpg | – … | – 100.jpg | – interfaces | – metadata | – data.json | – 1.json | – … | – 100.json | – reports | – scripts | – deploy_water.py | – create_metadata.py | – tests

Now we’re ready to mint our collection!

# Minting our collection

So our contract is deployed to the Rinkeby network and we have all of our artwork and metadata written to IPFS. Now it’s time to mint our collection.

## A script to mint our collection by calling our contract

Let’s write one more script in our scriptssubdirectory to mint our collection called create\_collection.py:

import json from pathlib import Path from brownie import ( accounts, config, WaterCollection, ) from scripts.WaterCollection.create_metadata import write_metadata def main(): # Get our account info dev = accounts.add(config[‘wallets’][‘from_key’]) # Get the most recent deployment of our contract water_collection = WaterCollection[-1] # Check the number of currently minted tokens existing_tokens = water_collection.tokenCounter() print(existing_tokens) # Check if we’eve already got our metadata hashes ready if Path(f”metadata/data.json”).exists(): print(“Metadata already exists. Skipping…”) meta_data_hashes = json.load(open(f”metadata/data.json”)) else: meta_data_hashes = write_metadata(100) for token_id in range(existing_tokens, 100): # Get the metadata hash for this token’s URI meta_data_hash = meta_data_hashes[token_id] # Call our createCollectible function to mint a token transaction = water_collection.createCollectible( meta_data_hash, {‘from’: dev, “gas_limit”: 2074044, “allow_revert”: True}) # Wait for 3 blocks to be created atop our transactions transaction.wait(3)

Again, please verify the code yourself, but this script essentially makes sure we have access to the IPFS addresses of our metadata and then creates our collection one by one using these addresses as our token URIs.

Now we can run the script with:

brownie run scripts/create_collection.py –network rinkeby

Let’s head over to [https://testnets.opensea.io/](https://testnets.opensea.io/) and put our contract address in the search bar. It may take several minutes to load, but if we check back we should see our collection here! We’ve now minted our collection! All that remains is to list our tokens.

# Redeploying on other networks

Now that we’ve done this on the Rinkeby test network, you might be wondering how we can redo this on a real network so that we can profit off of our hard work and artistry. Luckily, it’s as simple as changing the network argument that we provide to Brownie.

Say we’d like to deploy to the Polygon network we just have to rerun our scripts on the Polygon network (we can use the same IPFS addresses):

brownie run scripts/deploy_water.py –network polygon-main brownie run scripts/create_collection.py –network polygon-main

Just note that we’re going to need some MATIC (Polygon’s native token) to interact with the Polygon network. If we do this, we’ll be able to see our collection at [https://opensea.io/](https://opensea.io/) under the address of our contract on the Polygon network (we’ll get this when we run that first deploy script on the Polygon network).

# Listing our NFTs

Finally, let’s list our NFTs in a marketplace. For this, we’ll be using Zora, an NFT marketplace protocol, but feel free to explore other options on your own.

## Zora Asks Module

The Zora asks module allows us to list our NFTs by providing the address of our NFT contract, the token ID of the token to be listed, an ask currency, an ask price (in our ask currency), an address to deposit the funds from a sold NFT, and a finders fee to incentivize referrals to our NFTs. You can check out their docs here:

[Asks V1.1 | Zora Docs](https://docs.zora.co/docs/smart-contracts/modules/Asks/zora-v3-asks-v1.1)

## Etherscan API

Before we write our script to list these asks, we need a way to programmatically access the Zora contracts. To do so we’re going to use Etherscan’s API. Go ahead and get yourself a free API by creating an Etherscan account and following their instructions here:

Grab your API token and fill in the final line of your .envfile so that it reads ETHERSCAN\_TOKEN=<YOUR\_ETHERSCAN\_API\_TOKEN>. Now Brownie will be able to pull contracts from Ethereum networks behind the scenes.

Note that you can apply a very similar process for the Polygon network by getting a Polyscan API token here:

[PolygonScan APIs](https://polygonscan.com/apis)

And adding a new .enventry: POLYGONSCAN\_TOKEN=<YOUR\_POLYSCAN\_API\_TOKEN>.

## A script to list our NFT collection

Now for our final script. Again in the scriptssubdirectory, let’s create a new file called set\_asks.py:

from brownie import WaterCollection, network, accounts, config, Contract def main(): # Fill your own MetaMask public key here creator_address = “” net = network.show_active() water_collection = WaterCollection[-1] # Get the asks contract depening on the network if net == “polygon-main”: asks_address = “0x3634e984Ba0373Cfa178986FD19F03ba4dD8E469” asksv1 = Contract.from_explorer(asks_address) module_manager = Contract.from_explorer(“0xCCA379FDF4Beda63c4bB0e2A3179Ae62c8716794”) erc721_helper_address = “0xCe6cEf2A9028e1C3B21647ae3B4251038109f42a” water_address = “0x0d2964fB0bEe1769C1D425aA50A178d29E7815a0” weth_address = “0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619” elif net == “rinkeby”: asks_address = “0xA98D3729265C88c5b3f861a0c501622750fF4806” asksv1 = Contract.from_explorer(asks_address) module_manager = Contract.from_explorer(“0xa248736d3b73A231D95A5F99965857ebbBD42D85”) erc721_helper_address = “0x029AA5a949C9C90916729D50537062cb73b5Ac92” water_address = “0xFA3D765E90b3FBE91A3AaffF1a611654B911EADb” weth_address = “0xc778417E063141139Fce010982780140Aa0cD5Ab” dev = accounts.add(config[‘wallets’][‘from_key’]) # Give Zora permission to facilitate transactions with the ASK contract module_manager.setApprovalForModule(asks_address, True, {“from”: dev}) water_collection.setApprovalForAll(erc721_helper_address, True, {“from”: dev}) for token_id in range(100): price = (100 – token_id) * 10 ** 16 asksv1.createAsk(water_address, # Address of our contract token_id, # Token ID of the NFT to be listed price, # Our asking price weth_address, # The address of the token required to pay for our NFT creator_address, # The address where the funds will be sent to 0, # A finder reward {‘from’: dev}) # Sign our transaction with our account

This script is going to access the Zora asks contract and list our NFTs using a sliding price scale. Here, we’re asking that the NFTs be paid for in Wrapped ETH, however, you can change this if you’d like.

Also, note that this script is going to approve the Zora contract to move funds and NFTs on our behalf. It’s always good practice to verify the contract logic before approving it, but you can take my word for its legitimacy if you’d like.

Finally, we’ll run our script:

brownie run scripts/set_asks.py –network rinkeby

Awesome! We’ve now listed our collection on Rinkeby testnet and can start selling!

This tutorial covers listing your collection and accepting 1 token (wETH) from 1 chain (Rinkeby/Mainnet). If you’d like a super simple way to accept any token from any chain, check us out at [Brydge](https://brydge.network)!

Happy to help answer questions!



View Reddit by the_altoid_roadView Source

Leave a Reply

Your email address will not be published. Required fields are marked *