By Benjamin Fuentes


Layer 1 blockchains are safe and resilient to failures but have a low transaction per second (TPS) count and can suffer during activity peaks.

Layer 2 solutions such as sidechains can help to resolve these problems. A sidechain is a secondary blockchain that is paired to a parent blockchain and is able to communicate with it. A sidechain works like any blockchain and has the same core properties, with the added feature that its consensus is witnessed by the main chain.

A bridge is needed between the layer 1 and the layer 2 to ensure that tokens are safely mirrored on both sides.


At Marigold, we develop a sidechain solution and a native Tezos bridge:

- Deku-Canonical (Deku-C): a Tezos sidechain implementation;
- TzPortal: a multi-asset and multi-layer 2 bridge for Tezos. The bridge currently supports Deku as well as experimental layer 2 solutions like Chusai and TORU.

Supported assets

You will have to send assets from layer 1 to layer 2. Deku supports Tezos tickets so you can swap your L1 tokens against tickets to be sent to the L2.

Deku instances

You can either run your own Deku-C or use an existing instance on Jakartanet. We recently released an alphanet version of Deku on Jakartanet, which you can already freely use.

Information source

For a local setup, you would need to clone the deku repository and start your own instance on your machine. Instructions are provided in the project’s README.



We will now demonstrate TzPortal and Deku-C alphanet on Jakartanet. As Deku-C is still not supported by any mainstream wallet, you will have to use the command-line client to interact with the chain.


  1. First, you need an account/wallet for Jakartanet. Follow this link to install Temple wallet). ~It will create your first account~ Follow the instructions in Temple to     create your account.
  2. Get some free Tez from the Marigold Faucet (video tutorial); don’t forget to select Jakartanet network.
  3. You can set the address of TzPortal smart contract on your local shell for later commands:
  4. You will have to install nix and clone the deku-c repository
Information source

Tezos (L1) and Deku-C (L2) support both Ed25519 (aka tz1) and Secp256k1 (aka tz2) cryptographic curves. However, an address in Tezos doesn't exist on Deku-C until you create it. There is no import of private key in the current alphanet, so you would create distinct addresses in Deku-C.

1. Connect to TzPortal

Go to TzPortal (Jakarta) website https://jakartanet.tzportal.marigold.dev/


Click on Connect button to login with your account on Temple wallet.

2. Deposit some assets on Deku

Click on the Deposit Menu on top

In the following picture, we are connected with the tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb account (aliased as alice in tezos-client), and we list its assets on the left-side panel.


Now, we need to create a L2 address on Deku side. To generate a new account on Deku with the deku-cli, use

nix run .#deku-cli create-wallet

Do this twice and you get 2 output files with the following form:


On the first launch, nix will compile binaries and it could take a few minutes. Take a coffee


Let's send 0.00001 tz to our first account (tz1QuW9jPNiiVZ1zEmNCh6jK81934xp5C23U)


On the right panel, the Deku contract, you can se the global vault updated with +10 mutez represented by 10 XTZ tickets.

Information source

About quantity: the number of tickets can be misleading, as tickets quantity are naturals. The quantity of a given ticket corresponds to the quantity of the smallest decimal of your token:

tokenQuantity * 10^tokenSmallestDecimal = ticketQuantity

E.g.: 10 XTZ * 10^6 = 10,000,000 because XTZ has 6 decimals, so you will get the corresponding ticket; for XTZ, it is equivalent to mutez.

Information sourceAbout the Vault: it represents the global amount of ticket per token type

To know the balance of tz1QuW9jPNiiVZ1zEmNCh6jK81934xp5C23U, you also need to use the deku-cli:

nix run .#deku-cli get-balance -- --deku-node=https://peer-0.deku.marigold.dev tz1QuW9jPNiiVZ1zEmNCh6jK81934xp5C23U "(Pair \"$TICKETER\" 0x050505030b)"

Your balance should have increased by +10 XTZ tickets.

Information source

About ticket data field: 0x050505030b represents the "data" field of the XTZ tickets. For each token, data is packed as a specific series of bytes. For FA1.2 and FA2 its stores the address of the FA contract (so it differs from network to network)
For example, on Jakarta, "data" bytes are currently :

* XTZ : 050505030b (constant for all networks)
* kUSD : 0505080a00000016018b12e8b269f8658f6a8fb8005fb80d5145c32b2300
* uUSD : 0505080a00000016015d9c0a0f53eafb7ac93a2999a864187a74483ffa00
* EURL : 0505080a0000001601e1ca886c0383acc28be558b269155cc5fdc5795700

3. Do a layer 2 transfer

We will do a transfer from our first deku address tz1QuW9jPNiiVZ1zEmNCh6jK81934xp5C23U to our second deku address tz1XA2v9rgwheKay4TF3c3bzrpQgoQsd4rdC . Sending 1 XTZ ticket:

nix run .#deku-cli create-transaction -- --deku-node=https://peer-0.deku.marigold.dev tz1QuW9jPNiiVZ1zEmNCh6jK81934xp5C23U.tzsidewallet tz1XA2v9rgwheKay4TF3c3bzrpQgoQsd4rdC 1 "(Pair \"$TICKETER\" 0x050505030b)"

Check new balance of tz1XA2v9rgwheKay4TF3c3bzrpQgoQsd4rdC it can take up to 10 seconds to show a difference)

nix run .#deku-cli get-balance -- --deku-node=https://peer-0.deku.marigold.dev tz1XA2v9rgwheKay4TF3c3bzrpQgoQsd4rdC "(Pair \"$TICKETER\" 0x050505030b)"
Information source

The current block time on Deku-C alphanet is 5 seconds; depending on when your transaction was received, it might get included in the next block and not executed immediately. Block time in the stable release of Deku-C will probably be lower.

4. Withdraw your tickets

Last step, you want tz1XA2v9rgwheKay4TF3c3bzrpQgoQsd4rdC to withdraw 1 ticket and get back the corresponding 1 mutez to alice:

nix run .#deku-cli withdraw -- --deku-node=https://peer-0.deku.marigold.dev tz1XA2v9rgwheKay4TF3c3bzrpQgoQsd4rdC.tzsidewallet tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb 1 "(Pair \"$TICKETER\" 0x050505030b)"

The deku-cli prints out an operation hash, which you will need again later. Store the operation hash in a variable, e.g.


Withdrawal from the layer 2 works in several steps: after sending a withdraw transaction to Deku, you need to ask for a proof that the withdrawal was executed, which you will give back to TzPortal. Ask for the proof now:

nix run .#deku-cli withdraw-proof -- --deku-node=https://peer-0.deku.marigold.dev "$WITHDRAW_HASH" "$TICKETER%withdrawDEKU"

Now you have a proof, keep it and go to TzPortal Withdraw menu  !

(Pair (Pair "KT1JvEZvSuhFCiwwDV6HdCxbRcqbY9J46zKV%withdrawDEKU"
            (Pair (Pair 1 0x050505030b) 15 "tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb")
      { Pair 0x168da41d8c78c90c016873b6b98a6a9652d8561c2b3a323e93c43b8657021e80
             0x811b4c64da06d3992304d4bf2ca18d356a18a1471268ea32f31ff10dc799b303 ;
        Pair 0x98c351de2773f477523827b4148d03bea7b2a6ee59f4ed57874ba41bc203a71d
             0x2c8f6e1c858496f5199f75cd66aa4e98c94378a159776113a0e75323d134845d ;
        Pair 0x2b1cd3faa4794fc01cf6722c6054af8db7c9b344b8d6363cca78f4be586a80f6
             0xab83ae578d594ffe1f534afa556110ca4f687394560fdbd3303b878a49518724 ;
        Pair 0xcf061b98f9728aad5f3f297dbdccbde3fa3537173ad0e0b8e6ca0801e3d3770a
             0x4c728ea674556eb9bed4aac108c33f94a9c76170ce695a550cb6d69bc02fe54e })

On TzPortal, only the user mentionned in the withdraw command can do withdrawal operations

Enter the same quantity, token type, proof id (below as note 1), proof hash (as note 2) and eventually the proof list (as note 3):


Click on Withdraw button and wait for confirmation.

confetti ball Congratulations, you got back some XTZ! confetti ball



Network node synchronization

If you have this kind of error :

Http error response: (500)

Just retry to send the transaction a few times until it passes. It means that the Tezos node you are pointing too is a bit behind the last block sync.

Vault quantity not refreshing

For FA1.2 and FA2, there is a batch that runs every 15s to pull all pending deposits/withdraw and execute the transactions. It means it is not sync with your browser. Just wait maximum 15s and refresh the page to see last vault quantity.

Bad Deku proof

If you get this error during a withdraw:

unknown handles hash

It means that the proof you entered is wrong. Check again if you have the correct ticket token type, ticket quantity, proof id, proof hash and the list in right order of the proofs.

The issue comes from wrong ticket quantity.

There is a difference between token quantity and ticket quantity, because of the number of decimals. Be sure to multiply the token quantity by it's number of decimals to get the ticket quantity.

You are too fast between the withdraw-proof generation and the TzPortal withdraw button

It takes time for Deku to sync the withdraw from L2 to L1, just retry every 15s to not see the error message as invalid in Temple. It should take less than 30 seconds to synchronize.

Conclusion note

TzPortal V2 is currently under development. It will remove any cumbersome command lines and provide better UX like you can have on Ethereum bridges (Arbitrum, Optimism, StarkGate, ...)

If you want to know more about Marigold, please follow us on social media (Twitter, Reddit, Linkedin)!

Scroll to top