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.

Solution Thumbnail

ASA payment for a smart contract service using Algorand

Overview

This article presents a solution for how to use ASA payment for a smart contract service using Algorand. We will implement this using Algo Builder.

Requirements

Project structure

contracts:(algob-project)
├── assets
│   ├── asa.yaml
│   ├── clear.py
│   ├── escrow.py
│   ├── stateful.py
├── scripts
│   ├── deploy.js
├── package.json

dapp containing react app.

Overview

In this solution we have a smart contract (an app) which offers a service and we only register a new user if they pay a certain amount of specific ASA tokens.
You can find the code for solution here.
Note: One user can only register once and pay warcraft dapp multiple times.

The solution consists of two parts:
- contracts: This folder consists of implementation of ASA, smart signatures and smart contracts.
- dapp: This folder consists of user interface to interact with deployed contracts and ASA.

Contracts

contracts folder contains the logic for this solution. In this folder we will perform the following operations:

  • Create a token: WarcraftCoin (ASA token)
  • Create an app: Warcraft - it has only one function: register (user_addr)

We are using PyTEAL templates, you can read more about it from here

Escrow(Stateless contract)

Escrow account will be used to deposit ASA token(warcraft token).
In this contract we have optin and register branches.
Only manager can opt-in ASA to escrow address, this is done so that malicious user cannot spam escrow address with asa opt-in.

# Opt-in transaction
# Note: we are checking that first transaction is payment with amount 0
# and sent by store manager, because we don't want another
# user to opt-in too many asa/app and block this address
opt_in = And(
    Gtxn[0].amount() == Int(0),
    Gtxn[0].sender() == Tmpl.Addr("TMPL_MANAGER"),
    Gtxn[1].type_enum() == TxnType.AssetTransfer,
    Gtxn[1].asset_amount() == Int(0)
)

Register branch ensures that stateful contract is called.

register = And(
    commons_checks,
    Gtxn[1].type_enum() == TxnType.ApplicationCall,
    Gtxn[1].application_id() == Tmpl.Int("TMPL_APPLICATION_ID"),
    Gtxn[1].sender() == Gtxn[0].asset_sender()
)

Stateful contract

In this contract is remember in the contract state the store escrow account address and total_registered.
Moreover we hardcode TMPL_MANAGER(application manager) using template variable (set during smart contract loading).
This contract has two branches: on_update_escrow, on_register.

The on_update_escrow branch can only be called by application manager to update the escrow lsig address.
Note: we firstly need to create the application without setting the escrow account address, because the application ID is needed to properly initialize the escrow lsig.

on_update_escrow = Seq([
    Assert(
        And(
            basic_checks,
            Txn.sender() == Tmpl.Addr("TMPL_MANAGER")
        )
    ),
    App.globalPut(escrow, Txn.application_args[1]),
    Return(Int(1))
])

The on_register branch is used to register a new user to the contract in exchange of ASA token (WarcraftCoin). In this branch we check that user paid exactly 1 WarcraftCoin to the escrow. Finally, in his local state we set warcraft = 1 (to give him an access to the game) and increment global registered counter by 1.

on_register = Seq([
        Assert(
            And(
                basic_checks,
                Gtxn[0].type_enum() == TxnType.AssetTransfer,
                Gtxn[0].asset_amount() == Int(1),
                Gtxn[0].xfer_asset() == Tmpl.Int("TMPL_WARCRAFT_TOKEN"),
                Gtxn[0].asset_receiver() == App.globalGet(escrow)
            )
        ),
        App.localPut(Int(0), Bytes("warcraft"), Int(1)),
        App.globalPut(total_registered, Add(App.globalGet(total_registered), Int(1))),
        Return(Int(1))
])

EditorImages/2021/10/19 20:28/1-deploy.png

EditorImages/2021/10/19 20:28/2-deploy.png

EditorImages/2021/10/19 20:29/3-deploy.png

We prepared a sample deployment scripts (for ASA and Application) in scripts/deploy.js.

Web Dapp

We created a web dapp to demo an interact with deployed contracts. It uses @algo-builder/web and Algosigner.
It is created using create-react-app.
You can find the full code for in our dapp templates repository

User can use this dapp to pay warcraft coin and register themself in app, This dapp has a payment widget which handle this group transaction.

First you need to set ASA ID (WarcraftCoin) and App ID in the widget code. You can find these values after deploying the contracts (in Algo Builder artifacts).

After you have set the values you can start the app using yarn run start.

To execute transaction we create a new AlgoSigner context and use @algo-builer/web to create a transaction.

const web = new WebMode(AlgoSigner, CHAIN_NAME);
const groupTx = [
    {
        type: types.TransactionType.TransferAsset,
        sign: types.SignType.SecretKey,
        fromAccountAddr: fromAddress,
        toAccountAddr: toAddress,
        assetID: assetIndex,
        amount: 1,
        payFlags: {},
    },
    {
        type: types.TransactionType.OptInToApp,
        sign: types.SignType.SecretKey,
        fromAccountAddr: fromAddress,
        toAccountAddr: toAddress,
        appID: appIndex,
        payFlags: {},
    }
];
// show loading state on button while we send & wait for transaction response
setLoading(true);
let response = await web.executeTransaction(groupTx);
console.log(response);

EditorImages/2021/10/19 20:29/dapp.png

EditorImages/2021/10/19 20:29/dapp-2.png

EditorImages/2021/10/19 20:29/play.png