🌉Блокчэйн хоорондын гүүр

Нэг блокчэйнээс өөр нэг блокчэйнд токэнийг шууд зөөх боломжгүй гэж хэлж болно. Учир нь өөр өөр блокчэйн өөр өөрийн гэсэн дүрэм, төлбөрийн хэрэгсэлтэй байдаг. Гэтэл крипто хөрөнгийг блокчэйн хооронд зөөх нь DeFi-д зайлшгүй байх ёстой чухал хэрэгцээ юм. Үүнийг ухаалаг гэрээ, боодолтой токэний тусламжтайгаар холбогч гүүрийг ашиглан шийддэг.

Энэхүү заавраар бүгдээрээ Ethereum блокчэйн сүлжээн дахь ETH-ийг Корэкс сүлжээн дээр CRX болгох гүүрийг байгуулах жишээг харах болно.

Энд бичигдсэн код нь зөвхөн үзүүлэнд зориулж бичигдсэн тул үйлчилгээнд хэрэглэхэд тохиромжгүй гэдгийг анхаарна уу!

Хэрэгжүүлэлт

Хоёр блокчэйн хооронд крипто хөрөнгө дамжуулахдаа блокчэйн бүр дээр тус тусыг ухаалаг гэрээг үүсгэнэ. Ухаалаг гэрээнд хийгдсэн үйлдлүүд дээр event үүсгэгдэх ба энэ нь блокчэйнд хадгалагдан үлддэг. Бидний хэрэгжүүлэх гүүр, хоёр блокчэйнээс event сонсох ба сонссон event-ийн дагуу үйлдлийг хийнэ.

Ethereum сүлжээн дээрх ухаалаг гэрээ эхлээд ETH-ийн дүүргэлттэй байх ёстой. Мөн түүнчлэн TEO сүлжээн дээрх ухаалаг гэрээ CRX-ийн дүүргэлттэй байх ёстой.

Ethereum сүлжээн дээрх ухаалаг гэрээ

Зурагт харуулсны дагуу Ethereum сүлжээн дээр ETH2CRX гэрээ байрлана. Уг гэрээ нь дотроо ETH хадгалах бөгөөд корэкс дээр гүйлгээ хийх үнэ cost, болон CRX/ETH ханш crxeth-ийг хадгална.

  • toCorex үйлдлийг корэкс-ийн ямар хаягт шилжүүлэх хаягийг дамжуулан, ETH шилжүүлж гүйлгээ хийхэд ToCorex event ялгарах болно. Шилжүүлсэн ETH-ээс гүйлгээний хураамжыг авч, ханшаар хөрвүүлж event үүсгэн блокчэйнд хадгална. Энэ event-ийг гүүр сонсох юм.

  • toEthereum үйлдлийг зөвхөн owner дуудах бөгөөд корэкс сүлжээнээс ETH-ийг захиалсны дараагаар гүүрээр дамжин Ethereum сүлжээнд, захиалсан ETH-ийг түгээхэд ашиглана.

contract ETH2CRX {

    uint public cost;                // corex chain дээр гүйлгээ хийх үнэ
    uint public crxeth;
    address public owner;
    
    event ToCorex(address indexed dst, uint val);
    event ToEthereum(address indexed dst, uint val);

    constructor() public {
        owner = msg.sender;
        cost = 0.01 ether;
        crxeth = 607715;
    }

    function() external payable {
    }
    
    function toCorex(address to) external payable {
	    require(msg.value > cost);
	    emit ToCorex(to, (msg.value - cost) * crxeth);
    }

    function toEthereum(address payable dst, uint256 val) external payable {
	    require(msg.sender == owner);
	    dst.transfer(val);
	    emit ToEthereum(dst, val);
    }
}

TEO сүлжээн дээрх ухаалаг гэрээ

