SKALE Chain to SKALE Chain ERC1155 Transfer

As a SKALE chain Owner, there are two main steps to managing SKALE Chain to SKALE Chain (S2S) ERC1155 transfer through IMA:

Once you have completed step 1 to setup ERC1155 tokens with another chain, end-users can transfer ERC1155 tokens between SKALE Chains.

Setup ERC1155 S2S Transfers

The following one-time setup is required for SKALE Chains with a default access control policy (default settings are: whitelisting enabled, automatic deployment disabled). For more information on IMA access control, see here.

0. Assign role

There is a bug in the current IMA version where the CHAIN_CONNECTOR_ROLE is unassigned at deployment. In order to connect other chains to support S2S transfers, you must first assign this role to TokenManagerLinker.

await MessageProxyForSchain.grantRole(CHAIN_CONNECTOR_ROLE, TokenManagerLinker.address)

1. Connect each SKALE Chain

You must connect each chain in both directions by executing two connectSchain() transaction on each respective chain.

⇢ To connect the target chain to the origin chain, run connectSchain(targetSchainName) in TokenManagerLinker contract on the origin chain.

⇠ To connect the origin chain to the target chain, run connectSchain(originSchainName) in TokenManagerLinker contract on the target chain.

Only the REGISTRAR_ROLE in TokenManagerLinker may execute connectSchain. So you must be granted with this role on this contract.
  • IMA-JS

  • Web3 JS

// import & init ima-js here

export async function connectSchain(ima) {
    let address = "YOUR_ADDRESS";
    let privateKey = "YOUR_PRIVATE_KEY";

    let opts = {
        address: address,
        privateKey: privateKey // remove privateKey from txOpts to use Metamask signing
    };

    await ima.schain.tokenManagerLinker.connectSchain(schainName, opts);
}
const Web3 = require('web3');
const Tx = require('ethereumjs-tx').Transaction;

let schainABIs = require("[YOUR_SKALE_CHAIN_ABIs]");
let privateKey = Buffer.from('[YOUR_PRIVATE_KEY]', 'hex')
let account = "[YOUR_ACCOUNT_ADDRESS]";
let schainEndpoint = "[YOUR_SKALE_CHAIN_ENDPOINT]";
let chainId = "ORIGIN_CHAIN_ID";
let targetSchainName = "TARGET_SKALE_CHAIN_NAME";

const tokenManagerLinkerAddress = schainABIs.token_manager_linker_address;
const tokenManagerLinkerABI = schainABIs.token_manager_linker_abi;

const web3 = new Web3(new Web3.providers.HttpProvider(schainEndpoint));

let contract = new web3.eth.Contract(
  tokenManagerLinkerABI, 
  tokenManagerLinkerAddress
);

/* 
 * Prepare connectSchain
 */
let connectSchain = contract.methods.connectSchain(targetSchainName)
  .encodeABI();  

//get nonce
web3.eth.getTransactionCount(account).then((nonce) => {
  //create raw transaction
  const rawTx = {
    chainId: chainId,
    nonce: "0x" + nonce.toString(16),
    from: account, 
    nonce: "0x" + nonce.toString(16),
    data : connectSchain,
    to: tokenManagerLinkerAddress,
    gasPrice: 100000000000,
    gas: 8000000
  }

  //sign transaction
  const tx = new Tx(rawTx);
  tx.sign(privateKey);

  //serialize transaction
  const serializedTx = tx.serialize();

  //send signed transaction
  web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).
    on('receipt', receipt => {
      //record receipt to console
      console.log(receipt);
   }).
    catch(console.error);
});

2. Deploy ERC1155 on Origin

Deploy your ERC1155 contract on the Origin chain.

// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;

import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155.sol";
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";

contract ERC1155Example is AccessControlEnumerable, ERC1155 {

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor(
        string memory uri
    )
        ERC1155(uri)
    {
        _setRoleAdmin(MINTER_ROLE, MINTER_ROLE);
        _setupRole(MINTER_ROLE, _msgSender());
    }

    function mint(
        address account,
        uint256 id,
        uint256 amount,
        bytes memory data
    )
        external
    {
        require(hasRole(MINTER_ROLE, _msgSender()), "Sender is not a Minter");
        _mint(account, id, amount, data);
    }

    function mintBatch(
        address account,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    )
        external
    {
        require(hasRole(MINTER_ROLE, _msgSender()), "Sender is not a Minter");
        _mintBatch(account, ids, amounts, data);
    }
}

3. Deploy ERC1155 clone on Target

