DOT20 - Polkadot Fungible Inscription standard (draft)

Basic Concept

DOT-20(or DOT20) is the standard for Polkadot fungible inscription, designed to standardize the basic operations of the inscriptions. DOT-20 is designed with reference to the Ethereum ERC20 standard and optimized to be compatible with Polkadot and substrate based parachains.

Global Specifications

  • Polkadot inscriptions operate by creating transactions of utility batchAll(calls) and are based on the parsing of the memo within the system remarkWithEvent(remark).

  • If the value of each key within the memo is numeric, it may be used without quotes, including <amt, start, end, reserve, max, lim, etc.> The memo should adhere to the JSON standard format.

  • Transactions of the same type can be batched by attaching multiple calls with the same operator under one batchAll(calls).

  • At the end of each batchAll transaction, a call with the operator memo can be added to record non-transaction information, such as exchange buy/sell/trade details.

  • Inscription data precision: up to 32 digits for integers and 18 digits for decimals. The value range for inscription operations is 0 to 10^32, with the smallest nonzero value being 1 wei (10^-18)

  • In addition to the fields specified by the operator, other fields in the memo will not be recognized by the indexer. However, this transaction may still be a valid inscription operation and can be used to extend the protocol's usage in certain scenarios.

Operators

To operate the inscription, follow these steps on Polkadot JS

Deploy


Select the "utility" pallet from the dropdown menu on the left side of the page.
Choose "batchAll" under "batch" as the call to construct.
In the "calls" section, click the "+" button to add a new call.
In the new call, set the "Call" field to "system - remarkWithEvent(remark)".

Now, for the "remark: Bytes" field, you should input the following JSON string:

{
    "p": "dot-20", // Protocol name (required)
    "op": "deploy", // Deployment operator (required)
    "tick": "dota", // Ticker Name (required), must be 3-6 English letters, globally unique, case-insensitive, and each letter must pass ASCII validation
    "decimal": 18, // Inscription precision (optional), can be a number from 0 to 18, default is 18
    "mode": "fair", // Issuance mode (required), can be "fair," "normal," or "owner"
    "amt": 5000000, // Reward per block in fair mode (required for fair mode)
    "start": 18681993, // Minting start height (required for fair and normal modes)
    "end": 18723992, // Minting end height (required for fair mode)
    "max": 210000000000, // Maximum total supply (required for normal mode)
    "lim": 1000000, // Single mint amount (required for normal mode)
    "admin": "1FKmjePh2YT58zzKbuNJEM1vXe7uY3HuCugpNVmt1EWJEv7S" // Only admin address can mint (required for owner mode)
}

There are three issuance modes available: 
  1.Normal mode (normal, fixed amount of inscription minted per transaction)
  2.Fair mode (fair, evenly distributed inscriptions within a block)
  3.Owner mode (only the admin can mint inscriptions).

Note 1: The indexer will determine which fields are available based on the chosen mode. In fair mode, it reads the <amt,start,end> fields. In normal mode, it reads the <start,max,lim> fields. In owner mode, it only reads the <admin> field.
Note 2: Special handling will be applied to the "dota" inscription.[^1]
Note 3: When deploying, remove the comments after the commas in the JSON structure. The memo must adhere to the JSON data structure.
Note 4: In normal mode, the maximum value for <max> is 10^32, and the single <lim> minting amount must not exceed <max>. In fair mode, <amt>*(<end>-<start>+1) must not exceed 10^32.
Note 5: The <start> value must be equal to or later than the deployment block height; otherwise, the deployment will fail.
Note 6: Deployment operations do not support batch processing.

Mint

Construct a call using utility batchAll(calls) (for normal or fair mode) or multiple calls (for owner mode).
call0: system - remarkWithEvent(remark)
In the "remark: Bytes" field, insert a JSON string:

{
    "p": "dot-20",  // Protocol name (required)
    "op": "mint",   // Minting operator (required)
    "tick": "dota",  // Ticker Name (required)
    "to": "1FKmjePh2YT58zzKbuNJEM1vXe7uY3HuCugpNVmt1EWJEv7S",  // Inscription recipient address (optional, default is the sender's address)
    "lim": 800000   //Number of inscriptions to be minted (invalid for fair mode, optional for normal mode, defaults to the maximum value for single minting in normal mode, required for owner mode)
}

