SNIP-21 - Minor improvements to SNIP-20
This document describes several optional enhancements to the SNIP-20 spec. Contracts that support the existing SNIP-20 standard are still considered compliant, and clients should be written so as to benefit from these features when available, but provide fallbacks for when these features are not available.
The features specified in this document are mostly miscellaneous quality-of-life improvements, aimed at enriching UI integration, and making auditability of accounts easier (provided a viewing key, of course!).
The existing Transfer
, TransferFrom
, Send
, SendFrom
,
Burn
, Mint
, and BurnFrom
messages MAY
also accept a memo
field, which is just a regular string. This memo
SHOULD be saved in the transaction history of the balances involved in the
transaction (from
, sender
, and receiver
) and served as the memo
field
of items in the responses to the TransferHistory
and TransactionHistory
(introduced in this document) queries.
Example Transfer
message:
{
"recipient": "secret1xyz",
"amount": "123000000",
"memo": "A private message only the sender and recipient can see"
}
Example TransferHistory
response:
{
"transfer_history": {
"txs": [
{
"id": 123,
"from": "secret1xyz",
"sender": "secret1xyz",
"receiver": "secret1xyz",
"coins": {
"denom": "FOOBAR",
"amount": "123000000"
},
"memo": "Second private message"
},
{
"id": 123,
"from": "secret1xyz",
"sender": "secret1xyz",
"receiver": "secret1xyz",
"coins": {
"denom": "FOOBAR",
"amount": "123000000"
},
"memo": "First private message"
}
]
}
}
When sending a Receive
callback as part of a Send
or SendFrom
transaction,
The Receive
message SHOULD contain the same memo
that was supplied in the
original message.
For example sending this Send
message:
{
"send": {
"recipient": "secret1xyz",
"amount": "123000000",
"msg": "some base64 data here",
"memo": "My super secret memo"
}
}
would send this callback to the recipient contract:
{
"receive": {
"sender": "secret1xyz",
"from": "secret1xyz",
"amount": "123000000",
"msg": "some base64 data here",
"memo": "My super secret memo"
}
}
The TransferHistory
query MAY return an additional field total
which is a
64 bit JSON integer representing the total count of transfer events registered
in the history of the balance being queried.
The absence of this feature makes presenting good paging interfaces very hard
for user interfaces. There's no way of knowing how many pages there are in
the history without first fetching all the registered transfers and seeing when
we get an empty page. With this feature, if the UI wants to display 10 transfers
at a time, the first query to TransferHistory
will return the 10 latest
transfers, and the UI will know it can offer (total / 10) + 1
pages that can
be jumped to.
Example TransferHistory
response:
{
"transfer_history": {
"total": 200,
"txs": [
{
"id": 123,
"from": "secret1xyz",
"sender": "secret1xyz",
"receiver": "secret1xyz",
"coins": {
"denom": "FOOBAR",
"amount": "123000000"
}
},
{
"id": 123,
"from": "secret1xyz",
"sender": "secret1xyz",
"receiver": "secret1xyz",
"coins": {
"denom": "FOOBAR",
"amount": "123000000"
}
}
]
}
}
The items returned by the TransferHistory
query MAY contain additional fields
called block_time
and block_height
. These two fields MUST correspond to the
block time and block height of the block that included the transaction that
recorded the transfer being reported. The block time is in Unix time.
Example TransferHistory
response:
{
"transfer_history": {
"txs": [
{
"id": 123,
"from": "secret1xyz",
"sender": "secret1xyz",
"receiver": "secret1xyz",
"coins": {
"denom": "FOOBAR",
"amount": "123000000"
},
"block_time": 12006,
"block_height": 101
},
{
"id": 123,
"from": "secret1xyz",
"sender": "secret1xyz",
"receiver": "secret1xyz",
"coins": {
"denom": "FOOBAR",
"amount": "123000000"
},
"block_time": 12000,
"block_height": 100
}
]
}
}
The original TransferHistory
query paints an incomplete picture of users'
interactions with the token. It only records operations performed through
Transfer
, TransferFrom
, Send
, and SendFrom
, while the other operations
which move funds around - Mint
, Burn
, Deposit
, Withdraw
etc. are not
recorded and can not be easily tracked by users after the fact.
The following is the specification of the new TransactionHistory
query, which
aims to provide a complete description of the history of users' balances.
This query MUST be authenticated.
This query SHOULD return a list of json objects describing the changes to the balance of the querying address, in newest-first order. The user may optionally specify a limit on the amount of information returned by paging the available items.
The response should look as described below.
- The
total
field is REQUIRED (unlike as inTransferHistory
where it is missing for legacy reasons) and must equal to the number of recorded transactions related to the querying account. - The
id
field is optional, and may be used by contracts that choose to populate it. - The
memo
field may be missing ornull
, but if present it MUST be the memo that was attached to the message sent by the user in the described transaction. - The
block_time
andblock_height
MUST be the same as the fields of the same name described above in Block Time and Height.
More details are described below.
Name | Type | Description | optional |
---|---|---|---|
key | string | The viewing key | no |
address | string | Addresses SHOULD be a valid bech32 address, but contracts may use a different naming scheme as well | no |
page_size | number | Number of transactions to return, starting from the latest. i.e. n=1 will return only the latest transaction | no |
page | number | This defaults to 0. Specifying a positive number will skip page * page_size txs from the start. |
yes |
The action
field of each transaction under txs
is an object, which is one of
the variants of TxAction
.
{
"transaction_history": {
"total": 200,
"txs": [
{
"id": "optional ID",
"block_time": 12000,
"block_height": 100,
"coins": {
"denom": "coin denomination/name",
"amount": "Uint128"
},
"memo": "private message",
"action": {}
},
{ "...": "..." }
]
}
}
TxAction
has the following variants:
-
Transfer
- represents transactions triggered byTransfer
,TransferFrom
,Send
, andSendFrom
. The fields in this object correspond to the fields described inTransferHistory
.from
- The address from which funds were moved.sender
- The address which performed the operation.recipient
- The address to which funds were moved.
from
andsender
will differ in events created by the*From
messages, which use allowance from a thirdfrom
address to perform the operation.
{
"transfer": {
"from": "secret1xyz",
"sender": "secret1xyz",
"recipient": "secret1xyz"
}
}
Mint
- represents transactions triggered byMint
. Theminter
field shows the address of the minter which created the tokens, and therecipient
field shows the address of the account that received the tokens. This item will show up in both the minter's and recipient's histories.
{
"mint": {
"minter": "secret1xyz",
"recipient": "secret1xyz"
}
}
-
Burn
- represents transactions triggered byBurn
.burner
- The address that performed the burn.owner
- The address whose funds were burned.
This item will show up in both the burner's and owner's histories.
burner
andowner
will differ in events created byBurnFrom
,
{
"burn": {
"burner": "secret1xyz",
"owner": "secret1xyz"
}
}
Deposit
- represents aDeposit
operation, where native tokens were deposited to the contract for the address of the querier.
{
"deposit": {}
}
Redeem
- represents aRedeem
operation, where native tokens were withdrawn from the account of the address of the querier.
{
"redeem": {}
}