JS library to locally forge and unforge reveal and spend operations for Tezos

While working on the Tezos integration for our wallet AirGap, we ran into a couple of problems because we realized that currently no javascript library exists that allows for local forging and unforging of operations.

In Tezos, all transactions consist of so-called operations. These operations have different contents, but to pack them into an easily-transferrable string, they are "forged" into a single, base58-check encoded string. Currently, available wallets use a function from tezos-nodes to forge their operations (see update below). This means however, that you have to trust the node to do this correctly and they could for example, simply switch the receiving address of your transaction.

AirGap Vault is completely offline, and currently extracts all details from transactions it signs out of the transaction itself. This is important, to make sure that not only the signing process is correct — But rather the right thing is signed.

Building the solution

Due to the limitations of existing libraries, we went ahead and built a solution ourselves. The implemented forging is not in a library on its own, but baked (pun intended) into our coin-agnostic “airgap-coin-lib”, which allows for easy preparation, signing and broadcasting of transactions.

The library currently only supports reveal and spend operations. In the next few weeks we will also add originate and delegate operations.

Here is a small snippet of how we forge operations:

const spendOperation: TezosSpendOperation = {

kind: TezosOperationType.TRANSACTION,

fee: fee.toFixed(),

gas_limit: '10100', // taken from eztz

storage_limit: receivingBalance.isZero() ? '300' : '0', // taken from eztz

amount: values[0].toFixed(),

counter: counter.toFixed(),

destination: recipients[0],

source: this.getAddressFromPublicKey(publicKey)

} operations.push(spendOperation) try {

const tezosWrappedOperation: TezosWrappedOperation = {

branch: branch,

contents: operations

} const binaryTx = this.forgeTezosOperation(tezosWrappedOperation) return { binaryTransaction: binaryTx }

} catch (error) {

console.warn(error.message)

throw new Error('Forging Tezos TX failed.')

}

For those interested in the technical part, you can take a look how the forging happens in-depth here: https://github.com/airgap-it/airgap-coin-lib/blob/75b5f5f706d7b7b5de905d165e744c75bf0f7d9a/lib/protocols/TezosProtocol.ts#L513

If you install the airgap-coin-lib using NPM, you can use the functionality yourself - But you can also take a look at how we did it and copy the snippets you need. If there is a need, we will consider splitting it into its own module.

We also just released an update of our apps that includes Tezos support and we would be happy to get your feedback!

Our goal is to make the Tezos ecosystem more secure and accessible.

Update

Some projects like Kukai Wallet are aware of this issue and use a remote forging and local verification approach for mitigation.

Interested in AirGap? Stay in touch.

Telegram | GitHub | Website | Twitter | Reddit