The ERC1155 clone on the target chain MUST have both mint and burn functionalities. If the original ERC1155 contract on origin does not have burn functionalities, then you must add burn() to the clone.

// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;

import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol";
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";

contract ERC1155Example is AccessControlEnumerable, ERC1155Burnable {

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor(
        string memory uri
    )
        ERC1155(uri)
    {
        _setRoleAdmin(MINTER_ROLE, MINTER_ROLE);
        _setupRole(MINTER_ROLE, _msgSender());
    }

    function mint(
        address account,
        uint256 id,
        uint256 amount,
        bytes memory data
    )
        external
    {
        require(hasRole(MINTER_ROLE, _msgSender()), "Sender is not a Minter");
        _mint(account, id, amount, data);
    }

    function mintBatch(
        address account,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    )
        external
    {
        require(hasRole(MINTER_ROLE, _msgSender()), "Sender is not a Minter");
        _mintBatch(account, ids, amounts, data);
    }
}

4. Register ERC1155 in TokenManager on Origin

You need to register the origin token contract using the addERC1155TokenByOwner method in the TokenManagerERC1155 contract. addERC1155TokenByOwner(targetSchainName, originERC1155Address, targetERC1155Address)

  • IMA-JS

  • Web3 JS

// import & init ima-js here

export async function linkERC1155TokenOrigin(schain) {
    let erc1155OnOrigin = "[ADDRESS_OF_ERC1155_TOKEN_ON_ORIGIN]";
    let erc1155OnTarget = "[ADDRESS_OF_ERC1155_TOKEN_ON_TARGET]";

    const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';

    let address = "[YOUR_ADDRESS]";
    let privateKey = "[YOUR_PRIVATE_KEY]";

    let opts = {
        address: address,
        privateKey: privateKey // remove privateKey from txOpts to use Metamask signing
    };

    const isERC1155AddedOrigin = await schain.erc1155.getTokenCloneAddress(erc1155OnOrigin);
    if (isERC1155AddedOrigin === ZERO_ADDRESS) {
        await schain.erc1155.addTokenByOwner(
            targetChainName,
            erc1155OnOrigin,
            erc1155OnTarget,
            opts
        );
    }
}
const Web3 = require("web3");
const Tx = require("ethereumjs-tx").Transaction;

