TIP-5 - URI scheme (TON Crystal payment link)

TIP:      5
Layer:    Applications
Title:    URI scheme
Author:   Vladislav Ponomarev <vp@broxus.com>
Status:   Discussion
Type:     Standards track
Created:  2021-06-14


This TIP proposes a URI scheme for making TON Crystal payments.


Payment URIs has become widely adopted since the introduction of Bitcoin’s improvement proposals BIP-20, 21, 70, 71 and 72. For instance, most modern mobile wallets support the scanning of QR codes containing encoded bitcoin payment links.

The purpose of this URI scheme is to enable users to easily make payments in TON Crystals by simply clicking links on web pages or scanning QR Codes.


General rules for handling

FreeTON clients MUST NOT act on URIs without getting the user’s authorization. They SHOULD require the user to manually approve each payment individually, though in some cases, they MAY allow the user to make this decision automatically.

Operating system integration

Graphical FreeTON clients SHOULD register themselves as the handler for the ton: URI scheme by default if no other handler is already registered. If there is already a registered handler, they MAY prompt the user to change it once they first run the client.

EBNF grammar

tonurn             ::= protocol, ":", ["//"], tonaddress, ["?", tonparams];
protocol           ::= "freeton" | "ton";
tonaddress         ::= rawaddress | base64 | base64url;
rawaddress         ::= chainnumber, ":", {letter | digit};
chainnumber        ::= ["-"], digit, digit*;
base64             ::= base64character, {base64character};
base64character    ::= letter | digit | base64symbol;
base64symbol       ::= "/" | "+";

base64url          ::= base64urlcharacter, {base64urlcharacter};
base64urlcharacter ::= letter | digit | base64urlsymbol;
base64urlsymbol    ::= "-" | "_";

tonparams          ::= tonparam, ["&", tonparams*];
tonparam           ::= amountparam | labelparam | commentparam | versionparam | otherparam | reqparam | netparam;
amountparam        ::= amountnotation, "=", digit, digit*, [".", digit*];
amountnotation     ::= "amount" | "a";
labelparam         ::= labelnotation, "=", labelcharacter*;
labelnotation      ::= "label" | "l";
commentparam       ::= commentnotation, "=", labelcharacter*;
commentnotation    ::= "comment" | "c";
versionparam       ::= versionnotation, "=", digit, digit*, [".", digit, digit*];
versionnotation    ::= "version" | "v";
otherparam         ::= labelcharacter, labelcharacter*, "=", labelcharacter*;
reqparam           ::= "req-", otherparam;
netparam           ::= netnotation, "=", labelcharacter*;
netnotation        ::= "net" | "n";
labelcharacter     ::= letter | digit | urisymbol;
letter             ::= "A" | "B" | "C" | "D" | "E" | "F" | "G"
                     | "H" | "I" | "J" | "K" | "L" | "M" | "N"
                     | "O" | "P" | "Q" | "R" | "S" | "T" | "U"
                     | "V" | "W" | "X" | "Y" | "Z" | "a" | "b"
                     | "c" | "d" | "e" | "f" | "g" | "h" | "i"
                     | "j" | "k" | "l" | "m" | "n" | "o" | "p"
                     | "q" | "r" | "s" | "t" | "u" | "v" | "w"
                     | "x" | "y" | "z" ;
digit              ::= "0" | "1" | "2" | "3" | "4" | "5" | "6"
                     | "7" | "8" | "9";
urisymbol          ::= ":" | "/" | "?" | "#" | "[" | "]" | "@"
                     | "!" | "$" | "'" | "(" | ")" | "*" | "+"
                     | "," | ";" | "%" | "." ;

You can validate your payment link for validity at the EBNF evaluator here: EBNF Test.

The scheme component (ton: or freeton:) is case-insensitive, and implementations must accept any combination of uppercase and lowercase letters. The rest of the URI is case-sensitive, including the query parameter keys.

Query Keys

  • label or l: Label for that address (e.g. name of receiver or order number);
  • address: FreeTON address in raw or packed (base64 / base64url) representation;
  • comment or c: message to attach to the transaction while sending it to the network;
  • amount or a: amount of base currency units (see below);
  • version or v: applicable standard version. If omitted, version 1 will apply;
  • net or n: network to perform operation, e.g. “main” or “dev”;
  • (others): optional, for future extensions.

Transfer amount/size

If an amount is provided, it MUST be specified in decimal TON. All amounts MUST contain no commas and use a period (.) as the separating character to separate whole numbers and decimal fractions. I.e. amount=50.00 or amount=50 is treated as 50 TON, and amount=50,000.00 is invalid.

