You are currently viewing Pre-authorized transaction on the Stellar network

Pre-authorized transaction on the Stellar network

The Stellar network is known for fast transaction times and low network fees. One feature that is less prominent is its ability to handle pre-authorized transactions. 

On Stellar you can create transactions with certain conditions to ensure that it will only be processed by the network in the future. 

This is made possible by the multi-signature feature of an account. In addition to an account’s private key, It can hold other signature types. One of those types is the hash of a future transaction. 

In order to create a pre-authorized transaction, all we need to do is to create a future transaction with the right sequence number and add its hash as a signer on the account.

This is such a powerful feature. It allows transactions that meet the criteria to be submitted without ever knowing the secret key of the account. Pretty neat! 

For example, this can be used to send tokens to users after a token sale. The transactions can be pre-authorized before the sale and sent out after the sale.

Multi-signatures and pre-authorized transactions are key features when building smart contracts on Stellar.

Let’s see how we can build pre-authorized Stellar transactions in Javascript.

What we will be building?

We will be making a pre-authorized payment between two accounts; let’s call these sender and receiver. Both accounts will start out with the same balance, then we will create a payment transaction that will be submitted in the future from sender to receiver. This payment will already be authorized and will not need the sender to sign it again when it is being submitted. Let’s get started.

Requirements 

Let’s start off by creating a folder to store our code

mkdir preauth-tx 
cd preauth-tx

Next we will initialize a npm and a git repository

npm init 
git init

Once we are done setting up npm and git, we can download the required packages

npm install --save stellar-sdk node-fetch 

This will install the Stellar Javascript SDK and the node-fetch module.

Now that we have completed all the setup required, we can start writing some code.

Generate Stellar key pairs

We will generate 2 Stellar accounts to represent the sender and receiver.

const Stellar = require('stellar-sdk');
const sender = Stellar.Keypair.random();
const receiver = Stellar.Keypair.random();

This will create random public and private keypairs for the sender and receiver

Fund accounts on testnet

Now that we have the keys, we need to fund the accounts for them to be active on the network. We will use the friendbot to fund each account with 10,000XLM on the test network.

...
const fetch = require('node-fetch');
 try {
   const response = await fetch(
     `https://friendbot.stellar.org?addr=${sender.publicKey()}`,
   );
   return response.json();
 } catch (error) {
   console.log(error);
 }

Here we initialize the fetch module and create a GET request to the friendbot URL passing the public key of the sender as the addr query parameter.  This is also repeated for the receiver.

Get sequence number and balance

Next we take note of the sequence number and balances of the account. To do this, we load the account details from the network.

try {
const horizonTestnet = new Stellar.Server(
 'https://horizon-testnet.stellar.org',
);
 
   const senderDetail = await horizonTestnet.loadAccount(sender.publicKey();
   console.log(senderDetail); // displays the account object
 } catch (error) {
   console.log(error);
 }

senderDetail is an object that contains amongst other things,  the sequence number and the balances.

Sender:  { sequence: '5290136988286976', balance: '10000.0000000' }
Receiver:  { sequence: '5290141283254272', balance: '10000.0000000' }

Create payment transaction two(2) sequence numbers in the future

Let’s create a pre-authorized payment that will only be valid for N+2 sequence number; where N is the current sequence number.

   const paymentOp = Stellar.Operation.payment({
     destination: receiver.publicKey(),
     asset: Stellar.Asset.native(),
     amount: '5000',
     source: sender.publicKey(),
   });
 
   // goal is to increment the sequence number by 2, but we only call
   // incrementSequenceNumber() once here because the transaction builder also
   // auto increments the sequence number.
   senderAccount.incrementSequenceNumber();
   console.log(
     'Sender sequence number incremented to: ',
     senderAccount.sequenceNumber(),
   );

   // build the future tx with incremented sequence number
   const futureTx = new Stellar.TransactionBuilder(senderAccount, {
     fee: Stellar.BASE_FEE,
     networkPassphrase: Stellar.Networks.TESTNET,
   })
     .addOperation(paymentOp)
     .setTimeout(Stellar.TimeoutInfinite)
     .build();

Quite a number of things happening above. Let’s break it down.

  • First a payment operation is created that will send 5,000 XLM from sender to receiver.
  • Next we increment the sequence number once. Notice that we don’t increase it twice, because it is increased automatically when we build a transaction as well.
 Sender sequence number incremented to:  5290136988286977
 
  • Lastly, we add the payment operation to a new transaction and build it.

We can now take the hash futureTx and add it to the sender as a signer.

Save hash of transaction on account

   const setOptions = Stellar.Operation.setOptions({
     signer: {
       preAuthTx: futureTx.hash(),
       weight: 1,
     },
   });
 
   // get the sender account with the current sequence number.
   let currentSenderDetail = await horizonTestnet.loadAccount(sender.publicKey());
 
   const addSignerTx = new Stellar.TransactionBuilder(currentSenderDetail, {
     fee: Stellar.BASE_FEE,
     networkPassphrase: Stellar.Networks.TESTNET,
   })
     .setTimeout(Stellar.TimeoutInfinite)
     .addOperation(setOptions)
     .build();
 
   addSignerTx.sign(Stellar.Keypair.fromSecret(sender.secret()));
   await horizonTestnet.submitTransaction(addSignerTx);
  • We create a setOptions operation to add the hash of futureTx as a signer.
  • We reload the account detail of the sender again. This is done in order to get the current sequence number. If we try to submit it with the incremented sequence, it will fail.
  • Sign and submit the transaction.

If we now load the sender account again, we should be able to see the hash of futureTx as a signer.

At this point, we have pre-authorized the future payment transaction. If you look at the signers on the sender you should see something like this.

[ {
 weight: 1,
  key: 'GAOJWGFFJHUPCEMSUFYY72SVYVIZANNIYYPH34ASYDZJG2OT6L7DDGVI',
   type: 'ed25519_public_key' 
},
{ weight: 1,
   key: 'TCX3MPKGWXETZ5AEWPBS4QKZMXEZGFO5TFOKBFA4ON3JFJP3VFC6SZ6S',
   type: 'preauth_tx' } ] }

All we need to do now is submit the transaction with the right details and sequence number.

Submit pre-authorised transaction

Remember that we wanted the transaction to be 2 sequence numbers ahead. Adding the signer consumed one sequence number so we can now submit our pre-authorized transaction.

   await horizonTestnet.submitTransaction(futureTx); 

That’s all we need to do. We just submit the transaction that is already built and the payment will be made. 

To confirm that everything worked as expected, we can check the following: 

  • The hash should have been removed as a signer on the sender.
  • The balance of the receiver should be 15,000XLM 
{ sequence: '5290141283254272', balance: '15000.0000000' }

We have seen how to create pre-authorized transactions on Stellar using the Javascript SDK. When coupled with other Stellar protocol features, it can be a powerful tool for implementing conditional transactions.

You can find the source code on github. Feel free to reach out with any questions or comments.

Have any questions, want to share your thoughts or just say Hi? I’m always excited to connect! Follow me on Twitter or LinkedIn for more insights and discussions. If you’ve found this valuable, please consider sharing it on your social media. Your support through shares and follows means a lot to me! 

Resources

Leave a Reply