Non-fungible tokens (NFTs), which represent unique, non-interchangeable pieces of data, are one of the hottest topics in the blockchain ecosphere right now. They are implemented using smart contracts that can interact with any type of application, both blockchain-based (other smart contracts) and not (like web apps for example).
Chat bots are one of the sorts of applications that will be discussed in this article.
Chat bots simulate human conversions by implementing predetermined sets of actions and reactions that are triggered by a genuine end user when interacting with the bot. Azure Bot Service and Microsoft Bot Framework are one such framework and service combo for developing chat bots. which we’ll use in this post to build a bot that queries an Ethereum NFT contract, especially the CryptoPunks NFT contract.
Preconditions
Because we will be developing the bot in C# and deploying it to Azure, the following software will be required to be installed on the workstation:
Azure CLI Bot Framework Emulator — useful for local debugging Nethereum code generator — to produce C# code for interacting with the Ethereum contract, which may be deployed by issuing the following command (needs the.NET SDK):
dotnet tool install -g Nethereum.Generator.Console
Finally, we’ll need the project template for an empty C# bot, which can be installed by running the following command (the.NET SDK is required):
dotnet new -i Microsoft.Bot.Framework.CSharp.EmptyBot
The Robot
To begin, we will create a new empty folder for our source code and then run the following command to build the basic bot project:
dotnet new emptybot –name NftBot –output .
Next, we’ll need to get the CryptoPunks contract ABI and bytecode, which can be accessed on Etherscan at the following link: https://etherscan.io/address/0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb#code
Copy the contents of “Contract ABI” into a file named CryptoPunks.abi and the contents of “Contract Creation Code” into a file called CryptoPunks.bin, then store them in the Contracts subdirectory.
By executing the following command, we can now utilise Nethereum to produce C# code from these two files:
Nethereum.Generator.Console generate from-abi -abi ./Contracts/CryptoPunks.abi -bin ./Contracts/CryptoPunks.bin -cn CryptoPunks -ns NftBot.Contracts -o ./Contracts
We’ll also need to install Nethereum.Web3 in order for the application to have all of the dependencies it needs to use the produced code:
dotnet add package Nethereum.Web3
We’ll also need to install Nethereum.Web3 in order for the application to have all of the dependencies it needs to use the produced code:
For the sake of clarity, I will simply discuss the code for the bot’s actual implementation. The exact requirements for wiring the components using dependency injection are available in the aforementioned GitHub repository.
The bot will be implemented in a file named NftBot.cs, which will be put in the Bots subdirectory, and it will include two overrides for the base ActivityHandler class:
When a person enters the bot’s interface, OnMembersAddedAsync is triggered:
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken) { var welcomeText = “Hello and welcome!”; foreach (var member in membersAdded) { if (member.Id != turnContext.Activity.Recipient.Id) { await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText, welcomeText), cancellationToken); } } }
When a user delivers a message in the chat, OnMessageActivityAsync is triggered. This will interact with the CryptoPunks contract as follows:
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken) { var punkIndex = BigInteger.Parse(turnContext.Activity.Text); var punkIndexValid = punkIndex >= LowerIndexBound && punkIndex <= UpperIndexBound; var replyText = !punkIndexValid ? “Please use punk indexes between 0 and 9999” : await ProcessSaleOfferResult(punkIndex); await turnContext.SendActivityAsync(MessageFactory.Text(replyText), cancellationToken); }
In addition, there is an extra way for processing the outcome of the contract call:
private async Task<string> ProcessSaleOfferResult(BigInteger punkIndex) { var result = await _cryptoPunksService.PunksOfferedForSaleQueryAsync(punkIndex); if (!result.IsForSale) { return $”Punk {result.PunkIndex} is not for sale!”; } var replyBuilder = new StringBuilder(); replyBuilder.AppendLine($”Punk {result.PunkIndex} is for sale!”); replyBuilder.AppendLine($”By seller {result.Seller}”); replyBuilder.AppendLine($”With a minimum value of {Web3.Convert.FromWei(result.MinValue)} ETH”); return replyBuilder.ToString(); }
The entire bot file should look something like this:
using System.Collections.Generic; using System.Numerics;
using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Schema; using Nethereum.Web3; using NftBot.Contracts.CryptoPunks; using NftBot.Contracts.CryptoPunks.ContractDefinition; namespace NftBot.Bots { public class NftBot : ActivityHandler { private const int LowerIndexBound = 0; private const int UpperIndexBound = 9999; private readonly CryptoPunksService _cryptoPunksService; public NftBot(CryptoPunksService cryptoPunksService) { _cryptoPunksService = cryptoPunksService; } protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken) { var punkIndex = BigInteger.Parse(turnContext.Activity.Text); var punkIndexValid = punkIndex >= LowerIndexBound && punkIndex <= UpperIndexBound; var replyText = !punkIndexValid ? “Please use punk indexes between 0 and 9999” : await ProcessSaleOfferResult(punkIndex); await turnContext.SendActivityAsync(MessageFactory.Text(replyText), cancellationToken); } protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken) { var welcomeText = “Hello and welcome!”;
foreach (var member in membersAdded) { if (member.Id != turnContext.Activity.Recipient.Id) { await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText, welcomeText), cancellationToken); } } } private async Task<string> ProcessSaleOfferResult(BigInteger punkIndex) { var result = await _cryptoPunksService.PunksOfferedForSaleQueryAsync(punkIndex); if (!result.IsForSale) { return $”Punk {result.PunkIndex} is not for sale!”; } var replyBuilder = new StringBuilder(); replyBuilder.AppendLine($”Punk {result.PunkIndex} is for sale!”); replyBuilder.AppendLine($”By seller {result.Seller}”); replyBuilder.AppendLine($”With a minimum value of {Web3.Convert.FromWei(result.MinValue)} ETH”); return replyBuilder.ToString(); } } }
It is now possible to debug the bot by compiling it, launching it in debug mode, and directing the bot simulator to the bot controller’s route, which, if not altered from what the template created, should be at http://localhost:[PORT-NUMBER]/api/messages.
Deployment
To deploy the bot, we will first construct it in “Release” mode by running:
dotnet build –configuration Release
Then begin working on the Azure resources we’ll need for deployment, beginning by registering an Azure Active Directory application for it by running:
az ad app create –display-name “NftBotApp” –password “[PASSWORD]” –available-to-other-tenants
This method should return an appId field, which, along with the password, should be used to change the current appsettings. json file in the following format:
{ “MicrosoftAppType”: “MultiTenant”, “MicrosoftAppId”: “[APP-ID]”, “MicrosoftAppPassword”: “[PASSWORD]”, “MicrosoftAppTenantId”: “” }
Then, under the DeploymentTemplates subfolder, we will use one of the Azure Resource Manager templates generated by the template to create a resource group, application service plan, and application to host our bot by running the following command (note that the app ID and password are also required here):
az deployment sub create –template-file “./DeploymentTemplates/template-with-new-rg.json” –location eastus –parameters appType=”MultiTenant” appId=”[APP-ID]” appSecret=”[PASSWORD]” botId=”nft-bot” botSku=F0 newAppServicePlanName=”asp-nft-bot” newWebAppName=”app-nft-bot” groupName=”rg-nft-bot” groupLocation=”eastus” newAppServicePlanLocation=”eastus” –name “nft-bot”
Then, use the following command to prepare the project for deployment:
az bot prepare-deploy –lang Csharp –code-dir “.” –proj-file-path “NftBot.csproj”
Once the preparation is finished (a new deployment file is added to the root source folder), a ZIP file of the whole source folder (including the build related subfolders) must be produced (named NftBot.zip) and then utilised in the deployment by running:
az webapp deployment source config-zip –resource-group “rg-nft-bot” –name “app-nft-bot” –src “./NftBot.zip”
This procedure may take a few minutes to complete, but once completed, the bit is ready for testing.
Time to test the bot.
To test the bot, go to the newly created Azure Bot resource in the Azure Portal. Open the “Test in Web Chat” window and begin interacting with the bot by providing it CryptoPunk indexes (0–9999 are valid), seeing that some are not for sale and others are (with seller address and minimum value as advertised by the contract).
Conclusion
This is a very basic overview of how to link Ethereum contracts with Azure Bot implementations.
A real-world solution will most likely involve actions that also affect the status of the contract (such as purchasing a CryptoPunk) that are significantly more difficult, since they necessitate a wallet management solution, which is outside the scope of this paper (to keep it simple).