Note 1: The recipient address must adhere to the SS58 address standard, with Polkadot's mainnet addresses starting with a 1 and having a length of 47 or 48 characters.
Note 2: For normal mode, the default number of inscriptions minted in a single transaction is the <lim> quantity specified in the deploy field.
Note 3: For normal or fair minting modes, the indexer only considers the first call of the first transaction from the same sender within the same block; other transactions will be ignored.
Note 4: Following the above rules, minting does not support batch operations in both "fair" and "normal" mode.
Note 5: In normal mode, if the total minted amount equals <max>, subsequent minting will fail.
Note 6: In fair mode, if no one mints within a block, the inscriptions in that block will be destroyed, and the total inscription amount decreases accordingly.
Note 7: In owner mode, batch minting of inscriptions to multiple addresses is supported, with a transaction structure similar to batch transfers.

Transfer

Construct one or multiple calls using utility batchAll(calls).
call0: system - remarkWithEvent(remark)
In the "remark: Bytes" field, insert a JSON string with all fields being mandatory:

{
    "p": "dot-20",  # Protocol name
    "op": "transfer",  # Transfer operator
    "tick": "dota",  # Ticker Name
    "amt": 1000000,  # Amount to transfer
    "to": "1FKmjePh2YT58zzKbuNJEM1vXe7uY3HuCugpNVmt1EWJEv7S"  # Destination address for inscription transfer
}

[Optional] call1: system - remarkWithEvent(remark)
[Optional] call2: system - remarkWithEvent(remark)
...
[Optional] calln: system - remarkWithEvent(remark)
[Optional] calln+1: system - remarkWithEvent(remark)
{
    "p": "dot-20",  // Protocol name
    "op": "memo",  // Memo operator
    "text": "your extra messages here"  // Add remarks for this operation
}

Note 1: The recipient address must adhere to the SS58 address standard, with Polkadot's mainnet addresses starting with a 1 and having a length of 47 or 48 characters.
Note 2: Transfers support batch operations. The json format for call1 to calln follows call0, conforming to the JSON standard for transfers. It allows batch sending of different tick inscriptions to different target addresses.
Note 3: If any of the transfers in the above operations encounter an error, the entire batch of transfers will fail.
Note 4: Except for the last call, which has an operator of "memo", all other calls must have an operator of "transfer", or the transaction will fail.
Note 5: Transfer destination addresses are not limited and can include personal addresses as well as the null address 0x0.

Approve

Construct one or multiple calls using utility batchAll(calls).
call0: system - remarkWithEvent(remark)
In the "remark: Bytes" field, insert a JSON string with all fields being mandatory:

{
    "p": "dot-20",  // Protocol name
    "op": "approve",  // Approval operator
    "tick": "dota",  // Ticker Name
    "amt": 100000000,  // Amount to approve, with a maximum value of 10^32
    "to": "1FKmjePh2YT58zzKbuNJEM1vXe7uY3HuCugpNVmt1EWJEv7S"  // Address of the recipient of the inscription approval
}

[Optional] call1: system - remarkWithEvent(remark)
[Optional] call2: system - remarkWithEvent(remark)
...
[Optional] calln: system - remarkWithEvent(remark)
[Optional] calln+1: system - remarkWithEvent(remark)
{
    "p": "dot-20",  // Protocol name
    "op": "memo",  // Memo operator
    "text": "your extra messages here"  // Add remarks for this operation
}

