MRC-20

Please note that this protocol is still under testing on the testnet, and we will activate the protocol at an appropriate block height on the Bitcoin mainnet after thorough testing.

MRC-20 is a fungible token issuance protocol based on MetaID, allowing users to issue assets and define minting methods through the MRC-20 protocol. Compared to other asset issuance protocols, the primary feature of the MRC-20 protocol is its ability to closely integrate minting methods with personal chain data and the MetaID protocol, thus accommodating various on-chain activity asset issuance methods. In simple terms, MRC-20 is a fungible token issuance protocol designed to meet the needs of future Web3 activities.

Protocol Format

Deployment

Path: /ft/mrc20/deploy

The backend indexer should only validate and index deploy data that matches the specified path.

The PIN under this path does not accept modify and revoke operations

Protocol Format:

{
  "tick": "satoshi", // 2-24 characters
  "amtPerMint": "1000", // Total tokens obtained per mint [1, 1e12]
  "mintCount": "100", // Maximum allowable mint count [1, 1e12]
  "tokenName": "SatoshiTheLegend", // Optional, full name of the token, 0-48 characters
  "decimals": "8", // Optional, decimal places 0 to 12, default is 8  
  "premineCount": "60", // Optional, number of premined tokens during deployment, default is 0, [0, mintCount]
  "beginHeight": "851235", // Optional, the block height when minting event begins
  "endHeight": "851781", // Optional, the block height when minting event ends
  "metadata": "Arbitrary Data", // Optional, can contain additional information such as token description, icon, etc., no format requirements
  "payCheck": { // Optional, check payment to verify minting qualification
    "payTo": "address", // Check if output matches the specified address
    "payAmount": "" // Check if the specified amount of satoshi is paid
  },
  "pinCheck": { // Optional, check PIN to verify minting qualification
    "creator": "", // Creator of the PIN, using the complete MetaID
    "path": "/", // Path of the PIN
    "count": "1", // 0~n, required number of PINs
    "lvl": "6" // Minimum level of the PIN
  }
}

Key Points:

  • After a legitimate genesis transaction, the tick will be assigned a unique ID used to identify the MRC-20 token. This ID is represented by pinid.

  • The tick is globally unique and cannot be duplicated, and its validity is determined on a first-come-first-served basis.

  • Total supply = amtPerMint * mintCount.

  • metadata is a customizable information item that can bind token-related information, such as a description or image.

  • beginHeight and endHeight define the valid timeframe for minting events. If beginHeight is not specified, it defaults to the deployment PIN's block height; if endHeight is not specified, it implies no end time limit.

premineCount Explanation:

  • The deployer can set premine parameters, representing the number of minting premined during the deployment transaction.

  • The premined tokens are directly allocated to the deployer’s address upon confirmation of the deployment transaction.

  • Total premine tokens = amtPerMint * premineCount.

  • PremineCount is optional, with a default value of 0, ranging from 0 to mintCount. Values outside this range are considered invalid.

  • Example: In a deploy setting with amtPerMint = 10000, mintCount = 100, and premineCount = 60, 60 out of the total 100 minting events are premined, leaving 40 for public minting.

payCheck Explanation:

  • If the deployer sets the payCheck item, each minting event will verify if the minting transaction has paid the specified amount of satoshi to the specified address. If the requirement is not met, the minting is invalid.

  • The payTo item specifies the address to which satoshi must be transferred during minting; payAmount specifies the amount of satoshi to be transferred to the payTo address.

pinCheck Explanation:

  • If the deployer sets the pinCheck item, each minting event will verify if the minting transaction includes a PIN that meets the specified conditions. If the requirement is not met, the minting is invalid.

  • creator checks if the PIN creator is the specified MetaID user.

  • path checks if the PIN path matches the specified path.

  • path format should follow the MetaID protocol's path specification and supports path payload content matching.

    • /path[β€˜payload’] matches the entire content under the specified path payload.

    • /path[β€˜key’=’value’] matches the key and value under the specified path payload.

  • count verifies the number of PINs that meet the conditions, with a default value of 1.

  • lvl checks if the PIN level meets the requirement. lvl is determined by the PIN's PoP value. Refer to the PoP and Lvl value description for details.

  • The four parameters in pinCheck can be combined to form minting requirements adaptable to multiple scenarios.

