Tutorial

Transferring an Asset

This example demonstrates how Alice can define a new asset, issue units of that asset to herself, and transfer some units of the asset to her friend Bob. Alice and Bob need to create keys first. If you've reviewed the previous examples, the code below should look very familiar.

console.log('Creating a keystore for Alice...');
const ALICE_PASSWORD = 'aliceisthebest';
const aliceKeyStore = new KeyStore.KeyStore(ALICE_PASSWORD);
const aliceMasterKey = aliceKeyStore.genMasterKey(ALICE_PASSWORD);
const aliceKeyPair = aliceKeyStore.genKeyPair(aliceMasterKey, 'Alice');

console.log('Creating a keystore for Bob...');
const BOB_PASSWORD = 'bobisthebest';
const bobKeyStore = new KeyStore.KeyStore(BOB_PASSWORD);
const bobMasterKey = bobKeyStore.genMasterKey(BOB_PASSWORD);
const bobKeyPair = bobKeyStore.genKeyPair(bobMasterKey, 'Bob');

In practice, Alice and Bob would never define their keys in the same client environment. Exposing your secret key to users is unsafe!

Alice will now define an new asset.

const network = new Network.Network(PROTOCOL, HOST, QUERY_PORT, SUBMISSION_PORT, LEDGER_PORT);
const tokenCode = Ledger.random_asset_type();
const memo = 'this is a test asset';
const assetRules = Ledger.AssetRules.new();
let blockCount = BigInt((await network.getStateCommitment())[1]);
const definitionTransaction = Ledger.TransactionBuilder
  .new(blockCount).add_operation_create_asset(aliceKeyPair, memo, tokenCode, assetRules).transaction();

await network.submitTransaction(definitionTransaction);

console.log('Asset created.');

Alice will issue a few units of the asset to herself.

await Utils.sleep(4);
blockCount = BigInt((await network.getStateCommitment())[1]);
const zeiParams = Ledger.PublicParams.new();
const seqID = BigInt(await network.getIssuanceNum(tokenCode));
const amount = BigInt(5);
const confidential = false;
const issueTxn = Ledger.TransactionBuilder.new(blockCount)
  .add_basic_issue_asset(aliceKeyPair, tokenCode, seqID, amount, confidential, zeiParams).transaction();

const handle = await network.submitTransaction(issueTxn);

Alice can now construct a TransferAsset operation using Findora-Wasm's TransferOperationBuilder. First, Alice needs to fetch the asset record that she wishes to transfer. Included in the transaction status is a list of TXOSIDs of asset records created by that transaction.

await Utils.sleep(4);
const status = await network.getTxnStatus(handle);
const utxoSid = status.Committed[1][0];
const utxo = await network.getUtxo(utxoSid);
const assetRecord = Ledger.ClientAssetRecord.from_json(utxo.utxo);

Alice must also fetch the owner memo of the record she wishes to send. An owner memo contains data that allows record owners to decrypt confidential records. In this case, the asset record Alice is attempting to send isn't confidential, so the owner memo isn't very useful. We can set it to undefined.

const ownerMemo = undefined;

Alice can now construct her transfer operation. Alice will send most of the units in the asset record she has issued to Bob, but return a few to herself. The input and output amounts must be completely balanced. Alice must also sign the operation with her private key. In general, all owners of input records of transfers must sign the transfer.

const transferAmount = utxo.utxo.record.amount.NonConfidential;
const txoRef = Ledger.TxoRef.absolute(BigInt(utxoSid)); // Reference to the TXO SID being transferred. Absolute references are used for TXOs that have already been committed to the ledger while relative references point to records created within transactions.
const transferOp = Ledger.TransferOperationBuilder.new()
  .add_input_no_tracking(txoRef,
    assetRecord,
    ownerMemo,
    aliceKeyPair,
    BigInt(transferAmount - 2))
  .add_output_no_tracking(BigInt(transferAmount - 2), bobKeyPair.get_pk(), tokenCode)
  .add_output_no_tracking(BigInt(2), aliceKeyPair.get_pk(), tokenCode)
  .create()
  .sign(aliceKeyPair)
  .transaction();

Alice can now add her newly constructed transfer operation to a transaction builder, and submit the transaction to the ledger.

blockCount = BigInt((await network.getStateCommitment())[1]);
const transferTxn = Ledger.TransactionBuilder.new(blockCount)
  .add_transfer_operation(transferOp)
  .transaction();
await network.submitTransaction(transferTxn);

Bob can fetch his list of owned TXOSIDs and see that he has recieved a new asset.

const base64PubKey = Ledger.public_key_to_base64(bobKeyPair.get_pk());
await Utils.sleep(4);
const bobOwnedSids = await network.getOwnedSids(base64PubKey);
assert(bobOwnedSids.length == 1);