Sending a Transaction
Once a web application is connected to the Supra network via StarKey, it can prompt the user for permission to send transactions on their behalf.
To send a transaction, you will need to have a valid transaction object. It should look a little like this:
// Example transaction object
const transaction = {
data: '', // optional
from: from_address,
to: to_address, // blank ('') when sending hex-encoded data
value: amount, // blank ('') when sending hex-encoded data
}This plain form — no data — is for sending native SUPRA coin only.
To send a different coin, or call any other Move entry function, pass a hex-encoded, BCS-serialized raw transaction in data instead — see Building raw tx below. to and value are still required fields on the object, just set to '', not omitted.
However, this transaction object needs to be signed using the sender’s private key. This ensures that only the person holding the private key can send transactions from the public address.
To prompt StarKey to send a transaction to the network, use the following code snippet:
const provider = window.starkey?.supra
const transaction = {
data: '',
from: from_address,
to: to_address,
value: amount,
}
const txHash = await provider.sendTransaction(transaction)
console.log('txHash :: ', txHash)sendTransaction(params) — full signature
sendTransaction accepts either a plain native SUPRA coin transfer (as above, data empty) or a pre-built raw transaction in data (hex-encoded, BCS-serialized bytes) for sending any other coin or calling any other Move entry function.
interface SendTransactionParams {
data?: string // optional hex-encoded, BCS-serialized raw tx payload
from: string // sender address, must match the connected account
to: string // recipient address (unused when `data` targets a non-transfer entry function)
value: string // transfer amount, as a string; '' when using raw `data`
chainId: string
options?: {
waitForTransaction: boolean // true: resolve only once the tx completes. false: resolve with hash immediately.
}
}const txHash: string = await window.starkey.supra.sendTransaction({
from: accounts[0],
to: '0x161a497aa19321bce07277674a134deca15817ed87bd85b72d53b4c5dfa9ab6d',
chainId: networkData.chainId,
value: '100000000', // transfer amount
options: {
waitForTransaction: true,
},
})Returns: Promise<string> — the transaction hash.
'0xbdc5016f166f49979fb51dbd0407d7bf561b68eb9c0ad5a2849e01441988b4e3'Building raw tx data
Use this for anything beyond a native SUPRA transfer — sending another coin, or calling any other Move entry function.
createRawTransactionData(rawTxPayload) — deprecated
Deprecated. Use SupraClient.createSerializedRawTxObject below instead. Documented here only to help migrate existing code off it.
// deprecated
const rawTxPayload = [
senderAddr,
senderSequenceNumber,
moduleAddr,
moduleName,
functionName,
functionTypeArgs,
functionArgs,
optionalTransactionPayloadArgs,
]
const data = await window.starkey.supra.createRawTransactionData(rawTxPayload)SupraClient.createSerializedRawTxObject
Build the raw, BCS-serialized transaction with supra-l1-sdk’s SupraClient.createSerializedRawTxObject instead, then pass the result as data. Creates a serialized raw transaction for an entry_function_payload-type tx — under the hood it calls createRawTxObject and BCS-serializes the result.
async createSerializedRawTxObject(
senderAddr: HexString,
senderSequenceNumber: bigint,
moduleAddr: string,
moduleName: string,
functionName: string,
functionTypeArgs: TxnBuilderTypes.TypeTag[],
functionArgs: Uint8Array[],
optionalTransactionPayloadArgs?: OptionalTransactionPayloadArgs,
): Promise<Uint8Array>interface OptionalTransactionPayloadArgs {
maxGas?: bigint
gasUnitPrice?: bigint
txExpiryTime?: bigint
}Parameters:
| Param | Type | Description |
|---|---|---|
senderAddr | HexString | Sender account address |
senderSequenceNumber | bigint | Sender account sequence number |
moduleAddr | string | Target module address |
moduleName | string | Target module name |
functionName | string | Target function name |
functionTypeArgs | TxnBuilderTypes.TypeTag[] | Target function type args |
functionArgs | Uint8Array[] | Target function args |
optionalTransactionPayloadArgs | OptionalTransactionPayloadArgs (optional) | Optional gas/expiry overrides |
Returns: Promise<Uint8Array> — the serialized raw transaction, hex-encode it and pass as data to sendTransaction.
import { BCS, HexString, SupraClient } from 'supra-l1-sdk'
import { TypeTagParser } from 'supra-l1-sdk-core'
const supraClient = await SupraClient.init(rpcUrl)
const receiverAccount = HexString.ensure(toAddress)
const amountInBigInt = BigInt(amount)
const rawTransactionData = await supraClient.createSerializedRawTxObject(
HexString.ensure(connectedAccount),
(await supraClient.getAccountInfo(connectedAccount)).sequence_number,
'0x0000000000000000000000000000000000000000000000000001',
'supra_account',
'transfer_coins',
[new TypeTagParser('0x1::supra_coin::SupraCoin').parseTypeTag()],
[receiverAccount.toUint8Array(), BCS.bcsSerializeUint64(amountInBigInt)],
{
gasUnitPrice: gasUnitPrice ? BigInt(gasUnitPrice) : undefined,
maxGas: maxGasAmount ? BigInt(maxGasAmount) : undefined,
},
)
const rawTxData = Buffer.from(rawTransactionData).toString('hex')
const txHash = await window.starkey.supra.sendTransaction({
data: rawTxData,
from: connectedAccount,
to: '',
value: '',
chainId: networkData.chainId,
options: { waitForTransaction: true },
})For more raw-transaction construction patterns (via SupraClient.createSerializedRawTxObject / createRawTxObject), see the SDK’s own example.ts .
sendAutomationTransaction(params)
Takes the same SendTransactionParams shape as sendTransaction. Used to register an automation task (e.g. a scheduled/recurring entry-function call) rather than a plain transfer. Build the raw payload with SupraClient.createSerializedAutomationRegistrationTxPayloadRawTxObject — the automation-specific registration tx type — then hand the serialized result to this method instead of sendTransaction.
createSerializedAutomationRegistrationTxPayloadRawTxObject(
senderAddr: HexString,
senderSequenceNumber: bigint,
moduleAddr: string,
moduleName: string,
functionName: string,
functionTypeArgs: TxnBuilderTypes.TypeTag[],
functionArgs: Uint8Array[],
automation_max_gas_amount: bigint,
automation_gas_price_cap: bigint,
automation_fee_cap_for_epoch: bigint,
automation_expiration_timestamp_secs: bigint,
automation_aux_data: Uint8Array[],
optionalTransactionPayloadArgs?: OptionalTransactionPayloadArgs,
): Promise<Uint8Array>Parameters:
| Param | Type | Description |
|---|---|---|
senderAddr | HexString | Sender account address |
senderSequenceNumber | bigint | Sender account sequence number |
moduleAddr | string | Target module address |
moduleName | string | Target module name |
functionName | string | Target function name |
functionTypeArgs | TxnBuilderTypes.TypeTag[] | Target function type args |
functionArgs | Uint8Array[] | Target function args |
automation_max_gas_amount | bigint | Max gas amount for the automated transaction |
automation_gas_price_cap | bigint | Gas unit price upper limit the user is willing to pay |
automation_fee_cap_for_epoch | bigint | Maximum automation fee the user is willing to pay per epoch |
automation_expiration_timestamp_secs | bigint | Expiration time of the automated transaction, seconds since UTC epoch start |
automation_aux_data | Uint8Array[] | Reserved for future extensions of registration parameters |
optionalTransactionPayloadArgs | OptionalTransactionPayloadArgs (optional) | Optional gas/expiry overrides — see OptionalTransactionPayloadArgs above |
Returns: Promise<Uint8Array> — the serialized raw transaction; hex-encode it and pass as data to sendAutomationTransaction.
import { BCS, HexString, SupraClient } from 'supra-l1-sdk'
const supraClient = await SupraClient.init(rpcUrl)
const receiverAccount = HexString.ensure(receiverAddress)
const rawTransactionData = await supraClient.createSerializedAutomationRegistrationTxPayloadRawTxObject(
HexString.ensure(accounts[0]),
(await supraClient.getAccountInfo(accounts[0])).sequence_number,
'0000000000000000000000000000000000000000000000000000000000000001',
'supra_account',
'transfer',
[],
[receiverAccount.toUint8Array(), BCS.bcsSerializeUint64(1000)],
BigInt(500), // automation_max_gas_amount
BigInt(100), // automation_gas_price_cap
BigInt(1000000000), // automation_fee_cap_for_epoch
BigInt(Math.floor(Date.now() / 1000) + 2 * 60 * 60), // automation_expiration_timestamp_secs, 2 hours out
[], // automation_aux_data
)
const rawTxData = Buffer.from(rawTransactionData).toString('hex')
const txHash: string = await window.starkey.supra.sendAutomationTransaction({
data: rawTxData,
from: accounts[0],
to: '',
chainId: networkData.chainId,
value: '',
options: { waitForTransaction: true },
})Returns: Promise<string> — the transaction hash, same shape as sendTransaction.
supra-l1-sdk also exposes SupraClient.sendTxUsingSerializedRawTransaction, which signs with a local senderAccount and submits directly to the network, bypassing the wallet entirely — useful for backend/script contexts, not for a connected dApp. For a StarKey-connected dApp, submit through window.starkey.supra.sendAutomationTransaction as shown above instead, so the user signs via the wallet.
signTransaction(tx) — sponsor / fee-payer flow
Signs a transaction without broadcasting it. Used when a separate account (the sponsor) pays gas on behalf of the sender — the sender signs first, then the sponsor’s signature and the broadcast are handled separately.
tx takes the same shape as the sendTransaction parameters above (from, to, value, chainId, and data for anything beyond a native transfer) — the only difference is that StarKey signs without broadcasting.
const signerAuthenticator = await window.starkey.supra.signTransaction(tx)Returns:
interface Ed25519Authenticator {
Ed25519: {
public_key: string
signature: string
}
}{
Ed25519: {
public_key: '0xf348...42b',
signature: '0x94208328...c3a100b',
}
}Combine this with supra-l1-sdk’s SupraClient.signSupraMultiTransaction (sponsor side) and SupraClient.sendSponsorTransaction (broadcast) to complete a fee-payer transaction. See the supporting supra-l1-sdk calls reference for the full call list.
waitForTransactionWithResult({ hash })
Polls until a submitted transaction resolves, returning its final on-chain status. Useful when sendTransaction / sendAutomationTransaction were called with waitForTransaction: false and you need to confirm completion later.
const tx = await window.starkey.supra.waitForTransactionWithResult({
hash: '0xbdc5016f166f49979fb51dbd0407d7bf561b68eb9c0ad5a2849e01441988b4e3',
})Returns:
interface TransactionResult {
hash: string
status: 'Success' | 'Failed'
vmStatus: string
}{
hash: '0xbdc5016f166f49979fb51dbd0407d7bf561b68eb9c0ad5a2849e01441988b4e3',
status: 'Success',
vmStatus: 'Executed successfully',
}Resolves to null if the hash is invalid.
Error handling
sendTransaction/sendAutomationTransaction/signTransactionresolve tonull— they do not reject — when the user declines the confirmation prompt. Check fornullbefore using the returned hash or authenticator.waitForTransactionWithResultresolvesnullfor an invalid hash rather than throwing — check fornullbefore reading.status.- A resolved
status: 'Failed'means the transaction was included on-chain but the Move execution failed; inspectvmStatusfor the reason. See Errors for more.
Notes
frommust match the currently connected/active account — seegetActiveAccount().chainIdshould come fromgetChainId()rather than being hardcoded, so the transaction always targets the network the wallet is actually connected to.
Best practices
- Prefer
options.waitForTransaction: truefor simple flows where the UI can block on confirmation; usefalseplus a separatewaitForTransactionWithResultcall for flows that need to show an optimistic “pending” state immediately. - Always re-derive raw transaction
datawith a freshsequence_numberfromgetAccountInforight before signing/sending — a stale sequence number will cause the transaction to fail.