pinCheck Examples:

// No restrictions on path, difficulty, or creator; any PIN qualifies for minting
"pinCheck": {
  "count": "1"
}

// Requires a PIN from the simplebuzz protocol
"pinCheck": {
  "path": "/protocols/simplebuzz"
}

// Requires a PIN that follows a specific MetaID
"pinCheck": {
  "path": "/follow['metaid_of_me']"
}

// Requires a PIN that has a specific 'like'
"pinCheck": {
  "path": "/protocols/paylike['liketo'='abcdef...']"
}

// Any two PINs under /protocols qualify for minting
"pinCheck": {
  "path": "/protocols/*",
  "count": 2
}

// Requires owning my PIN to qualify for minting
"pinCheck": {
  "creator": "abcdef0123457" // Requires a PIN created by MetaID "abcdef0123457"
}

Minting

PIN Path: /ft/mrc20/mint

The backend indexer should only validate and index mint data that matches the specified path.

The PIN under this path does not accept modify and revoke operations

Protocol Format:

{
  "id": "479f8579e5dcdbef868a61541f0d55efccfee1704c8a03f07fb1d97577104d53i0" // tokenID, this tokenID is the PINID of the deploy transaction
}

Key Points:

  • The protocol has only one parameter, which is id, representing the tokenID to be minted. The tokenID corresponds to the PINID of the Deploy transaction.

  • The total amount of tokens minted is determined by the amtPerMint parameter in the Deploy file. If the minting is successful, the total amount of tokens minted will be transferred to the first satoshi of the first output of the Mint transaction.

  • The validity of the minting is determined by the indexer service, which verifies the minting transaction according to the relevant conditions and constraints specified in the Deploy PIN. The verification rules are based entirely on on-chain data, ensuring consistent results across different indexers developed by various developers.

  • If the Deploy file has pinCheck requirements, the Mint transaction input needs to reference one or more valid PINs to complete the PIN verification.

  • A single PIN can only be used once in the minting event of the same token.

  • If the Deploy file has payCheck requirements, the Mint transaction must have an output that meets the address and amount requirements specified in the payCheck.

  • Minting does not support cross-chain minting, meaning the Mint transaction must be on the same chain as its corresponding Deploy transaction.

Example of Mint Transaction Construction

Using Taproot envelope minting, the minting conditions for this MRC-20 require 3 pins (count: 3) and a payCheckcondition that requires paying 100,000 satoshi BTC to the address xxx.

Transfer

There are two forms of transfer: native transfer and data transfer.

Native Transfer (Direct Transfer)

A native transfer is a simple, Layer 1 transfer method that does not rely on writing transfer data. It is completed purely through UTXO transfers.

Native transfers are suitable for scenarios that do not require MRC-20 change, such as:

  1. Alice transfers all of a certain MRC-20 token to Bob.

  2. The quantity of a certain MRC-20 token that Alice needs to transfer to Bob is exactly the sum of one or more MRC-20 UTXOs.

Native Transfer Transaction Construction

When the input includes MRC-20 UTXO and the transaction does not specify transfer data (OP_RETURN or Taproot data), all MRC balances are transferred to the first non-OP_RETURN output.

Native Transfer

Data Transfer (MRC-20 Allocation)

  • Data transfer refers to the transfer method that explicitly writes transfer description data in the Taproot envelope specified by the MetaID protocol. It uses JSON format. By specifying, it defines how the MRC-20 balance in the input of the transaction is allocated to different outputs.

  • Data transfer is suitable for all complex transaction types that require multiple output allocations or need to handle balance change.

  • The PIN Path for data transfer is /ft/mrc20/transfer.

  • Data transfer uses a data structure similar to PIN, where the operation type is hide, meaning it does not generate a corresponding UTXO and does not carry a pinId.

  • If the allocation scheme exceeds the total amount of MR-C20 contained in the input, the allocation is considered invalid, and it falls back to the default direct transfer mechanism, transferring all MRC balances in the current transaction to the first non-OP_RETURN output.

  • Any unallocated balance in the input will use the default direct transfer mechanism, automatically assigning the remaining balance to the first non-OP_RETURN output. This mechanism can be seen as automatic change, defaulting the unallocated balance to the first output.

  • Other unrecognized data transfer formats are considered illegal and will fall back to the default direct transfer mechanism, transferring the MRC-20 balance in the transaction to the first output.

