We will create a simple stateful smart contract which will:
- Have a global variable
counter
- Will increment
counter
each time we call the application.
- Create a file with Approval Program
- Create a file with Clear program
- Deploy New Application
- Opt-In to Application
- Call Application
- Update Application
- Delete Application
We will create an approval program which has global counter
and it is incremented by 1 each time we call the application. Put the code below in /assets/approval_program.teal
:
#pragma version 4
int NoOp
txn OnCompletion
==
bnz application_call
int 1
return
application_call:
// read global state
byte "counter"
dup
app_global_get
// increment the value
int 1
+
// store to scratch space
dup
store 0
// update global state
app_global_put
// load return value as approval
load 0
return
This program has a single global key/value pair to store the number of times the program was called. The key is the string "counter" and the value will be defined as an integer when the application is created.
Now, we will create clear program. This program does not evaluate any conditions and simply approves the call. Put the code below in /assets/clear_program.teal
:
#pragma version 4
int 1
return
To deploy the contract use the following code in a new file named deploy.js
in scripts
folder of your project directory:
const masterAccount = deployer.accountsByName.get("master-account");
const creatorAccount = deployer.accountsByName.get("alice");
const algoTxnParams = {
type: types.TransactionType.TransferAlgo,
sign: types.SignType.SecretKey,
fromAccount: masterAccount,
toAccountAddr: creatorAccount.addr,
amountMicroAlgos: 200e6,
payFlags: {},
};
// transfer some algos to creator account
await deployer.executeTx(algoTxnParams);
In the code above we declared user accounts and funded creatorAccount
account. masterAccount
is the default account used in algob private net.
Firstly we need to fund the contract. master
account will fund it (as it has lot of algos):
// Create Application
// Note: An Account can have maximum of 10 Applications.
const sscInfo = await deployer.deployApp(
creatorAccount,
{
appName: "CounterApp",
metaType: types.MetaType.FILE
approvalProgramFilename: "approval_program.teal", // approval program
clearProgramFilename: "clear_program.teal", // clear program
localInts: 1,
localBytes: 1,
globalInts: 1,
globalBytes: 1,
},
{}
);
console.log(sscInfo);
Parameters passed are:
-
Approval Program
-
Clear Program
-
SSC(stateful smart contract) arguments which includes sender and details for storage usage.
-
Common transaction parameters (eg. totalFee)
-
In above code we have deployed a new application.
-
After deploying application you should see the following information on your terminal:
Created new app-id: 189
{
creator: 'EDXG4GGBEHFLNX6A7FGT3F6Z3TQGIU6WVVJNOXGYLVNTLWDOCEJJ35LWJY',
txID: 'BCP3ZKT26K2BEB475OMKQHOMBEFVWEHESRS3XMAWQEVASFP6JFUA',
confirmedRound: 13899,
appID: 189
}
To Opt-In to an application use the following code in one of your scripts (in ./scripts
):
await deployer.optInAccountToApp(account, applicationID, {}, {});
where Account
is the account you want to opt-in and applicationID is application index.
To call an application use the following code in one of your scripts (in ./scripts
):
const tx = {
type: types.TransactionType.CallApp,
sign: types.SignType.SecretKey,
fromAccount: creatorAccount,
appID: applicationID,
payFlags: {},
};
await deployer.executeTx(tx);
In tx
there are following parameters:
- we set the type which is
CallApp
- Call to stateful smart contract - set the sign to SecretKey (tx is signed by creatorAccount's sk)
- provide fromAccount details
- provide application index of SSC (retreived from checkpoint)
- provide payment flags, if any (eg. fee, firstValid, lastvalid etc)
Calling application each time will increase the stateful counter by 1. To view the global state of the application you can use the following code:
// Retreive Global State
let globalState = await readAppGlobalState(deployer, creatorAccount.addr, applicationID);
console.log(globalState);
Output:
[ { key: 'Y291bnRlcg==', value: { bytes: '', type: 2, uint: 2 } } ]
[ { key: 'Y291bnRlcg==', value: { bytes: '', type: 2, uint: 3 } } ]
here key 'Y291bnRlcg==' is base64 encoded form of counter
.
To update an application with (new_approval.teal, new_clear.teal), you can use:
const updatedRes = await deployer.updateApp(
"CounterApp", // app name use for checkpoint
creatorAccount,
{}, // pay flags
applicationID,
{
metaType: types.MetaType.FILE,
approvalProgramFilename: "new_approval.teal",
clearProgramFilename: "new_clear.teal",
},
{}
);
console.log("Application Updated: ", updatedRes);
To delete an existing application you can use:
const tx = {
type: types.TransactionType.DeleteApp,
sign: types.SignType.SecretKey,
fromAccount: creatorAccount,
appID: applicationID,
payFlags: {},
appArgs: [],
};
await deployer.executeTx(tx);
Note: Deleting non existing app will throw error.