[brc-20] The modular proposal by UniSat

Abstract

This proposal describes a module mechanism for brc-20.

The module mechanism provides a new approach to support various inscription-based applications within the existing brc-20 framework. Modules operate independently of each other, and each Indexer only needs to parse data relevant to their interested module while still maintaining brc-20 balance consistency with other indexers.

Motivation

The brc-20 itself is a very simple payment system, defining three operations for deployment, minting, and transferring tokens.

We realize that brc-20 also has great potential expansion capabilities and can support a variety of applications running in this basic payment system.

Description

The module mechanism is a simple way to introduce various applications into brc-20 and ensure their harmonious coexistence.

By deploying a module, an independent space is created within the brc-20 world. Users can deposit any brc-20 tokens into the module and withdraw their brc-20 balance from it.

Users send valid brc-20 transfer inscriptions to the module’s ID address to deposit their balance into the module.

In the module, users can generally only use the tokens they have deposited. Users can interact with the module using inscriptions or any other feasible method.

Module designers can define rules at their discretion, such as requiring confirmation of deposits. To facilitate consensus among indexers, a “contract” source code inscription ID must be specified when deploying a module. This content describes the logic of the module’s functionality in code but does not require the inscription to exist at the time of module deployment.

From the perspective of the indexer, modules are divided into two types: ignorable and non-ignorable, which we name black module and white module:

Black Module

Black modules only support deposits and do not support withdrawals. From the perspective of an indexer that does not parse it, brc-20 tokens appear to have been transferred to an address that only receives but does not send out.

White Module

White modules can support withdrawals but require that all indexers support the module mechanism and implement the consensus algorithm for this module. Withdrawals are illegal before a module transforms into a white module.

A black module can transform into a white module once accepted by all indexers, but a white module can not transform back into a black module.

Consensus can be reached by a vote of all participants to decide whether a module created from a source inscription should transform into a white module.

Operations

The module mechanism introduces two new inscriptions: deploy and withdraw, in addition to directly using the brc-20 transfer inscription to deposit tokens.

Deploying a New Module

The lower case inscription ID serves as the module’s ID. You need to set the name, source, and optional initialization data. There is no character limit for the name.

{
  "p": "brc20-module",
  "op": "deploy",
  "name": "demo",
  "source": "d2a30f6131324e06b1366876c8c089d7ad2a9c2b0ea971c5b0dc6198615bda2ei0",
  "init": {
      "fee": "0.01"
  }
}

Depositing BRC-20 Tokens into the Module (Deposit by Transfer)

The module’s ID address is in the form of an OP_RETURN ModuleID locking script address. To indexers that do not support modules, the balance appears to have been transferred to the module’s literal address, and no one can unlock it.

{
  "p": "brc-20",
  "op": "transfer",
  "tick": "ordi",
  "amt": "10"
}

The module ID after OP_RETURN is serialized as the 32-byte TXID, followed by the four-byte little-endian INDEX, with trailing zeroes omitted.

Withdrawing BRC-20 Tokens from the Module to a Target Address (Withdraw)

  • Similar to the transfer inscription, users need to first create a Withdraw inscription and then transfer it to the target address. The inscription can only be used once.

  • The user’s own brc-20 balance in the module will be withdrawn to the target address.

  • The content of inscription must include the module’s ID. Withdrawals are only valid if the module exists and is a white module.

{
  "p": "brc20-module",
  "op": "withdraw",
  "tick": "ordi",
  "amt": "10",
  "module": "8082283e8f0edfcce4901ff2d271b2eec4a4735b371bb6af53d64770651d8c14i0"
}

FAQ

Can anyone deploy a module?

  • Yes, anyone can define the functionality of a module and deploy an instance of it.

  • All other indexers will consider modules with unrecognized source IDs as black modules by default. Deployers must run their own module’s indexer.

After depositing into a black module, will you be unable to withdraw?

Although only white modules support withdrawals, black modules can easily support atomic exchange transactions between internal and external balances, allowing users who want to deposit into the module to exchange balances with users who want to withdraw. This way, users inside the module do not need to worry about being unable to withdraw.

Can the source code inscription-id change?

  • Currently, it cannot be changed and must be specified by the module author during deployment.

Is it possible to upgrade module code if there are bugs?

  • For black modules, indexers can be upgraded to fix bugs.

  • For white modules, if bugs are discovered in the code, a new version of the module with the bug fix can be deployed, and tokens can be transferred to the new version of the module. However, new versions of modules generally run in black mode first.

Acknowledgments

Domo originally mentioned the idea for the module, and the implementation was explored and attempted through multiple versions at UniSat. Thanks to Domo for his contributions, and thanks to everyone for reading.

Copyright

This proposal is licensed under the MIT License.

3 Likes

Example for brc-20 module: GroupSend

Abstract

This proposal describes a simple demonstration module for the brc-20 protocol.

Within the brc-20 module framework, we have established a straightforward method that allows users to distribute their balances within the module to a large number of recipients.

This proposal defines one operation within the brc-20 module: send.

Group send mechanism

Users can send brc-20 balances to multiple recipients simultaneously within the module. The send operation supports setting multiple target addresses for sending and independently setting amounts.

