Select Page
The federal government of Marshall Islands, a country with around 60,000 people dispersed over 1000 different islands, announced on February 26, 2018, to adopt SOV, a blockchain-based currency, as its new legal tender to tackle difficulties arising from cross-border transactions and other financial intricacies. Later, it was publicized that Algorand is to build the blockchain for this ambitious project, which, according to crypto experts, will help curb the high transaction costs and safeguard the nation against inflation.

So, what made Algorand stand out from other blockchain networks that made an entire country choose it to power the world’s first national digital currency?

Algorand was found to have the best functionality to issue, manage, and distribute the SOV on a global level owing to its promising ecosystem. It is a decentralized network designed to solve the blockchain trilemma of achieving speed, security, and decentralization. Created in 2017 by a team led by Silvio Micali, a computer scientist specializing in cryptography, it is one of the fastest blockchain networks acquiring near-instant completion that can process thousands of transactions per second and finalize transactions in less than five seconds. Moreover, it is a secure and scalable network.

With an entirely decentralized, transparent, and cost-effective public platform, Algorand serves as the right ecosystem for various blockchain development services like building smart contracts or dApps. In this article, we will dig into how to develop and deploy a dApp on Algorand step-by-step.

About Algorand

Algorand is a smart contract-oriented platform similar to Ethereum. It is a permissionless blockchain that has created a unique and frictionless platform by building on five key technology elements: pure proof-of-stake approach, global scale reach, broad decentralization, robust security, and guaranteed finality. It is the first blockchain network to provide instant transaction finality without forking. The native cryptocurrency of Algorand is known as Algo.

Algorand uses its trademarked ‘Pure Proof-of-Stake’ approach to confirm blocks to the blockchain. Consensus mechanisms like Proof-of-work rely on miners to compete with each other to solve complex mathematical problems, and the first miner to solve the problem gets to add a new block to the coin’s blockchain and receive the mining reward. However, it costs a lot to mine new blocks, both in equipment and electricity. The high cost makes profitable mining unattainable for most, leading to the centralization of mining power. In a proof-of-stake protocol, token owners participate in the network’s consensus mechanism by staking tokens. Although you can get rewards by staking your tokens, that does not necessarily mean you can also participate in the consensus mechanism. Here is where Algorand makes the difference.

Every token holder with at least one Algo in their account can choose to participate, in which case they can randomly be chosen to propose and add new blocks to the chain. The more tokens one hold, the more likely it is to be chosen. The Algorand foundation boasts that the Pure Proof-of-Stake approach has significant advantages over the Proof-of-Stake implementation by

  • Enhancing security,
  • Promoting decentralization, and
  • Allowing faster and less expensive transactions.

Now that we have learned about the Algorand ecosystem let us understand how to develop a dApp on Algorand.

How to build a dApp on Algorand?

For Algorand dApp development, let us consider a situation where a client named Ashley asks a developer to create an auction dApp for NFTs. The developer is asked to create a customized dApp with the following requirements:

  • For each piece of artwork, sellers must be able to generate a new auction. The contract should be able to hold the artwork from the opening of the auction until closing.
  • The auction can be terminated even before starting, where the item would return to the seller.
  • Sellers can reserve a price for the artwork, and if bidders do not meet the condition, the item will be returned to the seller.
  • During each bid, when the new bid is higher than the bid before it, the previous bidder will be refunded, and the new bid will be recorded.
  • When the auction is successful and the reserve price is met, the seller will get the full bid amount, and the highest bidder will receive the artwork.

Let us develop the dApp based on the above features using Python.

This tutorial is divided into two sections. In the first section, we will deploy the dApp and run a demo auction. The second part sketches an outline of the various components of the auction application.

Section 1: Launch the application

To run a node, Algorand issues a Docker instance. So, install docker before moving ahead to the next steps.

Prerequisites

  • Docker
  • Python 3.6 or higher

Step 1: Clone the auction demo application

Use the commands given below to replicate the repository:

git clone https://github.com/algorand/auction-demo  cd auction-demo

Step 2: Install Sandbox

