Every change on the Blockchain requires a transaction. A transaction wraps function calls in the form of clauses. Each clause sends instructions to an address that are encoded as hex string.
To send a transaction you will need to do multiple steps:
- Have Private Key at hand that will sign the transaction
- Encode the function calls into data calls
- Calculate how much gas the transaction will cost
- Build a transaction object with all the previous information
- Sign the transaction
- Send the transaction to the network
The instructions for executing a function on the blockchain needs to be encoded in a certain way.
There are different functions to help create the right format,
one is the callFunction that will call increment, a function of the smart contract available a the given address:
const clauses = [
Clause.callFunction(
'0x8384738c995d49c5b692560ae688fc8b51af1059',
new ABIFunction({
name: 'increment',
inputs: [],
outputs: [],
constant: false,
payable: false,
type: 'function',
})
),
];A clause can also send VET in the same action. Check the type definition to also learn more about the internals.
While reading on the blockchain is free, writing requires to pay the so-called gas fees to cover the cost of the transaction. Gas is paid in VTHO, the secondary token on VeChain.
To calculate the right amount of gas for your transaction, you can use estimateGas.
const gasResult = await thor.transactions.estimateGas(clauses, senderAddress);{% hint style="info" %} If you expect your contracts to have different results based on the sender, you can also pass in the sender address as optional second parameter. {% endhint %}
Once you have instructions + costs, you'll wrap them together into a transaction object
with buildTransactionBody.
const txBody = await thor.transactions.buildTransactionBody(
clauses,
gasResult.totalGas
);{% hint style="info" %} There are several options that can optionally be passed as third argument to enable fee delegation, dependency on other transactions, priority and an expiration. You will learn more about them in other sections. {% endhint %}
Once a transaction is built, it needs to be signed by an entity that will execute all the code. This also makes the origin verifiable.
It is a four steps process, of getting a signer first:
const wallet = new ProviderInternalBaseWallet(
[{ privateKey, address: senderAddress }]
);
const provider = new VeChainProvider(
// Thor client used by the provider
thorClient,
// Internal wallet used by the provider (needed to call the getSigner() method)
wallet,
// Enable fee delegation
false
);
const signer = await provider.getSigner(senderAddress);And using the signer to sign the transaction:
const rawSignedTx = await signer.signTransaction(tx, privateKey);signTransaction returns the fully signed transaction that can already be published using a POST request to the
/transactions endpoint of a VeChain node:
await fetch(`${nodeUrl}/transactions`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
raw: rawSignedTx,
}),
})For submission by SDK, the raw hex string needs to be restored into a transaction object:
const signedTx = Transaction.decode(
HexUInt.of(rawSignedTx).bytes,
true
);The signed transaction can be published to the network using sendTransaction,
which will post the data to the connected node:
const sendTransactionResult = await thor.transactions.sendTransaction(signedTx);sendTransaction returns a transaction id that can be used to track the status of the newly published transaction. waitForTransaction will resolve with the full receipt as soon as the result is available:
const txReceipt = await thor.transactions.waitForTransaction(
sendTransactionResult.id
);{% embed url="https://stackblitz.com/edit/vechain-academy-build-transaction-example?embed=1&file=index.mjs&hideExplorer=1&hideNavigation=1&view=editor" %}
Using executeTransaction the signed transaction will directly be published to the network
const tokenContract = '0x5ef79995fe8a89e0812330e4378eb2660cede699'; // B3TR
const amount = 1.234 * 10**18; // 1.234 B3TR
const transferAbi = new ABIFunction("function transfer(address to, uint256 value) public returns (bool)")
const transactionResult = await thorClient.transactions.executeTransaction(
signer,
tokenContract,
transferAbi,
[receiverAddress,amount]
)If your transaction is delegated, you need to enable the fee delegation on your provider,
const provider = new VeChainProvider(
// Thor client used by the provider
thorClient,
// Internal wallet used by the provider (needed to call the getSigner() method)
wallet,
// Enable fee delegation
true
);and the delegationUrl must be provided.
const transactionResult = await thorClient.transactions.executeTransaction(
signer,
tokenContract,
transferAbi,
[receiverAddress,amount],
{delegationUrl:'https://sponsor-testnet.vechain.energy/by/441'}
){% embed url="https://stackblitz.com/edit/vechain-academy-transfer-token?embed=1&file=index.mjs&hideExplorer=1&hideNavigation=1&view=editor" %}