Conclusion
The advent of blockchain bridges has made blockchain a more mainstream technology. Bridging solutions also aid the DeFi applications design that empowers the prospectus of a decentralized and financial system. By enabling connections between different blockchains or working together, blockchain bridges help users head towards the next-generation decentralized system. Thus, it aims to end the sovereignty of the centralized system from the business ecosystem. However, blockchain plans to bring about many new paradigms to reinvent the existing bridges and promote greater innovation and technological relevance.
Blockchain technology keeps evolving, and it has been changed significantly since 2008 when Satoshi Nakamoto introduced the first cryptocurrency, Bitcoin, to the world. Bitcoin brought along blockchain technology. Since then, multiple blockchain platforms have been launched. Every blockchain has unique features and functionality to fill the gap between blockchain technology and its real-world implications. Notwithstanding the amazing benefits of the blockchain, such as its decentralized nature, the immutability of records, distributed ledger, and smart contract technology, a major hurdle still affects blockchain’s mass adoption, which is the lack of interoperability.
Although public blockchains maintain transparency in the on-chain data, their siloed nature limits the holistic utilization of blockchain in decentralized finance and many other industries. Blockchains have unique capabilities that users often want to utilize together. However, that doesn’t seem possible since these blockchains work independently on their isolated ecosystem and abide by their own unique consensus rules. Independent blockchains can’t interact with each other to exchange information or value.
This interoperability issue becomes critical due to the expanding blockchain networks and more DeFi projects going cross-chain. Meanwhile, such siloed nature of blockchain contradicts the core principle of decentralization, which revolves around making blockchain accessible for everyone. Is there any solution to this lack of interoperability? How can someone from the Ethereum network access the data and resources available on a different blockchain like Binance? That’s where bridging solutions or blockchain bridges make a move.
Let’s explore the bridging solutions and their working mechanisms in this article. In addition, we will also learn to build a decentralized token bridge between Ethereum and Binance Smart Chain are two popular blockchains for DeFi development.
What are blockchain bridges?
A blockchain bridge enables interoperability and connectivity between two unique blockchains that operate under different consensus mechanisms. More clearly put, blockchain bridges allow two different blockchains to interact with each other. Blockchains can share smart contract execution instructions, transfer tokens, and share data & resources back and forth between two independent blockchains as they no longer remain limited by their origin. These blockchains can even access the off-chain data, such as access to the live chart of the stock market. Some of the widely used blockchain bridges are xPollinate, Matic Bridge, Binance Bridge. Blockchain bridges provide the following benefits to the users:
- Users can leverage the benefits of two separate blockchains to create dApps instead of only from the hosted blockchain. It means a user can deploy dApp on Solana and can power the dApp with Ethereum’s smart contract technology.
- Users can transfer tokens from a blockchain that charges high transaction costs to another blockchain where transaction costs are comparatively cheaper.
- With the ability to transfer tokens instantly, users can shift from a volatile cryptocurrency to Stablecoins quickly without taking the help of an intermediary.
- One can also host digital assets on a decentralized application of a different blockchain. For example, one can create NFTs on the Cardano blockchain and host them on the Ethereum marketplace.
- Bridging allows users to execute dAPPs across multiple blockchain ecosystems.
What are the Types of Blockchain Bridges?
To understand how blockchain bridges work, we first need to know how many types exist. Currently, two types of blockchain bridges are present; a federated bridge and a trustless bridge. Now, let’s understand their working mechanism.
Federated bridge
A federated bridge is also known as a centralized bridge. It is essentially a kind of centralized exchange where the users interact with a pool that can sometimes be a company or a middleman. If the token transfer occurs for Ether and BNB, there will be two large pools; one containing BNB and another containing Ether. As soon as the sender initiates the transfer with Ether, it gets added to the pool, and the pool sends him an equivalent amount of BNB out of the second pool. The centralized authority charges a small fee to regulate this process. However, the fee is a small amount that users can pay conveniently.
Trustless bridge
These are the purely decentralized bridge that eliminates the role of any third party. Trustless blockchain bridges don’t even use API to administer the process of burning and minting the token. Instead, smart contract plays a key role here. When a user initiates the token transfer through the trustless bridge, the smart contract freezes his current cryptos and provides him a copy of equivalent tokens on the new network. The smart contract then mints the token since it understands that the user has already frozen or burnt tokens on another network.
What are the main features of a bridging solution?
Lock and Mint
Tokens are not really transferred via a blockchain bridge. When a user transfers a token to another blockchain, a two-stage process takes place. At first, the tokens are frozen on the current blockchain. Then, a token of equal value is minted on the receiving blockchain. So, if the user wants to redeem the tokens, the bridge burns the equivalent token to unlock the original value.
Trust-based Solution
Trust-based decentralized blockchain bridges are popular even though they include a ‘merchant’ or trusted custodian. The custodian controls the fund (tokens) via wallet and helps ease off the token transfer process. Thus, high flexibility remains in many blockchain networks.
Assisting Sidechain
While a bridge links two different blockchains, a sidechain bridge connects a parent blockchain to its child blockchain. Since the parent and child blockchain exists on separate chains, they need a blockchain bridge to communicate or share data.
Robust Management
Bridge validators act as the network operators. These operators issue corresponding tokens in exchange for the token they receive from another network through a special smart contract.
Cross-chain Collaterals
Cross-chain collaterals help users to move assets from one blockchain of significant value to another with low fees. Earlier, the users were allowed to borrow assets only from their native chain. Now, they can leverage cross-chain borrowing through a blockchain bridge that requires additional liquidity.
Efficiency
Blockchain bridges authorize the regulation of spontaneous micro transfers. These transfers happen instantly between different blockchains at feasible and nominal rates.
Why is a bridging solution needed?
Following are the three big reasons a blockchain bridge or bridging solution is crucial:
Multi-blockchain token transfer
The most obvious yet crucial role of the blockchain bridge is that it enables cross-blockchain exchange. Users can instantly mint tokens on the desired blockchain without using any costly or time-taking exchange process.
Development
Blockchain bridges help various blockchains to develop by leveraging the abilities of each other. For instance, the features of Ethereum cannot be available on BSC. Bridging solutions let them work and grow together as a team player to solve the challenges occurring in the blockchain space.
Transaction fees
The last big reason behind someone’s need for a bridging solution is transaction fees, often high on popular blockchains. In contrast, newer blockchains don’t impose high transaction costs, though they lack security and other major features. So, bridges allow people to access new networks, transfer tokens to that network, and process transactions at a comparatively low cost.
How to build a decentralized token bridge between Ethereum and Binance Smart Chain?
Using this step-by-step procedure, you will learn how to build a completely decentralized bridge between Ethereum and Binance smart chain using the solidity programming language. Although many blockchain bridges use API to transfer tokens and information, APIs are vulnerable to hacks and can send bogus transactions once hacked. So, we will make the bridge fully decentralized by removing the API from the mechanism.
We allow the bridge script to generate a signed message that the contract will receive to mint the tokens after verifying the signature. The contract also makes sure that the message is unique and hasn’t been used before. That way, you give the signed message to the user, and they are in charge of submitting it to the blockchain to mint and pay for the transaction.
First set up a smart contract for the bridge base using the following functions
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import './Itoken.sol';
contract BridgeBase {
address public admin;
IToken public token;
mapping(address => mapping(uint => bool)) public processedNonces;
enum Step { Burn, Mint }
event Transfer(
address from,
address to,
uint amount,
uint date,
uint nonce,
bytes signature,
Step indexed step
);
constructor(address _token) {
admin = msg.sender;
token = IToken(_token);
}
function burn(address to, uint amount, uint nonce, bytes calldata signature) external {
require(processedNonces[msg.sender][nonce] == false, 'transfer already processed');
processedNonces[msg.sender][nonce] = true;
token.burn(msg.sender, amount);
emit Transfer(
msg.sender,
to,
amount,
block.timestamp,
nonce,
signature,
Step.Burn
);
}
function mint(
address from,
address to,
uint amount,
uint nonce,
bytes calldata signature
) external {
bytes32 message = prefixed(keccak256(abi.encodePacked(
from,
to,
amount,
nonce
)));
require(recoverSigner(message, signature) == from , 'wrong signature');
require(processedNonces[from][nonce] == false, 'transfer already processed');
processedNonces[from][nonce] = true;
token.mint(to, amount);
emit Transfer(
from,
to,
amount,
block.timestamp,
nonce,
signature,
Step.Mint
);
}
function prefixed(bytes32 hash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(
'\x19Ethereum Signed Message:\n32',
hash
));
}
function recoverSigner(bytes32 message, bytes memory sig)
internal
pure
returns (address)
{
uint8 v;
bytes32 r;
bytes32 s;
(v, r, s) = splitSignature(sig);
return ecrecover(message, v, r, s);
}
function splitSignature(bytes memory sig)
internal
pure
returns (uint8, bytes32, bytes32)
{
require(sig.length == 65);
bytes32 r;
bytes32 s;
uint8 v;
assembly {
// first 32 bytes, after the length prefix
r := mload(add(sig, 32))
// second 32 bytes
s := mload(add(sig, 64))
// final byte (first byte of the next 32 bytes)
v := byte(0, mload(add(sig, 96)))
}
return (v, r, s);
}
}
After constructing and deploying bridge base code, deploy Binance bridge using the following code
pragma solidity ^0.8.0;
import './BridgeBase.sol';
contract BridgeBsc is BridgeBase {
constructor(address token) BridgeBase(token) {}
}
Next, deploy another component of the decentralized token bridge; the Ethereum token bridge using the following code.
pragma solidity ^0.8.0;
import './BridgeBase.sol';
contract BridgeEth is BridgeBase {
constructor(address token) BridgeBase(token) {}
}
Once done with the contracts, mint and burn the IToken using the following code:
pragma solidity ^0.8.0;
interface IToken {
function mint(address to, uint amount) external;
function burn(address owner, uint amount) external;
}
Next, after minting and burning the IToken, program the migrations:
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
contract Migrations {
address public owner = msg.sender;
uint public last_completed_migration;
modifier restricted() {
require(
msg.sender == owner,
"This function is restricted to the contract's owner"
);
_;
}
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
}
Now, write the smart contract for the token base.
pragma solidity ^0.8.0;
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
contract TokenBase is ERC20 {
address public admin;
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
admin = msg.sender;
}
function updateAdmin(address newAdmin) external {
require(msg.sender == admin, 'only admin');
admin = newAdmin;
}
function mint(address to, uint amount) external {
require(msg.sender == admin, 'only admin');
_mint(to, amount);
}
function burn(address owner, uint amount) external {
require(msg.sender == admin, 'only admin');
_burn(owner, amount);
}
}
Once the token base is deployed, deploy the token on Binance smart chain using the given code:
pragma solidity ^0.8.0;
import './TokenBase.sol';
contract TokenBsc is TokenBase {
constructor() TokenBase('BSC Token', 'BTK') {}
}
Next, deploy the token on Ethereum using the given code:
pragma solidity ^0.8.0;
import './TokenBase.sol';
contract TokenEth is TokenBase {
constructor() TokenBase('ETH Token', 'ETK') {}
}
Once the token is deployed on Binance smart chain and Ethereum, we will program the migration function:
const Migrations = artifacts.require("Migrations");
module.exports = function (deployer) {
deployer.deploy(Migrations);
};
Now, deploy the bridge between Ethereum and Binance smart chain.
const TokenEth = artifacts.require('TokenEth.sol');
const TokenBsc = artifacts.require('TokenBsc.sol');
const BridgeEth = artifacts.require('BridgeEth.sol');
const BridgeBsc = artifacts.require('BridgeBsc.sol');
module.exports = async function (deployer, network, addresses) {
if(network === 'ethTestnet') {
await deployer.deploy(TokenEth);
const tokenEth = await TokenEth.deployed();
await tokenEth.mint(addresses[0], 1000);
await deployer.deploy(BridgeEth, tokenEth.address);
const bridgeEth = await BridgeEth.deployed();
await tokenEth.updateAdmin(bridgeEth.address);
}
if(network === 'bscTestnet') {
await deployer.deploy(TokenBsc);
const tokenBsc = await TokenBsc.deployed();
await deployer.deploy(BridgeBsc, tokenBsc.address);
const bridgeBsc = await BridgeBsc.deployed();
await tokenBsc.updateAdmin(bridgeBsc.address);
}
};
Once the bridge is deployed, deploy the decentralized bridge:
const TokenBsc = artifacts.require('./TokenBsc.sol');
module.exports = async done => {
const [recipient, _] = await web3.eth.getAccounts();
const tokenBsc = await TokenBsc.deployed();
const balance = await tokenBsc.balanceOf(recipient);
console.log(balance.toString());
done();
}
Next, program the bridge API that listens to the transfer events:
const Web3 = require('web3');
const BridgeEth = require('../build/contracts/BridgeEth.json');
const BridgeBsc = require('../build/contracts/BridgeBsc.json');
const web3Eth = new Web3('url to eth node (websocket)');
const web3Bsc = new Web3('https://data-seed-prebsc-1-s1.binance.org:8545');
const adminPrivKey = '';
const { address: admin } = web3Bsc.eth.accounts.wallet.add(adminPrivKey);
const bridgeEth = new web3Eth.eth.Contract(
BridgeEth.abi,
BridgeEth.networks['4'].address
);
const bridgeBsc = new web3Bsc.eth.Contract(
BridgeBsc.abi,
BridgeBsc.networks['97'].address
);
bridgeEth.events.Transfer(
{fromBlock: 0, step: 0}
)
.on('data', async event => {
const { from, to, amount, date, nonce, signature } = event.returnValues;
const tx = bridgeBsc.methods.mint(from, to, amount, nonce, signature);
const [gasPrice, gasCost] = await Promise.all([
web3Bsc.eth.getGasPrice(),
tx.estimateGas({from: admin}),
]);
const data = tx.encodeABI();
const txData = {
from: admin,
to: bridgeBsc.options.address,
data,
gas: gasCost,
gasPrice
};
const receipt = await web3Bsc.eth.sendTransaction(txData);
console.log(Transaction hash: ${receipt.transactionHash});
console.log( Processed transfer: - from ${from} - to ${to} - amount ${amount} tokens - date ${date} - nonce ${nonce} );
});
Now, deploy the Private key function to the Ethereum bridge.
const BridgeEth = artifacts.require('./BridgeEth.sol');
const privKey = 'priv key of sender';
module.exports = async done => {
const nonce = 1; //Need to increment this for each new transfer
const accounts = await web3.eth.getAccounts();
const bridgeEth = await BridgeEth.deployed();
const amount = 1000;
const message = web3.utils.soliditySha3(
{t: 'address', v: accounts[0]},
{t: 'address', v: accounts[0]},
{t: 'uint256', v: amount},
{t: 'uint256', v: nonce},
).toString('hex');
const { signature } = web3.eth.accounts.sign(
message,
privKey
);
await bridgeEth.burn(accounts[0], amount, nonce, signature);
done();
}
At last, program Token balance function for the bridge:
const TokenEth = artifacts.require('./TokenEth.sol');
module.exports = async done => {
const [sender, _] = await web3.eth.getAccounts();
const tokenEth = await TokenEth.deployed();
const balance = await tokenEth.balanceOf(sender);
console.log(balance.toString());
done();
}
To run the demo, follow the given steps:
To deploy bridge smart contract on Ethereum, type this given code in the Ethereum test net
~ETB/code/screencast/317-eth-bsc-decenrealized-bridge $ truffle migrate --reset --network ethTestnet
To deploy bridge smart contract on Binance smart chain, type this given code in the BSC testnet
~ETB/code/screencast/317-eth-bsc-decenrealized-bridge $ truffle migrate --reset --network bscTestnet