Create Publication

We are looking for publications that demonstrate building dApps or smart contracts!
See the full list of Gitcoin bounties that are eligible for rewards.

Tutorial Thumbnail
Intermediate · 30 minutes

Create an Online Game Marketplace to Buy/Sell Games

This tutorial teaches you how to:

  • Create Web Applications using the C#/.NET Framework
  • Install and use the Algorand Package in your application
  • Add interactivity between your application and the Algorand network to perform transactions

Requirements

Background

GameIT is an online decentralized game marketplace built on the Algorand Blockchain to trade games online.

Steps

1. Create a .NET Core Web Application

Open your visual Studio and proceed to create a Web Application using the .NET Core template option as shown below:
EditorImages/2021/02/26 18:37/2021-02-26_19_34_34-Window.png
Fig 1-1

After the step above in Fig 1-1, you will be prompted to select a project name.

In the next phase, you will be asked to select the type of NET Core Web Application you want to create as shown in Fig 1-2 below:
EditorImages/2021/02/26 18:44/2021-02-26_19_42_42-Window.png
Fig 1-2

2. Install the Algorand Package

After successfully creating the Web Application Project, install the Algorand SDK to the Application as shown in the image below using the Package Manager:
EditorImages/2021/02/26 18:58/2021-02-26_19_56_32-Window.png
Fig 2-1

3. User Account (Registration/Login)

  • Step 1: Create a new Class inside the Models folder called ApplicationUser as shown below:

using Microsoft.AspNetCore.Identity;

public class ApplicationUser : IdentityUser
{
    public string AccountAddress { get; set; }
    public string MnemonicKey { get; set; }
}

  • Step 2: Configure the Startup.cs class in Fig 3-1 below: EditorImages/2021/02/26 19:31/2021-02-26_20_29_34-Window.png
    Fig 3-1

services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

  • Step 3: Configure the Identity Pages for User Registration and Login Functionalities.
    Right Click on the Areas folder, Click on “Add New Scafolded Item”.
    Proceed to the next step as shown in the image below:
    EditorImages/2021/02/27 12:47/2021-02-27_13_37_17-Window.png
    Fig 3-2
    EditorImages/2021/02/27 12:47/2021-02-27_13_42_31-Window.png
    Fig 3-3

  • Step 4: Update the Registration.cs located inside Areas\Identity\Pages\Account\Register.cshtml.cs from the newly scaffolded item as shown below:
    EditorImages/2021/02/27 13:00/2021-02-27_13_57_38-Window.png
    Fig 3-4
    Update the user variable from
    var user = new Identity { UserName = Input.Email, Email = Input.Email };
    to
    var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email, Key = key, AccountAddress = address };
    This will automatically generate a new Account Address and a key when registering.
    Full OnPostAsync Method for Registration:

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl = returnUrl ?? Url.Content("~/");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                Algorand.Account account = new Algorand.Account();
                var key = account.ToMnemonic();
                var address = account.Address.ToString();
                var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email, Key = key, AccountAddress = address };
                var result = await _userManager.CreateAsync(user, Input.Password);
                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
                        protocol: Request.Scheme);


                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }

  • Step 5: Update the dependency Injection
    Update the Dependency Injection in every Controller associated with
    Login/Registration processes as shown in the image below:
    Replace IdentityUser with ApplicationUser as shown in the Image below:
    EditorImages/2021/02/27 13:16/2021-02-27_14_13_38-Window.png
    Fig 3-5
    Repeat the same step for every other one as shown in the image below:
    EditorImages/2021/02/27 13:22/2021-02-27_14_19_00-Window.png
    Fig 3-6

4. Create the Models and Game IT Controller

  • Step 1:
  • Create the Models

To Create the Game Model, open the Models folder and create the GameIT class, add the following fields inside the class as shown below:

public class GameIT
    {
        public int Id { get; set; }
        [DisplayName("Game Name")]
        public string GameName { get; set; }
        [DisplayName("Game Price")]
        public int GamePrice { get; set; }
        [DisplayName("Game Description")]
        public string GameDescription { get; set; }
        public ApplicationUser User { get; set; }
    }

  • Step 2:
    Create GameIT Controller

Navigate to the controllers folder and right click, click on add a controller and select the options in the images below:

EditorImages/2021/02/26 19:53/2021-02-26_20_51_13-Window.png
Fig 4-1
EditorImages/2021/02/26 20:03/2021-02-26_20_55_53-Window.png
Fig 4-2

5. Setting Up the Controller

  • Step 1:
    Inside the GameIT Controller, ensure to include the necessary Algorand using namespaces as shown below:

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using GameITAlgorand.Data;
using GameITAlgorand.Models;
using Microsoft.AspNetCore.Identity;
using Algorand.Client;
using Algorand;
using Algorand.V2.Model;
using Algorand.V2;
using Account = Algorand.Account;
using Microsoft.AspNetCore.Authorization;

  • Step 2:
    Update the class fields and constructor with the following code snippet shown below:

private readonly ApplicationDbContext _context;
        private static UserManager<ApplicationUser> _userManager;

        public GameITsController(ApplicationDbContext context,
            UserManager<ApplicationUser> userManager)
        {
            _context = context;
            _userManager = userManager;
        }

  • Step 3
    Inside the Controller, Navigate to the Create Method (HttpPost) and add the following code snippets as shown below:

[HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("Id,GameName,GamePrice,GameDescription")] GameIT gameIT)
        {
            if (ModelState.IsValid)
            {
                var user = await _userManager.FindByNameAsync(User.Identity.Name);
                gameIT.User = user;
                _context.Add(gameIT);
                await _context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            return View(gameIT);
        }

Do the same thing for the Edit HttpPost method to maintain the address, owner and mnemonic key. * Step 4
Configure the Index method as shown below:

// GET: GameITs
        public async Task<IActionResult> Index()
        {
            var user = await _userManager.FindByNameAsync(User.Identity.Name);
            ViewBag.Address = user.AccountAddress;
            return View(await _context.GameIT.Include(x => x.User).ToListAsync());
        }

  • Step 5
    Create a FundMethod method as shown below to process transfers on the Algorand Network.

Note this code uses account restore from a private key, this is not recommended for production use, and is provided for only for tutorial purposes. To integrate a web application use a wallet SDK to sign and send transactions, such as the AlgoSigner SDK for Chrome Extension. See tutorial here.

public static void FundMethod(string key, string receiver, int amount, string senderAddr)
        {
            string ALGOD_API_ADDR = "https://testnet-algorand.api.purestake.io/ps2"; //find in algod.net
            string ALGOD_API_TOKEN = "B3SU4KcVKi94Jap2VXkK83xx38bsv95K5UZm2lab"; //find in algod.token          
            string SRC_ACCOUNT = key;
            string DEST_ADDR = receiver;
            Account src = new Account(SRC_ACCOUNT);
            AlgodApi algodApiInstance = new AlgodApi(ALGOD_API_ADDR, ALGOD_API_TOKEN);
            try
            {
                var trans = algodApiInstance.TransactionParams();
            }
            catch (ApiException e)
            {
                Console.WriteLine("Exception when calling algod#getSupply:" + e.Message);
            }

            TransactionParametersResponse transParams;
            try
            {
                transParams = algodApiInstance.TransactionParams();
            }
            catch (ApiException e)
            {
                throw new Exception("Could not get params", e);
            }
            var amountsent = Utils.AlgosToMicroalgos(amount);
            var tx = Utils.GetPaymentTransaction(src.Address, new Address(DEST_ADDR), amountsent, "pay message", transParams);
            var signedTx = src.SignTransaction(tx);

            Console.WriteLine("Signed transaction with txid: " + signedTx.transactionID);

            // send the transaction to the network
            try
            {
                var id = Utils.SubmitTransaction(algodApiInstance, signedTx);
                Console.WriteLine("Successfully sent tx with id: " + id.TxId);
                Console.WriteLine(Utils.WaitTransactionToComplete(algodApiInstance, id.TxId));
            }
            catch (ApiException e)
            {
                // This is generally expected, but should give us an informative error message.
                Console.WriteLine("Exception when calling algod#rawTransaction: " + e.Message);
            }
        }

  • Step 6
    Configure the Details page that processes the transfer as shown below:

Full Details method code snippet:

[Authorize]
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
            var gameIT = await _context.GameIT.Include(x => x.User)
                .FirstOrDefaultAsync(m => m.Id == id);
            var user = await _userManager.FindByNameAsync(User.Identity.Name);
            var key = user.Key;
            var receiver = gameIT.User.AccountAddress;
            var amount = gameIT.GamePrice;
            var senderAddr = user.AccountAddress;
            FundMethod(key, receiver, amount, senderAddr);
            ViewBag.Success = "Transaction was Sucessful";
            if (gameIT == null)
            {
                return NotFound();
            }

            return View(gameIT);
        }

6. Configure the View Page

In this step, we will configure the Index.cs page of our Application. To do that, navigate to the GameITs folder inside the Views Folder and open the Index.cs page, replace the existing codes with the ones below as shown in the code snippet below:
Note: Only the owner of a game in auction can see the edit and delete links.

@model IEnumerable<GameITAlgorand.Models.GameIT>

@{
    ViewData["Title"] = "Index";
}

<h4>@ViewBag.Address</h4>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.GameName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.GamePrice)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.GameDescription)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.GameName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.GamePrice) algos
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.GameDescription)
            </td>
            <td>
                @if (User.Identity.Name == @item.User.UserName)
                {
                    <a asp-action="Edit" asp-route-id="@item.Id">Edit</a>
                    <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
                }
                <a asp-action="Details" asp-route-id="@item.Id">Buy Game</a>
            </td>
        </tr>
}
    </tbody>
