Asset to ERC1155 Bridge

From ArdorDocs
Jump to: navigation, search
This page contains changes which are not marked for translation.

Introduction

This is a tutorial for developers, who want to run a peg between Ardor assets and Ethereum tokens. It will introduce you to a new feature on Ardor platform - Asset to ERC-1155 Bridge - a contract for maintaining peg of ERC-1155 tokens to Assets on Ardor. The ERC-1155 tokens are considered a "wrapped" version of the Ardor assets, which exists on the Ethereum or some other EVM-compatible network. Please note that TestNet was used for the tutorial, but the steps remain the same for MainNet, where the only difference is using real currency/tokens.


Peg Initialization

1. Get an Ethereum full node

Install and sync an Ethereum full node, register with Infura or similar provider. Other EVM-compatible networks are also supported.

2. ERC-1155 tokens minting

Skip this step if auto-minting (autoMintErc1155) is going to be enabled.

For each asset which is going to be wrapped, mint a token with some ERC1155-compatible Ethereum contract. Remember that the amount of the minted ERC-1155 tokens must not exceed the asset quantity in circulation on the Ardor platform.

3. Install Ardor

Go to Ardordocs or Jelurida website do download the latest version of Ardor.

4. Handle the missing dependencies

  • Run the exportLibsMissingInArdor task in this project:
./gradlew exportLibsMissingInArdor
  • Copy the libraries from contract/libs-exported

LibsExportCopy.jpg

to the lib directory inside the Ardor installation directory.

LibsExportPaste.jpg

5. Configure the Ardor node for running the contract

Add the following configuration to the nxt.propertiesfile of the Ardor installation.

nxt.addOns=nxt.addons.ContractRunner
nxt.disableSecurityPolicy=true

Warning! Allowing connections to the Ardor node from outside its local machine is a bad idea. The triggerContractByRequest API, which is used to execute commands to the peg contract, is password protected. So executing any command requires the caller to have the Ardor node adminPassword, which means that he can modify the Ardor node in many ways including getting DB access to it.

The recommended approach is to develop a small proxy backed which to run on the same server as the Ardor node and which to expose only the getUnwrapDepositAddress and processUnwrapsForAccount commands.

Additionally, with Infura all requests to the Ethereum API are counted against a quota. The processUnwrapsForAccount command calls the Ethereum API even if there is no unwrap transfer to execute. So if using Infura, the backend must also implement some restriction on the calls to processUnwrapsForAccount.

6. Create Contract Runner configuration

The configuration is a JSON file, located in contract/src/test/resources/test_contract_config.json. Rename it to AssetsErc1155Peg.json and open it.

6.1. Secrets

Generate strong secrets for ethereumDepositAccountsSecret, ethereumBlockedAccountSecret and the contract account, which will act as Ardor Blocked Account and will own all locked tokens which are in circulation on Ethereum side. Set ethereumDepositAccountsSecret and ethereumBlockedAccountSecret to the generated values.

6.2. accountRS

Set in accountRS the address of the Ardor Blocked Account. To get the address login with the generated Ardor passphrase.

6.3. apiUrl

Set in apiUrl the URL of the HTTP API of your Ethereum full node. Other EVM-compatible networks like Polygon are also supported.

6.4. chainId

Set in chainId the chain ID of the Ethereum network as required by ERC-1155. A list of the chain IDs of the popular EVN-compatible networks is available here.

6.5. ethLogsBlockRange

Set in ethLogsBlockRange the max size of the block range allowed by the Ethereum full node when calling eth_getLogs. Value of 0 makes all eth_getLogs requests to be executed from the first to the latest block in the blockchain.

6.6. ethLogsIterations

Set ethLogsIterations to more than 1 to execute the calls to eth_getLogs in a loop. Each call will cover a subsequent block range of ethLogsBlockRange. Use this in case the full node has an unacceptable limit on the block range. The downside is that ethLogsIterations requests will be made when getting the logs in order to check for pending unwrapping or already processed wrappings. Make sure that ethLogsBlockRange * ethLogsIterations * average_block_time is at least 5-6 hours to allow users to process their unwrapping deposits.

6.7. Confirmations

Adjust ardorConfirmations and ethereumConfirmations - these are the number of blocks that the peg contract will wait before considering a transaction confirmed.

6.8. Fast Wrapping

Fast wrapping is enabled if fastWrappingConfirmations is set to less than ardorConfirmations.

6.8.1. fastWrappingMinDeadline

