Interchain Messaging

SKALE makes it possible to manage tokens for end users between Ethereum and your SKALE Chain. You can use the Interchain Messaging Agent to transfer ETH, ERC20, or Dai to a deposit box on Ethereum, and SKALE will create clones of the token on your SKALE Chain. You can then send the tokens back your end users, and SKALE will release the tokens from the deposit box back to the end user. To learn how to integrate this feature into your dApp, please see the guides below.
See the full code sample on Github.

ETH

The Messaging Transfer Agent can be used for managing ETH tokens between Ethereum and SKALE.
See the full code sample on Github, or view a live demo below:

Step 1: Deposit ETH on Ethereum

To send ETH from a user's wallet to the Deposit Box on Ethereum, you will need to use the deposit function within the DepositBox Smart Contract on the mainnet.

This method is called from the mainnet to "freeze" funds and move ETH into a safe Deposit Box.
The DepositBox Smart Contract is currently deployed to the Rinkeby testnet. You can get a copy of the deployed smart contract ABIs here.
function deposit(string memory schainID, address to) public payable {
        bytes memory empty;
        deposit(schainID, to, empty);
    }
Alternatively, you can choose to send a message with the ETH to the SKALE Chain by using the deposit function below.
function deposit(
  string memory schainID, 
  address to, 
  bytes memory data
) 
  public
  payable
  rightTransaction(schainID)
{
    bytes32 schainHash = keccak256(abi.encodePacked(schainID));
    address tokenManagerAddress = LockAndData(lockAndDataAddress)
      .tokenManagerAddresses(schainHash);
    bytes memory newData;
    newData = abi.encodePacked(bytes1(uint8(1)), data);
    Proxy(proxyAddress).postOutgoingMessage(
        schainID,
        tokenManagerAddress,
        msg.value,
        to,
        newData
    );
    LockAndData(lockAndDataAddress)
      .receiveEth.value(msg.value)(msg.sender);
}
Example Code
/*
 * This nodeJS script will deposit funds into your SKALE Deposit Box.
 *
 *  @param {String} private key - Provide your private key.
 *  @param {String} account - Provide your account address.
 *  @param {String} Rinkeby endpoint - Provide a Rinkeby endpoint
 *  @param {String} SKALE Chain id - Provide your SKALE Chain id
 */

const Web3 = require('web3');
const rinkebyJson = require("../contracts/rinkeby_proxy.json");
const Tx = require('ethereumjs-tx');

let privateKey = new Buffer('[YOUR_PRIVATE_KEY]', 'hex')
let account = "[YOUR_ACCOUNT_ADDRESS]";
let rinkebyJsonEndpoint = "[RINKEBY_ENDPOINT]";
let schainID = "[YOUR_SKALE_CHAIN_ID]";

const depositBoxAddress = rinkebyJson.deposit_box_address;
const abi = privateTestnetJson.deposit_box_abi;

const web3 = new Web3(rinkebyJsonEndpoint);

let contract = new web3.eth.Contract(abi, depositBoxAddress);

//prepare the smart contract function deposit(string schainID, address to)
let deposit = contract.methods.deposit(schainID, account).encodeABI();  

