Sponsored Transactions Front End and Back End#
The goal of this part of the tutorial is to set up the front end and the back end locally and to understand the two flows that the dApp provides.
You can explore the hosted sponsored transaction service. The sponsored transaction service consists of a front end and a back end. You can start your own service by following the instructions in the front end README.md file and then the instructions in the back end README.md file. Alternatively, follow the steps in the README.md file to deploy the Docker container. The docker container will set up the front end as well as the back end.
Note
Comprehensive instructions on how to set up the Concordium Wallet for Web, create an account in the Concordium Wallet for Web, get some testnet CCD and run a local front end can be found in wCCD frontend-set-up section.
The front end supports the following two flows with the Concordium Wallet for Web (or Concordium Wallet for Mobile that uses WalletConnect):
Create the message of a sponsored
updateOperator
transaction => Sign it with the wallet => Submit the signature to the/submitUpdateOperator
back-end endpoint.Mint an NFT to your wallet account => Create the message of a sponsored
transfer
transaction => Sign it with the wallet => Submit the signature to the/submitTransfer
back-end endpoint.
The back end is a server that exposes two endpoints:
POST /submitUpdateOperator
POST /submitTransfer
After submitting the signature to the /submitUpdateOperator
or the /submitTransfer
endpoint,
the back end creates a sponsored transaction and submits it on-chain to the permit
function in the smart contract {index: SMART_CONTRACT_INDEX, subindex: 0}.
The deployed smart contract code can be found here.
After the transaction is sent to the blockchain node, the back end returns the transaction hash to the front end.
The back-end server has to have access to a blockchain node and an account (with its associated private key) that is funded with some CCD to submit the sponsored transaction to the chain. The back-end wallet will pay for the transaction fees on behalf of the user.
Schemas#
The front end uses several json schemas in the ./frontend/src/constants.ts
file such as:
export const UPDATE_OPERATOR_SCHEMA = 'EAEUAAIAAAAGAAAAdXBkYXRlFQIAAAAGAAAAUmVtb3ZlAgMAAABBZGQCCAAAAG9wZXJhdG9yFQIAAAAHAAAAQWNjb3VudAEBAAAACwgAAABDb250cmFjdAEBAAAADA';
export const TRANSFER_SCHEMA = 'EAEUAAUAAAAIAAAAdG9rZW5faWQdAAYAAABhbW91bnQbJQAAAAQAAABmcm9tFQIAAAAHAAAAQWNjb3VudAEBAAAACwgAAABDb250cmFjdAEBAAAADAIAAAB0bxUCAAAABwAAAEFjY291bnQBAQAAAAsIAAAAQ29udHJhY3QBAgAAAAwWAQQAAABkYXRhEAEC';
export const SERIALIZATION_HELPER_SCHEMA = 'FAAFAAAAEAAAAGNvbnRyYWN0X2FkZHJlc3MMBQAAAG5vbmNlBQkAAAB0aW1lc3RhbXANCwAAAGVudHJ5X3BvaW50FgEHAAAAcGF5bG9hZBABAg==';
export const NONCE_OF_PARAMETER_SCHEMA = 'FAABAAAABwAAAHF1ZXJpZXMQARQAAQAAAAcAAABhY2NvdW50Cw==';
export const NONCE_OF_RETURN_VALUE_SCHEMA = 'FAEBAAAAEAEF';
export const MINT_PARAMETER_SCHEMA = 'FAABAAAABQAAAG93bmVyFQIAAAAHAAAAQWNjb3VudAEBAAAACwgAAABDb250cmFjdAEBAAAADA==';
These schemas can be generated in the smart contract folder with the command below.
cargo concordium build --schema-json-out ./
This command creates a file with the JSON schema of your smart contract in your current directory.
Explore your public key#
You can export your keys file from the Concordium Wallet for Web as follows:



In this exported file, you will find a verifyKey
(your public key
), which should be
identical to the displayed public key
at the front end. The front end also displays your next nonce associated with your account.
This nonce is initially 0 and increases every time a successful sponsored transaction
is recorded in the smart contract that was signed by your account.
Note
The nonce is used as a method of preventing replay attacks by ensuring that a signed message by your account can not be reused a second time.
Submit a sponsored updateOperator
transaction#