Creating a new group send module instance

{ 
  "p": "brc20-module",
  "op": "deploy",
  "name": "send",    
  "source": "253e7ebe28224c3c298cbe4f4ab90fbf6620d9e9b688e8ea064a6d52dae882e9i0",         // Points to code plaintext defining module functionality
  "init": {                    // Can set arbitrary parameters
  }
}

Sending your own BRC-20 to multiple target addresses within the module (Send)

  • Users need to first inscribe a send inscription, then activate it by transferring to module address (OP_RETURN ModuleID).

  • The inscription must include the module’s ID. If the module doesn’t exist, the send is invalid.

  • Similar to the transfer-inscription , the send inscription, upon creation, locks a corresponding amount of brc-20 tokens. Users must have available balances within the module.

  • The brc-20 tokens owned by the user within the module will be sent to multiple target addresses, increasing the balance of the target addresses within the module.

{
  "p": "brc20-send",
  "op": "send",
  "tick": "ordi",   
  "module": "027d6cd027af94d20dfde4d60cc1fdab718d27a67e378edb1a05cf9418aa2891i0",     // Sending from the module
  "group": {                // Target receiving addresses and amounts to send
      "bc1p..1": "10",
      "bc1p..2": "10",
      "bc1p..3": "10",
      "bc1p..4": "10",
      ...                        
  }
}

Source Code for Inscription

contract GroupSendContract{
    function send(string tick, address fromAddress, mapping(address => uint) group) {
        require(fromAddress == msg.sender, "Only the specified sender can make this payment");
        require(group.length > 0, "Group data must not be empty");

        for (address recipient in group) {
            uint amount = group[recipient];

            require(amount > 0, "Amount must be greater than zero");
            require(module[tick].balanceOf(fromAddress) >= amount, "Insufficient balance");
            require(module[tick].transferFrom(fromAddress, recipient, amount), "Transfer failed");
        }
    }    
}

EVM implementations without gas would have broken incentives for decentralization and availability.

Is “fee” in the example below put there for that or is it just an arbitrary parameter?

{
  "p": "brc20-module",
  "op": "deploy",
  "name": "demo",
  "source": "d2a30f6131324e06b1366876c8c089d7ad2a9c2b0ea971c5b0dc6198615bda2ei0",
  "init": {
      "fee": "0.01"
  }
}

Could the concept of a “black module” be used to migrate BRC20 tokens to other layers or more efficient protocols such as Taproot Assets? To me that seems like the biggest unlock for this proposal.

One way BRC-20 to TAP bridges could be simpler such as introducing a new BRC-20 transaction type that transfers the tokens to an invalid or vanity address for the existing BRC-20 protocol and with additional metadata (address or signature) for TAP to understand.

1 Like

It’s only more efficient if it’s live! but yes, the modular approach can transcend bitcoin

3 Likes

Whether to include gas is an internal design of each independent module. In fact, we include the concept of gas in the swap module. The fee here is just an arbitrary demonstration. Generally, it depends on the initialization variables in the source.

1 Like

For your information (excerpted from our FAQ)

Can you explain how brc-20 modules work and why should we care?

Anyone can implement their own modules, which is permissionless, much like UniSat has done with the brc20-swap module. All newly created modules start in ‘black module’ mode by default, meaning that all indexers treat them as blackboxes, not recognizing their contents.

To provide services to their users, module providers must independently index data within their modules. All activities within these modules remain hidden from the external world and have no impact on the existing brc-20 ecosystem.

Once the main indexers (Alex, Domo, Hiro, OKX, UniSat) are ready to recognize brc20-swap operations and are satisfied with its performance (let’s assume that they are happy with it), the brc20-swap module has the opportunity to be ‘accepted’ and become a part of the brc-20 as one ‘white module’.

Looking ahead, the core protocol will remain stable and straightforward. Many modules supporting various protocols, blockchains, and domains will be introduced. Some of these modules will gain acceptance from the main indexers.

This approach allows brc-20 to remain highly adaptable, permissionless (meaning anyone can create modules), decentralized (as indexers can choose which modules to support), and sustainable (modules are isolated without interfering with each other), all while keeping the core protocol simple and stable.

Numerous modules are currently in development (Sep. 2023), aiming to support different protocols, blockchains, and domains. brc-20 is showcasing its robustness and vitality through its inherent simplicity.

I heard withdrawal is restricted when in the ‘black module’, can you describe the details?

In the ‘black module’ state, the main indexers lack detailed information about the module. Allowing direct withdrawals in this state would result in varying states across different indexers, potentially causing chaos in the entire system. This is why direct withdrawal is not an option for ‘black modules.’

Nevertheless, there are multiple workarounds available. The most straightforward approach is to utilize existing marketplaces, where users can trade in-module assets (L2 assets) for classic brc-20 assets (L1 assets).

However, we offer a simpler solution: when a user requests a withdrawal, they simply submit the withdrawal request. If there is someone depositing assets, the deposit and withdrawal processes will automatically and atomically exchange, ensuring a smooth and atomic deposit/withdrawal happening at the same time.

1 Like