Data Transfer Format

Data transfer is written in the payload of the MetaID envelope as an array.

type MRC20AllocationItem = {
	id: string
	amount: string
	type?: 'transfer' | 'teleport' = 'transfer'
	vout?: number
	coord?: string
}

type MRC20Allocation = MRC20AllocationItem[]

Data Transfer Field Descriptions

  • id: The MRC20 tokenID of the allocated token.

  • amount: The quantity of token allocated.

  • type: The operation type. There are two types:

    • transfer: (default) Transfer, meaning the balance is transferred to a specific output in the current transaction. When the type is transfer, vout must be specified.

    • teleport: Transfer to a pre-existing UTXO on another chain. When the type is teleport, coord must be specified. Details of teleport are described in the next section.

  • vout: The output index to which the token is allocated.

  • coord: When teleporting, specifies the location of the UTXO on the other chain in the format ${txid}i${vout}.

Data Transfer Example

The following JSON example is the payload item in the MetaID protocol specification, and the outer layer should follow the MetaID envelope specification.

const allocation: MRC20Allocation = [
  {
    "amount": "100",
    "vout": 0,
    "id": "479f8579e5dcdbef868a61541f0d55efccfee1704c8a03f07fb1d97577104d53i0", // 'token1'
  },
  {
    "amount": "256",
    "vout": 3,
    "id": "bcccd98a7a1250f26b57d47cfdd36a95866d4bee59c32c9d4e71a6cc1f3429a5i0", // 'token2'
  },
  {
    "amount": "300",
    "vout": 3,
    "id": "479f8579e5dcdbef868a61541f0d55efccfee1704c8a03f07fb1d97577104d53i0", // 'token1'
  },
];

const pinData = {
	metaidFlag: 'metaid',
	operation: 'hide',
	path: '/ft/mrc20/transfer',
	contentType: 'application/json',
	payload: JSON.stringify(allocation) // Serializes the allocation data to JSON and puts it in the payload field
}

In the above transfer transaction, 100 of token1 are assigned to the output at index 0; 256 of token2 are assigned to the output at index 3; 300 of token1 are assigned to the output at index 3.

Full Allocation
Partial Allocation: Any unallocated balance is automatically assigned to the first output.
Invalid Allocation: If the allocation scheme exceeds the balance in the input, the scheme is ignored, and it falls back to the default direct transfer mechanism.

Teleport (Cross-Chain)

Teleport Example

const allocation: MRC20Allocation = [
  {
    amount: '100',
    id: '479f8579e5dcdbef868a61541f0d55efccfee1704c8a03f07fb1d97577104d53i0', // 'token1'
    coord: 'bcccd98a7a1250f26b57d47cfdd36a95866d4bee59c32c9d4e71a6cc1f3429a5i2', // Teleport coordinate, using pinId structure to locate utxo
    type: 'teleport',
  },
]

const pinData = {
	metaidFlag: 'metaid',
	operation: 'hide',
	path: '/ft/mrc20/transfer',
	contentType: 'application/json',
	payload: JSON.stringify(allocation) // Serializes the allocation data to JSON and puts it in the payload field
}

Teleport realizes the cross-chain functionality of MRC-20 token and can be seen as an extended dimension version of transfer. Transfer allocates token balances to the outputs of the current transaction, while teleport allocates balances to outputs on other chains.

  • coord (coordinate): Uses the format ${txid}i${vout} to locate the target UTXO on another chain.

  • The UTXO pointed to by coord is identified by the indexer to determine the chain it belongs to; no need to specify the chain in the data.

  • If the UTXO pointed to by coord does not exist (i.e., the indexer cannot find it), the transaction is still considered valid, but the teleported balance is considered burned.

  • Teleport can coexist with transfer in the same transaction, with teleport allocation having higher priority than transfer.

Teleport Example

Teleport and transfer can coexist at the same level

Teleport and transfer coexist

Last updated