Advanced SDK Concepts
Creating a prepared transaction
The FIO SDK offers some helper functions for integrators who wish to separate the creation of the transaction from the sending of the transaction. This is helpful if you want to have the ability to inspect the transaction prior to submitting it or if you want to have the ability to re-submit the same transaction in cases where the initial transaction send fails.
In the Typescript SDK, when preparing a transaction, setting setSignedTrxReturnOption
to true
will cause calls to the genericAction
action method to return a prepared transaction without submitting it to the chain. Once the prepared transaction has been captured, it can then be sent to the chain using executePreparedTrx
.
The following is an example using setSignedTrxReturnOption
and executePreparedTrx
to create and send a prepared transaction. The full working example can be found in the fiosdk_typescript-examples repo.
const transferFioPreparedTxn = async () => {
const user = new FIOSDK(
privateKey,
publicKey,
baseUrl,
fetchJson
);
let preparedTrx;
user.setSignedTrxReturnOption(true);
preparedTrx = await user.genericAction('pushTransaction', {
action: 'trnsfiopubky',
account: 'fio.token',
data: {
payee_public_key: payeeKey,
amount: amount,
max_fee: max_fee,
tpid: ''
}
});
const result = await user.executePreparedTrx('transfer_tokens_pub_key', preparedTrx);
user.setSignedTrxReturnOption(false);
};
Offline signing of transactions
For integrators who want to limit access to FIO private keys, the FIO SDK offers methods that separate the serialization and signing of transactions.
This fiosdk_typescript example demonstrates how to use the FIO Javascript SDK to enable offline signing of transactions. First, it creates a serialized transaction without requiring any FIO keys. It then passes the serialized transaction to the sign method to generate the signature.
const {FIOSDK } = require('@fioprotocol/fiosdk')
fetch = require('node-fetch')
const createHash = require('create-hash');
const properties = require('./properties.js')
const fetchJson = async (uri, opts = {}) => {
return fetch(uri, opts)
}
const baseUrl = properties.server + '/v1/'
const privateKey = properties.privateKey,
publicKey = properties.publicKey,
payeeKey = '', // FIO Public Key of the payee
amount = 1000000000,
max_fee = 100000000000
const main = async () => {
user = new FIOSDK(
'',
'',
baseUrl,
fetchJson
)
const chainData = await user.transactions.getChainDataForTx();
const transaction = await user.transactions.createRawTransaction({
action: 'trnsfiopubky',
account: 'fio.token',
data: {
payee_public_key: payeeKey,
amount: amount,
max_fee: max_fee,
tpid: ''
},
publicKey,
chainData,
});
const { serializedContextFreeData, serializedTransaction } = await user.transactions.serialize({
chainId: chainData.chain_id,
transaction,
});
// Pre-compute transaction ID
const txnId = createHash('sha256').update(serializedTransaction).digest().toString('hex');
const signedTransaction = await user.transactions.sign({
chainId: chainData.chain_id,
privateKeys: [privateKey],
transaction,
serializedTransaction,
serializedContextFreeData,
});
const result = await user.executePreparedTrx('transfer_tokens_pub_key', signedTransaction);
}
main();
Pre-compute transaction ID
Pre-computing the transaction ID for a FIO transaction is useful for integrators wanting to confirm transactions on the FIO chain. To calculate the transaction_id
for a FIO transaction prior to sending it to the blockchain, you perform a SHA-256 hash of the packed_trx
.
For example, here is a typical transaction you might pass to push_transaction
:
txn: { signatures:
[ 'SIG_K1_Km62xn9thv3LYQv356PJMj9bP5ZwHRWZ2CgGan75sbcMfeZ7gtLrD1yukDiLgmdPVLZV3tpH4FW4A96ZKs5U42uAsnuyDb' ],
compression: 0,
packed_context_free_data: '',
packed_trx:
'1958cb60285764a002ba0000000001003056372503a85b0000c6eaa6645232013059393021cea2d800000000a8ed32326812656274657374314066696f746573746e657402034243480342434818626974636f696e636173683a617364666173646661736466044441534804444153481764617368616464726573736173646661736466617364660046c323000000003059393021cea2d80000' }
The packed_trx
field contains the serialized transaction. The "Offline signing of transactions" code above provides an example of how to pre-compute the transaction ID from this serialized transaction.
const txnId = createHash('sha256').update(serializedTransaction).digest().toString('hex');
You can also manually view the pre-computed transaction ID by plugging the packed_trx
hex into the Binary hash field of a calculator and checking the SHA-256 result.
Pre-compute transaction ID lookup example
Using fiojs to package and send transactions
The FIO SDK wraps the fiosjs library to provide convenience methods for creating and submitting transactions to the FIO chain. Integrators who want to limit the number of third party libraries they embed or who want lower level access to FIO functionality in their applications can access the fiojs
library directly in their applications.
This fiojs example demonstrates how to use the fiojs
library to create a trnsfiopubky
transaction on the FIO chain:
const { Fio } = require('@fioprotocol/fiojs');
const { TextEncoder, TextDecoder } = require('text-encoding');
const fetch = require('node-fetch');
const properties = require('./properties.js')
const httpEndpoint = properties.server
const privateKey = properties.privateKey,
publicKey = properties.publicKey,
account = properties.account,
payeeKey = '', // FIO Public Key of the payee
amount = 1000000000,
maxFee = 100000000000
const fiojsTrnsfiopubky = async () => {
info = await (await fetch(httpEndpoint + '/v1/chain/get_info')).json();
blockInfo = await (await fetch(httpEndpoint + '/v1/chain/get_block', {body: `{"block_num_or_id": ${info.last_irreversible_block_num}}`, method: 'POST'})).json()
chainId = info.chain_id;
currentDate = new Date();
timePlusTen = currentDate.getTime() + 10000;
timeInISOString = (new Date(timePlusTen)).toISOString();
expiration = timeInISOString.substr(0, timeInISOString.length - 1);
transaction = {
expiration,
ref_block_num: blockInfo.block_num & 0xffff,
ref_block_prefix: blockInfo.ref_block_prefix,
actions: [{
account: 'fio.token',
name: 'trnsfiopubky',
authorization: [{
actor: account,
permission: 'active',
}],
data: {
payee_public_key: payeeKey,
amount: amount,
max_fee: maxFee,
tpid: '',
actor: account
}
}]
};
abiMap = new Map()
tokenRawAbi = await (await fetch(httpEndpoint + '/v1/chain/get_raw_abi', {body: `{"account_name": "fio.token"}`, method: 'POST'})).json()
abiMap.set('fio.token', tokenRawAbi)
var privateKeys = [privateKey];
const tx = await Fio.prepareTransaction({
transaction,
chainId,
privateKeys,
abiMap,
textDecoder: new TextDecoder(),
textEncoder: new TextEncoder()
});
pushResult = await fetch(httpEndpoint + '/v1/chain/push_transaction', {
body: JSON.stringify(tx),
method: 'POST',
});
json = await pushResult.json();
if (json.type) {
console.log('Error: ', json.fields[0].error);
} else {
console.log('Success. Transaction ID: ', json.transaction_id)
}
};
fiojsTrnsfiopubky();
Updated about 1 year ago