Develop NFT marketplace on Avalanche
With the emergence of NFT marketplaces, people now have an opportunity to showcase their collectibles digitally. This also implies that the people with digital assets and collectibles can manage their assets effectively on a more secure and digital platform. NFT marketplaces are lucrative as they let people trade and exchange their digital commodities minted and stored on the marketplace.
From digital collectibles and assets to the gaming industry, NFT marketplace development powers all industries globally. Some fundamental properties of an NFT marketplace that adds to its credibility are as follows:
- Unified and distinctive tokens
- Rareness
- Ownership rights
- Transparency
- Indivisibility
- Interoperability
- Secure asset transfer
- Different blockchain and token compatibility
People prefer different blockchains as per their feasibility for NFT marketplace development like Ethereum, Binance, etc. One such robust blockchain preferred by people for NFT marketplace development is Avalanche. One of the major reasons people prefer a blockchain like Avanlache is that it uses the Proof-of-Stake consensus mechanism instead of Proof-of-Work. PoS increases the transaction speed and lowers network congestion on the Avalanche network.
Without further ado, let’s dive into the basics of Avalanche protocol and understand the process of NFT marketplace development on it.
- Brief about Avalanche Network
- How does Avalanche’s consensus work?
- What are the key features of Snowman?
- What are the benefits of developing on the Avalanche platform?
- How to develop an NFT marketplace on Avalanche?
- What Avalanche development services does LeewayHertz provide?
Brief about Avalanche Network
Avalanche is a high-performance, customizable, scalable and secure blockchain network. It is one of the best decentralized smart contract platforms designed to scale global finance with instant transaction finality. Avalanche focuses on three major use cases as follows:
- Developing and launching scalable applications ranging from permissioned to permissionless deployments.
- Developing arbitrarily compact digital assets with customizable rules and covenants.
The major difference between Avalanche and other blockchain networks lies in the consensus protocol. Avalanche network functions via a novel PoS consensus approach which helps it achieve the security, quick finality and optimum level of throughput without minimizing decentralization of the network. It has an overarching motive to provide a unified platform for creating, transferring and trading digital assets in the form of NFTs.
Native token
Avalanche network’s native token AVAX is a hard-capped, scarce asset used for paying the fees, securing the platform by staking and providing a basic unit of account among the various subnets built on the Avalanche platform.
1 nAVAX = 0.000000001 AVAX
This platform has an easy user interface as anybody can build custom decentralized applications on it along with leveraging the unique advantages of Avalanche’s consensus model. Avalanche supports different consensus machines like WASM and EVM that are built using the GO programming language.
How does Avalanche’s consensus work?
Protocols in the Avalanche ecosystem function via repeated sub-sampled voting. At the time of determining whether a transaction is to be accepted or rejected, the validator will request a small subset of validators to confirm the state of the transaction. In the cases where the validator thinks that the transaction is invalid, has rejected the transaction already, or wants a conflicting transaction, the validator will then respond in the form of transaction rejection. In the opposite case, the validator will perceive that the transaction is accepted.
In case a large proportion (alpha a) of the validators tested responded in favor of accepting the transaction, then the validator will want to accept the transaction. In that scenario, when it is questioned about the transaction’s future, it will give a response for the transaction’s acceptance. In the same way, the validator will think of rejecting the transaction if a large proportion of the validators respond in favor of rejecting the transaction.
The validator will repeat the sampling process till the time all the alphas of the validators who were queried, respond in a similar way (accept or reject) for beta continuous rounds.
In the default case when a transaction has zero conflicts, the finalization is done spontaneously. When the conflicts happen, the unbiased validators instantly cluster around the conflicting transactions and enter a positive feedback loop until all the appropriate validators favor the transaction. This default case results in the acceptance of non-conflicting transactions and the acceptance of conflicting transactions.
Avalanche powers the Snowman Consensus Protocol. It is a chain-optimized consensus protocol that powers the network with high throughput, optimal smart contract functioning and maintains the network order. Avalanche platform features three built-in blockchains known as the Exchange Chain (X-Chain), Platform Chain (P-Chain) and the Contract Chain (C-Chain). Both the P-Chian and the C-Chain incorporate the Snowman consensus mechanism.
Now that we are familiar with the features of the Snowman Consensus Protocol, let’s look at some of its key features.
What are the key features of Snowman?
The fundamental features of the Snowman Consensus Protocol are as follows:
Speed
Snowman provides a novel consensus protocol that is developed by a team of Cornell computer scientists and has the capacity to permanently finalize transactions in approximately 1 second.
Scalability
Snowman increases the transaction processing power of the blockchain network by 4500 transactions per second. This is equal to an order of magnitude greater than all the existing blockchains.
Security
It ensures high security and guarantees network functioning well above the 51% standard of all other blockchain networks.
Flexible
Snowman Consensus Protocol helps in the easy creation of custom blockchains and dApps that have arbitrary logic.
Sustainability
It utilizes a sustainable Proof-of-Stake consensus algorithm instead of a Proof-of-Work consensus algorithm.
Smart contract support
Snowman Consensus Protocol also supports the creation of the Solidity smart contracts using the Ethereum tools like Remix, Truffle, Metamask; etc.
Private and public blockchains
Snowman consensus mechanism also helps in the seamless creation of own private and public blockchains on the Avalanche network.
Built for finance
It provides native support for easy development and trading the digital smart assets with custom and complex rulesets.
Let’s now explore the Avalanche architecture in the next section.
What are the benefits of developing on the Avalanche platform?
Avalanche offers innumerable benefits for its users. Talking about development and convenience, Avalanche has three main benefits as the following:
Fast and lost cost Solidity compatible dApp development
Avalanche protocol helps in the development and launch of robust Ethereum dApps. These decentralized applications quickly confirm and then process thousands of transactions. The TPS rate of the Avalanche platform is way above that of the other blockchain platforms.
Fundamental properties and the components of the platform, for instance, the subnetwork model, virtual machine, ethereum-standard RPC calls, etc; power the performance of the dApps and support the users by providing a superior experience.
Deployment of permissioned and customized blockchains
Avalanche functions in a way to caters to the diverse needs of its users. Therefore, it facilitates the deployment of custom-made blockchain platforms to suit the unique application requirements. Users can easily develop all the core components required for building a blockchain, for instance, the virtual machine lays the foundation for blockchain functioning.
Easy access to the network
The network powers the people to scale up to million other validators engaged voluntarily in the validation process. The moment someone buys a token, the person can then instantly join the network and participate as a validator. Avalanche also does not ask for complexly configured hardware to allow the people to join in.
Now that we know about the perks of developing on the Avalanche platform let us explore the step-by-step process of NFT marketplace development in the next section.
Launch a scalable and fast NFT Marketplace powered by Avalanche
Avalanche NFT Marketplace Development Company
How to develop an NFT marketplace on Avalanche?
In this NFt marketplace development process, React JS will be used to build the frontend and Ether JS for the smart interaction on the frontend. For the backend development, Avalanche smart contracts compiled and deployed using Hardhat will be used.
Pre-requisites
- React JS
- Hardhat
- Ether JS
- Solidity
Requirements
- Pre-install Node JS and npm
- Pre-install Hardhat
- Pre-install Metamask browser extension
- Pre-install create-react-app
Step 1
Build the workspace
Set up the workspace using the Hardhat and execute the
$ npx hardhat init
in the working directory. Then as prompted on the terminal, select:
Create a basic sample project
and add the below-mentioned file:
.gitignore
After doing the above steps, install all the dependencies and then delete the below-mentioned file from inside the contracts folder:
Greeter.sol
The initial workspace is now fully set up.
Step 2
NFT token creation
Mint the NFT token that will be stored and displayed on the NFT marketplace. For this, create a basic ERC-721 token by using the following command:
contract NFT is ERC721 { using Counters for Counters.Counter; Counters.Counter private _tokenIds; mapping (uint => uint) public itemValue; uint maxValue = 10000; // Max value of an item constructor() ERC721("Super NFT", "SPRNFT") {} /** Returns a random number */ function random() private view returns (uint) { return uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, block.number))); } function myItems() external view returns (uint[] memory items) { // Returns an array of items that the user owns items = new uint; uint _counter = 0; for(uint i = 1; i < _tokenIds.current(); i++) { // i = 1 because the token counter is increased before the id is assigned if(ownerOf(i) == msg.sender) { // if the user owns the item items[_counter] = i; // add the item to the array _counter++; // increase the counter } } return items; } function getItem() payable public returns (uint256) { require(msg.value == 0.5 ether); // 0.5 AVAX is the cost of an item _tokenIds.increment(); // Increase the counter uint256 newItemId = _tokenIds.current(); // Get the current counter value _mint(msg.sender, newItemId); // Mint the new item to the user itemValue[newItemId] = random() % maxValue; // Set the item value to a random number modulus used to make sure that the value isn't bigger than maxValue return newItemId; // Return the new item id } }
The logic in the above codes is very simple:
- The user will call the getItem() function and then will pay the 0.5AVAX
- Then the value of the token will be estimated via a random number
- Lastly, the token will be minted
Step 3
Auction contract
In this part of development, two contracts namely the AuctionManager and the Auction contracts will be used. The AuctionManger contract will develop all the auctions by deploying the Auction contract via the parameters. It works in the following way:
- The auction creator will enter the parameters targeting the direct buy price, starting price, minimum bid increment, auction endpoint and the token id.
- The auction creator then approves the minted NFT to be auctioned to the AuctionManager contract.
- The AuctionManager contract builds an auction using the parameters given on the platform and sends the minted NFT to the newly developed Auction contract.
- After the auction is done, the auction creator and the winner will have to call all the withdrawal functions for the retrieval of their tokens.
Start with the auction contract functions
This auction contract will allow all the users to place their bids, cancel the entire auction in case of zero bids and withdraw all their funds and tokens after the completion of the auction. The following function returns the current position of the auction:
contract Auction { function placeBid() payable external returns(bool){} // Place a bid on the auction function withdrawToken() external returns(bool){} // Withdraw the token after the auction is over function withdrawFunds() external returns(bool){} // Withdraw the funds after the auction is over function cancelAuction() external returns(bool){} // Cancel the auction function getAuctionState() public view returns(uint) {} // Get the auction state function allBids() external view returns (address[] memory, uint256[] memory) {} // Returns a list of all bids and addresses }
Events
There are four events in the contract as given below:
- NewBid
- WithdrawnToken
- WithdrawFunds
- AuctionCancelled
The NewBid event will be emitted every time when there is a new bid. The WithdrawToken will be emitted in a situation where the highest bidder withdraws his token. WithdrawFunds will be emitted at the time when the auction owner withdraws his funds. Lastly, the AuctionCancelled event will be emitted when the auction is canceled.
Follow the below-mentioned codes for the above events:
contract Auction { event NewBid(address bidder, uint bid); // A new bid was placed event WithdrawToken(address withdrawer); // The auction winner withdrawed the token event WithdrawFunds(address withdrawer, uint256 amount); // The auction owner withdrawed the funds event AuctionCancelled(); // The auction was cancelled }
Storage variables
In this part, the user is required to declare some new variables as defined below:
- endTime – This is defined as the auction’s end timestamp. The auction is bound to end if the current timestamp is equal to or greater than the actual endTime.
- startTime – This is the timestamp that sets the beginning of the auction.
- maxBid – This is the number of maximum bids.
- maxBidder – maxBidder is the address of the maximum bidder on the network.
- creator – Creator is defined as the auction creator’s address on the network.
- bids – These are the array of bids used to display the current bids on the web page.
- tokenId – This is the id of the token auctioned on the network.
- isCancelled – This event is active when the auction has been canceled.
- isDirectBuy – This event is active when someone places a bit with higher or of a value that is equal to the direct buying price.
- minIncrement – This is the event that activates the minimum increment for the bid.
- directBuyPrice – This event specifies the direct price for the auction.
- startPrice – This is the starting price of the auction.
- nftAddress – nftAddress states the address of an NFT contract.
- nft – This is the event stating the NFT token minted on the network.
Follow the below-mentioned codes for regulating the above events:
contract Auction { uint256 public endTime; // Timestamp of the end of the auction (in seconds) uint256 public startTime; // The block timestamp which marks the start of the auction uint maxBid; // The maximum bid address maxBidder; // The address of the maximum bidder address creator; // The address of the auction creator Bid[] public bids; // The bids made by the bidders uint tokenId; // The id of the token bool isCancelled; // If the the auction is cancelled bool isDirectBuy; // True if the auction ended due to direct buy uint minIncrement; // The minimum increment for the bid uint directBuyPrice; // The price for a direct buy uint startPrice; // The starting price for the auction IERC721 _nft; // The NFT token 1enum AuctionState { 2 OPEN, 3 CANCELLED, 4 ENDED, 5 DIRECT_BUY 6} 7 8struct Bid { // A bid on an auction 9 address sender; 10 uint256 bid; 11} }
Block numbers will be used instead of timestamps in this process. In Avalanche there is no specified block rate, therefore the user should not rely on the block numbers. They should use the block.timestamp to measure the time of block mining.
The auction process will end when the present timestamp is greater than or equal to the endTime.
Constructor function
Let’s now get some parameters for the Auction Manager contract in this section. Follow the codes given below for the same:
constructor(address _creator,uint _endTime,uint _minIncrement,uint _directBuyPrice, uint _startPrice,address _nftAddress,uint _tokenId){ creator = _creator; // The address of the auction creator endTime = block.timestamp + _endTime; // The timestamp which marks the end of the auction (now + 30 days = 30 days from now) startBlock = block.timestamp; // The timestamp which marks the start of the auction minIncrement = _minIncrement; // The minimum increment for the bid directBuyPrice = _directBuyPrice; // The price for a direct buy startPrice = _startPrice; // The starting price for the auction _nft = IERC721(_nftAddress); // The address of the nft token nftAddress = _nftAddress; tokenId = _tokenId; // The id of the token maxBidder = _creator; // Setting the maxBidder to auction creator. }
Building the functions
Let us begin with the getAuctionState event. Firstly, check the auction if it has been canceled and then return the AuctionState.CANCELLED in that situation. After that, check if anyone bids more than or equal to the direct buying price and then return the AuctionState.DIRECT_BUY in that situation. After that, check if the current timestamp is greater than or equal to the current endTIme and then return the AuctionState.ENDED in that case. Otherwise, return the AuctionState.OPEN.
Follow the below-mentioned codes for the above events:
// Get the auction state function getAuctionState() public view returns(AuctionState) { if(isCancelled) return AuctionState.CANCELLED; // If the auction is cancelled return CANCELLED if(isDirectBuy) return AuctionState.DIRECT_BUY; // If the auction is ended by a direct buy return DIRECT_BUY if(block.timestamp >= endTime) return AuctionState.ENDED; // The auction is over if the block timestamp is greater than the end timestamp, return ENDED return AuctionState.OPEN; // Otherwise return OPEN }
Then start placing the bids. Call the placeBid function and then send a specific amount of AVAX.
Before doing this check the following:
- The bidder should not be the auction creator
- The auction should be open
- The bid should be more than the starting price
- The bid should be more than the highest bidding amount along with the bid increment
Now follow the below-mentioned codes to place the bids:
function placeBid() payable external returns(bool){ require(msg.sender != creator); // The auction creator can not place a bid require(getAuctionState() == AuctionState.OPEN); // The auction must be open require(msg.value > startPrice); // The bid must be higher than the starting price require(msg.value > maxBid + minIncrement); // The bid must be higher than the current bid }
After this, set a new max bidder and then store the value of the last highest bid and the bidder as this information is required later in the process.
address lastHightestBidder = maxBidder; // The address of the last highest bidder uint256 lastHighestBid = maxBid; // The last highest bid maxBid = msg.value; // The new highest bid maxBidder = msg.sender; // The address of the new highest bidder
Then, check if the bid msg.value is more than or equal to the direct buy price. In that case, set the isDirectBuy to true; and then close the auction.
if(msg.value >= directBuyPrice){ // If the bid is higher or equal to the direct buy price isDirectBuy = true; // The auction has ended }
After this, push the value of new bids in the bids array.
bids.push(Bid(msg.sender,msg.value));
Lastly, if there is a previous bid then, refund the highest previous bid to the bidder of the previous auction. Then emit a NewBid event by using the following codes:
if(lastHighestBid != 0){ // if there is a bid address(uint160(lastHightestBidder)).transfer(lastHighestBid); // refund the previous bid to the previous highest bidder } emit NewBid(msg.sender,msg.value); // emit a new bid event return true;
Below mentioned is the complete function of the bidding process:
// Place a bid on the auction function placeBid() payable external returns(bool){ require(msg.sender != creator); // The auction creator can not place a bid require(getAuctionState() == AuctionState.OPEN); // The auction must be open require(msg.value > startPrice); // The bid must be higher than the starting price require(msg.value > maxBid + minIncrement); // The bid must be higher than the current bid + the minimum increment 1address lastHightestBidder = maxBidder; // The address of the last highest bidder 2uint256 lastHighestBid = maxBid; // The last highest bid 3maxBid = msg.value; // The new highest bid 4maxBidder = msg.sender; // The address of the new highest bidder 5if(msg.value >= directBuyPrice){ // If the bid is higher than the direct buy price 6 isDirectBuy = true; // The auction has ended 7} 8bids.push(Bid(msg.sender,msg.value)); // Add the new bid to the list of bids 9 10if(lastHighestBid != 0){ // if there is a bid 11 address(uint160(lastHightestBidder)).transfer(lastHighestBid); // refund the previous bid to the previous highest bidder 12} 13 14emit NewBid(msg.sender,msg.value); // emit a new bid event 15return true; // The bid was placed successfully }
Cancel auction
Follow the below-mentioned codes to cancel an auction:
function cancelAuction() external returns(bool){ // Cancel the auction require(msg.sender == creator); // Only the auction creator can cancel the auction require(getAuctionState() == AuctionState.OPEN); // The auction must be open require(maxBid == 0); // The auction must not be cancelled if there is a bid isCancelled = true; // The auction has been cancelled _nft.transferFrom(address(this), creator, tokenId); // Transfer the NFT token to the auction creator emit AuctionCanceled(); // Emit Auction Canceled event return true; }
Withdraw token
After completing the auction, the bidder with the highest bidding amount should be able to withdraw the NFT tokens. Then msg.sender should be the bidder with the highest amount and the auction should be completed either via direct buy or timeout, otherwise, the function will revert. After that, the NFT with token id tokenId will be sent to the highest bidder.
Set the maximum bidder’s initial value as per the auction creator’s wallet address, so in a case where the auction times out and no one is ready to bid, the auction creator gets to withdraw the token.
// Withdraw the token after the auction is over function withdrawToken() external returns(bool){ require(getAuctionState() == AuctionState.ENDED || getAuctionState() == AuctionState.DIRECT_BUY); // The auction must be ended by either a direct buy or timeout require(msg.sender == maxBidder); // The highest bidder can only withdraw the token _nft.transferFrom(address(this), maxBidder, tokenId); // Transfer the token to the highest bidder emit WithdrawToken(maxBidder); // Emit a withdraw token event }
Withdraw funds
After completing the auction, the auction creator should be able to withdraw all the funds. Make sure that the msg.sender is the auction owner and the auction is completed by either a direct buy or by timeout, otherwise the function will revert. After that, the maximum bid should be sent to the auction creator.
// Withdraw the funds after the auction is over
function withdrawFunds() external returns(bool){ require(getAuctionState() == AuctionState.ENDED || getAuctionState() == AuctionState.DIRECT_BUY); // The auction must be ended by either a direct buy or timeout require(msg.sender == creator); // The auction creator can only withdraw the funds address(uint160(msg.sender)).transfer(maxBid); emit WithdrawFunds(msg.sender,maxBid); // Emit a withdraw funds event }
Get the bids
Get a function to retrieve the list of the bidders and the bids:
// Returns a list of all bids and addresses function allBids() external view returns (address[] memory, uint256[] memory) { address[] memory addrs = new address; uint256[] memory bidPrice = new uint256; for (uint256 i = 0; i < bids.length; i++) { addrs[i] = bids[i].sender; bidPrice[i] = bids[i].bid; } eturn (addrs, bidPrice); }
Now the Auction contract is ready.
Step 4
Auction contract manager
Use the Auction contract manager to retrieve the list of all the auctions and to make the new ones. Follow the codes given below for a basic structure of this:
pragma solidity ^0.7.0; import "./Auction.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; contract AuctionManager { uint _auctionIdCounter; // auction Id counter mapping(uint => Auction) public auctions; // auctions 1function createAuction(uint _endTime, uint _minIncrement, uint _directBuyPrice,uint _startPrice,address _nftAddress,uint _tokenId) external returns (bool) {} // create an auction 2function getAuctions() external view returns(address[] memory _auctions) {} // Return a list of all auctions 3function getAuctionInfo(address[] calldata _auctionsList) external view 4 returns ( 5 uint256[] memory directBuy, 6 address[] memory owner, 7 uint256[] memory highestBid, 8 uint256[] memory tokenIds, 9 uint256[] memory endTime, 10 uint256[] memory startPrice, 11 uint256[] memory auctionState 12 ) {} // Return the information of each auction address }
Now build two functions, one for starting an auction and the other one for getting a list of auctions. Use the counter to assign a distinctive id for every auction, this will also help in keeping a track of the auctions.
Building the functions
Create an auction, but before doing so, check the following factors:
- The direct buy price is more than zero
- Starting price is more than the direct buying price
- The end time is more than 5 minutes, therefore, nobody can create an auction lasting for a few seconds.
After checking these factors, build the functions using the below-mentioned codes:
1agma solidity ^0.7.0; 2 3import "./Auction.sol"; 4import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 5 6contract AuctionManager { 7 uint _auctionIdCounter; // auction Id counter 8 mapping(uint => Auction) public auctions; // auctions 9 10 // create an auction 11 function createAuction(uint _endTime, uint _minIncrement, uint _directBuyPrice,uint _startPrice,address _nftAddress,uint _tokenId) external returns (bool){ 12 require(_directBuyPrice > 0); // direct buy price must be greater than 0 13 require(_startPrice < _directBuyPrice); // start price is smaller than direct buy price 14 require(_endTime > 5 minutes); // end time must be greater than 5 minutes (setting it to 5 minutes for testing you can set it to 1 days or anything you would like) 15 16 uint auctionId = _auctionIdCounter; // get the current value of the counter 17 _auctionIdCounter++; // increment the counter 18 Auction auction = new Auction(msg.sender, _endTime, _minIncrement, _directBuyPrice, _startPrice, _nftAddress, _tokenId); // create the auction 19 IERC721 _nftToken = IERC721(_nftAddress); // get the nft token 20 _nftToken.transferFrom(msg.sender, address(auction), _tokenId); // transfer the token to the auction 21 auctions[auctionId] = auction; // add the auction to the map 22 return true; 23 } 24 25 // Return a list of all auctions 26 function getAuctions() external view returns(address[] memory _auctions) { 27 _auctions = new address[](_auctionIdCounter); // create an array of size equal to the current value of the counter 28 for(uint i = 0; i < _auctionIdCounter; i++) { // for each auction 29 _auctions[i] = address(auctions[i]); // add the address of the auction to the array 30 } 31 return _auctions; // return the array 32 } 33 34 // Return the information of each auction address 35 function getAuctionInfo(address[] calldata _auctionsList) 36 external 37 view 38 returns ( 39 uint256[] memory directBuy, 40 address[] memory owner, 41 uint256[] memory highestBid, 42 uint256[] memory tokenIds, 43 uint256[] memory endTime, 44 uint256[] memory startPrice, 45 uint256[] memory auctionState 46 ) 47 { 48 directBuy = new uint256[](_auctionsList.length); // create an array of size equal to the length of the passed array 49 owner = new address[](_auctionsList.length); // create an array of size equal to the length of the passed array 50 highestBid = new uint256[](_auctionsList.length); 51 tokenIds = new uint256[](_auctionsList.length); 52 endTime = new uint256[](_auctionsList.length); 53 startPrice = new uint256[](_auctionsList.length); 54 auctionState = new uint256[](_auctionsList.length); 55 56 57 for (uint256 i = 0; i < _auctionsList.length; i++) { // for each auction 58 directBuy[i] = Auction(auctions[i]).directBuyPrice(); // get the direct buy price 59 owner[i] = Auction(auctions[i]).creator(); // get the owner of the auction 60 highestBid[i] = Auction(auctions[i]).maxBid(); // get the highest bid 61 tokenIds[i] = Auction(auctions[i]).tokenId(); // get the token id 62 endTime[i] = Auction(auctions[i]).endTime(); // get the end time 63 startPrice[i] = Auction(auctions[i]).startPrice(); // get the start price 64 auctionState[i] = uint(Auction(auctions[i]).getAuctionState()); // get the auction state 65 } 66 67 return ( // return the arrays 68 directBuy, 69 owner, 70 highestBid, 71 tokenIds, 72 endTime, 73 startPrice, 74 auctionState 75 ); 76 } 77 78}
Now all the contracts are done!
Step 5
AVAX Fuji Testnet
The next step is to test the marketplace on the AVAC Fuji Testnet. Add the AVAX Fuji Testnet to the Metamask. Then open the Metamask to view the networks and then click on the Custom RPC.
Then deploy the contracts on the FUJI testnet Settings and add the network configuration in the hardhat config file. this is how the file will work:
networks:{ ... fuji: { url: "https://api.avax-test.network/ext/bc/C/rpc", chainId: 43113, accounts: [ "PRIVATE_KEY", ], }, }
Lastly, add some AVAX for the contract deployment.
Step 6
Contract deployment
Use the hardhat to deploy the NFT and AuctionManager contracts to the Fuji Testnet. Begin by editing the scripts/deploy.js file; follow the codes given below for the same:
const main = async () => { const NftToken = await ethers.getContractFactory("NFT"); // NFT token contract const nftToken = await NftToken.deploy(); // NFT token contract instance await nftToken.deployed(); // wait for contract to be deployed const nftTokenAddress = await nftToken.address; // NFT token contract address const AuctionManager = await ethers.getContractFactory("AuctionManager"); // Auction Manager contract const auctionManager = await AuctionManager.deploy(); // Auction Manager contract instance await auctionManager.deployed(); // wait for contract to be deployed const auctionManagerAddress = await auctionManager.address; // Auction Manager contract address console.log(NFT deployed to: ${nftTokenAddress}); // NFT token contract address console.log(Auction Manager deployed to: ${auctionManagerAddress}); // Auction Manager contract address }; main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
After the editing of deploy.js is done, continue the process by executing the following lines on the terminal to run the deploy.js script:
$ npx hardhat compile # Compiles the contracts $ npx hardhat run scripts/deploy.js --network fuji # runs the "deploy.js" script on fuji test network, "fuji" is specified inside the hardhat config file
Then note the addresses positively as they are required afterward at the time of interaction with the contracts.
Step 7
React application
Develop an interface to communicate with the marketplace. For this, use react and ether.js.
Execute the following codes on the terminal to start the process of interface development:
create-react-app frontend # creates a react app inside the frontend folder cd frontend # go inside the frontend folder npm install --save ethers # install ethers package npm run start # start the react app
Then add the bootstrap CDN in the top/head section of the public/index.html file. Use the bootstrap to increase the speed of the process.
<head> ... + <link + rel="stylesheet" + href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" + /> </head> ... </body> + <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script> + <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script> + <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script> </html>
Begin with a new App.js file and then import the ethers library. Assign the contract addresses to all the contract strings.
Create an auction, but before creating one, mint an NFT for the auction by suing the below mentioned codes:
import React from "react"; import { ethers } from "ethers"; const NFT_ADDRESS = "0xeb2283672cf716fF6A1d880436D3a9074Ba94375"; // NFT contract address const AUCTIONMANAGER_ADDRESS = "0xea4b168866E439Db4A5183Dbcb4951DCb5437f1E"; // AuctionManager contract address class App extends React.Component { constructor(props) { super(props); this.state = { auctions: [], // Auctions to display newAuction: { // newAuction is a state variable for the form startPrice: null, endTime: null, tokenId: null, minIncrement: null, directBuyPrice: null, }, myItems: [], }; } render() { return ( <div> <div class="jumbotron d-flex align-items-center"> <div class="container"> <div class="auctions row"></div> </div> </div> <div class="container"> <form> <button type="submit" class="btn btn-primary"> Create Auction </button> </form> <button class="btn btn-fanger">Mint NFT</button> <p> Your items <br /> {(this.state.myItems || [""]).map((x) => `id: ${x} `) || ""} </p> </div> </div> ); } } export default App;
Step 8
Build the form
Start building the form and add a few elements to the form. Type in the start price, token id, minimum increment, duration of the auction and the direct price of buying.
<form> <div class="mb-3"> <label for="startprice" class="form-label"> Start Price </label> <input value={this.state.newAuction.startPrice} onChange={(e) => this.setState({ newAuction: { ...this.state.newAuction, startPrice: parseInt(e.target.value), }, }) } type="number" class="form-control" id="startprice" /> <label for="startprice" class="form-label"> Token Id </label> <input value={this.state.newAuction.tokenId} onChange={(e) => this.setState({ newAuction: { ...this.state.newAuction, tokenId: parseInt(e.target.value), }, }) } type="number" class="form-control" id="startprice" /> <label class="form-label">Minimum Increment</label> <input value={this.state.newAuction.minIncrement} onChange={(e) => this.setState({ newAuction: { ...this.state.newAuction, minIncrement: parseInt(e.target.value), }, }) } type="number" class="form-control" /> <label class="form-label">Direct Buy Price</label> <input value={this.state.newAuction.directBuyPrice} onChange={(e) => this.setState({ newAuction: { ...this.state.newAuction, directBuyPrice: parseInt(e.target.value), }, }) } type="number" class="form-control" /> <label class="form-label">Duration In Minutes</label> <input value={this.state.newAuction.endTime} onChange={(e) => this.setState({ newAuction: { ...this.state.newAuction, endTime: parseInt(e.target.value), }, }) } type="number" class="form-control" /> </div> <button type="button" onClick={() => console.log(this.state.newAuction)} class="btn btn-primary" > Create Auction </button> </form>
The form is now ready to regulate the interaction with the contracts.
Launch a scalable and fast NFT Marketplace powered by Avalanche
Avalanche NFT Marketplace Development Company
Step 9
Interact with the contracts
Contract ABI’s
Use the Contract Application Binary Interface (ABI) to communicate with the contracts on the network.
Then import these contracts in the React code at top of the App.js by running the following codes:
import AuctionArtifact from "./artifacts/Auction.json"; import AuctionManagerArtifact from "./artifacts/AuctionManager.json"; import NFTArtifact from "./artifacts/NFT.json";
After interacting with the contracts, the next step is to detect and connect to the MetaMask.
Step 10
Connect to the Metamask
To detect and connect to the MetaMask add an init function, that will be responsible for calling the user when the page is loaded:
class App extends React.Component { async init(){} ... }
Then connect the MetaMask wallet using the Ether.JS and call the init function the ComponentDidMount by running the following codes:
class App extends React.Component { ... async init() { if (window.ethereum) { await window.ethereum.enable(); // Enable the Ethereum client this.provider = new ethers.providers.Web3Provider(window.ethereum); // A connection to the Ethereum network this.signer = this.provider.getSigner(); // Holds your private key and can sign things this.setState({ currentAddress: this.signer.getAddress() }); // Set the current address this._auctionManager = new ethers.Contract( // We will use this to interact with the AuctionManager AUCTIONMANAGER_ADDRESS, AuctionManagerArtifact.abi, this.signer ); this._nft = new ethers.Contract( // We will use this to interact with the NFT contract NFT_ADDRESS, NFTArtifact.abi, this.signer ); } else { alert("No wallet detected"); } } componentDidMount() { this.init(); } ... }
Then select your account and continue the process, the MetaMask is finally connected to the web page.
Step 11
NFT Minting
Build the mint function which is required to call at the time of pressing the Mint NFt button.
Use the this.nft object that has been created earlier within the init function. Then, call the function getItem() that is located in the NFT contract and pass 0.5 AVAX as gas fees to min the NFTs:
async mint() { // hash is the hash of the transaction let { hash } = await this._nft.getItem({ // Calling the getItem function of the contract value: ethers.utils.parseEther("0.5"), // 0.5 AVAX }); console.log("Transaction sent! Hash:", hash); await this.provider.waitForTransaction(hash); // Wait till the transaction is mined console.log("Transaction mined!"); alert(`Transaction sent! Hash: ${hash}`); }
Then call the function when the button is pressed by running the following code:
<button type="button" onClick={() => this.mint()} class="btn btn-danger"> Mint NFT </button>
Step 12
Owned NFTs
USe the myItems function and retrieve the list of owned tokens:
async getItems() { let items = await this._nft.myItems(); // Get the tokens owned by the user console.log(items); }
Then call the function at the last of the init() function, so every time the wallet is connected, the list of owned NFTs will be retrieved:
async init() { if (window.ethereum) { // if window.ethereum is defined await window.ethereum.enable(); // Enable the Ethereum client this.provider = new ethers.providers.Web3Provider(window.ethereum); // A connection to the Ethereum network this.signer = this.provider.getSigner(); // Holds your private key and can sign things this._auctionManager = new ethers.Contract( // We will use this to interact with the AuctionManager AUCTIONMANAGER_ADDRESS, AuctionManagerArtifact.abi, this.signer ); this._nft = new ethers.Contract( // We will use this to interact with the NFT NFT_ADDRESS, NFTArtifact.abi, this.signer ); + this.getItems(); } else { alert("No wallet detected"); // No wallet detected } }
Then convert the array of values to normal numbers, that are in BigNumber format after reloading the page:
async getItems() { let items = await this._nft.myItems(); // Get the tokens owned by the user items = items.map((x) => x.toNumber()); // Converts BigNumber to number for each item in array this.setState({ myItems: items }); }
Lastly, click and refresh the page to view all the minted NFTs.
Step 13
Create an auction
Build a function for making auctions, but before calling the createAuction function in the Auction Manager, give the approval to the token to be used for the function. After giving approval to the Auction Manager, the token will be transferred to the newly created auction contract.
Check if there are any empty text fields
if ( !this.state.newAuction.minIncrement || !this.state.newAuction.directBuyPrice || !this.state.newAuction.startPrice || !this.state.newAuction.endTime || !this.state.newAuction.tokenId ) return alert("Fill all the fields");
Then call the approve function of the NFt contract to approve the AuctionManager contract to send the NFT token that will be put for the process of auction.
let { hash: allowance_hash } = await this._nft.approve( AUCTIONMANAGER_ADDRESS, this.state.newAuction.tokenId ); // Approve the AUCTIONMANAGER to transfer the token console.log("Approve Transaction sent! Hash:", allowance_hash); await this.provider.waitForTransaction(allowance_hash); // Wait till the transaction is mined console.log("Transaction mined!");
After this, call the createAuction function in the AuctionManager contract by running he following codes:
let { hash } = await this._auctionManager.createAuction( this.state.newAuction.endTime * 60, // Converting minutes to seconds ethers.utils.parseEther(this.state.newAuction.minIncrement.toString()), // Minimum increment in AVAX ethers.utils.parseEther(this.state.newAuction.directBuyPrice.toString()), // Direct buy price in AVAX ethers.utils.parseEther(this.state.newAuction.startPrice.toString()), // Start price in AVAX NFT_ADDRESS, // The address of the NFT token this.state.newAuction.tokenId // The id of the token ); console.log("Transaction sent! Hash:", hash); await this.provider.waitForTransaction(hash); // Wait till the transaction is mined console.log("Transaction mined!"); alert(`Transaction sent! Hash: ${hash}`);
Then run the below-mentioned codes for the final function:
async createAuction() { if ( !this.state.newAuction.minIncrement || !this.state.newAuction.directBuyPrice || !this.state.newAuction.startPrice || !this.state.newAuction.endTime || !this.state.newAuction.tokenId ) return alert("Fill all the fields"); let { hash: allowance_hash } = await this._nft.approve( AUCTIONMANAGER_ADDRESS, this.state.newAuction.tokenId ); // Approve the AUCTIONMANAGER to transfer the token console.log("Approve Transaction sent! Hash:", allowance_hash); await this.provider.waitForTransaction(allowance_hash); // Wait till the transaction is mined console.log("Transaction mined!"); let { hash } = await this._auctionManager.createAuction( // Create an auction this.state.newAuction.endTime * 60, // Converting minutes to seconds ethers.utils.parseEther(this.state.newAuction.minIncrement.toString()), // Minimum increment in AVAX ethers.utils.parseEther(this.state.newAuction.directBuyPrice.toString()), // Direct buy price in AVAX ethers.utils.parseEther(this.state.newAuction.startPrice.toString()), // Start price in AVAX NFT_ADDRESS, // The address of the NFT token this.state.newAuction.tokenId // The id of the token ); console.log("Transaction sent! Hash:", hash); await this.provider.waitForTransaction(hash); // Wait till the transaction is mined console.log("Transaction mined!"); alert(`Transaction sent! Hash: ${hash}`); }
Call the function after pressing the Create Auction button:
... <button type="button" + onClick={() => this.createAuction()} class="btn btn-primary" > Create Auction </button> ...
Fill the form and then press the Create Auction button to begin the auction process.
The first auction on the NFT Marketplace has finally begun!
Step 14
Get the list of auctions
Build two functions namely the getAuctions() and the getAuctionsInfo() to get a list of all the auctions taking place on the network:
async getAuctions() { let auctionsAddresses = await this._auctionManager.getAuctions(); // get a list of auction addresses let auctions = await this._auctionManager.getAuctionInfo(auctionsAddresses); // I'll just pass all the addresses here, you can build a pagination system if you want console.log("Auctions:", auctions); this.setState({ auctions }); // Update the state }
Then call this function at the end of the init( ) function:
... this._nft = new ethers.Contract(NFT_ADDRESS, NFTArtifact.abi, this.signer); // We will use this to interact with the NFT this.getItems(); + this.getAuctions(); ...
Organize the auction array and display them in the auction’s section section of the web page:
let new_auctions = []; for (let i = 0; i < auctions.endTime.length; i++) { let endTime = auctions.endTime[i].toNumber(); let tokenId = auctions.tokenIds[i].toNumber(); let auctionState = auctions.auctionState[i].toNumber(); let startPrice = ethers.utils.formatEther(auctions.startPrice[i]); let directBuyPrice = ethers.utils.formatEther(auctions.directBuy[i]); let highestBid = ethers.utils.formatEther(auctions.highestBid[i]); let owner = auctions.owner[i]; let newAuction = { endTime: endTime, startPrice: startPrice, owner: owner, directBuyPrice: directBuyPrice, tokenId: tokenId, highestBid: highestBid, auctionState: auctionState, auctionAddress: auctionsAddresses[i], }; new_auctions.push(newAuction); } this.setState({ auctions: new_auctions }); // Update the state
Then write a function to display an auction on the user-interface by running the following codes:
renderAuctionElement(auction) { let state = ""; if (auction.auctionState === 0) { state = "Open"; } if (auction.auctionState === 1) { state = "Cancelled"; } if (auction.auctionState === 2) { state = "Ended"; } if (auction.auctionState === 3) { state = "Direct Buy"; } return ( <div style={{ background: "yellow" }} class="col"> <p>ID: {auction.tokenId}</p> {/* ID of the token */} <p>Highest Bid: {auction.highestBid || 0}</p> {/* Highest bid */} <p>Direct Buy: {auction.directBuyPrice}</p> {/* Direct buy price */} <p>Starting Price: {auction.startPrice}</p> {/* Starting price */} <p>Owner: {auction.owner}</p> {/* Owner of the token */} <p> End Time: {Math.round((auction.endTime * 1000 - Date.now()) / 1000 / 60)}{" "} {/* Time left in minutes */} minutes </p> <p>Auction State: {state}</p> <button class="btn-primary">See More</button> </div> ); }
In the above codes, the auction.endTime which is given in seconds is converted to milliseconds by multiplying it by 1000. Then, the result is subtracted from Date.now(), which s given in milliseconds. After that, the result is divided by 1000 to convert it to seconds. Lastly, it is divided by 60 to convert in minutes.
In the next part, map all the auctions into the function in the render method by using the following codes:
render() { return ( <div class="jumbotron d-flex align-items-center"> <div class="container"> <div class="auctions row"> {this.state.auctions.map(this.renderAuctionElement)} </div> </div> </div> ) }
Finally, an auction has been created!
Step 15
Build an auction page
Use a state variable this.state.activeAuction to reflect the current auction. Then set this variable after pressing the see more button. If this variable does not contain a null value then it will reflect the auction data instead of the list of auctions. Set this variable back to null by using the go-back option.
Add a fresh setActiveAuction function by running the following codes:
setActiveAuction(auction) { this.setState({ activeAuction: auction }); }
Then, use the following codes to fetch the data of the previous bids when the setActiveAuction function is called:
async setActiveAuction(auction) { this._auction = new ethers.Contract( // Create a new instance of the contract auction.auctionAddress, AuctionArtifact.abi, this.signer ); let previousBids = await this._auction.allBids(); // Get the bids let bids = []; // A list of bids for (let i = 0; i < previousBids[0].length; i++) { // Loop through the bids bids.push({ // Add the bid to the list bidder: previousBids[0][i], // The bidder bid: ethers.utils.formatEther(previousBids[1][i]), // The bid }); } auction.bids = bids; // Add the bids array to the auction object this.setState({ activeAuction: auction }); // Update the state }
Use the this.auction function to interact with the auction contract. Then display the random value assigned to every NFT earlier, now on the auction page:
let auctionTokenValue = await this._nft.itemValue(auction.tokenId); // Get the value of the token auctionTokenValue = auctionTokenValue.toNumber(); // Convert BigNumber to number auction.auctionTokenValue = auctionTokenValue; // Add the value of the token to the auction object
To know about the highest bidder, run the following codes:
let highestBidder = await this._auction.maxBidder(); // Get the highest bidder auction.highestBidder = highestBidder; // Add the highest bidder to the auction object
Lastly, run the below-mentioned codes for the increment value:
let minIncrement = await this._auction.minIncrement(); // Get the minimum increment auction.minIncrement = ethers.utils.formatEther(minIncrement); // Add the minimum increment to the auction object
Step 16
Render the selected auction
Write a sample render function for the chosen auction as given below:
renderActiveAuction() { let activeAuction = this.state.activeAuction; let state = ""; if (activeAuction.auctionState === 0) { // If the auction is open state = "Open"; } if (activeAuction.auctionState === 1) { // If the auction is cancelled state = "Cancelled"; } if (activeAuction.auctionState === 2) { // If the auction is ended state = "Ended"; } if (activeAuction.auctionState === 3) { // If the auction is ended by a direct buy state = "Direct Buy"; } return ( <div> <div class="col"> <button class="btn-secondary" onClick={() => this.setState({ activeAuction: null })} > Go Back </button> <p>ID: {activeAuction.tokenId}</p> {/* ID of the token */} <p>Highest Bid: {activeAuction.highestBid || 0} AVAX</p> {/* Highest bid */} <p>Direct Buy: {activeAuction.directBuyPrice} AVAX</p>{" "} {/* Direct buy price */} <p>Minimum Increment: {activeAuction.minIncrement} AVAX</p>{" "} {/* Minimum increment in AVAX */} <p>Starting Price: {activeAuction.startPrice} AVAX</p> {/* Starting price */} <p>Owner: {activeAuction.owner}</p> {/* Owner of the token */} <p> End Time:{" "} {Math.round( (activeAuction.endTime * 1000 - Date.now()) / 1000 / 60 )}{" "} {/* Time left in minutes */} minutes </p> <p>Auction State: {state}</p> </div> <div class="col"> <h3>Bids</h3> <table class="table"> <thead> <tr> <th>Bidder</th> <th>Bid</th> </tr> </thead> <tbody> {activeAuction.bids.map((bid) => { return ( <tr key={bid.bidder}> <td>{bid.bidder}</td> <td>{bid.bid} AVAX</td> </tr> ); })} </tbody> </table> </div> <div class="col"> <div> <input type="number" placeholder="0.5" /> <button class="btn-primary">Place Pid</button> </div> <button class="btn-danger">Cancel Auction</button> <button class="btn-secondary">Withdraw Funds</button> <button class="btn-secondary">Withdraw Token</button> </div> </div> ); }
If the this.state.activeAuction is null, then return this function in place of the list of auctions. Edit the render function by running the following codes:
... <div class="container"> {this.state.activeAuction != null ? ( this.renderActiveAuction() ) : ( <div class="auctions row"> {this.state.auctions.map(this.renderAuctionElement)} </div> )} </div> ...
Step 17
Auction functions
Call the placeBid function from the auction contract and transfer some AVAX by running the below-mentioned:
async placeBid(amount) { if (!amount) return; amount = ethers.utils.parseEther(amount.toString()); // Amount in AVAX let { hash } = await this._auction.placeBid({ value: amount }); // Place a bid await this.provider.waitForTransaction(hash); // Wait till the transaction is mined alert(`Transaction sent! Hash: ${hash}`); // Show the transaction hash this.setActiveAuction(this.state.activeAuction); // Update the active auction }
Next, call the placeBid function at the time of pressing the button:
... <div> <input type="number" placeholder="0.5" onChange={(e) => this.setState({ bidAmount: e.target.value })} /> <button class="btn-primary" onClick={() => this.placeBid(this.state.bidAmount)} > Place Pid </button> </div> ...
Switch to another account on MetaMask before placing the bid as the auction creator cannot place the bids from the same account. Then wait a bit until the auction gets over and withdraw the token if nobody is bidding a higher value.
Step 18
Withdraw token
Call the withdrawToken function from the Auction contract and then reflect an alert after the transaction gets mined:
async withdrawToken() { let { hash } = await this._auction.withdrawToken(); // Withdraw the NFT token await this.provider.waitForTransaction(hash); // Wait till the transaction is mined alert(`Withdrawal Successful! Hash: ${hash}`); // Show the transaction hash window.location.reload(); // Reload the page }
Call this function at the time of pressing the withdraw token button:
... <button onClick={()=>this.withdrawToken()} class="btn-secondary">Withdraw Token</button> ...
Step 19
Withdraw funds
Call the withdrawFunds function from the Auction contract and then call the function when the transaction is mining is done:
async withdrawFunds() { let { hash } = await this._auction.withdrawFunds(); // Withdraw the funds await this.provider.waitForTransaction(hash); // Wait till the transaction is mined alert(`Withdrawal Successful! Hash: ${hash}`); // Show the transaction hash window.location.reload(); // Reload the page }
Call this function once the Withdraw Funds button is pressed:
... <button onClick={()=>this.withdrawFunds()} class="btn-secondary">Withdraw Funds</button> ...
Now withdraw the funds by switching back to the account created earlier for auction and confirm the transaction.
Step 20
Cancel Auction
Call the cancelAuction function of Auction contract and show the alert once the transaction is mined:
async cancelAuction() { let { hash } = await this._auction.cancelAuction(); // Cancel the auction await this.provider.waitForTransaction(hash); // Wait till the transaction is mined alert(`Auction Canceled! Hash: ${hash}`); // Show the transaction hash window.location.reload(); // Reload the page }
Then, call the function when the Cancel Auction button is clicked:
... <button onClick={() => this.cancelAuction()} class="btn-danger"> Cancel Auction </button> ...
Step 21
Deploy to Mainnet
Lastly, deploy the functional, marketplace to the Avalanche mainnet and add the network in the hardhat config file by running the following command:
networks:{ ... mainnet: { url: "https://api.avax.network/ext/bc/C/rpc", chainId: 43114, accounts: [ "PRIVATE_KEY", ], }, } $ npx hardhat compile # Compiles the contracts $ npx hardhat run scripts/deploy.js --network mainnet # runs the script on the Avalanche Mainnet, "mainnet" is specified inside the hardhat config file
Finally, the Avalanche NFT marketplace is live now!
Launch a scalable and fast NFT Marketplace powered by Avalanche
Avalanche NFT Marketplace Development Company
What Avalanche development services does LeewayHertz offer?
DeFi Apps Development
Our team develops DeFi Applications that fully comply with the Ethereum asset requirement. These applications support the trading of equities, commodities and alternative assets.
Solidity-based Smart Contracts
Our developers build solidity-based smart contracts through EVM (Ethereum virtual machine) and provide services that include architecture, designing, auditing and implementation of the Solidity smart contracts.
NFT Solutions
Our Avalanche developers have expertise in developing NFT solutions for blockchain-focused start-ups and enterprises. We develop and launch efficient NFT solutions based on the latest blockchain innovations.
Wallet Development
We design an easy-to-use and highly secure Avalanche wallet to trade assets on the network. We work according to the client’s needs and try to add and alter necessary attributes to ensure precision.
Final note
Avalanche has gained major traction and is moving fast towards shaping the future of the blockchain space. It is highly scalable in nature and functions on the basis of valuable consensus protocols like the Snowman. The regular updates on the Avalanche improve the platform in every aspect, making it more accessible for the users and the developers. It helps them in building innovative dApps by adding new use cases to the blockchain technology.
If you are looking for Avalanche NFT marketplace development, then connect with LeewayHertz. Our experts are right here to help you with Avalanche development and consultation.
Start a conversation by filling the form
All information will be kept confidential.
Insights
How to Create a Virtual Machine on Avalanche
This article will explain the basics of Avalanche protocol, the importance of a virtual machine on Avalanche and how to create a virtual machine on Avalanche.
How to Create a Custom Blockchain on Avalanche ?
Avalanche enables easy launch of EVM compatible dApps and blockcahins that can confirm transactions instantly and process thousands of transactions per second.
How to Develop and Deploy an Avalanche Smart Contract
Avalanche is one of the fastest, programmable and interoperable smart contracts platforms that permit anyone to develop custom-made applications, solidity-based smart contracts, NFT solutions etc.