//get nonce
web3.eth.getTransactionCount(account).then(nonce => {
  
  //create raw transaction
  const rawTx = {
    from: account, 
    nonce: "0x" + nonce.toString(16),
    data : deposit,
    to: depositBoxAddress,
    gasPrice: 0,
    gas: 8000000,
    value: web3.utils.toHex(web3.utils.toWei('1', 'ether'))
  }

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

  const serializedTx = tx.serialize();



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

Step 2: Exit from SKALE Chain

To send ETH back to the mainnet, you will need to use the exitToMain function within the TokenManager Smart Contract on the SKALE Chain.

This method is called from the SKALE Chain to send funds and move the token back to the mainnet.
The TokenManager Smart Contract is deployed to your SKALE Chain. Please reach out to your account manager to receive the ABIs specific for your SKALE Chain.
function exitToMain(address to, uint amount) public {
    bytes memory empty;
    exitToMain(to, amount, empty);
}
Alternatively, you can choose to send a message with the ETH back to the mainnet by using the exitToMain function below.
function exitToMain(address to, uint amount, bytes memory data) public receivedEth(amount) {
    bytes memory newData;
    newData = abi.encodePacked(bytes1(uint8(1)), data);
    ProxyForSchain(proxyForSchainAddress).postOutgoingMessage(
        "Mainnet",
        LockAndData(lockAndDataAddress)
            .tokenManagerAddresses(keccak256(abi.encodePacked("Mainnet"))),
        amount,
        to,
        newData
    );
}
Example Code
/*
 * This nodeJS script will allow you to remove funds from the Deposit Box..
 *
 *  @param {String} private key - Provide your private key.
 *  @param {String} account - Provide your account address.
 *  @param {String} SKALE Chain endpoint - Provide your SKALE Chain endpoint
 *  @param {String} SKALE Chain id - Provide your SKALE Chain id
 */

const Web3 = require('web3');
const schainJson = require("../contracts/schain_proxy.json");

const Tx = require('ethereumjs-tx');

let privateKey = new Buffer('[YOUR_PRIVATE_KEY]', 'hex')
let account = "[YOUR_ACCOUNT_ADDRESS]";
let schainEndpoint = "[YOUR_SKALE_CHAIN_ENDPOINT]";

const tokenManagerAddress = schainJson.token_manager_address;
const abi = schainJson.token_manager_abi;

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

let contract = new web3.eth.Contract(abi, tokenManagerAddress);

//prepare the smart contract function exitToMain(address to)
let exitToMain = contract.methods.exitToMain(
    account, web3.utils.toWei('1', 'ether'), 
    web3.utils.fromAscii("[YOUR_MESSAGE")
).encodeABI();  

web3.eth.getTransactionCount(account).then(nonce => {
  const rawTx = {
    nonce: nonce,
    from: account, 
    nonce: "0x" + nonce.toString(16),
    data : exitToMain,
    to: tokenManagerAddress,
    gasPrice: 0,
    gas: 8000000,
    value: 0
  }

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

const serializedTx = tx.serialize();

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

Step 3: Release ETH to User

To release funds to the end user on the mainnet, you will need to use the getMyEth function within the LockAndDataForMainnet Smart Contract on the mainnet.

This method is called from the mainnet to release tokens back to the end user.
The LockAndDataForMainnet Smart Contract is deployed to your SKALE Chain. Please reach out to your account manager to receive the ABIs specific for your SKALE Chain.
function getMyEth() public {
    require(
      address(this).balance >= approveTransfers[msg.sender], 
      "Not enough money"
    );
    require(
      approveTransfers[msg.sender] > 0, 
      "User has not money"
    );
    uint amount = approveTransfers[msg.sender];
    approveTransfers[msg.sender] = 0;
    msg.sender.transfer(amount);
}
Example Code
/*
 * This nodeJS script will allow you to remove funds from the Deposit Box..
 *
 *  @param {String} private key - Provide your private key.
 *  @param {String} account - Provide your account address.
 *  @param {String} Rinkeby endpoint - Provide a Rinkeby endpoint
 */

const Web3 = require('web3');
const rinkebyJson = require("../contracts/rinkeby_proxy.json");
const Tx = require('ethereumjs-tx');

let privateKey = new Buffer('[YOUR_PRIVATE_KEY]', 'hex')
let account = "[YOUR_ACCOUNT_ADDRESS]";
let rinkebyEndpoint = "[RINKEBY_ENDPOINT]";

const lockAndDataForMainnetAddress = privateTestnetJson.lock_and_data_for_mainnet_address;
const lockAndDataForMainnetABI = privateTestnetJson.lock_and_data_for_mainnet_abi;

const web3 = new Web3(rinkebyEndpoint);

let LockAndDataForMainnet = new web3.eth.Contract(
  lockAndDataForMainnetABI, 
  lockAndDataForMainnetAddress
);

//prepare the smart contract function getMyEth()
let getMyEth = LockAndDataForMainnet.methods.getMyEth().encodeABI();

web3.eth.getTransactionCount(account).then(nonce => {
  const rawTxGetMyEth = {
    from: account, 
    nonce: "0x" + nonce.toString(16),
    data : getMyEth,
    to: lockAndDataForMainnetAddress,
    gasPrice: 0,
    gas: 8000000,
    value: 0
  }

  const txGetMyEth = new Tx(rawTxGetMyEth);
  txGetMyEth.sign(privateKey);

  const serializedTxGetMyEth = txGetMyEth.serialize();

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

ERC20

The Messaging Transfer Agent can be used for managing ERC20 tokens between Ethereum and SKALE.
See the full code sample on Github, or view a live demo below:

Step 1: Deposit ERC20 on Ethereum

To send ERC20 tokens from a user's wallet to the Deposit Box on Ethereum, you will need to use the deposit function within the DepositBox Smart Contract on the mainnet.

This method is called from the mainnet to "freeze" funds and move ERC20 tokens into a safe Deposit Box.
The DepositBox Smart Contract is currently deployed to the Rinkeby testnet. You can get a copy of the deployed smart contract ABIs here.
function depositERC20(
    string memory schainID,
    address contractHere,
    address to,
    uint amount
)
    public
    payable
    rightTransaction(schainID)
{
    bytes32 schainHash = keccak256(abi.encodePacked(schainID));
    address tokenManagerAddress = ILockAndDataDB(lockAndDataAddress)
        .tokenManagerAddresses(schainHash);
    address lockAndDataERC20 = ContractManager(lockAndDataAddress).permitted(keccak256(abi.encodePacked("LockAndDataERC20")));
    address erc20Module = ContractManager(lockAndDataAddress).permitted(keccak256(abi.encodePacked("ERC20Module")));
    require(
        IERC20(contractHere).allowance(
            msg.sender,
            address(this)
        ) >= amount,
        "Not allowed ERC20 Token"
    );
    require(
        IERC20(contractHere).transferFrom(
            msg.sender,
            lockAndDataERC20,
            amount
        ),
        "Could not transfer ERC20 Token"
    );
    bytes memory data = IERC20Module(erc20Module)
        .receiveERC20(contractHere, to, amount, false);
    IMessageProxy(proxyAddress).postOutgoingMessage(
        schainID,
        tokenManagerAddress,
        msg.value,
        address(0),
        data
    );
    ILockAndDataDB(lockAndDataAddress).receiveEth.value(msg.value)(msg.sender);
}
Example Code
/* This nodeJS script will deposit funds into your SKALE Deposit Box.
*
*  @param {String} private key - Provide your private key.
*  @param {String} account - Provide your account address.
*  @param {String} Rinkeby endpoint - Provide a Rinkeby endpoint
*  @param {String} SKALE Endpoint - Provide your SKALE Chain endpoint
*  @param {String} SKALE Chain id - Provide your SKALE Chain id
*/

const Web3 = require('web3');
const Tx = require('ethereumjs-tx');
const fs = require('fs');
const rinkebyJson = require("../contracts/rinkeby_proxy.json");
const erc20RinkebyJson = require("../contracts/ERC20_rinkeby.json");
const schainJson = require("../contracts/schain_proxy.json");
require("dotenv").config();

function deposit() {
  let privateKey = new Buffer('[YOUR_PRIVATE_KEY]', 'hex')
  let account = "[YOUR_ACCOUNT_ADDRESS]";
  let rinkebyJsonEndpoint = "[RINKEBY_ENDPOINT]";
  let schainEndpoint = "[YOUR_SKALE_CHAIN_ENDPOINT]";
  let schainID = "[YOUR_SKALE_CHAIN_ID]";

  const depositBoxAddress = rinkebyJson.deposit_box_address;
  const depositBoxABI = rinkebyJson.deposit_box_abi;

  const tokenManagerAddress = schainJson.token_manager_address;
  const tokenManagerABI = schainJson.token_manager_abi;

  const erc20ABI = erc20RinkebyJson.zhelcoin_abi;
  const erc20Address = erc20RinkebyJson.zhelcoin_address;

  const web3ForMainnet = new Web3(rinkebyJsonEndpoint);
  const web3ForSchain = new Web3(schainEndpoint);

  let depositBox = new web3ForMainnet.eth.Contract(depositBoxABI, depositBoxAddress);

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

  let contractERC20 = new web3ForMainnet.eth.Contract(erc20ABI, erc20Address);
  //prepare the smart contract function deposit(string schainID, address to)

  let approve = contractERC20.methods.approve(
    depositBoxAddress, 
    web3ForMainnet.utils.toBN("1000000000000000000")
  ).encodeABI();

  let deposit = depositBox.methods.depositERC20(
    schainID, 
    erc20Address, 
    accountForSchain, 
    web3ForMainnet.utils.toBN("1000000000000000000")
  ).encodeABI();

  web3ForMainnet.eth.getTransactionCount(accountForMainnet).then(nonce => {
    
      //create raw transaction
      const rawTxApprove = {
        from: accountForMainnet, 
        nonce: "0x" + nonce.toString(16),
        data : approve,
        to: erc20Address,
        gasPrice: 0,
        gas: 8000000
      }
      nonce += 1;
      const rawTxDeposit = {
          from: accountForMainnet, 
          nonce: "0x" + nonce.toString(16),
          data : deposit,
          to: depositBoxAddress,
          gasPrice: 0,
          gas: 8000000,
          value: web3ForMainnet.utils.toHex(web3ForMainnet.utils.toWei('1', 'ether'))
      }
    
      //sign transaction
      const txApprove = new Tx(rawTxApprove);
      const txDeposit = new Tx(rawTxDeposit);
      txApprove.sign(privateKey);
      txDeposit.sign(privateKey);
    
      const serializedTxApprove = txApprove.serialize();
      const serializedTxDeposit = txDeposit.serialize();
    
    //send signed transaction
    web3ForMainnet.eth.sendSignedTransaction('0x' + serializedTxApprove.toString('hex')).
      on('receipt', receipt => {
        console.log(receipt);
        web3ForMainnet.eth.sendSignedTransaction('0x' + serializedTxDeposit.toString('hex')).
            on('receipt', receipt => {
                console.log(receipt);
                tokenManager.getPastEvents("ERC20TokenCreated", {
                    "filter": {"contractThere": [erc20Address]},
                    "fromBlock": 0,
                    "toBlock": "latest"
                }, (error, events) => {console.log(events);}).then((events) => {
                    console.log("New Created ERC20 clone on Skale Chain: " + 
                      events[0].returnValues.contractHere);
                    let jsonObject = {
                        erc20_address: events[0].returnValues.contractHere,
                        erc20_abi: erc20ABI
                    };

                    fs.writeFile(
                      "./contracts/ERC20_schain_proxy.json", 
                      JSON.stringify(jsonObject), 
                      function (err) {
                        if (err) {
                            return console.log(err);
                        }
                        console.log('Done, check ERC20_schain_proxy.json file in data folder.');
                        process.exit(0);
                    });
                });
            }).
            catch(console.error);
     }).
      catch(console.error);
  });
}

deposit();

Step 2: Exit from SKALE Chain

To send ERC20 tokens back to the mainnet, you will need to use the exitToMain function within the TokenManager Smart Contract on the SKALE Chain.

This method is called from the SKALE Chain to send funds and move the token back to the mainnet.
The TokenManager Smart Contract is deployed to your SKALE Chain. Please reach out to your account manager to receive the ABIs specific for your SKALE Chain.
function exitToMainERC20(
    address contractHere, 
    address to, 
    uint amount
) 
    public 
{
    address lockAndDataERC20 = ContractManager(lockAndDataAddress)
        .permitted(keccak256(abi.encodePacked("LockAndDataERC20")));
    address erc20Module = ContractManager(lockAndDataAddress)
        .permitted(keccak256(abi.encodePacked("ERC20Module")));
    require(
        ERC20Detailed(contractHere).allowance(
            msg.sender,
            address(this)
        ) >= amount,
        "Not allowed ERC20 Token"
    );
    require(
        ERC20Detailed(contractHere).transferFrom(
            msg.sender,
            lockAndDataERC20,
            amount
        ),
        "Could not transfer ERC20 Token"
    );
    require(
        ILockAndDataTM(lockAndDataAddress)
        .reduceGasCosts(
            msg.sender, 
            GAS_AMOUNT_POST_MESSAGE * AVERAGE_TX_PRICE
        ), 
        "Not enough gas sent"
    );
    bytes memory data = IERC20Module(erc20Module)
        .receiveERC20(contractHere, to, amount, false);
    IMessageProxy(proxyForSchainAddress).postOutgoingMessage(
        "Mainnet",
        ILockAndDataTM(lockAndDataAddress).
            tokenManagerAddresses(keccak256(abi.encodePacked("Mainnet"))),
        GAS_AMOUNT_POST_MESSAGE * AVERAGE_TX_PRICE,
        address(0),
        data
    );
}
Example Code
/* This nodeJS script will deposit funds into your SKALE Deposit Box.
*
*  @param {String} private key - Provide your private key.
*  @param {String} account - Provide your account address.
*  @param {String} Rinkeby endpoint - Provide a Rinkeby endpoint
*  @param {String} SKALE Endpoint - Provide your SKALE Chain endpoint
*  @param {String} SKALE Chain id - Provide your SKALE Chain id
*/

const Web3 = require('web3');
const Tx = require('ethereumjs-tx');
const fs = require('fs');
const rinkebyJson = require("../contracts/rinkeby_proxy.json");
const erc20SchainJson = require("../contracts/ERC20_schain_proxy.json");
const schainJson = require("../contracts/schain_proxy.json");
require("dotenv").config();

function exit() {
  let privateKey = new Buffer('[YOUR_PRIVATE_KEY]', 'hex')
  let account = "[YOUR_ACCOUNT_ADDRESS]";
  let rinkebyJsonEndpoint = "[RINKEBY_ENDPOINT]";
  let schainEndpoint = "[YOUR_SKALE_CHAIN_ENDPOINT]";
  let schainID = "[YOUR_SKALE_CHAIN_ID]";

  const depositBoxAddress = rinkebyJson.deposit_box_address;
  const depositBoxABI = rinkebyJson.deposit_box_abi;

  const tokenManagerAddress = schainJson.token_manager_address;
  const tokenManagerABI = schainJson.token_manager_abi;

  const erc20ABI = erc20SchainJson.erc20_abi;
  const erc20Address = erc20SchainJson.erc20_address;

  const web3ForMainnet = new Web3(rinkebyJsonEndpoint);
  const web3ForSchain = new Web3(schainEndpoint);

  let depositBox = new web3ForMainnet.eth.Contract(depositBoxABI, depositBoxAddress);

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

  let contractERC20 = new web3ForSchain.eth.Contract(erc20ABI, erc20Address);
  //prepare the smart contract function deposit(string schainID, address to)

  let approve = contractERC20.methods.approve(
    tokenManagerAddress, 
    web3ForSchain.utils.toBN("1000000000000000000")
  ).encodeABI();

  let deposit = tokenManager.methods.exitToMainERC20(
    erc20Address, 
    account, 
    web3ForSchain.utils.toBN("1000000000000000000")).encodeABI();

  web3ForSchain.eth.getTransactionCount(account).then(nonce => {
    
      //create raw transaction
      const rawTxApprove = {
        from: account, 
        nonce: "0x" + nonce.toString(16),
        data : approve,
        to: erc20Address,
        gasPrice: 0,
        gas: 8000000
      }
      nonce += 1;
      const rawTxDeposit = {
          from: account, 
          nonce: "0x" + nonce.toString(16),
          data : deposit,
          to: tokenManagerAddress,
          gasPrice: 0,
          gas: 8000000,
          value: web3ForSchain.utils.toHex(web3ForSchain.utils.toWei('1', 'ether'))
      }
    
      //sign transaction
      const txApprove = new Tx(rawTxApprove);
      const txDeposit = new Tx(rawTxDeposit);
      txApprove.sign(privateKey);
      txDeposit.sign(privateKey);
    
      const serializedTxApprove = txApprove.serialize();
      const serializedTxDeposit = txDeposit.serialize();
    
    //send signed transaction
    web3ForSchain.eth.sendSignedTransaction('0x' + serializedTxApprove.toString('hex')).
      on('receipt', receipt => {
        console.log(receipt);
        web3ForSchain.eth.sendSignedTransaction('0x' + serializedTxDeposit.toString('hex')).
            on('receipt', receipt => {
                console.log(receipt);
            }).
            catch(console.error);
     }).
      catch(console.error);
  });
}

exit();