FreeTON clients MAY display the amount in any format that is not intended to deceive the user. However, they SHOULD choose a foremost least confusing format, and only after that most reasonable given the amount requested. Thus, for example, so long as most users work in TON units, values should always be displayed in TON by default, even if mTON or kTON would otherwise be a more logical interpretation of the amount.

Forward compatibility

Variables that are prefixed with a req- are considered required. If a client does not implement any variables prefixed with req-, it MUST consider the entire URI invalid. Any other variables that are not implemented but are not prefixed with a req- can be safely ignored.


Simpler syntax

This section is non-normative and does not cover all possible syntax. Please see the BNF grammar above for the normative syntax.

[foo] means optional, <bar> are placeholders



:warning: Characters must be URI encoded properly.

Just address (in different formats)

ton: protocol
freeton: protocol
URL-like link

Address with name


Request 100.2 TON


Payment with comment


Some future version that has variables that are (currently) not understood and required and thus invalid


Some future version that has variables that are (currently) not understood but not required and thus valid


Reserved standard extensions

The future standard extensions are reserved:

  • TIP-5/1 - URI scheme (TIP-3 fungible token payment link)
  • TIP-5/2 - URI scheme (TIP-3 non-fungible token payment link)

What about an optional bounce flag?

1 Like

Good question: it is a good practice to send non-bounceable transaction to any address, unless specified otherwise.

I consider that if the recipient requests the payment to the specific address, it goes without saying that the sender’s wallet should switch the bounce off.


However, for such purposes, there may be used the parameter req-bounce=true which is allowed by the standard.

MTW team will add this implementation. But the initial source (BIP) didn’t assume to work with smart contracts. Maybe has a reason to add the new optional parameter “v” (version). If by default then the initial TIP-5 format. In the opposite case, many developers will add new “req” parameters from themselves and we will lose the uniform specification. Better do add some opportunity to introduce the additional standards in the future.
For example, the community needs to have support for NFT (as you mentioned), here will be v=2 (for example), in this version need to add the required parameters - asset address (asset, NFT root TON address) and token id (tokenid). Then applications that will support TIP-5 will be forced to check “v” parameter (or this can be omitted) and work by the “internal” standard or by the initial.
Offered changes:

tonparam           ::= amountparam | labelparam | commentparam | otherparam | versionparam | reqparam;
versionparam        ::= "v=", digit, digit*;

Also, maybe has the sense to support short parameters name, because URL is under 2000 characters.

Also, maybe has the sense to add some batch mode. For mass payments. Because for now we can use the link to pay to the one address. In the real web life often need to send to the several addresses (airdrops, etc.)

What does that mean?

Thank you, excellent and valuable comments.

I assumed to have a special extension or even a separate standard in the future to work with smart contracts as there are several concerns to address:

  1. Limitation of the URI string (~1KB max), whereas contract payload may easily be over a few MB;
  2. Necessity to specify ABI scheme to interact with the contract (becomes even more complex with different scheme versions).

As of now, I would prefer to have a simple yet working basis covering the key scenario: transferring one transaction to one address. Then, all other cases should be introduced as the standard extensions.

Good suggestion, I will add it to the standard.

Agree, will also introduce this.

I intentionally omitted this case as it will complicate the basic standard version. We can add it as an extension after the first version is approved.

Introduced short param names and version to the standard.

What does mean what? :upside_down_face:

We have meant that to work with smart contract need to add some input options for standard smart contract. Like “asset” + “tokenid” for NFT. They have TIP-3 standard.
I don’t believe that we need to URI scheme or another standard to specify ABI. My personal opinion that it must be included in the wallet. From here we can conclude that maybe even not “v” needed, but maybe standard name. Or “v” with TIP-N, where v=N and N is version.
In this case we will be able to cover any possible extension of this standard.

If sum up:

  1. URI scheme with v parameter, which number is TIP number.
  2. The wallet must include in itself ABI if TIP provide such specification.
  3. The wallet must support input parameters from such TIP.

Also if we will have a batch feature as possible implementation. Need to decide how we will be able to add additional addresses and amounts…
In this case, we can’t use this format:


I don’t have any thoughts now how to extend this standard on batch feature. Because then we will have only protocol name and parameters.

Yes, this can be an option for regular payments and specify the number of the standard extension. But for contract calls, I’m not sure.

In my humble opinion, contract calls deserve a separate standard, or even separate protocol, e.g. toncontract:, to clearly distinguish use cases.

In an ultimate situation, toncontract: can be used to perform simple transfers as well, as all wallets in FreeTON are contracts.