CRX2ETH гэрээ нь CRX-ийг ETH болгох мөн Ethereum сүлжээнээс орж ирсэн захиалгын CRX-ийг түгээх үйлдлүүдийг хариуцна. Уг гэрээ нь дотроо гэрээний эзэн owner, CRX-ийг ETH болгох ханш ethcrx, Ethereum сүлжээн дээр хийгдэх гүйлгээний хураамж cost зэргийг агуулна.

  • toEthereum үйлдлийг CRX-ийг ETH болгоход ашиглана. Ethereum сүлжээн дээрх хаягийг дамжуулах ба уг үйлдлийг хийхдээ CRX шилжүүлэх ёстой. Шилжүүлсэн CRX-ээс ETH сүлжээн дээр хийгдэх гүйлгээний хураамжыг хасаж ethcrx ханшаар хөрвүүлэн ToEthereum event үүсгэн блокчэйнд хадгална.

  • toCorex үйлдлийг зөвхөн гэрээний эзэн дуудна. Ethereum сүлжээнээс CRX-ийг захиалах үед гүүрээр дамжин корэкс сүлжээнд, захиалсан CRX-ийг түгээхэд хэрэглэнэ.

pragma solidity ^0.5.0;

contract CRX2ETH {

    uint public cost;                // ethereum chain дээр гүйлгээ хийх үнэ
    uint public ethcrx;
    address public owner;
    
    event ToCorex(address indexed dst, uint val);
    event ToEthereum(address indexed dst, uint val);

    constructor() public {
        owner = msg.sender;
        cost = 6077.15 ether;
        ethcrx = 1.64e-6 ether;
    }

    function() external payable {
    }
    
    function toCorex(address payable dst, uint val) external payable {
	require(msg.sender == owner);
	dst.transfer(val);
	emit ToCorex(dst, val);
    }

    function toEthereum(address payable dst, uint val) external payable {
	require(msg.value > cost);
        emit ToEthereum(dst, (msg.value - cost) * ethcrx);
    }
}

Ухаалаг гэрээний event сонсох

Ухаалаг гэрээний event-үүд блокчэйнд хадгалагдан үлддэг тул хэзээч гэсэн тэр event-ийг ухаж үзэх боломжтой гэсэн үг юм. Ухаалаг гэрээний үүсгэсэн event-ийг web3js ашиглан хоёр янзаар сонсох боломжтой.

1. Өнгөрсөн event-үүдийг унших

Contract тохиолдол үүсгэсний дараагаар getPastEvents функцийг ашиглан тухайн гэрээний бүх event-үүдийг шүүн харах боломжтой. Доорх жишээнд гэрээний ToCorex event-ийг блок 0-ээс эхлэн хамгийн сүүлийн блок хүртэл түүж харж байна. Маш их өгөгдөл буцаах боломжтой бөгөөд удаан ажиллана. Энэ фукцийн нэг дутагдалтай тал бол event-үүдийг индекслэх системийг гүүр дээр хэрэгжүүлж өгөх шаардлагатай.

const addr = '<address of the contract>'
abi = JSON.parse('<contract abi>');
ETH2CRX = new this.web3.eth.Contract(abi, addr);
let options = {
    filter: {
        value: []    
    },
    fromBlock: 0,                  //Number || "earliest" || "pending" || "latest"
    toBlock: 'latest'
};

ETH2CRX.getPastEvents('ToCorex', options)
    .then(results => console.log(results))
    .catch(err => throw err);

2. Гэрээний event функцийг ашиглах

Гэрээний event функцийг ашиглан гэрээнээс гарсан өөрчлөлтүүдийг чагнах боломжтой. Доор жишээнд ToCorex event-ийг сонсох ба data дээр шинээр үүсгэгдсэн event-ийн мэдээлэл ирдэг. Уг арга ажиллахын тулд ABI зөв байх ёстойг анхаарна уу.

const addr = '<address of the contract>'
abi = JSON.parse('<contract abi>');
ETH2CRX = new this.web3.eth.Contract(abi, addr);
let options = {
    filter: {
        value: [],
    },
    fromBlock: 0
};

ETH2CRX.events.ToCorex(options)
    .on('data', event => eth2crx_process(event))
    .on('changed', changed => console.log(changed))
    .on('error', err => throw err)
    .on('connected', str => console.log(str))

Ухаалаг гэрээний функцийг дуудах

Ethereum сүлжээн дээр ToCorex event-ийг сонсон ирсэн тохиолдолд eth2crx_process функцийг дуудна. Уг функц нь TEO сүлжээн дээрх CRX2ETH гэрээний toCorex функцийг дуудаж зохиох хэмжээний CRX-ийг харгалзах хаягт шилжүүлнэ. Гүйлгээний хураамж гэрээний эзнээс гарах бөгөөд мөнгөн дүн нь гэрээнд агуулагдах CRX-ээс шилжүүлэгдэх болно.

