ZRC3’s Grand Rewrite

Starling Foundries
Builders of Zilliqa
6 min readApr 10, 2021

--

I wrote over a year ago about ZRC3 — my take on Meta Transactions for Zilliqa. After that draft was submitted, I got some good feedback from the team and noted that in order to maintain an interface in common with ZRC2, I would have to wait for that standard to stabilize. It’s a good thing I did, because ZRC2 is unrecognizable from when I forked it to now. Fortunately, the Zilswap exchange defines an implicit standard for fungible tokens, which, since my rewrite of ZRC3 follows that interface, we can get compatibility with OpFI for free!

The first PR I wrote made dramatic changes to the ZRC2 code, not just adding to it — but also changing it to use new terms and a new implicit assumption of authority. This was motivated by my distaste for the Operator pattern that has taken hold on many ERC standards and is present in ZRC2. However that is a matter of taste and now for the sake of compatibility, I have permitted for its inclusion in ZRC3. Not all operator patterns are elective, but the ZRC2 operator is entirely elective as there is no default operator at the contract-level, so it can safely be ignored by uninterested token holders. I also maintain compatibility with the additional code for minting, burning coins. Though it is as of yet untested, this should permit ZRC3 to work with any wallet or exchange that supports ZRC2 tokens, this includes the Zilswap exchange.

What are Meta Transactions to me anyway?

Meta Transactions are a broad concept borrowed from Ethereum where it is typically used to load authenticated data onto a smart contract. This pattern is needed for many Ethereum projects including the ‘gasless’ relay system whereby one or more nodes accept transactions signed with a hidden private key and then wrap that transaction in a valid Ethereum mainchain transaction. This allows tokens to seem like native coins in that the transfer fee is either hidden or denominated as a fee in the coin they are transferring. After all, why should I pay for XSGD transfers in $ZIL when there is a good chance that some holders of XSGD have no immediate access to ZIL.

Beyond that, this signed data can be used as a pattern for layer two integration, sidechains, sidetrees, for multiwallet solutions and as a bridge between two blockchain mainnets.

ZRC-3 doesn’t cover all these applications, but it is reassuring that this is both practical and robust to do so in a Scilla smart contract.

From Ethereum sprang…

The Smart Contract design begins from ZRC2, which is itself a port of Ethereum’s ERC-777. This tasteful selection imitates ERC-20’s most desirable parts while avoiding several pitfalls in the earlier tokens design that made it unsafe to use certain portions of the token contract. It is also more extensible and modular — including optional logic for minters, burncoins and operator nodes. In the ZRC repository you will discover that this is all included in the standard. ZRC-2 coins enjoy compatibility with several wallets and exchanges, including Zilswap. The gZil governance coin is basically a ZRC2 token as well.

So where do I start with a Meta Transactions implementation? We could use the Gnosis safe contracts, or the Zeplin gas relay, perhaps Austin Griffith’s Delegated Subscriptions or the new(ish) EIP standardizing a metatransaction from the signed data angle. Well I combed the EIP repo and found all of them for us, and we’re lucky that we have the gift of hindsight because none are really ideal. The closest to our standard is the ERC-965, which identifies itself as the Meta Transactions extension of ERC-777. It was almost perfect with the exception that nonce calculation was confusing and led to the possibility of abuse by the entity you trusted to relay your transaction. I found a way around that abuse by combining a bit of ERC-1776’s transaction identifier pattern and stirring in replay protection from EIP-712. Yes I was probably too thorough as it took me nearly two years and 4 rewrites to get it to where it is today. But I can tell you as of now that the contract works and I’ve personally sent several Meta Transactions over the Zilliqa Testnet.

What’s it like?

Signed checks handed to merchants or for delayed payments like rent

As a user, I wanted Meta Transactions to be safe above all else — in that the spender and holder of the tokens is never at risk of losing funds. The patterns between the person who is sending a token and the relayer who is forwarding his signed check and paying gas on his behalf is essential to avoiding issues. ERC-965 was optimal in this case because it used an explainable pattern — it’s equivalent to adding a checkbook to your bank account. Instead of trusting the operator to only spend funds when you give him permission, this pattern only allows you to spend funds, but you can communicate with your bank account through a tangible proxy. In the old bank it was paper with a serial number, now its a Meta Transaction defined by all the variables and the contract that holds your token demands to show proof that the owner of the funds did authorize this exact spend in the form of a cryptographic signature of the entire message hash.

{
"from" : "0xa0000000...",
"to" : "0xb0000000...",
"amount" : 100,
"contract" : "0x12395345...",
"fee" : 2,
"nonce" : 12,
"signature" : "0xasdlj3io2j...",
"pubkey" : "asdfg123..."


}//an example Meta Transaction

I’d say it’s a bit better than those silly checks Di Caprio was stitching together from toys in Catch Me If You Can. It get’s even better, because I wanted it to be totally safe, I gave every user the ability to void a check after they’ve written it. This of course is optional — in some cases the merchant would refuse a check if you could then easily cancel the transaction. On the other hand, if you’re going to be your own bank you’ve got to handle your own fraud cases. If the optional transition ChequeVoid is included:

transition ChequeVoid(pubkey: ByStr33, from: ByStr20, to: ByStr20, amount: Uint128, fee: Uint128, nonce:Uint128, signature: ByStr64)

Then you’d want to be aware as the relayer that all Meta Transactions should be cleared ASAP. This makes the only concern ZRC3 token owners have is that a relay might not send their transaction in time, in which case they can send their same Meta Transaction to any other relay willing to do it, or they can send it themselves. I don’t expect transaction censorship to be a problem as good relays will have incentive via the optional fee to work hard to maintain trust and thus preference in an open market.

Can you use it now?

Ultimately I’d suggest waiting as the draft has been idling in the open PRs for a couple months now. I understand, the Polychain bridge and so many other big things coming up are taking priority from the massively caffeinated Zilliqa Research team. I can tell you that the interface is unlikely to change, making it safe to build against. Just start on the testnet and wait along with me for the draft to be accepted, then its pretty safe to use it on the mainnet without giving up on whatever fixes and upgrades might be soon coming. But if you use it and have issues I’d love to find out. The relay.js repo has literally everything you need to get started — dockerfiles, deploy scripts, the contract itself and a relay (also called a bouncer proxy). I’d appreciate any feedback or bug reports going to the Issue tracker there.

EDIT: As of 5/28/2021 ZRC3 has been accepted as a standard.

--

--

Starling Foundries
Builders of Zilliqa

Errant scientist with solutions looking for problems. I like blockchains and geoscience the most.