📤Sending Transaction
The flow from crafting transactions to sending transactions is simple using the SDK. The process is divided into four steps:
Creating a
Transaction
by providing a simpleTransactionRequest
.Signing the created
Transaction
.Proving the signed
Transaction
to get the shielded transaction (ZTransaction
).Sending the proved transaction.
Creating Transaction
As mentioned before, creating transactions in zkFi can be done by creating TransactionRequest
objects. Below is a transaction request to send 100 of WETH (asset id 65537) to vitalik.eth
and pay gas fees in WETH:
import { Transaction } from '@zkfi-tech/transaction';
import { TransactionType, TransactionRequest } from '@zkfi-tech/shared-types';
const txReq: TransactionRequest = {
type: TransactionType.TRANSFER,
assetIds: 65537,
values: [100],
feeAssetId: 65537,
to: 'vitalik.eth',
payload: '0x'
}
const tx: Transaction = await zkfi.createTransaction(txReq);
Signing Transaction
To authorize the created Transaction
i.e tx
above it needs to be signed by the owner. This is as simple as:
const signedTx: Transaction = await zkfi.signTransaction(tx);
Proving Transaction
Now, we need to prove this signed transaction to include ZK proof in it & hide all the sensitive fields. This too, is as simple as:
const ztx: ZTransaction = await zkfi.proveTransaction(signedTx);
ZTransaction
The zkfi.proveTransaction(signedTx)
returns a ZTransaction
object which represent a shielded transaction. This is the object which represents a transaction to be execute on the Labyrinth protocol and hence is a very important piece. Here's what it contains:
struct ZTransaction {
ZTransactionType txType;
uint16 revokerId;
uint256 addressTreeRoot;
uint256 commitmentTreeRoot;
uint256 feeData;
uint248[] pubAssets;
uint256[] nullifiers;
uint256[] commitments;
bytes proof;
bytes[] noteMemos;
bytes assetMemo;
bytes complianceMemo;
bytes targetData;
bytes refundData;
}
txType
: Type of transaction- DEPOSIT, TRANSFER, WITHDRAW, CONVERT
revokerId
: Id of revoker used for this transaction. This will enable the selected revoker to decrypt this transaction details, but only once the deanonymisation request have been approved by the guardian network.
addressTreeRoot
: Recent merkle root of address tree.
commitmentTreeRoot
Recent merkle root of commitment tree.
feeData
Packed fee data (20-byte paymaster address + 12-byte fee value)
pubAssets
An array of encoded bytes (3 bytes assetId + 28 bytes asset value) for publicly spent assets. If the shielded transaction is coming through the bundler, the fee asset in which the bundler fee is paid to the paymaster, is the first element in this array.
nullifiers
Revealed nullifiers of input/spent notes.
commitments
New commitments of output notes to be inserted in the commitment merkle tree. This merkle tree holding all note commitments is used to create a proof of inclusion by a user attempting to spend a note on the Labyrinth protocol.
proof
Abi encoded ZK proof used to prove the validity of the transaction along with obscuring sensitive transaction details, thus enforcing privacy!
noteMemos
Memos for output notes. This is list of encrypted notes' fields for each note and can be decrypted by the owner of the notes.
assetMemo
This is empty for non-TRANSFER transactions. For TRANSFER transactions,
this is encrypted assets using sender's key that were transferred to receiver. These can only be decrypted by the sender.
complianceMemo
Encrypted compliance data that was created using compliance encryption key.
targetData
Target address (first 20-bytes) for withdraw/adapter concatenated with any required payload.
refundData
Refund address (first 32-byte) for public deposit to shielded account concatenated with refund memo.
Sending Transaction
By default, the transaction is crafted by the SDK such that DEPOSIT
transactions are meant to be sent via your public wallet (which is a must for DEPOSIT
since you are sending tokens to contract) and other types are meant to be sent via a bundler, which is recommended for privacy. However, you can override this behavior to send transactions other than DEPOSIT
via the public wallet itself - but it will damage privacy and should be prevented.
The shielded transaction can be used as input to the zkFi on-chain Pool
contract by calling its transact()
method:
function transact(ZTransaction memory ztx) external;
But before passing the TypeScript object ZTransaction
to transact
you have to convert it to input suitable for the contract. Doing so is as simple as calling ztx.toSolidityInput()
.
ZTransaction ztx = zkfi.proveTransaction(signedTx);
const poolContract = ...
const inp = ztx.toSolidityInput();
await poolContract.transact(inp);
Last updated