If you are a Mac or Linux user, run the following codes directly from your terminal. However, if you use Windows, run from a terminal supporting bash, such as Git Bash. We can clone the sandbox repository to “./_sandbox” and start some docker containers using this command:

./sandbox up

Step 3: Setup environment and run tests

Install the necessary prerequisites and use a Python virtual environment for the project. The below commands can activate the virtual environment and then install every dependency, including PyTeal and the Algorand Python SDK.

Now, set up a Python environment or “venv” (one time):

python3 -m venv venv

Activate “venv”. Substitute “bin” with “Scripts” on Windows.

If your shell is bash/zsh, use the following codes:

. venv/bin/activate

But, if your shell is fish, activate venv with:

. venv/bin/activate.fish

Now, install dependencies:

pip3 install -r requirements.txt

Run the file, “example.py,” which can run a 30-second auction:

python3 example.py
#output 
Ashley is generating temporary accounts...
Ashley is generating an example NFT... 
The NFT ID is: 15
Ashley is creating auction smart contract that lasts 30 seconds to auction off NFT... 
Ashley is setting up and funding NFT auction... 
Ashley's algo balance: 99998100 algos 
The smart contract now holds the following: {0: 202000, 15: 1} 
Jasmine wants to bid on NFT, her algo balance: 100000100 algos 
Jasmine is placing bid for: 1000000 algos 
Jasmine is opting into NFT with id: 15 
Ashley is closing out the auction.... 
The smart contract now holds the following: {0: 0} 
Jasmine's NFT balance: 1 for NFT ID: 15
Ashley's balances after auction: {0: 101197100, 15: 0} Algos 
Jasmine's balances after auction: {0: 98997100, 15: 1} Algos

You can run tests if needed:

pytest

The above step will run through every test in the “operations_test.py” file and take more than six minutes to execute.

When the testing is finished, use the given command to shutdown the sandbox:

./sandbox down

Now let us delve into the code.

Section 2: Application Overview

The auction demo application auctions off an NFT using a smart contract. To achieve this functionality, the smart contract provides four different techniques.

The first method develops the smart contract and sets up the auction state on the blockchain, which involves the seller’s account, the particular NFT ID to auction, the auction’s beginning and ending time, the reserved auction price, and the minimum bid increment.

The second technique completes the auction by funding the smart contract with a limited number of algos (to meet minimum balance criteria and pay transaction fees) and putting the NFT into the smart contract.

The bid scenario is created using the third way. The potential buyer submits the bid to the smart contract using algos. The contract will hold the algos if the bid is successful, and bidding can only be done between the auction’s start and end times. The contract immediately returns the algos of the prior higher bidder if the bid replaces a previous bid.

The auction can be closed out using the fourth and final method of the contract, which will either assign the NFT to the highest bidder and transfer the algos to the seller or return the NFT to the seller and close out any remaining algos to the seller.

Smart contracts are recorded on the blockchain and can be accessed remotely using a transaction type known as an application transaction. Using the PyTeal library with the Algorand Python SDK is the easiest way to create smart contracts. Write the smart contract logic that will be saved (the on-chain value transfer logic) using PyTeal. Use the SDK to deploy the smart contract and then develop the application transactions that will interface with it (the off-chain logic and smart contract triggers).

The smart contract code and SDK activities are separated into two files by the auction demo code. “contracts.py” has all the logic for the smart contract, and “operations.py” includes the SDK code to deploy the smart contract and interact with it when deployed.

The Smart Contract

The smart contract code is written in the “contracts.py” file using the PyTeal library. A new smart contract is deployed in this scenario for each new auction. As we mentioned earlier, every auction requires the ability to create, set up, bid on, and close off an auction. We’ll go over where each of these eventualities is represented in the code in a moment.

On Algorand, smart contracts are made up of two separate ‘programs.’ The approval program is the first, and the clear program is the second. Because the approval program contains the majority of the code, we don’t need to bother about the clear program for now.

Let’s move on to the top-level routing code for the smart contract’s approval program:

