Class

AssembledTransaction

contract.AssembledTransaction()

The main workhorse of Client. This class is used to wrap a transaction-under-construction and provide high-level interfaces to the most common workflows, while still providing access to low-level stellar-sdk transaction manipulation.

Most of the time, you will not construct an AssembledTransaction directly, but instead receive one as the return value of a Client method. If you're familiar with the libraries generated by soroban-cli's contract bindings typescript command, these also wraps Client and return AssembledTransaction instances.

Let's look at examples of how to use AssembledTransaction for a variety of use-cases:

1. Simple read call

Since these only require simulation, you can get the result of the call right after constructing your AssembledTransaction:

const { result } = await AssembledTransaction.build({
  method: 'myReadMethod',
  args: spec.funcArgsToScVals('myReadMethod', {
    args: 'for',
    my: 'method',
    ...
  }),
  contractId: 'C123…',
  networkPassphrase: '…',
  rpcUrl: 'https://…',
  publicKey: undefined, // irrelevant, for simulation-only read calls
  parseResultXdr: (result: xdr.ScVal) =>
    spec.funcResToNative('myReadMethod', result),
})

While that looks pretty complicated, most of the time you will use this in conjunction with Client, which simplifies it to:

const { result }  = await client.myReadMethod({
  args: 'for',
  my: 'method',
  ...
})

2. Simple write call

For write calls that will be simulated and then sent to the network without further manipulation, only one more step is needed:

const assembledTx = await client.myWriteMethod({
  args: 'for',
  my: 'method',
  ...
})
const sentTx = await assembledTx.signAndSend()

Here we're assuming that you're using a Client, rather than constructing AssembledTransaction's directly.

Note that sentTx, the return value of signAndSend, is a SentTransaction. SentTransaction is similar to AssembledTransaction, but is missing many of the methods and fields that are only relevant while assembling a transaction. It also has a few extra methods and fields that are only relevant after the transaction has been sent to the network.

Like AssembledTransaction, SentTransaction also has a result getter, which contains the parsed final return value of the contract call. Most of the time, you may only be interested in this, so rather than getting the whole sentTx you may just want to:

const tx = await client.myWriteMethod({ args: 'for', my: 'method', ... })
const { result } = await tx.signAndSend()

3. More fine-grained control over transaction construction

If you need more control over the transaction before simulating it, you can set various MethodOptions when constructing your AssembledTransaction. With a Client, this is passed as a second object after the arguments (or the only object, if the method takes no arguments):

const tx = await client.myWriteMethod(
  {
    args: 'for',
    my: 'method',
    ...
  }, {
    fee: '10000', // default: BASE_FEE
    simulate: false,
    timeoutInSeconds: 20, // default: DEFAULT_TIMEOUT
  }
)

Since we've skipped simulation, we can now edit the raw transaction and then manually call simulate:

tx.raw.addMemo(Memo.text('Nice memo, friend!'))
await tx.simulate()

If you need to inspect the simulation later, you can access it with tx.simulation.

4. Multi-auth workflows

Soroban, and Stellar in general, allows multiple parties to sign a transaction.

Let's consider an Atomic Swap contract. Alice wants to give 10 of her Token A tokens to Bob for 5 of his Token B tokens.

const ALICE = 'G123...'
const BOB = 'G456...'
const TOKEN_A = 'C123…'
const TOKEN_B = 'C456…'
const AMOUNT_A = 10n
const AMOUNT_B = 5n

Let's say Alice is also going to be the one signing the final transaction envelope, meaning she is the invoker. So your app, from Alice's browser, simulates the swap call:

const tx = await swapClient.swap({
  a: ALICE,
  b: BOB,
  token_a: TOKEN_A,
  token_b: TOKEN_B,
  amount_a: AMOUNT_A,
  amount_b: AMOUNT_B,
})

But your app can't signAndSend this right away, because Bob needs to sign it first. You can check this:

const whoElseNeedsToSign = tx.needsNonInvokerSigningBy()

You can verify that whoElseNeedsToSign is an array of length 1, containing only Bob's public key.

Then, still on Alice's machine, you can serialize the transaction-under-assembly:

const json = tx.toJSON()

And now you need to send it to Bob's browser. How you do this depends on your app. Maybe you send it to a server first, maybe you use WebSockets, or maybe you have Alice text the JSON blob to Bob and have him paste it into your app in his browser (note: this option might be error-prone 😄).

Once you get the JSON blob into your app on Bob's machine, you can deserialize it:

const tx = swapClient.txFromJSON(json)

Or, if you're using a client generated with soroban contract bindings typescript, this deserialization will look like:

const tx = swapClient.fromJSON.swap(json)