Only brc-20 token could be used as fee, or you have solution to use bitcoin as fee?

Using btc or brc20 as the fee depends on the module’s design selection. module protocol itself does not have a mandatory requirement.
Yes, it is possible to use btc as fee.

:face_with_monocle:I need to understand it again

now we have uniswap on bitcoin

when can we have makerdao and compound

You can refer here: [BRC-100] Introduction to BRC-100, we can have makerdao and compound by BRC-100 :muscle: :muscle: :muscle:

There are major problems with this new mess you are proposing; what constitutes “all participents”, who decides that? What voting mechanism is used? Does this mean 100% of participents have to agree on black modules? So any single dev that just started working on an indexer, can also vote against a black module becoming white? Or do just the power players say what is white? And if that is the case, why don’t they just make their own centralized system, instead of forcing all the smaller dev teams to play forever catchup on indexers? Also the code being in plain text doesn’t allow for any verification that a module will perform the plain text, it is way too loose, and is ripe for manipulation. If anything it should at least be using something like a solitdity compiler wtih a version and actual byte code be put in the inscription so that same exact code can be verified to be run and agreed upon by anyone implementing a module in their indexer. Use something like a modified version of the Ethereum byte code instruction set, then at least we have compilers arleady made and can make real verifiable contracts code. Even then, though, that doesn’t guarantee the same code witll be run across different indexers. This standard, just like the original, is horrible; at least Domo had the good sense to put “do not use this for anything” on the original… and he was right because BRC-20 sucks, and now trying to extend it in this way, adds so many more problems to it, and at the end of the day, it is just everyone with their bags of BRC-20s trying to get rid of them for more than they paid for them, with no actual use of crypto as currency, just these useless zero sum games, meanwhile the Fed prints trillions of USD per month now out of thin air, and everyone is just trying to screw everyone else and get their share of this Fed counterfeit money. This really is just a way of sneaking in forks to the existing BRC-20 protocol… this is another Segwit, a pretend soft fork, that really is a hard fork, that will punish everyone who doesn’t move with everyone else.

2 Likes

:heart:very amazing concept

I think it’s an interesting concept and I want to provide my view on some unanswered questions:

  1. Reaching consensus by vote
    Darrow raised some valid concerns but the solution should, in my opinion, be demand driven. If there’s a critical mass of users and volume then the market clearly wants modules regardless of what a few developers want or think. In the end, this is what we’re developing for-the users.

Now, we can argue what a critical mass will look like but in reality the quantity is just semantics and not rooted in fostering innovation. The question is really: “What does this implementation cost the BRC20 ecosystem compared to the value for end-users”. I would argue it’s a low cost.

  1. Module performance
    Any module or smart contract must be verified on-chain. However, it’s a “cross that bridge when we get there” problem. We can only assess the modules retrospectively, thus after they have been battle-tested. There will be scams/exploits/hacks like on all other blockchains running on smart-contracts. At least, the modules respect the token balance so ordi is not minted out of the blue. It’s entirely input/output until we find a better solution but we simply can’t hold back because of xyz that might or might not happen to “wildcard” modules because it’s a user-problem. The users must make the right risk assessment before using a module.

As said, let the market decide and the developers will focus on trying new things.

Every unique indexer is already a “fork” of BRC20 @darrow

BRC20 is not a ‘real’ protocol, it is just JSON files inscribed on Bitcoin in a format that Domo chose. Every unique Indexer indexing “BRC20” is its own “fork” of BRC20, plain and simple (aka currently each indexer has its own ‘definition’ of what the BRC20 protocol is). BestInSlot has done a good job of trying to find a list of common definitions for key factors of the “protocol”, but it still leads different Indexers to have different “perceptions” of the BRC20 protocol state.

This is one of the reasons why “indexer” protocols that exist ATOP of a Blockchain are a little bit “brain-breaky” if you insist on staying in the crypto world of defining a “token”. The BRC20 DATA itself is Distributed Data(Bitcoin), secured by a Decentralized Network(Bitcoin). When you use an application that is “protocol-izing” this Distributed, Secure data OFF-CHAIN (ex. all of Ordinals), you are using a “fork”(perception/definition) of that particular Indexer’s processing of the Data. Everything is already different on the backend of these Indexers, what Unisat/any proposal upgrade is suggesting only seems ‘scary’ because most don’t know this "protocol state uncertainty’ is how the current “Indexer” ecosystem already works.

I’ll summarize my thoughts with this: BRC20 doesn’t need a “decentralized” Indexer, it needs a Distributed Index first. a common dataset that all Indexers can point to and easily interpret. THEMOTO is currently working on this with Motoracer, as we would love to put an end to this silliness and start correctly defining what this $1.5bil+ market is xD

2 Likes

Your characterization of the structural challenges of trying to make a messaging protocol like BRC20 work on Bitcoin is sound. I wish the market were more rational or cautious, but we don’t get to choose that.

I’m curious about this common dataset – can you provide more resources?

1 Like

Agree with that idea !

(´• ω •`)ノ Spot on !

We need a consonance for the indexes to refer too. we should use the same script incompleteness to make it simply stupid.

This problem also with runes and anything on ordinals.