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
- Algorand .NET SDK Package
- Visual Studio 2019 IDE with .NET Core Framework installed
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
- 2. Install the Algorand Package
- 3. User Account (Registration/Login)
- 4. Create the Models and Game IT Controller
- 5. Setting Up the Controller
- 6. Configure the View Page
- 7. Launch the Web Application to Test
- 8. Fund Account on Testnet
- 9. Complete Solution
- 10. Complete Tutorial Video Solution
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:
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:
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:
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:
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:
Fig 3-2
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:
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:
ReplaceIdentityUser
withApplicationUser
as shown in the Image below:
Fig 3-5
Repeat the same step for every other one as shown in the image below:
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:
Fig 4-1
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 aFundMethod
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 theDetails
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 commandsCtrl + f5
to launch. -
Step 2
Register an account (user1)
Fig 7-1 -
Step 3
Navigate to GameIt Index.cs page and click on the Create as shown below:
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:
Fig 8-1
Back in the Application, click on Buy Game after funding your account address as shown in the image below:
Fig 8-2
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.
9. Complete Solution
The source code can be found in the GitHub link: https://github.com/Sethmichael01/GameITAlgorand
10. Complete Tutorial Video Solution