const crx_owner_addr = '<address of the owner of CRX2ETH contract>'
const crx_owner_privateKey = '<private key of the owner>'
function eth2crx_process(e) {
    if (e.event == 'ToCorex') {
	let dst = e.returnValues.dst;
	let val = e.returnValues.val;
	transfer({
	    from: crx_owner_addr,
	    to: crx2eth_addr,
	    value: '0',
	    data: CRX2ETH.methods.toCorex(dst, val).encodeABI(),
	    privateKey: crx_owner_privateKey,
	}).then(tx => {console.log('Tx successful: ', tx.transactionHash)});
    } else {
	try {
	    console.log(e.event, ' occurred: ', e.returnValues)
	} catch (e) {
	    console.log('Error occured: ', e)
	}
    }
}

Дээрх жишээнд агуулагдаж буй гүйлгээ хийхtransfer функцийн кодыг доор оруулав.

async transfer({ from, to, value, data, privateKey }) {
    const chainId = await this.web3.eth.getChainId()
    const gasPrice = await this.web3.eth.getGasPrice();
    const privateKeyBuffer = EthUtil.toBuffer(privateKey);
    
    const nonce = await this.web3.eth.getTransactionCount(from, 'pending');
    const rawTx = {
	from,
	to,
	chainId: Web3.utils.toHex(chainId),
	value: Web3.utils.toHex(value),
	gasLimit: Web3.utils.toHex(50000),
	gasPrice: Web3.utils.toHex(gasPrice),
	data: data,
	nonce: Web3.utils.toHex(nonce),
    };

    const tx = new Tx(rawTx);
    tx.sign(privateKeyBuffer);
    const serializedTx = tx.serialize();
    const res = await this.web3.eth.sendSignedTransaction(`0x${serializedTx.toString('hex')}`);

    return res;
}

Бүгдийг нь нийлүүлсэн код

Бидний жишээнд харуулсан гүүрийг доорх client.js, main.js файлуудад харуулав.

  • client.js - Блокчэйнтэй холбогдох хэсгийг агуулах код

  • main.js - Гүүрийг хэрэгжүүлсэн код

client.js
const EthUtil = require('ethereumjs-util');
const Web3 = require('web3');
const Tx = require('ethereumjs-tx');

class Client {

    constructor(connection) {
	this.web3 = new Web3(connection);
    }
    async transfer({ from, to, value, data, privateKey }) {
	const chainId = await this.web3.eth.getChainId()
	const gasPrice = await this.web3.eth.getGasPrice();
	const privateKeyBuffer = EthUtil.toBuffer(privateKey);
	
	const nonce = await this.web3.eth.getTransactionCount(from, 'pending');
	const rawTx = {
	    from,
	    to,
	    chainId: Web3.utils.toHex(chainId),
	    value: Web3.utils.toHex(value),
	    gasLimit: Web3.utils.toHex(50000),
	    gasPrice: Web3.utils.toHex(gasPrice),
	    data: data,
	    nonce: Web3.utils.toHex(nonce),
	};

	const tx = new Tx(rawTx);
	tx.sign(privateKeyBuffer);
	const serializedTx = tx.serialize();
	const res = await this.web3.eth.sendSignedTransaction(`0x${serializedTx.toString('hex')}`);

	return res;
    }

    get_contract(abi, addr) {
	var contract = new this.web3.eth.Contract(abi, addr);
	return contract;
    }
    async get_balance(addr) {
	const bal = await this.web3.eth.getBalance(addr);
	return bal;
    }
};

module.exports = Client;
main.js
const Web3 = require('web3');
const net = require('net')
const Client = require('./client');

