We request all of the records for the program and then filter the spent records to get the records representing the NFTs.
2. Request an Execution of the authorize Function
const aleoTransaction = Transaction.createTransaction(
publicKey!,
WalletAdapterNetwork.Testnet,
NFTProgramId,
'authorize',
[nftRecords[0], '101u64'], // This should be a random number in production, generated on the back end of a DApp
0,
);
const txId = await (wallet?.adapter as LeoWalletAdapter).requestExecution(aleoTransaction);
The authorize function takes two parameters: the NFT record and a nonce . The nonce should be randomly generated on the backend of the application to prevent the user from choosing a specific random number. This prevents this authorization being used in replay attacks.
3. Get the execution
const newStatus = await (wallet?.adapter as LeoWalletAdapter).transactionStatus(txId);
if (newStatus === 'Finalized') {
const execution = await (window as any).leoWallet.getExecution(
transactionId
);
console.log(execution);
}
The execution takes some time to generate so on some interval, check the status of the transaction. When it's Finalized, you can get the execution.
In general, it should be faster than a real transaction as no fee proof is generated.
Additionally, you want to ensure the execution.global_state_root exists on chain and that the serial number execution.transitions[0].inputs[0].id does not exist on chain. It's extremely important to verify these two pieces of data. Otherwise, malicious users will be able to authorize using either a fraudulent NFT or an already spent NFT.
Together, these checks ensure that a valid record exists on-chain and it has not been spent all without paying any fees.
Additionally, by saving the serial number of the record, DApps can automatically invalidate the user session if the serial number appears on chain.
Some tips:
To get the verifying key for a given function, you can use the following helper methods:
To check if the serial number has been spent, you can use the following method from the Demox RPC:
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({
"jsonrpc": "2.0",
"id": 1,
"method": "serialNumbers",
"params": {
"serialNumbers": [
"5228865531938159265544362265315800751823578819356920124710614945837981163945field",
]
}
});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("http://localhost:3000", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
The execution from the authorize function cannot be broadcast successfully. It will fail every time. To safely prevent all broadcasting attempts by potentially malicious DApps, ensure the finalize statement contains an always fail assertion. For example:
finalize authorize(
)
{
// fails on purpose, so that the nft is not burned.
assert_eq(0u8, 1u8);
}