If Fast wrapping is enabled, set in fastWrappingMinDeadline the minimal transaction deadline required for the transaction to be eligible for fast wrapping.

6.9. Ardor Fees

ardorInitialFeeOverpayPercent and ardorRetryFeeOverpayPercent are percentage with which to increase the Ardor transaction fee initially and each time a transaction expires, without being accepted in a block. The default transactions deadline is 15 min. ardorInitialFeeOverpayPercent is a string and may be negative, in which case the initial fee is below the automatically calculated by the node fee.

6.10. ethereumRetryTimeout

Time in seconds to wait for Ethereum transaction to be accepted in a block before retrying with higher fee.

6.11. Ethereum Fees

ethereumGasPriceInitialOverpay and ethereumGasPriceRetryOverpay are percentage with which to increase the Ethereum transaction fee initially and each time a transaction is not accepted before ethereumRetryTimeout. ethereumGasPriceInitialOverpay is string and may be negative in which case the starting gas price is below the one calculated by the Ethereum node

6.12. contractAddress

Set in contractAddress the address of the contract which will manage the wrapped assets on Ethereum side. For example the contract address of OpenSea on Polygon Mumbai testnet is 0x2953399124F0cBB46d2CbACD8A89cF0599974963

6.13. Auto-minting

To enable the auto-minting of wrapping ERC-1155 tokens, set autoMintErc1155 to true. When auto-minting is enabled, the tokens on ethereum side are automatically minted when a wrapping is complete and burned when un-wrapping is complete. For this, the contract specified in contractAddress must implement the ERC1155MintBurn interface. Use the deployEthContract command before contractAddress is set to deploy a basic implementation of such auto-mintable contract provided in ERC1155MintBurn. Then set the ID of the deployed contract in contractAddress

6.13.1 autoMintWhitelistedIssuers

If auto-minting is enabled, set in autoMintWhitelistedIssuers an array of Ardor account IDs specifying the asset issuers whose assets will be processed by AssetsErc1155Peg.

6.14. assetIdToErc1155IdMap

If auto-minting is disabled, set in assetIdToErc1155IdMap a JSON object mapping between the known asset IDs on Ardor and the ERC-1155 tokens IDs. The JSON keys must contain asset IDs as unsigned integer strings. The values must contain the corresponding ERC-1155 token IDs as unsigned integer string, or a string with format '0x' followed by up to 64 hex chars (32 bytes in hex format).

Example:

JsonExample.jpg

7. Start and configure the Contract Runner

Start Ardor, create or use an existing Asset, but make sure that the asset decimals are 0 and the asset quantity does not exceed the amount of the minted ERC-1155 tokens in circulation. Set the configuration built on step 6. See How to configure the Contract Runner and use the Node Processes UI in order to utilize the encryption supported by it.

If auto-minting is enabled, start Ardor with empty contractAddress, execute the deployEthContract command, set the result in contractAddress and restart Ardor.

8. Deploy the AssetsErc1155Peg contract

Before deploying, navigate to contract/conf and create nxt-deployContract.properties file, since the task reads the Contract Manager configuration from it. Set at least contract.manager.secretPhrase property to the previously generated secret of the Ardor Blocked Account. The deployment can be executed remotely by setting the contract.manager.serverAddress property.

Warning! Make sure the Ardor Blocked Account secret is secured - delete it from nxt-deployContract.properties or delete the whole file. This file is used only by the deployContract task.

Deploy the contract by executing the deployContract gradle task:

 ./gradlew deployContract

9. Get the peg addresses

This step can be skipped if auto-minting (autoMintErc1155) is enabled.

With the Contract Runner started on the Ardor node, call the triggerContractByRequest API with command getPegAddresses and you'll get response like:

{
  "ardorBlockedAccount": "ARDOR-J24K-QWD8-XHEX-36P7X",
  "ethereumBlockedAccount": "0xd1a49a09d7d1be11108f2629859695aec32f3e2b",
  "requestProcessingTime": 0 
}

PegAddress.jpg

See the Commands Reference section for more details about the available commands.

10. Transfer all minted ERC-1155 tokens to the Ethereum Blocked Account

This step can be skipped if auto-minting is enabled.

The Ethereum Blocked Account address is returned in ethereumBlockedAccount.

This way the peg contract is initialized in fully unwrapped state - all tokens are in circulation on Ardor side and can be wrapped into ERC-1155 tokens. Alternatively if all Ardor assets are transferred to the ardorBlockedAccount, and the ERC-1155 tokens are released in circulation on Ethereum side, the contract will be in "fully wrapped" state.