program = Cond( 
[Txn.application_id() == Int(0), on_create], 
[Txn.on_completion() == OnComplete.NoOp, on_call], 
[ 
Txn.on_completion() == OnComplete.DeleteApplication, on_delete, 
], 
[ 
Or( 
Txn.on_completion() == OnComplete.OptIn, 
Txn.on_completion() == OnComplete.CloseOut, Txn.on_completion() == OnComplete.UpdateApplication, 
), 
Reject(), 
], 
)

This code takes care of any application transaction sent to the approval program. These types are distinguished by their “OnComplete” values. You may have observed that the first line stands out from the others. The application ID of a smart contract is always zero when it is first formed. The first line uses this to send the “on_create” variable to the auction creation logic.

Next, we’ll look at the creation code.

1. Create Auction
on_create_start_time = Btoi(Txn.application_args[2]) 
on_create_end_time = Btoi(Txn.application_args[3]) 
on_create = Seq( 
App.globalPut(seller_key, Txn.application_args[0]), App.globalPut(nft_id_key, Btoi(Txn.application_args[1])), App.globalPut(start_time_key, on_create_start_time), App.globalPut(end_time_key, on_create_end_time), App.globalPut(reserve_amount_key, Btoi(Txn.application_args[4])), App.globalPut(min_bid_increment_key, Btoi(Txn.application_args[5])), App.globalPut(lead_bid_account_key, Global.zero_address()), 
Assert( 
And( 
Global.latest_timestamp() < on_create_start_time, on_create_start_time < on_create_end_time 
) 
), 
Approve(), 
)

This code collects all auction parameters and stores them in the smart contract’s on-chain global state. It also checks to see if the start and end times have elapsed or not.

A “NoOp” call is the next conditional in our main program routing code. The “NoOp” application transaction is simply a smart contract call that includes arguments such as strings that can be used to activate additional conditions. We may provide a “NoOp” call with the ‘setup’ or ‘bid’ argument in this contract, and it will route to the right logic for each of those circumstances. The “on_call” variable is used to display the next layer routing.

on_call_method = Txn.application_args[0] 
on_call = Cond( 
[on_call_method == Bytes("setup"), on_setup], 
[on_call_method == Bytes("bid"), on_bid], 
)
2. Setup the auction:

Let’s get to the “on_setup” code by following the ‘setup’ path.

on_setup = Seq( 
Assert(Global.latest_timestamp() < App.globalGet(start_time_key)), 
# opt into NFT asset -- because you can't opt in if you're already opted in, this is what 
# we'll use to make sure the contract has been set up InnerTxnBuilder.Begin(), 
InnerTxnBuilder.SetFields( 
{ 
TxnField.type_enum: TxnType.AssetTransfer, 
TxnField.xfer_asset: App.globalGet(nft_id_key), TxnField.asset_receiver: Global.current_application_address(), 
} 
), 
InnerTxnBuilder.Submit(), 
Approve(), 
)

This code checks that the start time has not passed and then uses a smart contract transaction to enroll the smart contract into the NFT going to be auctioned. It’s worth noting that this transaction will be grouped with two others: a payment transaction to fund the smart contract with some algos to cover minimum balance requirements and transaction fees, an application transaction to initiate the setup, and an asset transfer transaction to move the NFT from the seller’s account to the smart contract.

3. Bid in the auction:

Let’s have a look at the “on_bid” scenario, which will be tested if a user submits a ‘bid’ with a “NoOp” application transaction.