Then you can have Bob sign it. What Bob will actually need to sign is some auth entries within the transaction, not the transaction itself or the transaction envelope. Your app can verify that Bob has the correct wallet selected, then:

await tx.signAuthEntries()

Under the hood, this uses signAuthEntry, which you either need to inject during initial construction of the Client/AssembledTransaction, or which you can pass directly to signAuthEntries.

Now Bob can again serialize the transaction and send back to Alice, where she can finally call signAndSend().

To see an even more complicated example, where Alice swaps with Bob but the transaction is invoked by yet another party, check out test-swap.js.

Constructor

# new AssembledTransaction()

View Source lib/contract/assembled_transaction.js, line 225

Members

# Errors

A list of the most important errors that various AssembledTransaction methods can throw. Feel free to catch specific errors in your application logic.

View Source lib/contract/assembled_transaction.js, line 293

Tx

# built

The Transaction as it was built with raw.build() right before simulation. Once this is set, modifying raw will have no effect unless you call tx.simulate() again.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 32

# isReadCall

Whether this transaction is a read call. This is determined by the simulation result and the transaction data. If the transaction is a read call, it will not need to be signed and sent to the network. If this returns false, then you need to call signAndSend on this transaction.

View Source lib/contract/assembled_transaction.js, line 678

# needsNonInvokerSigningBy

Get a list of accounts, other than the invoker of the simulation, that need to sign auth entries in this transaction.

Soroban allows multiple people to sign a transaction. Someone needs to sign the final transaction envelope; this person/account is called the invoker, or source. Other accounts might need to sign individual auth entries in the transaction, if they're not also the invoker.

This function returns a list of accounts that need to sign auth entries, assuming that the same invoker/source account will sign the final transaction envelope as signed the initial simulation.

One at a time, for each public key in this array, you will need to serialize this transaction with toJSON, send to the owner of that key, deserialize the transaction with txFromJson, and call AssembledTransaction#signAuthEntries. Then re-serialize and send to the next account in this list.

View Source lib/contract/assembled_transaction.js, line 588

function

# needsNonInvokerSigningBy

Get a list of accounts, other than the invoker of the simulation, that need to sign auth entries in this transaction.

Soroban allows multiple people to sign a transaction. Someone needs to sign the final transaction envelope; this person/account is called the invoker, or source. Other accounts might need to sign individual auth entries in the transaction, if they're not also the invoker.

This function returns a list of accounts that need to sign auth entries, assuming that the same invoker/source account will sign the final transaction envelope as signed the initial simulation.

One at a time, for each public key in this array, you will need to serialize this transaction with toJSON, send to the owner of that key, deserialize the transaction with txFromJson, and call AssembledTransaction#signAuthEntries. Then re-serialize and send to the next account in this list.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 174

TransactionBuilder

# raw

The TransactionBuilder as constructed in AssembledTransaction.build. Feel free set simulate: false to modify this object before calling tx.simulate() manually. Example:

const tx = await myContract.myMethod(
  { args: 'for', my: 'method', ... },
  { simulate: false }
);
tx.raw.addMemo(Memo.text('Nice memo, friend!'))
await tx.simulate();

View Source lib/minimal/contract/assembled_transaction.d.ts, line 25

# private server

The Soroban server to use for all RPC calls. This is constructed from the rpcUrl in the options.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 73

# sign

Sign the transaction with the signTransaction function included previously. If you did not previously include one, you need to include one now.

View Source lib/contract/assembled_transaction.js, line 507

function

# sign

Sign the transaction with the signTransaction function included previously. If you did not previously include one, you need to include one now.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 138

# signAndSend

Sign the transaction with the signTransaction function included previously. If you did not previously include one, you need to include one now. After signing, this method will send the transaction to the network and return a SentTransaction that keeps track * of all the attempts to fetch the transaction.

View Source lib/contract/assembled_transaction.js, line 556

function

# signAndSend

Sign the transaction with the signTransaction function included previously. If you did not previously include one, you need to include one now. After signing, this method will send the transaction to the network and return a SentTransaction that keeps track * of all the attempts to fetch the transaction.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 153

# signAuthEntries

If AssembledTransaction#needsNonInvokerSigningBy returns a non-empty list, you can serialize the transaction with toJSON, send it to the owner of one of the public keys in the map, deserialize with txFromJSON, and call this method on their machine. Internally, this will use signAuthEntry function from connected wallet for each.

Then, re-serialize the transaction and either send to the next needsNonInvokerSigningBy owner, or send it back to the original account who simulated the transaction so they can AssembledTransaction#sign the transaction envelope and AssembledTransaction#send it to the network.

Sending to all needsNonInvokerSigningBy owners in parallel is not currently supported!

View Source lib/contract/assembled_transaction.js, line 621

function

# signAuthEntries