export function registerOnOriginChain() {
  let originABIs = require("./contracts/origin_ABIs.json");
  let originERC1155ABI = require("./contracts/origin_ERC1155_ABI.json");
  let targetERC1155ABI = require("./contracts/target_ERC1155_ABI.json");

  let privateKey = Buffer.from(
    "SCHAIN_OWNER_PRIVATE_KEY",
    "hex"
  );
  
  let erc1155OwnerForOrigin =
    process.env.SCHAIN_OWNER_ACCOUNT;

  let origin = process.env.ORIGIN_ENDPOINT;
  let targetChainName = process.env.TARGET_CHAIN_NAME;
  let originChainId = process.env.ORIGIN_CHAIN_ID;

  const originTokenManagerAddress = originABIs.token_manager_erc1155_address;
  const originTokenManagerABI = originABIs.token_manager_erc1155_abi;

  const erc1155AddressOnOrigin = originERC1155ABI.erc1155_address;
  const erc1155AddressOnTarget = targetERC1155ABI.erc1155_address;

  const web3ForOrigin = new Web3(origin);

  let TokenManager = new web3ForOrigin.eth.Contract(
    originTokenManagerABI,
    originTokenManagerAddress
  );

  /**
   * Uses the SKALE TokenManagerERC1155
   * contract function addERC1155TokenByOwner
   */
let addERC1155TokenByOwner = TokenManager.methods
    .addERC1155TokenByOwner(targetChainName, erc1155AddressOnOrigin, erc1155AddressOnTarget)
    .encodeABI();

    web3ForOrigin.eth.getTransactionCount(erc1155OwnerForOrigin).then((nonce) => {
    const rawTxAddERC1155TokenByOwner = {
      chainId: originChainId,
      from: erc1155OwnerForOrigin,
      nonce: "0x" + nonce.toString(16),
      data: addERC1155TokenByOwner,
      to: originTokenManagerAddress,
      gas: 6500000,
      gasPrice: 100000000000,
      value: web3ForOrigin.utils.toHex(
        web3ForOrigin.utils.toWei("0", "ether")
      )
    };

    //sign transaction
    const txAddERC1155TokenByOwner = new Tx(rawTxAddERC1155TokenByOwner, {
      chain: "rinkeby",
      hardfork: "petersburg"
    });

    txAddERC1155TokenByOwner.sign(privateKey);

    const serializedTxDeposit = txAddERC1155TokenByOwner.serialize();

    //send signed transaction (addERC1155TokenByOwner)
    web3ForOrigin.eth
      .sendSignedTransaction("0x" + serializedTxDeposit.toString("hex"))
      .on("receipt", (receipt) => {
        console.log(receipt);
      })
      .catch(console.error);
  });

5. Register ERC1155 clone in TokenManger on Target

You need to register the target token contract using the addERC1155TokenByOwner method in the TokenManagerERC1155 contract on the targetSchain. addERC1155TokenByOwner(originSchainName, originERC1155Address, targetERC1155Address)

The parameters on target TokenManagerERC1155.addERC1155TokenByOwner() are not symmetric to origin! The only parameter to swap is the first parameter for SchainName.
  • IMA-JS

  • Web3 JS

// import & init ima-js here

export async function linkERC1155TokenOrigin(schain) {
    let erc1155OnOrigin = "[ADDRESS_OF_ERC1155_TOKEN_ON_ORIGIN]";
    let erc1155OnTarget = "[ADDRESS_OF_ERC1155_TOKEN_ON_TARGET]";

    const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';

    let address = "[YOUR_ADDRESS]";
    let privateKey = "[YOUR_PRIVATE_KEY]";

    let opts = {
        address: address,
        privateKey: privateKey // remove privateKey from txOpts to use Metamask signing
    };

    const isERC1155AddedTarget = await schain.erc1155.getTokenCloneAddress(erc1155OnTarget);
    if (isERC1155AddedTarget === ZERO_ADDRESS) {
        await schain.erc1155.addTokenByOwner(
            originChainName,
            erc1155OnOrigin,
            erc1155OnTarget,
            opts
        );
    }
}
const Web3 = require("web3");
const Tx = require("ethereumjs-tx").Transaction;

export function registerOnTargetChain() {
  let targetABIs = require("./contracts/target_ABIs.json");
  let originERC1155ABI = require("./contracts/origin_ERC1155_ABI.json");
  let targetERC1155ABI = require("./contracts/target_ERC1155_ABI.json");

  let privateKey = Buffer.from(
    "SCHAIN_OWNER_PRIVATE_KEY",
    "hex"
  );
  
  let erc1155OwnerForTarget =
    process.env.SCHAIN_OWNER_ACCOUNT;

  let target = process.env.TARGET_ENDPOINT;
  let originChainName = process.env.ORIGIN_CHAIN_NAME;
  let targetChainId = process.env.TARGET_CHAIN_ID;

  const targetTokenManagerAddress = targetABIs.token_manager_erc1155_address;
  const targetTokenManagerABI = targetABIs.token_manager_erc1155_abi;

  const erc1155AddressOnOrigin = originERC1155ABI.erc1155_address;
  const erc1155AddressOnTarget = targetERC1155ABI.erc1155_address;

  const web3ForTarget = new Web3(target);

  let TokenManager = new web3ForTarget.eth.Contract(
    targetTokenManagerABI,
    targetTokenManagerAddress
  );

  /**
   * Uses the SKALE TokenManagerERC1155
   * contract function addERC1155TokenByOwner
   */
let addERC1155TokenByOwner = TokenManager.methods
    .addERC1155TokenByOwner(originChainName, erc1155AddressOnOrigin, erc1155AddressOnTarget)
    .encodeABI();     // IMPORTANT: arguments here are not symmetric to origin addERC1155TokenByOwner

    web3ForTarget.eth.getTransactionCount(erc1155OwnerForTarget).then((nonce) => {
    const rawTxAddERC1155TokenByOwner = {
      chainId: targetChainId,
      from: erc1155OwnerForTarget,
      nonce: "0x" + nonce.toString(16),
      data: addERC1155TokenByOwner,
      to: targetTokenManagerAddress,
      gas: 6500000,
      gasPrice: 100000000000,
      value: web3ForTarget.utils.toHex(
        web3ForTarget.utils.toWei("0", "ether")
      )
    };

    //sign transaction
    const txAddERC1155TokenByOwner = new Tx(rawTxAddERC1155TokenByOwner, {
      chain: "rinkeby",
      hardfork: "petersburg"
    });

    txAddERC1155TokenByOwner.sign(privateKey);

    const serializedTxDeposit = txAddERC1155TokenByOwner.serialize();

    //send signed transaction (addERC1155TokenByOwner)
    web3ForTarget.eth
      .sendSignedTransaction("0x" + serializedTxDeposit.toString("hex"))
      .on("receipt", (receipt) => {
        console.log(receipt);
      })
      .catch(console.error);
  });

6. Assign MINTER and BURNER roles to TokenManager on Target.

Now you need to assign the TokenManagerERC1155 contact on your SKALE Chain as the MINTER_ROLE and BURNER_ROLE for the ERC1155 clone on the target. With OpenZeppelin’s framework, you simply need to execute an AddMinter, AddBurner, and/or grantRole transaction on the SKALE chain token contract.

Example Add Minter Role

  • Web3 JS

import Common from "ethereumjs-common";
const Tx = require("ethereumjs-tx").Transaction;
const Web3 = require("web3");

let schainABIs = "[YOUR_SKALE_CHAIN_ABIs]";
let schainERC1155ABI = "[YOUR_SCHAIN_ERC1155_ABI]";
let chainId = "[YOUR_SKALE_CHAIN_CHAIN_ID]";

const customCommon = Common.forCustomChain(
    "mainnet",
    {
      name: "skale-network",
      chainId: chainId
    },
    "istanbul"
  );

let contractOwnerPrivateKey = new Buffer("[YOUR_PRIVATE_KEY]", 'hex');

let contractOwnerAccount = "[CONTRACT_OWNER_ACCOUNT]"; // SKALE Chain owner or authorized deployer account

let schainEndpoint = "[YOUR_SKALE_CHAIN_ENDPOINT]";

const erc1155ABI = schainERC1155ABI.erc1155_abi;
const erc1155Address = schainERC1155ABI.erc1155_address;

const tokenManagerAddress = schainABIs.token_manager_erc1155_address;

const web3ForSchain = new Web3(schainEndpoint);

let schainERC1155Contract = new web3ForSchain.eth.Contract(
    erc1155ABI,
    erc1155Address
);

let addMinter = schainERC1155Contract.methods
    .addMinter(tokenManagerAddress)
    .encodeABI();

  web3ForSchain.eth.getTransactionCount(contractOwnerAccount).then((nonce) => {
    //create raw transaction
    const rawTxAddMinter = {
      from: contractOwnerAccount,
      nonce: nonce,
      data: addMinter,
      to: erc1155Address,
      gasPrice: 100000000000,
      gas: 8000000
    };
    //sign transaction
    const txAddMinter = new Tx(rawTxAddMinter, { common: customCommon });
    txAddMinter.sign(contractOwnerPrivateKey);

    const serializedTxAddMinter = txAddMinter.serialize();

    //send signed transaction (add minter)
    web3ForSchain.eth
      .sendSignedTransaction("0x" + serializedTxAddMinter.toString("hex"))
      .on("receipt", (receipt) => {
        console.log(receipt);
      })
      .catch(console.error);
  });

Get Started with S2S ERC1155 Transfer

Be sure to follow any one-time setup and mapping steps described here before initiating S2S transfers.

1. S2S Transfer ERC1155 From Origin

To send ERC1155 tokens from a user’s wallet to another SKALE Chain, you will need to use the transferToSchainERC1155 function within the TokenManagerERC1155 IMA contract on the origin chain.

This method is called from the origin chain to move ERC1155 tokens to a target chain.

The TokenManagerERC1155 contract is predeployed on each SKALE Chain. To get the SKALE Chain ABIs, check out the current release page.

It’s also possible to transfer a batch of ERC1155 tokens at once. To do that, just pass tokenIds and amounts arrays instead of tokenId and amount to transferToSchain function.
  • IMA-JS

  • Web3 JS

export function initTestTokenContractSchain(schain) {
    // initialize ERC1155 contract
    const abiData = require("[ERC1155_ABI_ON_ORIGIN_CHAIN]");
    return new schain.web3.eth.Contract(
        abiData.erc1155_abi,
        abiData.erc1155_address);
}


export async function transfer() {
    const schain = new SChain(sChainWeb3, sChainAbi); // origin schain

    let tokenName = "[TOKEN_NAME]";
    let schainERС1155 = initTestTokenContractSchain(imaschain);

    let address = "YOUR_ADDRESS";
    let privateKey = "YOUR_PRIVATE_KEY";

    let tokenId = "[TOKEN_ID]";
    let amount = "[AMOUNT]";

    let opts = {
        address: address,
        privateKey: privateKey // remove privateKey from txOpts to use Metamask signing
    };

    schain.erc1155.addToken(tokenName, schainERC1155);

    await schain.erc1155.approveAll(tokenName, tokenId, opts);
    await schain.erc1155.transferToSchain(
        targetSchainName,
        schainERС1155.address,
        tokenId,
        amount,
        opts
    );
}
const Web3 = require("web3");
const Tx = require("ethereumjs-tx").Transaction;

let originABIs = "IMA_ABI_ON_ORIGIN";
let originERC1155ABI = "ERC1155_ABI_ON_ORIGIN";

let privateKey = Buffer.from("END_USER_PRIVATE_KEY", 'hex')
let address = "ADDRESS";

let origin = "ORIGIN_ENDPOINT";
let targetChainName = "TARGET_CHAIN_NAME";
let originChainId = "ORIGIN_CHAIN_ID";

const tokenManagerAddress = originABIs.token_manager_erc1155_address;
const tokenManagerABI = originABIs.token_manager_erc1155_abi;

const erc1155ABI = originERC1155ABI.erc1155_abi;
const erc1155Address = originERC1155ABI.erc1155_address;
const tokenId = "TOKEN_ID";

const web3ForOrigin = new Web3(origin);

let tokenManager = new web3ForOrigin.eth.Contract(
    tokenManagerABI,
    tokenManagerAddress
);

const customCommon = Common.forCustomChain(
    "mainnet", {
        name: "skale-network",
        chainId: originChainId
    },
    "istanbul"
);

let contractERC1155 = new web3ForOrigin.eth.Contract(erc1155ABI, erc1155Address);

let approve = contractERC1155.methods
    .approve(
        tokenManagerAddress,
        tokenId,
        web3ForOrigin.utils.toHex(web3ForOrigin.utils.toWei("1", "ether"))
    )
    .encodeABI();

let transfer = tokenManager.methods
    .transferToSchainERC1155(
        targetChainName,
        erc1155Address,
        tokenId,
        web3ForOrigin.utils.toHex(web3ForOrigin.utils.toWei("1", "ether"))
    )
    .encodeABI();

web3ForOrigin.eth.getTransactionCount(address).then(nonce => {
    //create raw transaction
    const rawTxApprove = {
        chainId: originChainId,
        from: address,
        nonce: "0x" + nonce.toString(16),
        data: approve,
        to: erc1155Address,
        gas: 6500000,
        gasPrice: 100000000000
    };

    //sign transaction
    const txApprove = new Tx(rawTxApprove, {
        common: customCommon
    });
    txApprove.sign(privateKey);

    const serializedTxApprove = txApprove.serialize();

    //send signed transaction (approve)
    web3ForOrigin.eth
        .sendSignedTransaction("0x" + serializedTxApprove.toString("hex"))
        .on("receipt", receipt => {
            console.log(receipt);
            web3ForOrigin.eth
                .getTransactionCount(address)
                .then(nonce => {
                    const rawTxDeposit = {
                        chainId: originChainId,
                        from: address,
                        nonce: "0x" + nonce.toString(16),
                        data: transfer,
                        to: tokenManagerAddress,
                        gas: 6500000,
                        gasPrice: 100000000000
                    };

                    //sign transaction
                    const txDeposit = new Tx(rawTxDeposit, {
                        common: customCommon
                    });

                    txDeposit.sign(privateKey);

                    const serializedTxDeposit = txDeposit.serialize();

                    //send signed transaction (deposit)
                    web3ForOrigin.eth
                        .sendSignedTransaction("0x" + serializedTxDeposit
                            .toString("hex"))
                        .on("receipt", receipt => {
                            console.log(receipt);
                        })
                        .catch(console.error);
                });
        })
        .catch(console.error);
});

2. Exit from SKALE Chain

To exit ERC1155 tokens back to the origin chain, you will need to use the transferToSchainERC1155 function within the TokenManagerERC1155 IMA contract on the target chain, and specify the erc1155Address on the origin chain.

The TokenManagerERC1155 IMA contract is pre-deployed to your SKALE Chain. Please reach out to your account manager to receive the ABIs specific for your SKALE Chain.

It’s also possible to transfer a batch of ERC1155 tokens at once. To do that, just pass tokenIds and amounts arrays instead of tokenId and amount to transferToSchain function.
  • IMA-JS

  • Web3 JS

export function initTestTokenContractSchain(schain) {
    // initialize ERC1155 contract
    const abiData = require("[ERC1155_ABI_ON_TARGET_CHAIN]");
    return new schain.web3.eth.Contract(
        abiData.erc1155_abi,
        abiData.erc1155_address);
}


export async function transfer() {
    const schain = new SChain(sChainWeb3, sChainAbi); // target schain

    let tokenName = "[TOKEN_NAME]";
    let schainERC1155 = initTestTokenContractSchain(schain);

    let address = "YOUR_ADDRESS";
    let privateKey = "YOUR_PRIVATE_KEY";

    let tokenAddressOriginChain = "[TOKEN_ADDRESS]";

    let tokenId = "[TOKEN_ID]";
    let amount = "[AMOUNT]";

    let opts = {
        address: address,
        privateKey: privateKey // remove privateKey from txOpts to use Metamask signing
    };

    schain.erc1155.addToken(tokenName, schainERC1155);

    await schain.erc1155.approveAll(tokenName, tokenId, opts);
    await schain.erc1155.transferToSchain(
        targetSchainName,
        tokenAddressOriginChain, // KEY DIFFERENCE - you should set address of the token on the origin chain
        tokenId,
        amount,
        opts
    );
}
const Web3 = require("web3");
const Tx = require("ethereumjs-tx").Transaction;

let targetABIs = "IMA_ABI_ON_TARGET";
let originERC1155ABI = "ERC1155_ABI_ON_ORIGIN";

let privateKey = Buffer.from("END_USER_PRIVATE_KEY", 'hex')
let address = "ADDRESS";

let target = "TARGET_ENDPOINT";
let originChainName = "ORIGIN_CHAIN_NAME";
let targetChainId = "TARGET_CHAIN_ID";

const tokenManagerAddress = targetABIs.token_manager_erc1155_address;
const tokenManagerABI = targetABIs.token_manager_erc1155_abi;

const erc1155ABI = targetERC1155ABI.erc1155_abi;
const erc1155TargetAddress = targetERC1155ABI.erc1155_address;
const erc1155OriginAddress = originERC1155ABI.erc1155_address;
const tokenId = "TOKEN_ID";

const web3ForTarget = new Web3(target);

let tokenManager = new web3ForTarget.eth.Contract(
    tokenManagerABI,
    tokenManagerAddress
);

const customCommon = Common.forCustomChain(
    "mainnet", {
        name: "skale-network",
        chainId: targetChainId
    },
    "istanbul"
);

let contractERC1155 = new web3ForTarget.eth.Contract(erc1155ABI, erc1155TargetAddress);

let approve = contractERC1155.methods
    .approve(
        tokenManagerAddress,
        tokenId,
        web3ForTarget.utils.toHex(web3ForTarget.utils.toWei("1", "ether"))
    )
    .encodeABI();

let transfer = tokenManager.methods
    .transferToSchainERC1155(
        originChainName,
        erc1155OriginAddress,
        tokenId,
        web3ForTarget.utils.toHex(web3ForTarget.utils.toWei("1", "ether"))
    )
    .encodeABI();

    web3ForTarget.eth.getTransactionCount(address).then(nonce => {
    //create raw transaction
    const rawTxApprove = {
        chainId: targetChainId,
        from: address,
        nonce: "0x" + nonce.toString(16),
        data: approve,
        to: erc1155Address,
        gas: 6500000,
        gasPrice: 100000000000
    };

    //sign transaction
    const txApprove = new Tx(rawTxApprove, {
        common: customCommon
    });
    txApprove.sign(privateKey);

    const serializedTxApprove = txApprove.serialize();

    //send signed transaction (approve)
    web3ForTarget.eth
        .sendSignedTransaction("0x" + serializedTxApprove.toString("hex"))
        .on("receipt", receipt => {
            console.log(receipt);
            web3ForTarget.eth
                .getTransactionCount(address)
                .then(nonce => {
                    const rawTxDeposit = {
                        chainId: targetChainId,
                        from: address,
                        nonce: "0x" + nonce.toString(16),
                        data: transfer,
                        to: tokenManagerAddress,
                        gas: 6500000,
                        gasPrice: 100000000000
                    };

                    //sign transaction
                    const txDeposit = new Tx(rawTxDeposit, {
                        common: customCommon
                    });

                    txDeposit.sign(privateKey);

                    const serializedTxDeposit = txDeposit.serialize();

                    //send signed transaction (deposit)
                    web3ForTarget.eth
                        .sendSignedTransaction("0x" + serializedTxDeposit
                            .toString("hex"))
                        .on("receipt", receipt => {
                            console.log(receipt);
                        })
                        .catch(console.error);
                });
        })
        .catch(console.error);
});