This can be one solution. Another one is to host smart contracts of specific versions in the Free TON · GitHub repo under the sequential (or hierarchical) number and refer to it in URI.

That is why it is not included in this standard and not even reserved yet. There can be two ways:

  1. We will extend the standard of a simple payment link by adding an array option in the link
  2. We will create a separate protocol for batch payments, e.g. tonbatch:, which will clearly indicate another syntax to the wallet.

I personally prefer the second scenario, although it may refer to TIP-5 in terms of params naming and meaning. The reason for this is that simple payment links are normally the dominant scenario in all protocols, be it the classic payto: from the fiat world or the bitcoin: from crypto ecosystem.

Also, as for the batch payments, I’m not sure it will be convenient to encode them in the QR code, as it may become unreadable.

I agree, but for many users doesn’t exist a distinguishment between TON/ standard token and NFT. That’s why, I think, we can’t split on regular transfer for TON and tokens.
For others, not TIP-3, etc. standards we can add toncontract:, but I am not sure about tokens in this case… Because as you have mentioned - any wallet, already smart contract.

Totally agree due to limitation of URI size, etc… I think that better to implement multiply scanning several qr`s.

Then we don’t to continue discussing about batch mode.

I have no idea to split this - that’s why I reserved extensions 5/1 and 5/2 from the same standard.

So be it :slight_smile:
Let’s discuss the batch payment in a separate thread.

Well, it’s not clear for me why to start the work from sketch, when there already is a working schema. Maybe lets develop it further together? It will bring more value to the community, than creating one more separate version.

Existing schemes:

Eng: Notion – The all-in-one workspace for your notes, tasks, wikis, and databases.

Ru: Notion – The all-in-one workspace for your notes, tasks, wikis, and databases.


Good to know that it exists :slight_smile:

However, the current scheme is tailored specifically to the Surf and may not apply to every wallet. E.g., such functions as vote, chat or debot require specific types of user interaction flows that are not common in a regular wallet.

This standard is named the “TON Crystal payment link” for a reason. It is a widely adopted and understandable type of standard among different blockchains, and it focuses specifically on payments, as the most popular case.

However, I liked with network switch parameter, I will also add it to the scheme.

Both schemes can be compatible, but in case of TIP-5 the action may be passed as a parameter.

I think the URI scheme should not copy an inferior cryptocurrency designs such as Bitcoin. There is a functionality Free TON browser supports by default such as DeNS, DeBots and other extended smart contract stuff. For example if I wanna call a smart contract address by DeNS in a normal browser (supported by some extention or whatever) I should just type: ton://dens_name and Parameters should follow.

The main thing here is that most users and developers who interact with cryptocurrency are familiar with patterns introduced by the bitcoin community. There is zero sense to fight this as it has already proven to be a viable concept.

I really doubt all wallets should support all functionalities that are available in Surf.

The main purpose of this standard is to introduce a simple way to encode payment links in TON Crystals (and, in the future, transfer NFTs, and TIP3 tokens) and generate widely accepted QR codes on its basis. These then can be used for different purposes, but mainly in e-commerce and to transfer the value between accounts.

The DeNS is a good concept, but it is overkill for such an objective. BTW, which DeNS is the official for FreeTON now? freeton.domains?

1 Like

I totally disagree with the fact that users should stick to the “payment” pattern as most of the blockchains do. FreeTON is not just “another blockchain”: different smart-contracts have different roles which should be reflected when interacting with them via URI schemes links.

For instance, there is not need to send any payment to debot address directly, that’s why URI scheme in Surf splits the links by types depending on the desired need, i.e.
ton://debot/<address>?message=<message> - processes the debot with the specified address, while
ton://transfer/<address>?amount=<amount>&comment=<comment> - processes the payment as a regular transfer (but who said it couldn’t be an invoice type of a payment as another kind of URI with additional params?).

Such approach is way more flexible as well as very clear to users as they can vividly see what action is supposed to happen when following the link.


It is like to say all browsers should not support html standards. The creation of new web requires all browsers to do precisely that.

Any standard should be extendable. If we have a standard that covers more usecases it is generally considered a better standard. Promoting a narrow standard is trying to actively demotivate creating better wallets (which should include browser functionality as this is one of the most important goals of Free TON design).

Please think not just on how to lock users into your wallets but on how to provide better user experience for user interaction with all Free TON dapp ecosystem.

Why is that an “overkill”? In what sense? It is extremely lightweight and fast solution — faster than any other name resolution mechanism on the Internet today. You should do your homework I think.