Transactions

Transactions are atomic ledger updates that issue, transfer, and/or retire tokens in the ledger. A transaction is comprised of one or more actions.

All actions in a transaction occur simultaneously. Each action operates on tokens of a single flavor, but you can use multiple actions in a single transaction to atomically operate on multiple flavors.

  • Issue – The issue action issues new tokens into an account. It accepts a flavor id, an amount, and a destination account id. The transaction must be signed with the flavor's key(s).
  • Transfer – The transfer action transfers tokens from one account to another. It accepts a flavor id, an amount, a source account id, a destination account id, and an optional filter. The transaction must be signed with the source account's key(s).
  • Retire – The retire action retires tokens from an account. It accepts a flavor id, an amount, a source account id, and an optional filter. The transaction must be signed with the source account's key(s).

Data Structure

Field Descriptions

Field Type Description
id string Cryptographic, globally unique identifier of the transaction.
timestamp string Time (in RFC3339 format) that the transaction was committed to the ledger.
sequence number integer Absolute position in the ledger.
actions array A sequential list of actions affecting the ledger in the transaction.
tags JSON object User-specified key-value data about the transaction.

Example object

{
  id: "",
  sequenceNumber: 1,
  timestamp: "",
  actions: [
    {
      type: "issue",
      flavorId: "",
      amount: 1,
      destinationAccountId: "",
      tags: {}
    },
    {
      type: "transfer",
      flavorId: "",
      amount: 1,
      sourceAccountId: "",
      sourceAccountTags: {},
      destinationAccountId: "",
      destinationAccountTags: {},
      tags: {}
    },
    {
      type: "retire",
      flavorId: "",
      amount: 1,
      sourceAccountId: "",
      sourceAccountTags: {},
      tags: {}
    }
  ],
  tags: {}
}
{
  id: "",
  sequenceNumber: 1,
  timestamp: "",
  actions: [
    {
      type: "issue",
      flavorId: "",
      amount: 1,
      destinationAccountId: "",
      tags: {}
    },
    {
      type: "transfer",
      flavorId: "",
      amount: 1,
      sourceAccountId: "",
      sourceAccountTags: {},
      destinationAccountId: "",
      destinationAccountTags: {},
      tags: {}
    },
    {
      type: "retire",
      flavorId: "",
      amount: 1,
      sourceAccountId: "",
      sourceAccountTags: {},
      tags: {}
    }
  ],
  tags: {}
}
{
  id: "",
  sequence_number: 1,
  timestamp: "",
  actions: [
    {
      type: "issue",
      flavor_id: "",
      amount: 1,
      destination_account_id: "",
      tags: {},
    },
    {
      type: "transfer",
      flavor_id: "",
      amount: 1,
      source_account_id: "",
      source_account_tags: {},
      destination_account_id: "",
      destination_account_tags: {},
      tags: {}
    },
    {
      type: "retire",
      flavor_id: "",
      amount: 1,
      source_account_id: "",
      source_account_tags: {},
      tags: {}
    }
  ],
  tags: {}

Examples

Issue

Issue USD to Alice.

Transaction tx = new Transaction.Builder()
  .addAction(new Transaction.Builder.Action.Issue()
    .setFlavorId("usd")
    .setAmount(100)
    .setDestinationAccountId("alice")
  ).transact(ledger);
ledger.transactions.transact(builder => {
  builder.issue({
    flavorId: 'usd',
    amount: 100,
    destinationAccountId: 'alice'
  })
}).then(tx => ...)
tx = ledger.transactions.transact do |builder|
  builder.issue(
    flavor_id: 'usd',
    amount: 100,
    destination_account_id: 'alice'
  )
end

Transfer

Transfer USD from Alice to Bob.

Transaction tx = new Transaction.Builder()
  .addAction(new Transaction.Builder.Action.Transfer()
    .setFlavorId("usd")
    .setAmount(10)
    .setSourceAccountId("alice")
    .setDestinationAccountId("bob")
  ).transact(ledger);
ledger.transactions.transact(builder => {
  builder.transfer({
    flavorId: 'usd',
    amount: 10,
    sourceAccountId: 'alice',
    destinationAccountId: 'bob'
  })
}).then(tx => ...)
tx = ledger.transactions.transact do |builder|
  builder.transfer(
    flavor_id: 'usd',
    amount: 10,
    source_account_id: 'alice',
    destination_account_id: 'bob'
  )
end

Retire

Retire USD from Bob.

Transaction tx = new Transaction.Builder()
  .addAction(new Transaction.Builder.Action.Retire()
    .setFlavorId("usd")
    .setAmount(5)
    .setSourceAccountId("bob")
  ).transact(ledger);
ledger.transactions.transact(builder => {
  builder.retire({
    flavorId: 'usd',
    amount: 5,
    sourceAccountId: 'bob'
  })
}).then(tx => ...)
tx = ledger.transactions.transact do |builder|
  builder.retire(
    flavor_id: 'usd',
    amount: 5,
    source_account_id: 'bob'
  )
end

Multi-asset transfer

Transfer USD and EUR from Alice to Bob.

Transaction tx = new Transaction.Builder()
  .addAction(new Transaction.Builder.Action.Transfer()
    .setFlavorId("usd")
    .setAmount(10)
    .setSourceAccountId("alice")
    .setDestinationAccountId("bob")
  ).addAction(new Transaction.Builder.Action.Transfer()
    .setFlavorId("eur")
    .setAmount(20)
    .setSourceAccountId("alice")
    .setDestinationAccountId("bob")
  ).transact(ledger);
ledger.transactions.transact(builder => {
  builder.transfer({
    flavorId: 'usd',
    amount: 10,
    sourceAccountId: 'alice',
    destinationAccountId: 'bob'
  })
  builder.transfer({
    flavorId: 'eur',
    amount: 20,
    sourceAccountId: 'alice',
    destinationAccountId: 'bob'
  })
}).then(tx => ...)
tx = ledger.transactions.transact do |builder|
  builder.transfer(
    flavor_id: 'usd',
    amount: 10,
    source_account_id: 'alice',
    destination_account_id: 'bob'
  )
  builder.transfer(
    flavor_id: 'eur',
    amount: 20,
    source_account_id: 'alice',
    destination_account_id: 'bob'
  )
end

Multi-account transfer

Transfer USD from Alice to Bob and transfer EUR from Bob to Carol.

Transaction tx = new Transaction.Builder()
  .addAction(new Transaction.Builder.Action.Transfer()
    .setFlavorId("usd")
    .setAmount(10)
    .setSourceAccountId("alice")
    .setDestinationAccountId("bob")
  ).addAction(new Transaction.Builder.Action.Transfer()
    .setFlavorId("eur")
    .setAmount(20)
    .setSourceAccountId("bob")
    .setDestinationAccountId("carol")
  ).transact(ledger);
ledger.transactions.transact(builder => {
  builder.transfer({
    flavorId: 'usd',
    amount: 10,
    sourceAccountId: 'alice',
    destinationAccountId: 'bob'
  })
  builder.transfer({
    flavorId: 'eur',
    amount: 20,
    sourceAccountId: 'bob',
    destinationAccountId: 'carol'
  })
}).then(tx => ...)
tx = ledger.transactions.transact do |builder|
  builder.transfer(
    flavor_id: 'usd',
    amount: 10,
    source_account_id: 'alice',
    destination_account_id: 'bob'
  )
  builder.transfer(
    flavor_id: 'eur',
    amount: 20,
    source_account_id: 'bob',
    destination_account_id: 'carol'
  )
end

Add action tags

Add action tags to the issue action to denote the source of the deposit.

Transaction tx = new Transaction.Builder()
  .addAction(new Transaction.Builder.Action.Issue()
    .setFlavorId("usd")
    .setAmount(100)
    .setDestinationAccountId("alice")
    .addActionTagsField("source", "wire")
  ).transact(ledger);
ledger.transactions.transact(builder => {
  builder.issue({
    flavorId: 'usd',
    amount: 100,
    destinationAccountId: 'alice',
    actionTags: {source: 'wire'}
  })
}).then(tx => ...)
tx = ledger.transactions.transact do |builder|
  builder.issue(
    flavor_id: 'usd',
    amount: 100,
    destination_account_id: 'alice',
    action_tags: {source: 'wire'}
  )
end

Add transaction tags

Add transaction tags to record store location and invoice number.

Transaction tx = new Transaction.Builder()
  .addAction(new Transaction.Builder.Action.Transfer()
    .setFlavorId("usd")
    .setAmount(10)
    .setSourceAccountId("alice")
    .setDestinationAccountId("merchant")
  )
  .addAction(new Transaction.Builder.Action.Issue()
    .setFlavorId("merchantpoints")
    .setAmount(10)
    .setDestinationAccountId("alice")
  )
  .addTransactionTagsField("invoice", "123")
  .addTransactionTagsField("storeId", "456")
  .transact(ledger);
ledger.transactions.transact(builder => {
  builder.transfer({
    flavorId: 'usd',
    amount: 10,
    sourceAccountId: 'alice',
    destinationAccountId: 'merchant'
  })
  builder.issue({
    flavorId: 'merchantpoints',
    amount: 10,
    destinationAccountId: 'alice'
  })
  builder.transactionTags: {
    invoice: '123',
    storeId: '456'
  }
}).then(tx => ...)
tx = ledger.transactions.transact do |builder|
  builder.transfer(
    flavor_id: 'usd',
    amount: 10,
    source_account_id: 'alice',
    destination_account_id: 'merchant'
  )
  builder.issue(
    flavor_id: 'merchantpoints',
    amount: 10,
    destination_account_id: 'alice'
  )
  builder.transaction_tags = {
    invoice: '123',
    store_id: '456',
  }
end

Querying Examples

By flavor

Query all transactions that transferred USD.

Transaction.ItemIterable txs = new Transaction.ListBuilder()
  .setFilter("actions(type=$1 AND flavorId=$2)")
  .addFilterParameter("transfer")
  .addFilterParameter("usd")
  .getIterable(ledger);
for (Transaction tx : txs) {
  System.out.println("transaction id: " + tx.id);
}
let all = client.transactions.list(
  filter: 'actions(type=$1 AND flavorId=$2)',
  filterParams: ['transfer', 'usd']
).all()

while (true) {
  let { value: tx, done: done } = await all.next()
  if (done) { break }
  console.log(tx)
}
ledger.transactions.list(
  filter: 'actions(type=$1 AND flavor_id=$2)',
  filter_params: ['transfer', 'usd']
).each do |tx|
  puts tx.to_json
end

By account

Query all transactions where Alice transferred any tokens to Bob

Transaction.ItemIterable txs = new Transaction.ListBuilder()
  .setFilter("actions(sourceAccountId=$1 AND destinationAccountId=$2)")
  .addFilterParameter("alice")
  .addFilterParameter("bob")
  .getIterable(ledger);
for (Transaction tx : txs) {
  System.out.println("transaction id: " + tx.id);
}
let all = ledger.transactions.list({
  filter: 'actions(sourceAccountId=$1 AND destinationAccountId=$2)',
  filterParams: ['alice', 'bob']
}).all()

while (true) {
  let { value: tx, done: done } = await all.next()
  if (done) { break }
  console.log(tx)
}
ledger.transactions.list(
  filter: 'actions(source_account_id=$1 AND destination_account_id=$2)',
  filter_params: ['alice', 'bob']
).each do |tx|
  puts tx.to_json
end

By action tags

Query all issuances where deposit source was wire transfer.

Transaction.ItemIterable txs = new Transaction.ListBuilder()
  .setFilter("actions(type=$1 AND tags.source=$2)")
  .addFilterParameter("issue")
  .addFilterParameter("wire")
  .getIterable(ledger);
for (Transaction tx : txs) {
  System.out.println("transaction id: " + tx.id);
}
let all = ledger.transactions.list({
  filter: 'actions(type=$1 AND tags.source=$2)',
  filterParams: ['issue', 'wire']
}).all()

while (true) {
  let { value: tx, done: done } = await all.next()
  if (done) { break }
  console.log(tx)
}
ledger.transactions.list(
  filter: 'actions(type=$1 AND tags.source=$2)',
  filter_params: ['issue', 'wire']
).each do |tx|
  puts tx.to_json
end

By flavor tag

Query all transactions where any type of currency was issued.

Transaction.ItemIterable txs = new Transaction.ListBuilder()
  .setFilter("actions(type=$1 AND snapshot.flavorTags.type=$2)")
  .addFilterParameter("issue")
  .addFilterParameter("currency")
  .getIterable(ledger);
for (Transaction tx : txs) {
  System.out.println("transaction id: " + tx.id);
}
ledger.transactions.list({
  filter: 'actions(type=$1 AND snapshot.flavorTags.type=$2)',
  filterParams: ['issue', 'currency']
}).all(tx => {
  console.log(tx)
})
ledger.transactions.list(
  filter: 'actions(type=$1 AND snapshot.flavor_tags.type=$2)',
  filter_params: ['issue', 'currency']
).each do |tx|
  puts tx.to_json
end

By transaction tags

Query all transactions for a store.

Transaction.ItemIterable txs = new Transaction.ListBuilder()
  .setFilter("actions(snapshot.transactionTags.storeId=$1)")
  .addFilterParameter("456")
  .getIterable(ledger);
for (Transaction tx : txs) {
  System.out.println(tx.tags);
  System.out.println(tx.actions.get(0).snapshot.transactionTags);
}
let all = ledger.transactions.list({
  filter: 'actions(snapshot.transactionTags.storeId=$1)',
  filterParams: ['456']
}).all()

while (true) {
  let { value: tx, done: done } = await all.next()
  if (done) { break }
  console.log(tx.tags)
  console.log(tx.actions[0].snapshot.transactionTags)
}
ledger.transactions.list(
  filter: 'actions(snapshot.transaction_tags.store_id=$1)',
  filter_params: ['456']
).each do |tx|
  puts tx.tags
  puts tx.actions.first.snapshot.transaction_tags
end