Tokens
All value in a ledger is represented as tokens. Each token is an indivisible, single unit of a flavor, which denotes a specific type of value - for example, USD
or points
.
To create new value in a ledger, you issue tokens into an account with a transaction. To transfer value in a ledger, you transfer tokens from one account to another. To remove value from a ledger, you retire tokens from an account.
Token Tags
When issuing or transferring tokens into an account, you may wish to tag them for later distinction by providing token tags.
You can then specify a filter when transferring or retiring tokens from the account to target the tokens with specific tags.
Unlike the tags on other objects in the ledger, token tags only exist on the current state of the tokens in an account. Once those tokens are transferred in a transaction, the tags are removed and new tags can be added as they land in the destination account.
Updating tags
Since tokens are the fundamental state of the ledger, in order to update the tags on a group of tokens, you must create a transaction. In the future we will provide a special method for building a transaction that updates tags on tokens, but for now, you transfer them to and from the same account.
Data Structure
The ledger does not store individual token objects, but rather token group objects, which each represent an amount
of identical tokens.
If every token is unique, each token group will have an amount
of 1
.
Field Descriptions
Field | Type | Description |
---|---|---|
amount | integer | Amount of tokens. |
tags | JSON object | Arbitrary, user-supplied, key-value data about the tokens. |
flavor id | string | The id of the flavor of the tokens. |
flavor tags | JSON object | The tags of the flavor of the tokens. |
account id | string | The id of the account that holds the tokens. |
account tags | string | The tags of the account that holds the tokens. |
Example Object
- Java
- Node.js
- Ruby
{
amount: 10,
tags: {},
flavorId: ""
flavorTags: {},
accountId: "",
accountTags: {},
}
{
amount: 10,
tags: {},
flavorId: ""
flavorTags: {},
accountId: "",
accountTags: {},
}
{
amount: 10,
tags: {},
flavor_id: ""
flavor_tags: {},
account_id: "",
account_tags: {},
}
Queries
There are two different types of queries that you can perform on tokens:
- A list query lists tokens in the ledger
- A sum query is an aggregate over the
amount
fields in a filtered list of tokens.
Both queries accept a filter to narrow the results.
List tokens
The list tokens query is useful when you use token tags to differentiate tokens of the same flavor in an account.
For example, if your application tracks stock certificates by tagging Acme stock tokens with a cost basis and purchase date in the token tags, you can list the tokens in Alice's account to see all her certificates.
Sum tokens
The sum tokens query is how you calculate balances in the ledger.
For example, to calculate all the USD balance in Alice's account, you sum all the USD tokens in her account. Or to calculate the total amount of USD in the ledger, you sum all the USD tokens across all accounts.
Examples
Issue tokens
Issue 100 USD tokens to Alice's account.
- Java
- Node.js
- Ruby
Transaction tx = new Transaction.Builder()
.addAction(new Transaction.Builder.Action.Issue()
.setFlavorId("usd")
.setAmount(100)
.setDestinationAccountId("alice")
).transact(ledger);
await ledger.transactions.transact(b => {
b.issue({
amount: 100,
flavorId: 'usd',
destinationAccountId: 'alice',
})
})
tx = ledger.transactions.transact do |builder|
builder.issue(
flavor_id: 'usd',
amount: 100,
destination_account_id: 'alice'
)
end
Issue tokens with tags
Issue 100 debt tokens to Alice's account that are due December, 2018:
- Java
- Node.js
- Ruby
Transaction tx = new Transaction.Builder()
.addAction(new Transaction.Builder.Action.Issue()
.setFlavorId("debt")
.setAmount(100)
.setDestinationAccountId("alice")
.addTokenTagsField("due", "Dec2018")
).transact(ledger);
await ledger.transactions.transact(b => {
b.issue({
amount: 100,
flavorId: 'debt',
destinationAccountId: 'alice',
tokenTags: { due: 'Dec2018' },
})
})
tx = ledger.transactions.transact do |builder|
builder.issue(
flavor_id: 'debt',
amount: 100,
destination_account_id: 'alice',
token_tags: {due: 'Dec2018'}
)
end
Transfer tokens
Transfer 20 USD tokens from Alice's account to Bob's account.
- Java
- Node.js
- Ruby
Transaction tx = new Transaction.Builder()
.addAction(new Transaction.Builder.Action.Transfer()
.setFlavorId("usd")
.setAmount(20)
.setSourceAccountId("alice")
.setDestinationAccountId("bob")
).transact(ledger);
await ledger.transactions.transact(b => {
b.transfer({
amount: 20,
flavorId: 'usd',
sourceAccountId: 'alice',
destinationAccountId: 'bob',
})
})
tx = ledger.transactions.transact do |builder|
builder.transfer(
flavor_id: 'usd',
amount: 100,
source_account_id: 'alice',
destination_account_id: 'bob'
)
end
Update tags by transferring tokens to same account
Update the due date from Dec2018
to Feb2019
on 20 debt tokens
in Alice's account:
- Java
- Node.js
- Ruby
Transaction tx = new Transaction.Builder()
.addAction(new Transaction.Builder.Action.Transfer()
.setFlavorId("debt")
.setAmount(20)
.setSourceAccountId("alice")
.setDestinationAccountId("alice")
.setFilter("tags.due=$1")
.addFilterParameter("Dec2018")
.addTokenTagsField("due", "Feb2019")
).transact(ledger);
await ledger.transactions.transact(b => {
b.transfer({
amount: 20,
flavorId: 'debt',
sourceAccountId: 'alice',
destinationAccountId: 'alice',
filter: 'tags.due=$1',
filterParams: ['Dec2018'],
tokenTags: { due: 'Feb2019' },
})
})
tx = ledger.transactions.transact do |builder|
builder.transfer(
flavor_id: 'debt',
amount: 20,
source_account_id: 'alice',
destination_account_id: 'alice',
filter: 'tags.due=$1',
filter_params: ['Dec2018'],
token_tags: {due: 'Feb2019'}
)
end
Retire tokens
Retire 5 USD tokens from Bob's account.
- Java
- Node.js
- Ruby
Transaction tx = new Transaction.Builder()
.addAction(new Transaction.Builder.Action.Retire()
.setFlavorId("usd")
.setAmount(5)
.setSourceAccountId("bob")
).transact(ledger);
await ledger.transactions.transact(b => {
b.retire({
amount: 5,
flavorId: 'usd',
sourceAccountId: 'bob'
})
})
tx = ledger.transactions.transact do |builder|
builder.retire(
flavor_id: 'usd',
amount: 5,
source_account_id: 'bob'
)
end
Retire tokens filtered by tags
Retire 80 of Alice's debt tokens due December 2018:
- Java
- Node.js
- Ruby
Transaction tx = new Transaction.Builder()
.addAction(new Transaction.Builder.Action.Retire()
.setFlavorId("debt")
.setAmount(80)
.setSourceAccountId("alice")
.setFilter("tags.due=$1")
.addFilterParameter("Dec2018")
).transact(ledger);
await ledger.transactions.transact(b => {
b.retire({
flavorId: 'debt',
amount: 80,
sourceAccountId: 'alice',
filter: 'tags.due=$1',
filterParams: ['Dec2018']
})
})
tx = ledger.transactions.transact do |builder|
builder.retire(
flavor_id: 'debt',
amount: 80,
source_account_id: 'alice',
filter: 'tags.due=$1',
filter_params: ['Dec2018']
)
end
Sum tokens in an account
Query the first two pages of "balances" in Alice's account.
- Java
- Node.js
- Ruby
TokenSum.Page page1 = new Token.SumBuilder()
.setFilter("accountId=$1")
.addFilterParameter("alice")
.addGroupByField("flavorId")
.setPageSize(10)
.getPage(ledger);
for (TokenSum balance : page1.items) {
System.out.println("amount: " + balance.amount);
System.out.println("flavor: " + balance.flavorId);
}
String cursor = page1.cursor;
TokenSum.Page page2 = new Token.SumBuilder()
.getPage(ledger, cursor);
for (TokenSum balance : page2.items) {
System.out.println("amount: " + balance.amount);
System.out.println("flavor: " + balance.flavorId);
}
let page1 = await ledger.tokens
.sum({
filter: 'accountId=$1',
filterParams: ['alice'],
groupBy: ['flavorId'],
})
.page({ size: 10 })
page1.items.forEach(balance => {
// console.log(balance)
})
let page2 = await ledger.tokens
.sum()
.page({ cursor: page1.cursor })
page2.items.forEach(balance => {
// console.log(balance)
})
page1 = ledger.tokens.sum(
filter: 'account_id=$1',
filter_params: ['alice'],
group_by: ['flavor_id'],
).page(size: 10)
page1.each do |balance|
puts 'amount: ', balance.amount
puts 'flavor: ', balance.flavor_id
end
cursor = page1.cursor
page2 = ledger.tokens.sum.page(cursor: cursor)
page2.each do |balance|
puts 'amount: ', balance.amount
puts 'flavor: ', balance.flavor_id
end
List tokens in an account
Query the first two pages of tokens in Alice's account.
- Java
- Node.js
- Ruby
TokenGroup.Page page1 = new Token.ListBuilder()
.setFilter("accountId=$1")
.addFilterParameter("alice")
.getPage(ledger);
for (TokenGroup group : page1.items) {
...
}
String cursor = page1.cursor;
TokenGroup.Page page2 = new Token.ListBuilder()
.getPage(ledger, cursor);
for (TokenGroup group : page2.items) {
...
}
page1 = await ledger.tokens
.list({
filter: 'accountId=$1',
filterParams: ['alice'],
})
.page({ size: 10 })
page1.items.forEach(group => {
// console.log(group)
})
page2 = await ledger.tokens
.list()
.page({ cursor: page1.cursor })
page2.items.forEach(group => {
// console.log(group)
})
page1 = ledger.tokens.list(
filter: 'account_id=$1',
filter_params: ['alice'],
).page(size: 10)
page1.each do |group|
puts group.to_json
end
cursor = page1.cursor
page2 = ledger.tokens.list.page(cursor: cursor)
page2.each do |group|
puts group.to_json
end