Building a Multi-Chain Bridge DApp with Next.js, Solidity, Axios, and Hardhat
Sadikur Rahman
Experienced .NET Developer, Team Lead, and Blockchain Enthusiast with Proficiency in SQL Server, Frontend Development, and Node.js. Building Innovative Solutions( mobile and web application) for Finance, Education.
Welcome to the exciting world of decentralized applications (DApps) and blockchain development! In this series, I'll guide you through the process of building your first DApp, starting with a simple and easy project. Every 15 days, we'll bring you a new project to enhance your skills and understanding of blockchain development.
In this tutorial, I'll guide you through the process of building a decentralized application (DApp) that serves as a multi-chain bridge, connecting Ethereum, Binance Smart Chain (BSC), and Polygon. The project will utilize Next.js for the front end, Solidity for smart contracts, Axios for HTTP requests, and Hardhat for Ethereum development.
Project Overview
The DApp will allow users to lock tokens on one blockchain and unlock them on another, creating a seamless bridge between different chains. We'll implement Ethereum, BSC, and Polygon bridges, each with specific logic for locking and unlocking tokens.
Bridge.sol
We start by creating a generic bridge contract that defines the basic interface for token locking and unlocking. The contract also includes events to log these operations.
// contracts/Bridge.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IBridge {
function lockTokens(uint256 amount, bytes32 destinationChain, address recipient) external;
function unlockTokens(uint256 amount, bytes32 sourceChain, address recipient) external;
}
contract Bridge is IBridge {
IERC20 public token;
constructor(address _token) {
token = IERC20(_token);
}
function lockTokens(uint256 amount, bytes32 destinationChain, address recipient) external override {
require(token.allowance(msg.sender, address(this)) >= amount, "Insufficient allowance");
require(token.transferFrom(msg.sender, address(this), amount), "Token transfer failed");
emit TokensLocked(amount, destinationChain, msg.sender, recipient);
}
function unlockTokens(uint256 amount, bytes32 sourceChain, address recipient) external override {
require(token.transfer(recipient, amount), "Token transfer failed");
emit TokensUnlocked(amount, sourceChain, msg.sender, recipient);
}
event TokensLocked(uint256 amount, bytes32 indexed destinationChain, address indexed sender, address indexed recipient);
event TokensUnlocked(uint256 amount, bytes32 indexed sourceChain, address indexed sender, address indexed recipient);
}
EthereumBridge.sol, BSCBridge.sol, PolygonBridge.sol
We then extend the generic bridge contract to create specific bridges for Ethereum, BSC, and Polygon, adding chain-specific logic.
EthereumBridge.sol
// contracts/EthereumBridge.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./Bridge.sol";
contract EthereumBridge is Bridge {
constructor(address _token) Bridge(_token) {}
function mintWrappedTokens(uint256 amount, address sender, address recipient) internal {
// Simple minting logic for Ethereum (update balances and emit Mint event)
// Update sender and recipient balances
balances[sender] -= amount;
balances[recipient] += amount;
// Emit Mint event
emit Mint(sender, recipient, amount);
}
event Mint(address indexed sender, address indexed recipient, uint256 amount);
}
BSCBridge.sol
// contracts/BSCBridge.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./Bridge.sol";
contract BSCBridge is Bridge {
constructor(address _token) Bridge(_token) {}
function mintWrappedTokens(uint256 amount, address sender, address recipient) internal {
// Simple minting logic for BSC (update balances and emit Mint event)
// Update sender and recipient balances
balances[sender] -= amount;
balances[recipient] += amount;
// Emit Mint event
emit Mint(sender, recipient, amount);
}
event Mint(address indexed sender, address indexed recipient, uint256 amount);
}
PolygonBridge.sol
// contracts/PolygonBridge.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./Bridge.sol";
contract PolygonBridge is Bridge {
constructor(address _token) Bridge(_token) {}
function mintWrappedTokens(uint256 amount, address sender, address recipient) internal {
// Simple minting logic for Polygon (update balances and emit Mint event)
// Update sender and recipient balances
balances[sender] -= amount;
balances[recipient] += amount;
// Emit Mint event
emit Mint(sender, recipient, amount);
}
event Mint(address indexed sender, address indexed recipient, uint256 amount);
}
The specific bridge contracts extend the generic Bridge contract and implement a simple mintWrappedTokens function, which represents the chain-specific logic for minting wrapped tokens.
Hardhat Configuration
Ensure Hardhat is configured with the necessary networks for Ethereum, BSC, and Polygon. Your hardhat.config.js should look something like this:
// hardhat.config.js
require('@nomiclabs/hardhat-waffle');
module.exports = {
networks: {
hardhat: {},
rinkeby: {
url: 'https://rinkeby.infura.io/v3/YOUR_INFURA_API_KEY',
accounts: ['YOUR_PRIVATE_KEY'],
},
bsc: {
url: 'https://bsc-dataseed.binance.org/',
accounts: ['YOUR_PRIVATE_KEY'],
},
polygon: {
url: 'https://polygon-mainnet.infura.io/v3/YOUR_INFURA_API_KEY',
accounts: ['YOUR_PRIVATE_KEY'],
},
},
solidity: '0.8.0',
};
Replace 'YOUR_INFURA_API_KEY' and 'YOUR_PRIVATE_KEY' with your Infura API key and private key, respectively.
Smart Contract Deployment
Use a deployment script, deploy.js, to deploy your token contract and bridge contracts. This script should be placed in the scripts directory.
// scripts/deploy.js
async function main() {
const [deployer] = await ethers.getSigners();
console.log('Deploying contracts with the account:', deployer.address);
const Token = await ethers.getContractFactory('YourToken');
const token = await Token.deploy();
console.log('Token address:', token.address);
const Bridge = await ethers.getContractFactory('Bridge');
const bridge = await Bridge.deploy(token.address);
console.log('Bridge address:', bridge.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Ensure to replace 'YourToken' with the actual name of your token contract.
Run the deployment script:
npx hardhat run scripts/deploy.js --network hardhat
领英推荐
API Routes for Next.js
Create API routes in the pages/api directory to handle interactions with the deployed smart contracts using Axios.
pages/api/lock-tokens.js
// pages/api/lock-tokens.js
import axios from 'axios';
export default async function handler(req, res) {
try {
const response = await axios.post('YOUR_SMART_CONTRACT_ENDPOINT/lock-tokens', req.body);
res.status(200).json(response.data);
} catch (error) {
console.error('Error locking tokens:', error);
res.status(500).json({ success: false, error: error.message });
}
}
pages/api/unlock-tokens.js
// pages/api/unlock-tokens.js
import axios from 'axios';
export default async function handler(req, res) {
try {
const response = await axios.post('YOUR_SMART_CONTRACT_ENDPOINT/unlock-tokens', req.body);
res.status(200).json(response.data);
} catch (error) {
console.error('Error unlocking tokens:', error);
res.status(500).json({ success: false, error: error.message });
}
}
pages/api/configure-ethereum-bridge.js
// pages/api/configure-ethereum-bridge.js
import axios from 'axios';
export default async function handler(_, res) {
try {
const response = await axios.post('YOUR_SMART_CONTRACT_ENDPOINT/configure-ethereum-bridge');
res.status(200).json(response.data);
} catch (error) {
console.error('Error configuring Ethereum bridge:', error);
res.status(500).json({ success: false, error: error.message });
}
}
pages/api/configure-bsc-bridge.js
// pages/api/configure-bsc-bridge.js
import axios from 'axios';
export default async function handler(_, res) {
try {
const response = await axios.post('YOUR_SMART_CONTRACT_ENDPOINT/configure-bsc-bridge');
res.status(200).json(response.data);
} catch (error) {
console.error('Error configuring BSC bridge:', error);
res.status(500).json({ success: false, error: error.message });
}
}
api/configure-polygon-bridge.js
// pages/api/configure-polygon-bridge.js
import axios from 'axios';
export default async function handler(_, res) {
try {
const response = await axios.post('YOUR_SMART_CONTRACT_ENDPOINT/configure-polygon-bridge');
res.status(200).json(response.data);
} catch (error) {
console.error('Error configuring Polygon bridge:', error);
res.status(500).json({ success: false, error: error.message });
}
}
Replace 'YOUR_SMART_CONTRACT_ENDPOINT' with the actual endpoint where your smart contract API is hosted.
Next.js Frontend
Create a simple Next.js frontend to interact with the DApp. Below is a basic example:
pages/index.js
// pages/index.js
import { useState } from 'react';
import axios from 'axios';
const Home = () => {
const [amount, setAmount] = useState('');
const [destinationChain, setDestinationChain] = useState('');
const [recipient, setRecipient] = useState('');
const [message, setMessage] = useState('');
const handleLockTokens = async () => {
try {
await axios.post('/api/lock-tokens', { amount, destinationChain, recipient });
setMessage('Tokens locked successfully!');
} catch (error) {
console.error('Error locking tokens:', error);
setMessage('Error locking tokens.');
}
};
const handleUnlockTokens = async () => {
try {
await axios.post('/api/unlock-tokens', { amount, sourceChain: destinationChain, recipient });
setMessage('Tokens unlocked successfully!');
} catch (error) {
console.error('Error unlocking tokens:', error);
setMessage('Error unlocking tokens.');
}
};
const handleConfigureBridge = async () => {
try {
if (destinationChain === 'Ethereum') {
await axios.post('/api/configure-ethereum-bridge');
} else if (destinationChain === 'BSC') {
await axios.post('/api/configure-bsc-bridge');
} else if (destinationChain === 'Polygon') {
await axios.post('/api/configure-polygon-bridge');
}
setMessage(`${destinationChain} bridge configured successfully!`);
} catch (error) {
console.error(`Error configuring ${destinationChain} bridge:`, error);
setMessage(`Error configuring ${destinationChain} bridge.`);
}
};
return (
<div>
<h1>Multi-Chain Bridge DApp</h1>
<div>
<label>
Amount:
<input type="text" value={amount} onChange={(e) => setAmount(e.target.value)} />
</label>
</div>
<div>
<label>
Destination Chain:
<input type="text" value={destinationChain} onChange={(e) => setDestinationChain(e.target.value)} />
</label>
</div>
<div>
<label>
Recipient:
<input type="text" value={recipient} onChange={(e) => setRecipient(e.target.value)} />
</label>
</div>
<div>
<button onClick={handleLockTokens}>Lock Tokens</button>
<button onClick={handleUnlockTokens}>Unlock Tokens</button>
<button onClick={handleConfigureBridge}>Configure Bridge</button>
</div>
<div>
<p>{message}</p>
</div>
</div>
);
};
export default Home;
Running the Project
npx hardhat run scripts/deploy.js --network hardhat
npx hardhat node
npx hardhat run scripts/deploy.js --network localhost
npm run dev
Visit https://localhost:3000 in your browser to interact with the DApp. You can lock and unlock tokens, and configure bridges between different chains.
This is a basic example to get you started. In a real-world scenario, you would need to handle additional aspects such as user authentication, error handling, and security measures. Additionally, thoroughly test your DApp before deploying it to a production environment.
Congratulations on completing your first project! You've taken a significant step into the world of blockchain development. We hope you enjoyed building the Multi-Chain Bridge DApp and gained valuable insights into smart contract development, decentralized applications, and blockchain networks.
For the next leg of our journey, get ready to dive into the exciting realm of Non-Fungible Tokens (NFTs). In our upcoming project, we'll guide you through building a Simple NFT Market using Solidity, Hardhat, Axios, and Next.js. This project will explore the creation, minting, and trading of NFTs on the blockchain.
Stay tuned for the next tutorial in 15 days, where we'll embark on the journey of crafting your own NFT market. Keep coding, exploring, and expanding your skills in the fascinating world of blockchain technology. See you in the next project!