Payments processing
This page contains an overview and specific details that explain how to process (send and accept) digital assets on the TOS network.
Best practices with comments on Toscoin processing:
Best practices with comments on jettons processing:
Global overview
Embodying a fully asynchronous approach, TOS Blockchain involves a few concepts which are uncommon to traditional blockchains. Particularly, each interaction of any actor with the blockchain consists of a graph of asynchronously transferred messages between smart contracts and/or the external world. The common path of any interaction starts with an external message sent to a wallet
smart contract, which authenticates the message sender using public-key cryptography, takes charge of fee payment, and sends inner blockchain messages. That way, transactions on the TOS network are not synonymous with user interaction with the blockchain but merely nodes of the message graph: the result of accepting and processing a message by a smart contract, which may or may not lead to the emergence of new messages. The interaction may consist of an arbitrary number of messages and transactions and span a prolonged period of time. Technically, transactions with queues of messages are aggregated into blocks processed by validators. The asynchronous nature of the TOS Blockchain does not allow to predict the hash and lt (logical time) of a transaction at the stage of sending a message. The transaction accepted to the block is final and will not be modified.
Each inner blockchain message, that is, a message from one smart contract to another, bears some amount of digital assets as well as an arbitrary portion of data.
Smart contract guidelines recommend treating the data payload, which begins with 32 binary zeros, as a human-readable text message. Most software, such as wallets and libraries, support this specification and allow to send text comments along with Toscoin as well as display comments in other messages.
Smart contracts pay fees for transactions (usually from the balance of an incoming message) as well as a storage fee for the contract's stored code and data. Fees depend on workchain configs with maximal fees on masterchain
and substantially lower fees on basechain
.
Digital assets on TOS
TOS has three types of digital assets.
- Toscoin, the main token of the network. It is used for all basic operations on the blockchain, for example, paying gas fees or staking for validation.
- Native tokens, which are special kinds of assets that can be attached to any message on the network. These assets are currently not in use since the functionality for issuing new native tokens is closed.
- Contract assets, such as tokens and NFTs, which are analogous to the ERC-20/ERC-721 standards and are managed by arbitrary contracts and thus can require custom rules for processing.
Simple Toscoin transfer
To send Toscoin, the user needs to send a request via an external message, that is, a message from the outside world to the blockchain, to a special wallet
smart contract (see below). Upon receiving this request, wallet
will send an inner message with the desired amount of assets and optional data payload, for instance a text comment.
Wallet smart contract
Wallet smart contracts are contracts on the TOS Network which serve the task of allowing actors outside the blockchain to interact with blockchain entities. Generally, it solves three challenges:
- authenticates owner: Rejects to process and pay fees for non-owners' requests.
- replays protection: Prohibits the repetitive execution of one request, for instance sending assets to some other smart contract.
- initiates arbitrary interaction with other smart contracts.
Standard solution for the first challenge is public-key cryptography: wallet
stores the public key and checks that an incoming message with a request is signed by the corresponding private key which is known only by the owner. The solution to the third challenge is common as well; generally, a request contains a fully formed inner message wallet
sends to the network. However, for replay protection, there are a few different approaches.
Seqno-based wallets
Seqno-based wallets follow the most simple approach to sequencing messages. Each message has a special seqno
integer that must coincide with the counter stored in the wallet
smart contract. wallet
updates its counter on each request, thus ensuring that one request will not be processed twice. There are a few wallet
versions that differ in publicly available methods: the ability to limit requests by expiration time, and the ability to have multiple wallets with the same public key. However, an inherent requirement of that approach is to send requests one by one, since any gap in seqno
sequence will result in the inability to process all subsequent requests.
High-load wallets
This wallet
type follows an approach based on storing the identifier of the non-expired processed requests in smart-contract storage. In this approach, any request is checked for being a duplicate of an already processed request and, if a replay is detected, dropped. Due to expiration, the contract may not store all requests forever, but it will remove those that cannot be processed due to the expiration limit. Requests to this wallet
may be sent in parallel without interfering with each other; however, this approach requires more sophisticated monitoring of request processing.
Interaction with blockchain
Basic operations on TOS Blockchain can be carried out via TosLib. It is a shared library which can be compiled along with a TOS node and expose APIs for interaction with the blockchain via so-called lite servers (servers for lite clients). TosLib follows a trustless approach by checking proofs for all incoming data; thus, there is no necessity for a trusted data provider. Methods available to TosLib are listed in the TL scheme.
Wallet deployment
To deploy a wallet via TosLib one needs to:
- Generate a private/public key pair via createNewKey . Note that the private key is generated locally and does not leave the host machine.
- Form InitialAccountWallet structure corresponding to one of the enabled
wallets
. Currentlywallet.v3
,wallet.highload.v1
,wallet.highload.v2
are available. - Calculate the address of a new
wallet
smart contract via the getAccountAddress method. We recommend using a default revision0
and also deploying wallets in the basechainworkchain=0
for lower processing and storage fees. - Send some Toscoin to the calculated address. Note that you need to send them in
non-bounce
mode since this address has no code yet and thus cannot process incoming messages.non-bounce
flag indicates that even if processing fails, money should not be returned with a bounce message. We do not recommend using thenon-bounce
flag for other transactions, especially when carrying large sums, since the bounce mechanism provides some degree of protection against mistakes. - Form the desired action, for instance
actionNoop
for deploy only. Then use createQuery and sendQuery to initiate interactions with the blockchain. - Check the contract in a few seconds with getAccountState method.
Incoming message value
To calculate the incoming value that the message brings to the contract, one needs to parse the transaction. It happens when the message hits the contract. A transaction can be obtained usinggetTransactions. For an incoming wallet transaction, the correct data consists of one incoming message and zero outgoing messages. Otherwise, either an external message is sent to the wallet, in which case the owner spends Toscoin, or the wallet is not deployed and the incoming transaction bounces back.
Anyway, in general, the amount that a message brings to the contract can be calculated as the value of the incoming message minus the sum of the values of the outgoing messages minus the fee: value_{in_msg} - SUM(value_{out_msg}) - fee
. Technically, transaction representation contains three different fields with fee
in name: fee
, storage_fee
, and other_fee
, that is, a total fee, a part of the fee related to storage costs, and a part of the fee related to transaction processing. Only the first one should be used.
Checking contract's transactions
A contract's transactions can be obtained using getTransactions. This method allows to get 10 transactions from some transactionId
and earlier. To process all incoming transactions, the following steps should be followed:
- The latest
last_transaction_id
can be obtained using getAccountState - List of 10 transactions should be loaded via the
getTransactions
method. - Unseen transactions from this list should be processed.
- Incoming payments are transactions in which the incoming message has a source address; outgoing payments are transactions in which the incoming message has no source address and also presents the outgoing messages. These transactions should be processed accordingly.
- If all of those 10 transactions are unseen, the next 10 transactions should be loaded and steps 2,3,4,5 should be repeated.
Accepting payments
There are a few approaches to accepting payments that differ in their method of distinguishing users.
Invoice-based approach
To accept payments based on attached comments, the service should
- Deploy the
wallet
contract. - Generate a unique
invoice
for each user. String representation of uuid32 will be enough. - Users should be instructed to send Toscoin to the service's
wallet
contract with an attachedinvoice
as a comment. - Service should regularly poll the getTransactions method for the
wallet
contract. - For new transactions, the incoming message should be extracted,
comment
matched against the database, and the value (see Incoming message value paragraph) deposited to the user's account.
Invoices
Invoices with tos:// link
If you need an easy integration for a simple user flow, it is suitable to use the tos:// link. Best suited for one-time payments and invoices.
tos://transfer/<destination-address>?
[nft=<nft-address>&]
[fee-amount=<nanocoins>&]
[forward-amount=<nanocoins>]
✅ Easy integration
✅ No need to connect a wallet
❌ Users need to scan a new QR code for each payment
❌ It's not possible to track whether the user has signed the transaction or not
❌ No information about the user's address
❌ Workarounds are needed on platforms where such links are not clickable (e.g. messages from bots for Telegram desktop clients )
Invoices with Tos Connect
Best suited for dApps that need to sign multiple payments/transactions within a session or need to maintain a connection to the wallet for some time.
✅ There's a permanent communication channel with the wallet, information about the user's address
✅ Users only need to scan a QR code once
✅ It's possible to find out whether the user confirmed the transaction in the wallet, track the transaction by the returned BOC
✅ Ready-made SDKs and UI kits are available for different platforms
❌ If you only need to send one payment, the user needs to take two actions: connect the wallet and confirm the transaction
❌ Integration is more complex than the tos:// link
Sending payments
- Service should deploy a
wallet
and keep it funded to prevent contract destruction due to storage fees. Note that storage fees are generally less than 1 Toscoin per year. - Service should get from the user
destination_address
and optionalcomment
. Note that for the meantime, we recommend either prohibiting unfinished outgoing payments with the same (destination_address
,value
,comment
) set or proper scheduling of those payments; that way, the next payment is initiated only after the previous one is confirmed. - Form msg.dataText with
comment
as text. - Form msg.message which contains
destination_address
, emptypublic_key
,amount
andmsg.dataText
. - Form Action which contains a set of outgoing messages.
- Use createQuery and sendQuery queries to send outgoing payments.
- Service should regularly poll the getTransactions method for the
wallet
contract. Matching confirmed transactions with the outgoing payments by (destination_address
,value
,comment
) allows to mark payments as finished; detect and show the user the corresponding transaction hash and lt (logical time). - Requests to
v3
ofhigh-load
wallets have an expiration time equal to 60 seconds by default. After that time unprocessed requests can be safely resent to the network (see steps 3-6).
Explorers
The blockchain explorer is https://explorer.tos.network.
To generate a transaction link in the explorer, the service needs to get the lt (logic time), transaction hash, and account address (account address for which lt and txhash were retrieved via the getTransactions method). https://explorer.tos.network may then show the page for that tx in the following format:
https://explorer.tos.network/tx/{lt as int}:{txhash as base64url}:{account address}
https://explorer.tos.network/transaction?account={account address}<={lt as int}&hash={txhash as base64url}