Skip to main content

Claiming mainchain ZEN

This section covers the steps to follow to claim old mainchain ZEN balances after the migration. There is no time window in place for claiming tokens.

Prerequisites

Before going through the claim process you will need to have some ETH on Base L2 to process the claim. Ensure you have at least 0.000004 ETH per claim to cover the necessary transaction costs. If you have multiple claims, the total amount in your wallet must be sufficient to cover all of them.

Option 1: Use a Centralized Exchange

The most straightforward way to get ETH on Base is by using a centralized exchange like Coinbase or Binance.

  1. Buy ETH on your preferred exchange.
  2. Withdraw it to your Base wallet address (e.g., MetaMask).
    • If using Coinbase or Binance, select "Base" as the destination network.
    • For other exchanges, you may need to first withdraw to Ethereum and then bridge to Base using a third-party bridge (see Option 2).

Option 2: Bridge ETH from Ethereum or Other Chains

If your exchange doesn't support direct Base withdrawals, or if you already have ETH on another network (like Ethereum Mainnet, Arbitrum, or Optimism), you can bridge ETH to Base using Superbridge.

  1. Connect your wallet (e.g., MetaMask).
  2. Choose the source network and Base as the destination.
  3. Enter the amount and confirm the bridge transaction.
  4. ETH will typically arrive on Base within a few minutes.

Option 3: Buy ETH Using Credit Card

Some services allow you to buy ETH directly on Base using a debit card, credit card, or other local payment methods. Popular onramps include MoonPay or Onramp. Note that these services usually require a minimum purchase of $20-$25. Once ETH is obtained, bridge it to Base using Option 2.

To confirm that you've received ETH on Base, check your wallet or visit https://basescan.org.

Who needs to execute the manual claim?

Only ZEN balances coming from the old ZEND Mainchain will need to be manually claimed. If you only had funds on EON Chain, your balances will be automatically moved to the same address in the new chain, and no manual operation will be needed.

Why is the manual claim needed?

A simple manual claim of the funds is required because the address format on the two chains is different (Bitcoin-format in the old Horizen chain, Ethereum format in the new Horizen chain). The on-chain UTXO structure does not track the original key/address owning it, making it impossible to automatically map between old and new addresses.

How to claim

The old mainchain is a Bitcoin-like chain, where funds are locked in multiple cryptographic "boxes" called UTXO. To unlock funds, you will need to generate a signature of a specific message with the same private key able to "unlock" the corresponding UTXOs. Two additional methods are developed to allow a "direct" claim that doesn't need any signed message. The claim will then be performed on-chain, by calling a method on the official Horizen migration contract. Here the details about the method call:

Pay-to-pub-key hash Claim

This is the most common format of UTXO, used for example by the Horizen Sphere Wallet and many other non-custodial wallets. The Solidity method to claim this kind of UTXOs is the following:

function claimP2PKH(address destAddress,
bytes memory hexSignature,
PubKey calldata pubKey) public

Parameters

  • destAddress — Destination address on Base of the funds to be claimed. This can be generated by any keypair (using the same previous private-key is not mandatory), and can be different from the tx caller.
  • hexSignature — ECDSA/Secp256k1 Signature generated with the private key associated with the UTXOs to claim, of the following message: ”ZENCLAIM” + destAddress (destAddress here is represented in the hex form according to EIP-55, with “0x” prefix. No space has to be inserted after “ZENCLAIM” string).
  • pubKey — Public key associated to the same private key used for the signature and owning the UTXOs. Must be always sent in uncompressed form: the PubKey data structure is composed of two bytes32 fields, that represents two components x and y (first 32 bytes and second 32 bytes).

Events emitted

After a successful claim the following event will be emitted:

event Claimed(address destAddress, bytes20 zenAddress, uint256 amount)

Pay-to-script-hash Multisig Claim

This kind of UTXO is associated to multi-signature wallets. Note: we do not support any other type of Pay-to-script-hash different from the Multisig one.

The Solidity method to claim this kind of UTXOs is the following:

function claimP2SH(address destAddress,
bytes[] memory hexSignatures,
bytes memory script,
PubKey[] calldata pubKeys)

Parameters

  • destAddress — Destination address on Base of the funds to be claimed. This can be generated by any keypair (using the same previous private key is not mandatory), and can be different from the tx caller.
  • hexSignatures[] — List of ZEND ECDSA/Secp256k1 Signatures generated with the private keys associated to the multisig address, of the following message: ”ZENCLAIM” + zen_multisig_address + destAddress
    • zen_multisig_address: hex representation of zen multisig address derived from this script (base58check decoded representation without chain prefix (leading 2 bytes removed, so in total 20 bytes) prepended with “0x” prefix)
    • destAddress: destination address represented in the hex form according to EIP-55, with “0x” prefix.
    • No space has to be inserted between the three parts of the string.
    • The minimum amount of valid signatures defined in the multisig script must be satisfied (example: 3 out of 5).
    • The list size has always to be equal to the total number of signatures accepted by the script, and in case a signature is not present the corresponding element should be set to a 0 bytes array.
    • For example: if the scripts accepts 2 out of 3 signatures, and we have only A and C signatures, the hexSignatures list will be: [SigA, 0, SigC]
  • script — Full UTXO redeem script.
  • pubKeys — List of public keys accepted by the script. Must be always sent in uncompressed form: the PubKey data structure is composed of two bytes32 fields, that represents two components x and y (first 32 bytes and second 32 bytes). The array length and position of elements must correspond to those defined in the script and to the array of signatures. If a key signature is not present, also the corresponding pubKeys X and Y must both be bytes32(0)