const crx2eth_addr = '0x.....';
const crx2eth_abi_json = '[{"constant": true,"inputs": [],"name": "cost","outputs": [{"name": "","type": "uint256"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": false,"inputs": [{"name": "dst","type": "address"}],"name": "toEthereum","outputs": [],"payable": true,"stateMutability": "payable","type": "function"},{"constant": true,"inputs": [],"name": "owner","outputs": [{"name": "","type": "address"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": true,"inputs": [],"name": "ethcrx","outputs": [{"name": "","type": "uint256"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": false,"inputs": [{"name": "dst","type": "address"},{"name": "val","type": "uint256"}],"name": "toCorex","outputs": [],"payable": true,"stateMutability": "payable","type": "function"},{"inputs": [],"payable": false,"stateMutability": "nonpayable","type": "constructor"},{"payable": true,"stateMutability": "payable","type": "fallback"},{"anonymous": false,"inputs": [{"indexed": true,"name": "dst","type": "address"},{"indexed": false,"name": "val","type": "uint256"}],"name": "ToCorex","type": "event"},{"anonymous": false,"inputs": [{"indexed": true,"name": "dst","type": "address"},{"indexed": false,"name": "val","type": "uint256"}],"name": "ToEthereum","type": "event"}]';
const crx2eth_abi = JSON.parse(crx2eth_abi_json)
const crx_owner_addr = '0x.....'
const crx_owner_privateKey = '0x.....';

const eth2crx_addr = '0x.....';
const eth2crx_abi_json = '[{"constant":true,"inputs":[],"name":"cost","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"crxeth","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"}],"name":"toCorex","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"val","type":"uint256"}],"name":"toEthereum","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"val","type":"uint256"}],"name":"ToCorex","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"val","type":"uint256"}],"name":"ToEthereum","type":"event"}]';
const eth2crx_abi = JSON.parse(eth2crx_abi_json)
const eth_owner_addr = '0x.....';
const eth_owner_privateKey = '0x.....';

var corex_web3 = new Web3.providers.IpcProvider('/home/corex/.corexchain/corex.ipc', net)
var eth_web3 = new Web3.providers.WebsocketProvider('ws://localhost:8545');
var corex_cli = new Client(corex_web3);
var eth_cli = new Client(eth_web3);

var crx2eth_contract = corex_cli.get_contract(crx2eth_abi, crx2eth_addr);
var eth2crx_contract = eth_cli.get_contract(eth2crx_abi, eth2crx_addr);

let options = {
    filter: {
        value: [],
    },
    fromBlock: 0
};

function crx2eth_process(e) {
    if (e.event == 'ToEthereum') {
	let dst = e.returnValues.dst;
	let val = e.returnValues.val;
	eth_cli.transfer({
	    from: eth_owner_addr,
	    to: eth2crx_addr,
	    value: '0',
	    data: eth2crx_contract.methods.toEthereum(dst, val).encodeABI(),
	    privateKey: eth_owner_privateKey,
	}).then(tx => { console.log('Tx successful: ', tx.transactionHash) });
    } else {
	try {
	    console.log(e.event, ' occurred: ', e.returnValues)
	} catch (e) {
	    console.log('Error occured: ', e)
	}
    }
}

function eth2crx_process(e) {
    if (e.event == 'ToCorex') {
	let dst = e.returnValues.dst;
	let val = e.returnValues.val;
	eth_cli.transfer({
	    from: crx_owner_addr,
	    to: crx2eth_addr,
	    value: '0',
	    data: crx2eth_contract.methods.toCorex(dst, val).encodeABI(),
	    privateKey: crx_owner_privateKey,
	}).then(tx => {console.log('Tx successful: ', tx.transactionHash)});
    } else {
	try {
	    console.log(e.event, ' occurred: ', e.returnValues)
	} catch (e) {
	    console.log('Error occured: ', e)
	}
    }
}

try {
    crx2eth_contract.events.ToEthereum(options)
	.on('data', event => crx2eth_process(event))
	.on('changed', changed => console.log(changed))
	.on('error', err =>  { throw err;})
	.on('connected', str => console.log(str));
} catch (e) {
    console.log(e)
}

try {
    crx2eth_contract.events.ToCorex(options)
	.on('data', event => console.log(event))
	.on('changed', changed => console.log(changed))
	.on('error', err =>  { throw err;})
	.on('connected', str => console.log(str))
} catch (e) {
    console.log(e);
}

try {
    eth2crx_contract.events.ToEthereum(options)
	.on('data', event => console.log(event))
	.on('changed', changed => console.log(changed))
	.on('error', err =>  { throw err;})
	.on('connected', str => console.log(str))
} catch (e) {
    console.log(e);
}

try {
    eth2crx_contract.events.ToCorex(options)
	.on('data', event => crx2eth_process(event))
	.on('changed', changed => console.log(changed))
	.on('error', err => { throw err;})
	.on('connected', str => console.log(str))
} catch (e) {
    console.log(e);
}


console.log('Listening for events');

Last updated