Note 1: The approved amount can be updated by the approver, with a maximum approval value of 10^32. Setting the updated value to 0 is equivalent to revoking the approval.
Note 2: Approvals support batch operations. The json format for call1 to calln follows call0, conforming to the JSON standard for approvals. It allows batch approval of different tick inscriptions.
Note 3: If any of the approval operations encounter an error (e.g., if the tick doesn't exist), the entire batch of approvals will fail.
Note 4: The approved amount can be greater than the current balance held by the approver.
Note 4: Except for the last call, which has an operator of "memo", all other calls must have an operator of "approve", or the transaction will fail.

TransferFrom

Construct one or multiple calls using utility batchAll(calls).
call0: system - remarkWithEvent(remark)
In the "remark: Bytes" field, insert a JSON string with all fields being mandatory:

{
    "p": "dot-20",  // Protocol name
    "op": "transferFrom",  // Authorized transfer operator
    "tick": "dota",  // Ticker Name
    "amt": 9900,  // Amount to transfer
    "from": "1FKmjePh2YT58zzKbuNJEM1vXe7uY3HuCugpNVmt1EWJEv7S",  // Inscription authorizer's address
    "to": "1GF1ir7LCjK35qSzEGi6tVPQ4Gh7enWFVgGQ5QuUfVZn8zLX"  // Destination address for inscription transfer
}

[Optional] call1: system - remarkWithEvent(remark)
{
    "p": "dot-20",  // Protocol name
    "op": "transferFrom",  // Authorized transfer operator
    "tick": "idot",  // Ticker Name
    "amt": 99,  // Amount to transfer
    "from": "1GF1ir7LCjK35qSzEGi6tVPQ4Gh7enWFVgGQ5QuUfVZn8zLX",  // Inscription authorizer's address
    "to": "1FKmjePh2YT58zzKbuNJEM1vXe7uY3HuCugpNVmt1EWJEv7S"  // Destination address for inscription transfer
}

[Optional] call2: system - remarkWithEvent(remark)
{
    "p": "dot-20",  // Protocol name
    "op": "transferFrom",  // Authorized transfer operator
    "tick": "dota",  // Ticker Name
    "amt": 100,  // Amount to transfer
    "from": "1FKmjePh2YT58zzKbuNJEM1vXe7uY3HuCugpNVmt1EWJEv7S",  // Inscription authorizer's address
    "to": "13EADjZkn3b7PjCUxYZBsgvm8ANVnEaAVp21tiekxTWEQWha"  // Destination address for inscription transfer
}

[Optional] call3: system - remarkWithEvent(remark)
{
    "p": "dot-20",  // Protocol name
    "op": "transferFrom",  // Authorized transfer operator
    "tick": "idot",  // Ticker Name
    "amt": 1,  // Amount to transfer
    "from": "1GF1ir7LCjK35qSzEGi6tVPQ4Gh7enWFVgGQ5QuUfVZn8zLX",  // Inscription authorizer's address
    "to": "13EADjZkn3b7PjCUxYZBsgvm8ANVnEaAVp21tiekxTWEQWha"  // Destination address for inscription transfer
}

[Optional] calln: system - remarkWithEvent(remark)
[Optional] calln+1: system - remarkWithEvent(remark)
{
    "p": "dot-20",  // Protocol name
    "op": "memo",  // Memo operator
    "text": "your extra messages here"  // Add remarks for this operation
}

Note 1: Transactions can only be initiated by the inscription approver's authorized recipient.
Note 2: After a successful transfer, the sender's approved amount from the approver's address will be deducted, and inscriptions will be transferred from the approver's address to the destination address. For each tick, ensure that the sum of transferred amounts is less than or equal to the approver's approved amount for the sender and is also less than or equal to the current number of inscriptions owned by the approver.
Note 3: Authorized transfers support batch operations. The memo format for call1 to calln follows call0, conforming to the JSON standard for authorized transfers. It allows batch authorized transfers of different ticks from different approvers' addresses to different destination addresses.
Note 4: If any of the authorized transfer operations encounter an error, the entire batch of authorized transfers will fail.
Note 5: Except for the last call, which has an operator of "memo", all other calls must have an operator of "transferFrom", or the transaction will fail.

Batch Bundle Operations

Construct one or multiple batchAll(calls) using utility batch(calls).
call0: utility - batchAll(calls)
    call: call:0
    call: call:1
    ...
    call: call:n

call1: utility - batchAll(calls)
    call: call:0
    call: call:1
    ...
    call: call:n

call2: utility - batchAll(calls)

...

callm: utility - batchAll(calls)
    call: call:0
    call: call:1
    ...
    call: call:n

Note 1: Within a batch operation, you can load multiple batchAll operations to simultaneously complete transfer, approve, and transferFrom operations within a single batch transaction.
Note 2: If any of the batchAll operations within a batch operation is determined to be invalid, it does not affect the validation assessment of subsequent batchAll operations.
Note 3: deploy and mint operators do not support batch operations.

References

[^1] DOTA Inscription deploy transaction

Revision History

  • v0.1 - Protocol Draft

Last updated