11. Token Metadata

If you are deploying your own contract (which is likely the case when auto-mint is enabled), it is a good idea to allow clients to get metadata about the wrapping tokens on Ethereum side. This must be done according to the ERC-1155 metadata specification.


URI tutorial

The uri is specified when executing deployEthContract.

For the purposes of the current document, IPFS will be used to host the metadata. Our goal is to be able to dynamically add new assets, without re-deploying the Ethereum contract. Therefore "ipfs://<CID>/" url scheme cannot be used here, since the CID of the folder/file changes with each edit. We need to use IPNS. Unfortunately, by experimenting, we discovered that currently URLs with "ipns://" schemes are not supported by OpenSea, so our final solution is to use an IPFS gateway and set an URL with "https" schema as contract URI.

We aim that our URI looks like this (we'll use the ipfs.io gateway, this could be changed with other gateways):

https://ipfs.io/ipns/<ipfs_public_key>/{id}/metadata.json

To obtain the ipfs_public_key, execute ipfs name publish <CID_of_the_root_directory> (nft folder in this example). Do this every time a change has happened in that directory, so the data can be published.

ipfs name publish QmabN9zQoLtYWCUMuGpJ9qUTc4yCSu1RmsxgqvLqDH44Xc

Uri tutorial1.jpg

Uri tutorial2.jpg

You will get a response:

Published to k51qzi5uqu5dgjvdga00na9szdfos2asdqzm0uz2452rq1l4462fkz8t5um3rz: /ipfs/QmabN9zQoLtYWCUMuGpJ9qUTc4yCSu1RmsxgqvLqDH44Xc

This is the public key - k51qzi5uqu5dgjvdga00na9szdfos2asdqzm0uz2452rq1l4462fkz8t5um3rz. So the final uri should look like this:

https://ipfs.io/ipns/k51qzi5uqu5dgjvdga00na9szdfos2asdqzm0uz2452rq1l4462fkz8t5um3rz/{id}/metadata.json

Metadata configuration example, when auto-mint is enabled

IPFS will be used to store the metadata.

Create a json file (metadata.json for example) and populate it according to the ERC-1155 specification.

{
  "name": "test mint",
  "description": "test text test text test text",
  "image": "ipfs://QmXRxXD3FUeXfd24x4LUZXjZ9rEdLSg2UCa2KHYoMdg4rp/mint.jpg",
  "properties": {
    "simple_property": "test value",
    "rich_property": {
      "name": "test-mint",
      "value": "100",
      "display_value": "100",
      "class": "emphasis",
      "css": {
        "color": "#ffffff",
        "font-weight": "bold",
        "text-decoration": "underline"
      }
    },
    "array_property": {
      "name": "Name",
      "value": [1,2,3,4],
      "class": "emphasis"
    }
  }
}

Deploy IPFS node and create a folder - nft for example. Create another folder inside and name it to the ID of the Ardor Asset in hexadecimal format - use autoMintIdConvert command to convert the decimal asset id to hex. Put the metadata.json file in the hex-named directory.


Metadata-tutorial-new.jpg

Remove the contractAddress and enable the auto-minting feature.

Auto-mint-tutorial1.jpg


Upload the contract in Ardor and execute the deployEthContract command.

DeployEthContract example.jpg


The uri from the URI tutorial will be used in this example:

{"command":"deployEthContract","uri":"https://ipfs.io/ipns/k51qzi5uqu5dgjvdga00na9szdfos2asdqzm0uz2452rq1l4462fkz8t5um3rz/{id}/metadata.json"}


You will get ethContractAddress:

{
   "requestProcessingTime": 18846,
   "ethContractAddress": "0xf32f1b4bf84dfa4a1d17722c7b502353bbc14eab"
}


Use it in the contractAddress and don't forget to add autoMintWhitelistedIssuers Ardor accounts:

Auto-mint-tutorial2.jpg


Re-upload the contract to Ardor again and deploy it by executing:

./gradlew deployContract

Finally, transfer some Ardor assets to the [#6.2._accountRS|Ardor Blocked Account] and add the Ethereum address as message, where to receive the freshly minted tokens.

12. Wrapping test

If auto-minting is enabled, wrapping happens when any asset, issued by some of the accounts in the autoMintWhitelistedIssuers list is transferred to the Ardor Blocked Account. The result is a freshly minted ERC-1155 token on Ethereum side with ID equal to the asset ID.

If auto-minting is disabled, wrapping happens when an asset, which is supported according to the assetIdToErc1155IdMap configuration, is transferred to the [[#6.2._accountRS|Ardor Blocked Account].

The transaction must contain in its message the Ethereum address which to receive the wrapped tokens. It is recommended to use the Encrypted Message functionality in Ardor in order to preserve privacy.


Test Example:

Note: if auto-mint is disabled, make sure that all minted ERC-1155 tokens are transferred to the Ethereum Blocked Account.

Issue an asset with some account. The asset must be with 0 decimals. Depending on whether auto-minting is enabled or not, add the issuer account to autoMintWhitelistedIssuers or map the asset id to an ERC-1155 token id in assetIdToErc1155IdMap. Then transfer some (or all) of the asset quantity to the Ardor Blocked Account, adding a Message (encrypted is advised for privacy), containing the Ethereum account address, which to receive the wrapped tokens.

TransferAssetToABA.jpg

After the configured ardorConfirmations and ethereumConfirmations had passed, the Ethereum tokens should be transferred to the recipient Ethereum account.

Fast Wrapping

A fast wrapping option is available in case the pegged tokens don't have significant economic value. If enabled, the wrapping transactions are confirmed after fewer blocks (configurable via fastWrappingConfirmations) if the following conditions are met:

  • The transaction deadline is greater than fastWrappingMinDeadline
  • The transaction Economic Clustering (EC) block is more than ardorConfirmations blocks before the transaction height
  • The transaction is not phased
  • The transaction is not referencing other transaction

These restrictions are meant to reduce the chances for a transaction to become invalid in case of spontaneous blockchain reorganization. An attacker with enough forging power can still invalidate such transaction and do a double spend. So the fast confirmation mode must be used only for low-value assets.

13. Unwrapping test

Unwrapping is triggered by a call to processUnwrapsForAccount, but before this call the tokens to unwrap must be transferred to an UnwrapDepositAccount - an Ethereum account dedicated to and generated for the Ardor account which will receive the unwrapped assets.

To get the UnwrapDepositAccount address use the getUnwrapDepositAddress command.


Example: Use getUnwrapDepositAddress command to get the depositAccount address of the account, where the wrapped ERC-1155 tokens to be received.

{"command":"getUnwrapDepositAddress","ardorRecipientPublicKey":"85e2097c2365d5a33a872d465dfc8425dcc394147ab69988fb937be668266f59"}
{
   "depositAddress": "0x13ebd674b0001dd8f6cfeaba94131034524fd03f",
   "requestProcessingTime": 1
}


Transfer the wrapped ERC-1155 tokens to the depositAccount address and make sure they are received.

Call processUnwrapsForAccount for the Ardor account, where the wrapped tokens are transferred.

{"command":"processUnwrapsForAccount","ardorRecipientPublicKey":"85e2097c2365d5a33a872d465dfc8425dcc394147ab69988fb937be668266f59"}
{
   "requestProcessingTime": 1397
   "starts": 1
}

If started successfully, you will observe "starts": 1 in the Response.

Navigate to Ardor Dashboard where you should see an ongoing Asset transfer transaction. Then open the Ethereum Blocked Account, where the unwrapped ERC-1155 tokens are received.

The ERC-1155 tokens will be burned, if auto-mint is enabled.


Commands reference

The AssetsErc1155Peg supports several commands through the triggerContractByRequest Ardor API.

In all cases the contractName parameter of that API must be set to AssetsErc1155Peg.

The rest of the parameters can be provided either as JSON object in the setupParams parameter, or as separate parameters.

A mandatory command parameter is used to distinguish the different operations supported by AssetsErc1155Peg. Each command has its own set of additional parameters.

Example:

ArdorAPI.jpg


The following sub-sections contain a reference of the supported commands:


getPegAddresses

Returns the addresses of Ardor Blocked Account and Ethereum Blocked Account.


No parameters required


Response
{
  "ardorBlockedAccount": "ARDOR-...", // The address of the Ardor Blocked Account
  "ethereumBlockedAccount": "0x..."// The address of the Ethereum Blocked Account
}


getUnwrapDepositAddress

Returns the address for depositing ERC-1155 tokens in order to unwrap them back to Ardor. This address is specific for each Ardor account.


Parameters
  • ardorRecipientPublicKey The public key of the Ardor account for which an address for unwrapping is generated


Response
{
 "depositAddress": "0x..." //An Ethereum address
}


processUnwrapsForAccount

  1. Calculate the UnwrapDepositAccount similarly to getUnwrapDepositAddress
  2. Gets all transfers to the UnwrapDepositAccount in the last 100000 blocks
  3. Filter out any transfers for which the unwrapping is currently being processed or is completed
  4. Start a background unwrap task for any transfers left


Parameters
  • ardorRecipientPublicKey The public key of the Ardor account to which to send the unwrapped tokens


Response

A JSON object with the number of tasks started or filtered out transfers by filter category. Fields with value 0 are not returned

{
  "starts": 0123456789, //Number of tasks started by the current request
  "skippedAlreadyPending": 0123456789, //Transfers skipped during the request because there is already a background task processing them
  "skippedCompleted": 0123456789, //Transfers which were already completed and there is an outgoing Ardor transaction for them
  "unknownTokens": 0123456789 // Transfers of tokens not in the assetIdToErc1155IdMap configuration
}

processWrapsAtHeight

Utility command to manually process the wrapping at some height. There is no check about whether the transactions at this height were already processed, so special care must be taken to not process same asset transfers twice.

This command returns before the transaction was confirmed on Ethereum side. Use the getWrappingLog command to poll the final result of the wrapping.


Parameters
  • height Height of the ardor blockchain for which to process the wrappings


Response
{
   "transactionsLog": [ //An array of objects
       {
           "fullHash": "...", //fullHash of the Ardor transaction which transfers assets to the Ardor Blocked Account and triggered the wrapping
           "error": "...", //Error message in case of error before the transfer was initiated on Ethereum side
       }
   ],
   "requestProcessingTime": 4
}


getWrappingLog

Returns a log of all wrappings processed during the current execution of the contract.


No parameters required


Response
{
   "log": [
       {
           "fullHash": "...", //fullHash of the Ardor transaction which transfers assets to the Ardor Blocked Account and triggered the wrapping
           "error": "...", //In case the wrap failed
           "success": "0x..." //Transaction ID on Ethereum side
       }
   ],
   "requestProcessingTime": 2
}


secretToEthPrivateKey

Utility for computing the ethereum private key from secret string. The private key can then be imported in e.g. Metamask.

To get the private key of an unwrapping deposit account, concatenate ethereumDepositAccountsSecret from the peg configuration and the ardor public key of the account.

For example if ethereumDepositAccountsSecret is

harmony repeat ourselves woman empty outside liquid truth journey shiver mention curl

and the public key of the account is

0507916d19b81d9f714c4f9eaf7ad4742b013c106c34cb9d2fb663d5e101df75

Then the secret parameter should be

harmony repeat ourselves woman empty outside liquid truth journey shiver mention curl0507916d19b81d9f714c4f9eaf7ad4742b013c106c34cb9d2fb663d5e101df75


Parameters
  • secret A string


Response
{
  "privateKeyHex": "0x...", //Private key in hex format. This can be used with Metamask
  "privateKeyNumeric": "..." //Same private key as decimal string 
}


autoMintIdConvert

Utility for converting between the decimal asset IDs used in Ardor to hexadecimal format, primarily used in Ethereum. Even though the IDs of the auto-minted Ethereum tokens which wrap the Ardor assets are equal to the Ardor asset ID, in some cases, for example when ERC-1155 metadata is handled, the ID is needed in hexadecimal format. This command simply converts from decimal to hexadecimal or vice versa.


Parameters
  • assetId ID in decimal format as used in Ardor

OR

  • tokenId ID in hexadecimal format as appearing in Ethereum. The "0x" prefix is optional


Response

If assetId is provided:

{
 
 "tokenIdHex": "0x...", //hex string
 "erc1155MetadataId": "0000000000000000000000000000000000000000000000000123456789abcdef...", //0-padded hex string as expected by the 
  metadata specification
 "requestProcessingTime": 1
}

Or if tokenId is provided:

{
 "assetId": "...", //Ardor asset ID as unsigned decimal string
 "requestProcessingTime": 1
}


deployEthContract

Deploys a contract specifically developed to support auto-minting of ERC-1155 tokens for wrapping Ardor assets.

To prevent abuse or errors, this API is available only if the contractAddress is empty.


Parameters
  • uri The URI which will be returned by the contract to clients for getting tokens metadata. See the ERC-1155 metadata specification.


Note that the parameter is uri, not url, please make sure it is typed correctly.


Response
{
 "ethContractAddress":"0x54c8d419a5b9d2d6ff68ff826258456ee37e214b",
 "requestProcessingTime":16743
}