NFT Drop Created with Reach
Introduction: NFT Drop Dapp
The business case for the NFT Drop Dapp is to utilize Reach Lang and the Algorand Blockchain to provide businesses with a secure, and exciting way of handling the sale, creation, and the distribution of NFTs. The NFT Drop Dapp was built using the Reach language version 0.1 and currently supports the latest version (0.1.12). The goal of building this Dapp was to derive an understanding of the Reach language and the functionality it offers. Specifically, the Dapp should accept parameters, aggregate users, verify participation, and mint tokens.
The Dapp was designed, developed, and implemented over a few months with slight changes to the design made throughout development. Initially, the goal when minting NFTs was to utilize the randomness of user’s addresses to allocate features in a random manner. This feature, however, was swapped out due to complexity and time constraints.
The use case for the NFT Drop Dapp revolves around a single creator and multiple customers. The creator sets parameters for how many customers can join the drop, how much it should cost to join the drop, and the deadline to join. The creator then starts the drop and customers attempt to join the pool of successful customers. To do this, customers must pay the designated price within the deadline time frame and be within the allotted amount. Once the deadline to join the pool has finished the Creator creates another deadline equal to the previous deadline. This time, the deadline allows Customers to claim their NFTs if they are in the pool of successful customers. On a successful claim the Creator must mint the NFT, and transfer it to the correct customer. Once the second deadline has passed, customers can no longer claim their NFT and should be refunded of their balance if they paid but failed to claim.
Creator and Customer Classes
|Figure 1 - Class Structure - index.rsh
The creator and customer classes, as shown in Figure 1, were among the first things to be designed, and implemented. In Reach, classes with single instances are each constructed as a “Participant” and classes with multiple instances as a “ParticipantClass”. As such, the creator class was designated a “Participant” and the customer class, a “ParticipantClass”. Later, the customer was changed to be an “API” class to avoid possible deprecation of “ParticipantClass”. This change eliminates inefficiency between the frontend/backend communication by specifying the calls in either a fork, or a parallel reduce.
The creator ended up with four functions implemented on the front end. The raffleReady function starts customer contract creation on the front end, and has each of them attempt to join through an api call. The getParams function retrieves an object holding parameters set by the creator on the front end. The startMint function has customers attempt to retrieve an NFT for themselves by making api calls on the front end. Lastly, newToken creates a new non-network token for a successful user and transfers it to the account accordingly on the front end.
The customer API class consists of two api calls that occur during the aggregating phase and the retrieving phase respectively. First, joinPool has users specify the amount they are paying to join the pool. If they are successful the call will notify them with a boolean true value. Then, retrieveMint has customers request their token be minted. Although it accepts an address, customers cannot have an NFT minted for someone else or mint someone else’s NFT for themselves.
Creating the Drop
|Figure 2 - Raffle Construction - index.rsh
Above in Figure 2, the creator accepts the parameters sent from the frontend, and publishes them. A pool of UInt values is created to store the address and prove membership later. Once the deadline is created, the creator “notifies” the customers and they create their contracts.
|Figure 3 - Frontend Raffle Notice - index.mjs
|Figure 4 - Frontend Customers Joining - index.mjs
After the creator calls startCustomers in Figure 3, each testCustomer tries creating a contract and joining the pool as shown in Figure 4. A mapping for accounts to their respective contracts is created for simplicity.
Joining the Pool
|Figure 5 - Reservation Handling - index.rsh
A parallel reduce function was used to accept payments and aggregate users as seen in Figure 5. The invariant holds the contracts balance equal to the number of joined customers multiplied by the price. So long as the amount of joined customers is less than the max, the customer paid price is correct, they haven’t already joined, and they are within the deadline to join then they will be successful in joining the pool. At the end of the deadline either the creator or any of the customers can publish.
|Figure 6 - Frontend Claiming - index.mjs
|Figure 7 - Claim Handling - index.rsh
Following the expiration of the join deadline, a new deadline is created for users to claim their NFT as shown in Figure 7. The creator then notifies customers that the minting process has begun and they begin attempting to claim their NFT in Figure 6. Customers will only be successful if they successfully joined the pool during the previous phase, they haven’t already claimed their NFT, and they are within the time frame to claim their NFT. Upon successful claim and mint, the user is deleted from the pool. When the deadline is finished, the contract’s balance is transferred to the creator and the contract exits.
|Figure 8 - Ready to Mint - index.mjs
|Figure 9 - Frontend Minting - index.mjs
Minting was initially done on the back end to ensure a fair mint and transfer. The current iteration shown in Figure 9 utilizes the launchToken and transfer functionality on the front end for token minting and transfers. After a successful verification that an address should have an NFT minted, the back end sends the address to the creators front end. With an honest creator a unique token is minted, transferred to the account corresponding with the given address, and sent back to the contract.
Running the Dapp
|Figure 10 - Run Dapp
To run the program navigate to the nftSystem directory and run the command
./reach run. Doing so will compile the code and perform a variety of built in security checks performed by Reach to prevent vulnerabilities in the Dapp. The compiled code will then execute and kick off an NFT Drop.
|Figure 11 - Customers Join Output
Above in Figure 11, the terminal outputs whether customers were able to join the reservation pool. As modeled in the index.mjs file, each customer attempts to reserve a spot in the pool twice, but as dictated in the smart contract can only successfully reserve one spot. Additionally, the contract creator specifies a max size of 4 for the reservation pool which results in Customer #0 through Customer #3 successfully reserving a single spot each, and Customer #4 failing both times to reserve a spot.
|Figure 12 - Customers Claiming Output
Figure 12, shows the output after the deadline to join the reservation pool has ended. Once again, each customer attempts to claim multiple NFTs but can only successfully one given that they had successfully reserved one prior.
|Figure 13 - End State
Figure 13, shows an end balance for the creator, and the unique NFT id for each customer that successfully claimed an NFT.
There is still much that can be improved in the current iteration of the Dapp. The first missing functionality stems from the nature of the claim process. Since there is a window of time users have to claim their NFT, the possibility of failing to receive an nft despite paying exists. Ideally, this functionality should be changed to allow either all members of the pool the ability to receive their token without worrying about a deadline, or refund them if they couldn’t successfully claim. Additionally, the minting process needs to be added to the back end, or more verification should exist to ensure an untrustworthy creator is properly minting tokens. Lastly, random values can be utilized via the front end to then procedurally generate images for each NFT.
The design, development, and implementation of the NFT Drop Dapp was successful in building a Dapp that could accept payments, verify group membership, and correctly distribute tokens. Additionally, a greater understanding of the Reach language and its functionality was derived. More specifically, my understanding of how the language operates and it’s problem solving features has grown tremendously. I’m excited to use the skills, and my understanding of Reach’s capabilities to continue to develop with it.