</table>
[Authorize]
public async Task<IActionResult> BuyIT(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var gameITModel = await _context.GameITModel
                .SingleOrDefaultAsync(m => m.Id == id);
            var userIt = await _userManager.FindByNameAsync(User.Identity.Name);
            AlgodApi algodApiInstance = new AlgodApi("https://testnet-algorand.api.purestake.io/ps1", "B3SU4KcVKi94Jap2VXkK83xx38bsv95K5UZm2lab");
            //BuyerMnemonnic
            Account src = new Account(userIt.MnnemonicKey);
            var buyerAddress = userIt.AccountAddress;
            var sellerAddress = gameITModel.GameAddressOwner;
            //var c = "OQIM6O74DXB454SCLT7KH7GJ5HJJE6DQDPPW6JNPVSRZ6RU4GVQMGKTH2Q";
            //Console.WriteLine(account1.Address.ToString());
            //Console.WriteLine(account2.Address.ToString());
            var accountInfo = algodApiInstance.AccountInformation(buyerAddress.ToString());
            Console.WriteLine(string.Format($"Account Balance: {0} microAlgos", accountInfo.Amount));
            var acts = algodApiInstance.AccountInformation(buyerAddress.ToString());
            var befores = "Account 1 balance before: " + acts.Amount.ToString();
            Console.WriteLine(acts);
            Console.WriteLine(befores);
            TransactionParams transParams = null;
            try
            {
                transParams = algodApiInstance.TransactionParams();
            }
            catch (ApiException err)
            {
                throw new Exception("Could not get params", err);
            }
            var amount = Utils.AlgosToMicroalgos(Convert.ToDouble(gameITModel.Price));
            var tx = Utils.GetPaymentTransaction(new Address(buyerAddress), new Address(gameITModel.GameAddressOwner), amount, "pay message", transParams);
            var signedTx = src.SignTransaction(tx);
            ViewBag.Tran = $"Signed transaction with txid: {signedTx.transactionID}";
            //Console.WriteLine("Signed transaction with txid: " + signedTx.transactionID);

            // send the transaction to the network
            try
            {
                var ids = Utils.SubmitTransaction(algodApiInstance, signedTx);
                ViewBag.Sent = $"Successfully sent tx with id: {ids.TxId}";
                //Console.WriteLine("Successfully sent tx with id: " + ids.TxId);
                ViewBag.WaitTran = Utils.WaitTransactionToComplete(algodApiInstance, ids.TxId);
                //Console.WriteLine(Utils.WaitTransactionToComplete(algodApiInstance, ids.TxId));
            }
            catch (ApiException e)
            {
                // This is generally expected, but should give us an informative error message.
                ViewBag.Exc = $"Exception when calling algod#rawTransaction: {e.Message}";
                //Console.WriteLine("Exception when calling algod#rawTransaction: " + e.Message);
            }
            ViewBag.Success = $"You have successefully arrived the end of this test, please press and key to exist.";
            //Console.WriteLine("You have successefully arrived the end of this test, please press and key to exist.");
            //Console.ReadKey();
            if (gameITModel == null)
            {
                return NotFound();
            }

            return View(gameITModel);
        }

7. Launch the Web Application to Test

In this step, we will test our application to see how it works!!

  • Step 1
    Launch the Application by clicking on Debug and start the Application or run the commands Ctrl + f5 to launch.

  • Step 2
    Register an account (user1) EditorImages/2021/02/26 21:10/2021-02-26_22_01_03-Window.png
    Fig 7-1

  • Step 3
    Navigate to GameIt Index.cs page and click on the Create as shown below:
    EditorImages/2021/02/26 21:27/2021-02-26_22_20_25-Window.png
    Fig 7-2

  • Step 4
    Create a new Game to sell, enter the Name, Amount and Game Description.

  • Step 5
    Logout after creating the game successfully and Register as a new user (user2). User 2 will be used to buy games on the network.

8. Fund Account on Testnet

The following tutorial is for demo purposes and is being worked on the TestNet, no real transactions will be made.
Visit: https://bank.testnet.algorand.network/
Check the “I am not a Robot box”, paste in your account address (user2 address) in the field below as shown in Fig 8-1: EditorImages/2021/02/26 21:41/2021-02-26_22_39_25-Window.png
Fig 8-1
Back in the Application, click on Buy Game after funding your account address as shown in the image below:
EditorImages/2021/02/26 23:26/2021-02-27_00_13_59-Window.png
Fig 8-2
EditorImages/2021/02/26 23:38/2021-02-27_00_31_28-Window.png
Fig 8-3
Visit https://testnet.algoexplorer.io/
As shown in Fig 8-4 below, search for your account details via the Search bar and make sure it is running on Testnet.
EditorImages/2021/02/26 23:43/2021-02-27_00_41_14-Window.png

9. Complete Solution

The source code can be found in the GitHub link: https://github.com/Sethmichael01/GameITAlgorand

10. Complete Tutorial Video Solution