on_bid_txn_index = Txn.group_index() - Int(1) 
on_bid_nft_holding = AssetHolding.balance( Global.current_application_address(), App.globalGet(nft_id_key) 
) 
on_bid = Seq( 
on_bid_nft_holding, 
Assert( 
And( 
# the auction has been set up 
on_bid_nft_holding.hasValue(), 
on_bid_nft_holding.value() > Int(0), 
# the auction has started 
App.globalGet(start_time_key) <= Global.latest_timestamp(), 
# the auction has not ended 
Global.latest_timestamp() < App.globalGet(end_time_key),  # the actual bid payment is before the app call Gtxn[on_bid_txn_index].type_enum() == TxnType.Payment, Gtxn[on_bid_txn_index].sender() == Txn.sender(), Gtxn[on_bid_txn_index].receiver()  == Global.current_application_address(), Gtxn[on_bid_txn_index].amount() >= Global.min_txn_fee(), 
)
), 
If(
Gtxn[on_bid_txn_index].amount() 
>= App.globalGet(lead_bid_amount_key) + App.globalGet(min_bid_increment_key) 
).Then(
Seq(
If(App.globalGet(lead_bid_account_key) != Global.zero_address()).Then( repayPreviousLeadBidder(    App.globalGet(lead_bid_account_key), App.globalGet(lead_bid_amount_key), 
) 
),
App.globalPut(lead_bid_amount_key, Gtxn[on_bid_txn_index].amount()), App.globalPut(lead_bid_account_key, Gtxn[on_bid_txn_index].sender()), App.globalPut(num_bids_key, App.globalGet(num_bids_key) + Int(1)), 
Approve(), 
) 
), 
Reject(), 
)

Here, we determine if the bid is higher than the existing bid leader (stored in the contract state on-chain). If this is a new bidder, and the new bid exceeds the previous one, the smart contract issues a refund transaction for the previous highest bidder. This code also confirms that the auction is still running and that the bidder sent a payment transaction (their bid) along with the smart contract application transaction.

4. Close out the auction:

A “DeleteApplication” application transaction is used to end the auction. After assessing the logic in the “on_delete” variable, this transaction will attempt to delete the smart contract.

on_delete = Seq( 
If(Global.latest_timestamp() < App.globalGet(start_time_key)).Then( 
Seq( 
# the auction has not yet started, it's ok to delete 
Assert( 
Or( 
# sender must either be the seller or the auction creator Txn.sender() == App.globalGet(seller_key), 
Txn.sender() == Global.creator_address(), 
)
), 
# if the auction contract account has opted into the nft, close it out closeNFTTo(App.globalGet(nft_id_key), App.globalGet(seller_key)), 
# if the auction contract still has funds, send them all to the seller closeAccountTo(App.globalGet(seller_key)), 
Approve(), 
) 
), 
If(App.globalGet(end_time_key) <= Global.latest_timestamp()).Then(  Seq(  # the auction has ended, pay out assets If(App.globalGet(lead_bid_account_key) != Global.zero_address()) .Then(  If( App.globalGet(lead_bid_amount_key)  >= App.globalGet(reserve_amount_key) 
) 
.Then( 
# the auction was successful: send lead bid account the nft closeNFTTo( 
App.globalGet(nft_id_key), App.globalGet(lead_bid_account_key), 
) 
) 
.Else( 
Seq( 
# the auction was not successful because the reserve was not met: return 
# the nft to the seller and repay the lead bidder closeNFTTo( 
App.globalGet(nft_id_key), App.globalGet(seller_key) 
), 
repayPreviousLeadBidder( App.globalGet(lead_bid_account_key), App.globalGet(lead_bid_amount_key), 
), 
) 
) 
) 
.Else( 
# the auction was not successful because no bids were placed: return the nft to the seller closeNFTTo(App.globalGet(nft_id_key), App.globalGet(seller_key)) 
), 
# send remaining funds to the seller 
closeAccountTo(App.globalGet(seller_key)), Approve(), 
) 
), 
Reject(), 
)

Because it must handle numerous close-out scenarios, this code is somewhat long. The first case is when the auction is canceled before it even begins where the smart contract returns the NFT and all algos back to the seller. The smart contract will transfer the NFT to the winning bidder and send the algos to the seller if the auction is completed and the reserve is reached. If the bidder cannot meet the reserve, the highest bidder will be refunded and the seller will get the NFT back. Likewise, if no one bids in the auction, the NFT and the algos will be refunded to the seller. Any account can end the auction.

Deploying and communicating with the smart contract

We learned how the smart contract program protects crucial value transfer scenarios during auctions (e.g., sending bids, closing it out). Let’s see how to put it on the blockchain and communicate with it now. The relevant transactions are built using Algorand Python SDK in the “operations.py” file. It has a method for each of the four auction scenarios supported by the application. The first scenario, which is stated in the “createAuctionApp” method, is to deploy and build the auction contract.