If AssembledTransaction#needsNonInvokerSigningBy returns a non-empty list, you can serialize the transaction with toJSON, send it to the owner of one of the public keys in the map, deserialize with txFromJSON, and call this method on their machine. Internally, this will use signAuthEntry function from connected wallet for each.

Then, re-serialize the transaction and either send to the next needsNonInvokerSigningBy owner, or send it back to the original account who simulated the transaction so they can AssembledTransaction#sign the transaction envelope and AssembledTransaction#send it to the network.

Sending to all needsNonInvokerSigningBy owners in parallel is not currently supported!

View Source lib/minimal/contract/assembled_transaction.d.ts, line 192

Tx

# signed

The signed transaction.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 78

Api.SimulateTransactionResponse

# simulation

The result of the transaction simulation. This is set after the first call to simulate. It is difficult to serialize and deserialize, so it is not included in the toJSON and fromJSON methods. See simulationData cached, serializable access to the data needed by AssembledTransaction logic.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 41

# private simulationResult

Cached simulation result. This is set after the first call to AssembledTransaction#simulationData, and is used to facilitate serialization and deserialization of the AssembledTransaction.

Most of the time, if you need this data, you can call tx.simulation.result.

If you need access to this data after a transaction has been serialized and then deserialized, you can call simulationData.result.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 54

# private simulationTransactionData

Cached simulation transaction data. This is set after the first call to AssembledTransaction#simulationData, and is used to facilitate serialization and deserialization of the AssembledTransaction.

Most of the time, if you need this data, you can call simulation.transactionData.

If you need access to this data after a transaction has been serialized and then deserialized, you can call simulationData.transactionData.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 67

object

# static Errors

A list of the most important errors that various AssembledTransaction methods can throw. Feel free to catch specific errors in your application logic.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 85

Methods

# isReadCall() → {boolean}

Whether this transaction is a read call. This is determined by the simulation result and the transaction data. If the transaction is a read call, it will not need to be signed and sent to the network. If this returns false, then you need to call signAndSend on this transaction.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 201

boolean

# restoreFootprint(restorePreamble, accountopt) → {Promise.<Api.GetTransactionResponse>}

Restores the footprint (resource ledger entries that can be read or written) of an expired transaction.

The method will:

  1. Build a new transaction aimed at restoring the necessary resources.
  2. Sign this new transaction if a signTransaction handler is provided.
  3. Send the signed transaction to the network.
  4. Await and return the response from the network.

Preconditions:

  • A signTransaction function must be provided during the Client initialization.
  • The provided restorePreamble should include a minimum resource fee and valid transaction data.
Parameters:
Name Type Attributes Description
restorePreamble object

The preamble object containing data required to, build the restore transaction.

account Account <optional>

The account that is executing the footprint restore operation. If omitted, will use the account from the AssembledTransaction.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 227

  • Throws an error if no signTransaction function is provided during Client initialization.
Error
  • Throws a custom error if the restore transaction fails, providing the details of the failure.
AssembledTransaction.Errors.RestoreFailure
Promise.<Api.GetTransactionResponse>

# send() → {Promise.<SentTransaction.<T>>}

Sends the transaction to the network to return a SentTransaction that keeps track of all the attempts to fetch the transaction.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 145

Promise.<SentTransaction.<T>>

# toJSON() → {string}

Serialize the AssembledTransaction to a JSON string. This is useful for saving the transaction to a database or sending it over the wire for multi-auth workflows. fromJSON can be used to deserialize the transaction. This only works with transactions that have been simulated.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 94

string

# toXDR() → {string}

Serialize the AssembledTransaction to a base64-encoded XDR string.

View Source lib/minimal/contract/assembled_transaction.d.ts, line 100

string

# static build(options) → {Promise.<AssembledTransaction.<T>>}

Construct a new AssembledTransaction. This is the only way to create a new AssembledTransaction; the main constructor is private.

This is an asynchronous constructor for two reasons:

  1. It needs to fetch the account from the network to get the current sequence number.
  2. It needs to simulate the transaction to get the expected fee.

If you don't want to simulate the transaction, you can set simulate to false in the options.

Parameters:
Name Type Description
options AssembledTransactionOptions.<T>

View Source lib/minimal/contract/assembled_transaction.d.ts, line 132

Promise.<AssembledTransaction.<T>>
Example
const tx = await AssembledTransaction.build({
  ...,
  simulate: false,
})

# static fromXDR(options, encodedXDR, spec) → {AssembledTransaction.<T>}

Deserialize the AssembledTransaction from a base64-encoded XDR string.

Parameters:
Name Type Description
options Omit.<AssembledTransactionOptions.<T>, ("args"|"method"|"parseResultXdr")>
encodedXDR string
spec Spec

View Source lib/minimal/contract/assembled_transaction.d.ts, line 109