The GIF shows an example of input parameters that can be used to successfully execute a sponsored updateOperator
transaction.
The transaction should be successfully submitted by the back end to the blockchain which can be checked on the
blockchain explorer by clicking the transaction hash displayed at the front end.
When exploring the code, you will realize that the input parameter to the permit
function
and the message
that is signed in the wallet are rather complex. For example, the message
for the sponsored updateOperator
transaction is as follows:
const operatorAction = addOperator
? {
Add: [],
}
: {
Remove: [],
};
const updateOperator = [
{
operator: {
Account: [operator],
},
update: operatorAction,
},
];
const payload = serializeTypeValue(updateOperator, toBuffer(UPDATE_OPERATOR_SCHEMA, 'base64'));
const message = {
contract_address: {
index: Number(process.env.SMART_CONTRACT_INDEX),
subindex: 0,
},
nonce: Number(nonce),
timestamp: expiryTimeSignature,
entry_point: 'updateOperator',
payload: Array.from(payload),
};
Because the schema is included in the smart contract, you can use the format returned by the below command to get a
template for how to create the message
and input parameters for the functions in the smart contract:
cargo concordium build --schema-template-out -
The above command will print the schema template to the console. The command has to be executed in the smart contract folder.
Submit a sponsored transfer
transaction#

The GIF shows an example of input parameters that can be used to successfully execute a sponsored transfer
transaction.
The transaction should be successfully submitted by the back end to the blockchain which can be checked on the
blockchain explorer by clicking the transaction hash displayed at the front end. You can also check that your to
account is now the owner of the token_id
.
When exploring the code, you will realize that the input parameter to the permit
function
and the message
that is signed in the wallet are rather complex. For example, the message
for the sponsored transfer
transaction is as follows:
const transfer = [
{
amount: '1',
data: [],
from: {
Account: [from],
},
to: {
Account: [to],
},
token_id: tokenID,
},
];
const payload = serializeTypeValue(transfer, toBuffer(TRANSFER_SCHEMA, 'base64'));
const message = {
contract_address: {
index: Number(process.env.SMART_CONTRACT_INDEX),
subindex: 0,
},
nonce: Number(nonce),
timestamp: expiryTimeSignature,
entry_point: 'transfer',
payload: Array.from(payload),
};
Because the schema is included in the smart contract, you can use the format returned by the below command to get a
template on how to create the message
and input parameters for the functions in the smart contract:
cargo concordium build --schema-template-out -
The above command will print the schema template to the console. The command has to be executed in the smart contract folder.
Note
The NFT smart contract used in the hosted demo app is meant for exploring sponsored transactions.
To ease this process, the mint function can be called by any account to mint a token.
Token_ids
are stored in the smart contract in little-endian order and a counter is used to
increase the token_id
by 1 every time the mint function is invoked. The wallet displays the token_id
bytes in a hex string.
In other words, the first token minted will have the token_id=01000000
,
the second token minted will have 02000000
, …, 0f000000
, 10000000
, …, ff000000
, 00010000
, … .
You can look up your minted token_id
in the Concordium Wallet for Web by getting the smart contract
index as seen below and searching for tokens in that contract that are owned by your account.







Note
Every token_id
would usually have
its own metadata associated with it by e.g. appending the TOKEN_METADATA_URL
with the token_id
. But to simplify the
metadata management for Concordium, the NFT smart contract used in the hosted demo app returns
the same metadata hash (TOKEN_METADATA_URL
) for all token_ids
.
The NFT picture in the below URL changes every time you access https://picsum.photos/40/40
. Hence, the Concordium Wallet for Web depicts random new pictures
for your minted NFT.
const TOKEN_METADATA_URL: &str = "https://gist.githubusercontent.com/abizjak/ab5b6fc0afb78acf23ee24d979eb7639/raw/7c03f174d628df1d2fd0dc8cffb319c89e770708/metadata.json";