def createAuctionApp( 
client: AlgodClient, 
sender: Account, 
seller: str, 
nftID: int, 
startTime: int, 
endTime: int, 
reserve: int, 
minBidIncrement: int, 
) -> int: 
"""Create a new auction. 
Args: 
client: An algod client. 
sender: The account that will create the auction application. 
seller: The address of the seller that currently holds the NFT being auctioned. 
nftID: The ID of the NFT being auctioned. 
startTime: A UNIX timestamp representing the start time of the auction. This must be greater than the current UNIX timestamp. endTime: A UNIX timestamp representing the end time of the auction. This must be greater than startTime. 
reserve: The reserve amount of the auction. If the auction ends without a bid that is equal to or greater than this amount, the auction will fail, meaning the bid amount will be refunded to the lead bidder and the NFT will return to the seller. 
minBidIncrement: The minimum difference required between a new bid and the current leading bid. 
Returns: 
The ID of the newly created auction app. 
""" 
approval, clear = getContracts(client) 
globalSchema = transaction.StateSchema(num_uints=7, num_byte_slices=2) 
localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=0) 
app_args = [ 
encoding.decode_address(seller), 
nftID.to_bytes(8, "big"), 
startTime.to_bytes(8, "big"), 
endTime.to_bytes(8, "big"), 
reserve.to_bytes(8, "big"), 
minBidIncrement.to_bytes(8, "big"), 
] 
txn = transaction.ApplicationCreateTxn( 
sender=sender.getAddress(), on_complete=transaction.OnComplete.NoOpOC, approval_program=approval, 
clear_program=clear, 
global_schema=globalSchema, 
local_schema=localSchema, 
app_args=app_args, 
sp=client.suggested_params(), 
) 
signedTxn = txn.sign(sender.getPrivateKey()) client.send_transaction(signedTxn) 
response = waitForTransaction(client, signedTxn.get_txid()) 
assert response.applicationIndex is not None and response.applicationIndex > 0 return response.applicationIndex

The parameters for the auction (reserve price, NFT ID, and so on) are configured, and the smart contract is deployed in this function. It uses the “ApplicationCreateTxn” function, which sends the smart contract’s compiled version to the blockchain. The smart contract will be given a unique ID and an Algorand address once launched.

Next is the “setupAuctionApp” function, used to fund the smart contract with a small amount of algos to support fees and the Algorand account’s minimum balance requirement, an application transaction instructing the smart contract to set up the auction, and an asset transfer transaction from the NFT to the smart contract.