Events emitted

After a successful claim the following event will be emitted:

event Claimed(address destAddress, bytes20 zenAddress, uint256 amount)

Direct Claim - 1st method

This method allows a direct claim of special UTXOs generated deterministically from a BaseAddress. This is a special usecase for users that can't generate a signed message, and requires they create this special UTXO in the old mainchain before the migration.

This method can be invoked by anyone (not mandatory the sender of the claim tx to be the same destination address).

The Solidity method to execute this claim is the following:

function claimDirect(address baseDestAddress) public

Parameters

  • baseDestAddress — Destination address on Base of the funds to be claimed.

Any Base address is a valid baseDestAddress, and that address could claim the funds for the Zend Address generated as such:

  1. Calculate SHA256 hash of the baseDestAddress hex
  2. Calculate Ripemd160 hash of the output from step 1
  3. Concatenate prefix: 0x2089 for ZEND Mainnet or 0x2098 for ZEND testnet
  4. Encode it in Base 58

The procedure could be resumed by the formula: base58.encode(‘0x2089’ + Ripemd160(SHA256(baseDestAddress hex)))

An example Javascript implementation is the following:

const createHash = require('create-hash')
const bs58check = require('bs58check')

const prefix = '2089'
const baseDestAddress = //Base address in string form without '0x' prefix

const ZENDTransferAddress = bs58check.encode(
Buffer.from(
prefix +
createHash('rmd160').update(
createHash('sha256').update(
Buffer.from(baseDestAddress, 'hex')
).digest()
).digest('hex'),
'hex')
)
console.log(ZENDTransferAddress)

The owner of an arbitrary Zend address should migrate its funds on the Zend address generated in this way BEFORE the Zend Migration.

As example, we consider a Zend address owner preparing for the migration that wants to use the direct claim:

  1. He generates a Base wallet and gets its address, for example: 0x6ebacd4a2a48728e98aAAA101C59f2e0c57fA987
  2. He executes the code above with parameter baseDestAddress = 6ebacd4a2a48728e98aAAA101C59f2e0c57fA987. The output is zncwpByDSdYjCw3HipRY8MS5dRRsxSR7AGU
  3. Before the Zend Migration, he sends a transaction to move the ZEN from his original address to the generated one (zncwpByDSdYjCw3HipRY8MS5dRRsxSR7AGU)
  4. After the migration, he (or anyone else) can invoke the method claimDirect(0x6ebacd4a2a48728e98aAAA101C59f2e0c57fA987) on the migration Smart Contract.
  5. The ZEN balance will be restored as ZEN ERC-20 token balance on Base chain on the address 0x6ebacd4a2a48728e98aAAA101C59f2e0c57fA987

Events emitted

After a successful claim the following event will be emitted:

event Claimed(address destAddress, bytes20 zenAddress, uint256 amount)

Direct Claim - 2nd method

This method allows a direct claim of special UTXOs generated deterministically from a BaseAddress. This is a special usecase for users that can't generate a signed message, and requires they create this special UTXO in the old mainchain before the migration. It is similar to the previous 'Direct Claim - 1st method', but here the UTXO is a P2SH 1-of-2 multisig, on which one of the public keys is calculated from the beneficiary Base address. The other public key is a real one, so it is possible for the user to remain in control of their funds on Zend using this owned key.

This method can be invoked by anyone (not mandatory the sender of the claim tx to be the same destination address).

The Solidity method to execute this claim is the following:

function claimDirectMultisig(bytes memory script, address baseDestAddress) public

Parameters

  • script — P2SH redeem Script of the 1-of-2 multisig address to claim
  • baseDestAddress — Destination address on Base of the funds to be claimed.

Any Base address is a valid baseDestAddress, and that address could claim the funds for the multisig Zend Address generated as such:

  1. Calculate SHA256 hash of the baseDestAddress hex
  2. Create a 1-of-2 multisig address using as second public key the hash calculated at Step 1 with "02" as prefix

An example Javascript implementation for the multisig Zend Address calculation is the following:

const zencashjs = require('zencashjs')
const bs58check = require('bs58check')
const createHash = require('create-hash')

const baseDestAddress = //Base address in string form without '0x' prefix

const directMultisigPubKey1 = //Insert any owned public key
const directMultisigPubKey2 = "02"+createHash('sha256').update(Buffer.from(baseDestAddress, 'hex')).digest('hex')

multisigScript = zencashjs.address.mkMultiSigRedeemScript([directMultisigPubKey1, directMultisigPubKey2], 1, 2);
const zenDirectMultisigAddress = zencashjs.address.multiSigRSToAddress(multisigScript);

console.log(multisigScript)
console.log(zenDirectMultisigAddress)

The owner of an arbitrary Zend address should migrate its funds on the multisig Zend address generated in this way before the migration, then invoke claimDirectMultisig method on claim smart contract with multisigScript and baseDestAddress as parameters.

Events emitted

After a successful claim the following event will be emitted:

event Claimed(address destAddress, bytes20 zenAddress, uint256 amount)

Security and Audits

Important: Only use the official claim page: https://www.horizen.io/zenclaim. Be on the lookout for scams or nefarious actors sending you to other websites which may look like Horizen's. Always verify the URL when claiming.

The smart contracts have been audited by two independent firms and the claim tools have also been fully audited: