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
- Good knowledge about Blockchain and Algorand.
- Detailed introduction to algob
- Detailed knowledge about assets, accounts, transactions.
- Detailed knowledge about smart signatures and smart contracts algorand smart contracts.
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))
])
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);