def setupAuctionApp( 
client: AlgodClient, 
appID: int, funder: Account, nftHolder: Account, nftID: int, nftAmount: int, ) 
appAddr = getAppAddress(appID) suggestedParams = client.suggested_params() 
fundingAmount = ( # min account balance 100_000 # additional min balance to opt into NFT + 100_000 # 3 * min txn fee + 3 * 1_000 ) 
fundAppTxn = transaction.PaymentTxn( sender=funder.getAddress(), 
receiver=appAddr, amt=fundingAmount, sp=suggestedParams, ) 
setupTxn = transaction.ApplicationCallTxn( sender=funder.getAddress(), 
index=appID, on_complete=transaction.
OnComplete.NoOpOC, app_args=[b"setup"], 
foreign_assets=[nftID], sp=suggestedParams, )
fundNftTxn = transaction.AssetTransferTxn( sender=nftHolder.getAddress(), 
receiver=appAddr, index=nftID, amt=nftAmount, sp=suggestedParams, ) transaction.assign_group_id([fundAppTxn, setupTxn, fundNftTxn]) 
signedFundAppTxn = fundAppTxn.sign(funder.getPrivateKey()) 
signedSetupTxn = setupTxn.sign(funder.getPrivateKey()) 
signedFundNftTxn = fundNftTxn.sign(nftHolder.getPrivateKey()) 
client.send_transactions([signedFundAppTxn, signedSetupTxn, signedFundNftTxn]) 
waitForTransaction(client, signedFundAppTxn.get_txid())

The three transactions are combined and uploaded to the blockchain.

The “placeBid” function is used in the auction application to place a bid on a certain NFT. This function generates a payment transaction from the bidder to the smart contract and an application transaction declaring the bid to the smart contract.

def placeBid(client: AlgodClient, appID: int, bidder: Account, bidAmount: int) 
appAddr = getAppAddress(appID) 
appGlobalState = getAppGlobalState(client, appID) 
nftID = appGlobalState[b"nft_id"] 
if any(appGlobalState[b"bid_account"]):
# if "bid_account" is not the zero address prevBidLeader = encoding.encode_address(appGlobalState[b"bid_account"]) 
else: prevBidLeader = None suggestedParams = client.suggested_params() 
payTxn = transaction.PaymentTxn( sender=bidder.getAddress(), 
receiver=appAddr, amt=bidAmount, sp=suggestedParams, ) 
appCallTxn = transaction.ApplicationCallTxn( sender=bidder.getAddress(), 
index=appID, on_complete=transaction.OnComplete.NoOpOC, app_args=[b"bid"], 
foreign_assets=[nftID], # must include the previous lead bidder here to the app can refund that bidder's payment accounts=[prevBidLeader] 
if prevBidLeader is not None else [], sp=suggestedParams, ) 
transaction.assign_group_id([payTxn, appCallTxn]) signedPayTxn = payTxn.sign(bidder.getPrivateKey()) 
signedAppCallTxn = appCallTxn.sign(bidder.getPrivateKey()) 
client.send_transactions([signedPayTxn, signedAppCallTxn]) 
waitForTransaction(client, appCallTxn.get_txid())

These two transactions have been combined. On Algorand, grouping transactions, also known as atomic transfers, is a powerful feature.

The auction is ended with the “closeAuction” function. Any account can access this function.

def closeAuction(client: AlgodClient, appID: int, closer: Account):  
appGlobalState = getAppGlobalState(client, appID) 
nftID = appGlobalState[b"nft_id"] accounts: List[str] = [encoding.encode_address(appGlobalState[b"seller"])] 
if any(appGlobalState[b"bid_account"]): 
# if "bid_account" is not the zero address accounts.append(encoding.encode_address(appGlobalState[b"bid_account"])) 
deleteTxn = transaction.ApplicationDeleteTxn( sender=closer.getAddress(), 
index=appID, accounts=accounts, foreign_assets=[nftID], sp=client.suggested_params(), ) 
signedDeleteTxn = deleteTxn.sign(closer.getPrivateKey()) 
client.send_transaction(signedDeleteTxn) 
waitForTransaction(client, signedDeleteTxn.get_txid())

Using the “ApplicationDeleteTxn” function, this method deletes an application transaction.

You’ve now completed the guide to launching an Algorand auction dApp.

Conclusion

Algorand is one of the leading blockchain protocols launched to overcome various drawbacks existing in the blockchain predecessors and attempts to solve the blockchain trilemma of acquiring decentralization, scalability, and security. The unique approach of Pure-Proof-of-Stake and other advantages that the infrastructure provides make it a stable and robust platform to develop dApps with assured privacy.

With extensive experience in several blockchain development services, LeewayHertz aims to develop and deploy end-to-end scalable blockchain solutions to clients. If you are searching for a proficient blockchain development company to launch a dApp on the Algorand network, we are happy to assist you throughout the development process.

Please connect with our blockchain experts for consultancy and development services related to dApps, smart contracts, and custom blockchains development on the Algorand blockchain

Webinar Details

Author’s Bio

Akash Takyar
Akash Takyar
CEO LeewayHertz
Akash Takyar is the founder and CEO at LeewayHertz. The experience of building over 100+ platforms for startups and enterprises allows Akash to rapidly architect and design solutions that are scalable and beautiful.
Akash's ability to build enterprise-grade technology solutions has attracted over 30 Fortune 500 companies, including Siemens, 3M, P&G and Hershey’s. Akash is an early adopter of new technology, a passionate technology enthusiast, and an investor in AI and IoT startups.

Start a conversation by filling the form

Once you let us know your requirement, our technical expert will schedule a call and discuss your idea in detail post sign of an NDA.
All information will be kept confidential.

Insights

Follow Us