JuChain’s cross-chain bridge is a decentralized service that allows developers to transfer assets between different testnets, such as the JuChain Testnet, BSC Testnet (Chapel), and ETH Testnet (Holesky). It operates through on-chain smart contracts (primarily the BridgeBank contract) and off-chain components (Relayers and Signers) that work together to manage locking, unlocking, minting, and burning operations, ensuring the security and efficiency of cross-chain asset transfers.
Architecture Overview
JuChain’s cross-chain bridge adopts a typical "lock/burn & mint/unlock" model, combined with an off-chain validation mechanism. Below are the core components and their interactions:
BridgeBank Contract: The core smart contract deployed on each supported chain (JuChain, BSC, ETH).
On the Source Chain: Responsible for receiving and locking (lock) users’ original assets or burning (burnBridgeTokens) assets bridged back, and triggering corresponding events (LogLock, LogBtcTokenBurn).
On the Target Chain: Responsible for minting (mintBridgeTokens) bridged assets or unlocking (unlock) returned original assets based on validated information.
Relayers: Off-chain services that monitor events from the BridgeBank contracts on each chain.
Monitoring: Continuously listen for LogLock events on the source chain (for ETH/BSC -> JuChain) or LogBtcTokenBurn events on JuChain (for JuChain -> ETH/BSC).
Submission: Upon detecting relevant events, collect event data and submit it to Signers for validation.
Execution: After receiving valid signatures/authorizations from Signers, call the appropriate methods (mintBridgeTokens or unlock) on the target chain’s BridgeBank contract to complete the cross-chain operation. The diagram shows multiple Relayers (Relayer_0, Relayer_1, Relayer_2), suggesting redundancy or parallel processing mechanisms.
Signers (Validators): Off-chain services responsible for verifying the authenticity and validity of cross-chain events.
Validation: Receive event data from Relayers and independently verify whether the event genuinely occurred and is valid on the source chain.
Authorization: Upon successful validation, generate signatures or other forms of authorization, allowing Relayers to execute operations on the target chain. The diagram includes signer0 and signer1 (possibly multiple signer1 instances or representing a multi-signature group), indicating that the validation process may involve multiple parties or vary by chain/process. The index information at signer0 (Index: 0:for juchain, 1:eth, 2:bsc) suggests that validation nodes internally distinguish between different chains.
Users: The end-users initiating cross-chain transfers, interacting with the source chain’s BridgeBank contract to start the process.
Admin: A role potentially responsible for providing initial liquidity, maintaining contracts, or performing other administrative tasks (e.g., depositing 20 into BSC as shown in the diagram).
This architecture ensures the security of cross-chain operations, as actions on the target chain (minting/unlocking) require confirmation from off-chain validators.
Contract Addresses and Token Information
Below are the detailed addresses of the cross-chain bridge-related contracts and tokens across different networks. Developers must ensure they use the correct network:
Network
Contract/Token
Address
Description/Link
--- Mainnet ---
ETH Mainnet
USDT (pre-bridge)
USDT contract on ETH Mainnet
BSC Mainnet
USDT (pre-bridge)
USDT contract on BSC Mainnet
JuChain Mainnet
BridgeBank
Core cross-chain contract on JuChain Mainnet
JuChain Mainnet
USDT (post-bridge)
Bridged USDT token on JuChain Mainnet
JuChain Mainnet
BNB (post-bridge)
Bridged BNB token on JuChain Mainnet
JuChain Mainnet
ETH (post-bridge)
---
(Not available)
ETH Mainnet
Signer Address
0xc739962C7805a46BEd5bDADB4Df033e9B9aC1ff2
Used to validate ETH -> JuChain transactions
BSC Mainnet
Signer Address
0xc3F59038F2fceDec5f41f46aBb130ca4446556E1
Used to validate BSC -> JuChain transactions
JuChain Mainnet
Signer Address
0xA62b1782af4AfFd74CEcFC5E0BA96E1b31eb371C
Used to validate JuChain -> ETH/BSC transactions
--- Testnet ---
JuChain Testnet
BridgeBank
Core cross-chain contract on JuChain Testnet
JuChain Testnet
USDT (post-bridge)
Bridged USDT token on JuChain Testnet
JuChain Testnet
tBNB (post-bridge)
Bridged tBNB token on JuChain Testnet
JuChain Testnet
tETH (post-bridge)
Bridged tETH token on JuChain Testnet
BSC Testnet (Chapel)
BridgeBank
Core cross-chain contract on BSC Testnet
BSC Testnet (Chapel)
USDT (pre-bridge)
USDT contract on BSC Testnet
ETH Testnet (Holesky)
BridgeBank
Core cross-chain contract on ETH Holesky Testnet
ETH Testnet (Holesky)
USDT (pre-bridge)
USDT contract on ETH Holesky Testnet
BridgeBank Contract Functions
The BridgeBank contract is the core of the cross-chain bridge, providing the following key functions that developers can interact with via ABI:
lock (ETH/BSC -> JuChain): Locks tokens on the source chain (ETH/BSC) and initiates a cross-chain transfer. This triggers the LogLock event, monitored by Relayers.
burnBridgeTokens (JuChain -> ETH/BSC): Burns bridged tokens on JuChain and initiates a return transfer to the source chain. This triggers the LogBtcTokenBurn event, monitored by Relayers.
Parameters: _chainID (target chain ID), _receiver (recipient address on the source chain), _bridgeTokenAddress (bridged token address on JuChain), _amount (amount).
unlock (JuChain -> ETH/BSC completion): Unlocks original tokens on the target chain (ETH/BSC). Called by a Relayer after receiving Signer authorization.
Parameters: _recipient (recipient address), _token (token address), _name (token name), _amount (amount). (ETH/BSC ABI also includes _claimID).
mintBridgeTokens (ETH/BSC -> JuChain completion): Mints bridged tokens on the target chain (JuChain). Called by a Relayer after receiving Signer authorization.
JuChain’s cross-chain bridge supports the following workflows:
From JuChain to BSC/ETH
Burn Assets (User Action - JuChain):
On the JuChain Testnet, the user calls the burnBridgeTokens function of the BridgeBank contract.
Parameters include: target chain ID (97 for BSC Chapel, 17000 for ETH Holesky), recipient address (on the target chain), token address (bridged token on JuChain), and amount.
The function burns the bridged tokens on JuChain and triggers the LogBtcTokenBurn event.
Relayers monitor and detect the LogBtcTokenBurn event on JuChain.
Relayers submit the event data to Signers.
Signers verify the validity of the burn event (confirming the transaction was successfully executed on JuChain) and generate authorization (e.g., a signature).
After receiving authorization, the Relayer calls the unlock function on the target chain’s (BSC or ETH) BridgeBank contract, attaching the authorization data.
The target chain’s BridgeBank contract verifies the authorization and unlocks/transfers the equivalent amount of original tokens to the user-specified recipient address.
From BSC/ETH to JuChain
Lock Assets (User Action - BSC/ETH):
On the BSC or ETH Testnet, the user calls the lock function of the BridgeBank contract.
Parameters include: recipient address (on JuChain), token address (original token on the source chain), and amount.
The function locks the user’s tokens and triggers the LogLock event.
After receiving authorization, the Relayer calls the mintBridgeTokens function on the JuChain Testnet’s BridgeBank contract, attaching the authorization data.
The JuChain BridgeBank contract verifies the authorization and mints the equivalent amount of bridged tokens to the user-specified recipient address.
Code Examples
Below are code examples using Web3.js to interact with the cross-chain bridge:
Cross-Chain Transfer from JuChain to BSC
const { Web3 } = require('web3');
// Connect to JuChain Testnet
const web3 = new Web3('https://testnet-rpc.juchain.org');
// BridgeBank ABI and contract address
const bridgeBankABI = [/* JuChain BridgeBank ABI from above */]; // Use the full JuChain BridgeBank ABI
const bridgeBankAddress = '0x3516949D3c530E4FB65Fa2a02ef808e5587ebaBE';
// Create contract instance
const bridgeBank = new web3.eth.Contract(bridgeBankABI, bridgeBankAddress);
// Account setup
const privateKey = 'your_private_key'; // !!Do not hardcode private keys in production code
const account = web3.eth.accounts.privateKeyToAccount(privateKey);
web3.eth.accounts.wallet.add(account);
// ERC20 token ABI (for balance checks and approvals)
const erc20ABI = [
{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},
{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},
{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},
{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},
{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},
{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}
];
// Cross-chain parameters
const bscChainID = 97; // BSC Testnet Chapel Chain ID
const receiverAddress = '0xrecipient_address'; // Recipient address on BSC
const tokenAddress = '0x16E0499Cb600ef4F4FbEca756E90D658D9a74E4D'; // USDT address on JuChain
const amount = web3.utils.toWei('10', 'ether'); // Transfer 10 tokens (assuming 18 decimals)
// Perform cross-chain transfer
async function crossChainTransfer() {
try {
// 1. Check account balance (for Gas and possible Service Fee)
const balance = await web3.eth.getBalance(account.address);
console.log('Account JU Balance:', web3.utils.fromWei(balance, 'ether'), 'JU');
// 2. Check token balance
const tokenContract = new web3.eth.Contract(erc20ABI, tokenAddress);
const tokenBalance = await tokenContract.methods.balanceOf(account.address).call();
console.log('Token Balance:', web3.utils.fromWei(tokenBalance, 'ether')); // Ensure correct decimals
if (BigInt(tokenBalance) < BigInt(amount)) {
console.error('Error: Insufficient token balance');
return;
}
// 3. Approve token usage (only needed for lock operations, not burn in most cases)
// Note: burnBridgeTokens typically doesn’t require approve since it burns user-owned tokens directly.
// Uncomment this section if your burnBridgeTokens requires token transfer first.
/*
const allowance = await tokenContract.methods.allowance(account.address, bridgeBankAddress).call();
if (BigInt(allowance) < BigInt(amount)) {
console.log('Insufficient allowance, approving...');
// Approve a sufficiently large amount
const maxApproval = '115792089237316195423570985008687907853269984665640564039457584007913129639935'; // 2^256 - 1
const approveTx = await tokenContract.methods.approve(bridgeBankAddress, maxApproval).send({
from: account.address,
gas: 200000 // Estimate or set appropriate Gas
});
console.log('Approval successful, Tx Hash:', approveTx.transactionHash);
// Wait for approval transaction confirmation
await web3.eth.getTransactionReceipt(approveTx.transactionHash);
}
*/
// 4. Get cross-chain service fee
let fee;
try {
fee = await bridgeBank.methods.bridgeServiceFee().call();
console.log('Service Fee:', web3.utils.fromWei(fee.toString(), 'ether'), 'JU'); // Ensure fee is a string
} catch (error) {
console.error('Failed to fetch service fee, possibly due to contract or RPC issue, using default:', error.message);
fee = web3.utils.toWei('0.01', 'ether'); // Default fee 0.01 JU
}
// Convert BigInt or number to string to avoid type mismatch
const feeString = fee.toString();
const amountString = amount.toString();
// 5. Estimate Gas
let estimatedGas;
try {
estimatedGas = await bridgeBank.methods.burnBridgeTokens(
bscChainID,
receiverAddress,
tokenAddress,
amountString
).estimateGas({ from: account.address, value: feeString });
console.log('Estimated Gas:', estimatedGas.toString());
} catch (error) {
console.error('Gas estimation failed, check parameters or network status, using default:', error.message);
estimatedGas = 500000n; // Use BigInt as default Gas limit
}
// Convert BigInt to number or string for transaction
const gasLimit = BigInt(estimatedGas) + (BigInt(estimatedGas) / 2n); // Add 50% Gas buffer
// 6. Call burnBridgeTokens function
console.log(`Preparing transaction: burnBridgeTokens(${bscChainID}, ${receiverAddress}, ${tokenAddress}, ${amountString}) with value: ${feeString}`);
const tx = await bridgeBank.methods.burnBridgeTokens(
bscChainID,
receiverAddress,
tokenAddress,
amountString
).send({
from: account.address,
value: feeString,
gas: gasLimit.toString(), // Send requires string or number
// EIP-1559 fee parameters (if supported by network)
// maxPriorityFeePerGas: web3.utils.toWei('2', 'gwei'), // Tip
// maxFeePerGas: web3.utils.toWei('50', 'gwei') // Max fee cap
// Or Legacy Gas Price:
// gasPrice: web3.utils.toWei('10', 'gwei')
});
console.log('Cross-chain transfer transaction hash:', tx.transactionHash);
console.log('Transaction sent to JuChain, please wait for Relayer and Signer to process and complete on BSC...');
} catch (error) {
console.error('Cross-chain transfer failed:', error);
if (error.receipt) {
console.error("Transaction failure receipt:", error.receipt);
}
}
}
crossChainTransfer();
Cross-Chain Transfer from BSC to JuChain
const { Web3 } = require('web3');
// Connect to BSC Testnet (use a public or your own node)
const bscRpcUrl = 'https://data-seed-prebsc-1-s1.binance.org:8545/'; // Example public node
// const bscRpcUrl = 'https://rpc.ankr.com/bsc_testnet_chapel/YOUR_ANKR_KEY'; // Ankr example
const web3 = new Web3(bscRpcUrl);
// BridgeBank ABI and contract address (on BSC)
const bridgeBankABI = [/* ETH/BSC BridgeBank ABI from above */]; // Use the full BSC BridgeBank ABI
const bridgeBankAddress = '0x30DBF30Eb71ddb49d526AFdb832C7Ba4D85953f6';
// Create contract instance
const bridgeBank = new web3.eth.Contract(bridgeBankABI, bridgeBankAddress);
// Account setup
const privateKey = 'your_private_key'; // !!Do not hardcode private keys in production code
const account = web3.eth.accounts.privateKeyToAccount(privateKey);
web3.eth.accounts.wallet.add(account);
// ERC20 token ABI (generic)
const erc20ABI = [
{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},
{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},
{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},
{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},
{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},
{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}
];
// Cross-chain parameters
const receiverAddress = '0xrecipient_address'; // Recipient address on JuChain
const tokenAddress = '0xcD1093897a5dB4a9aF153772B35AAA066ab969f3'; // USDT address on BSC Testnet
const amount = web3.utils.toWei('10', 'ether'); // Transfer 10 tokens (assuming 18 decimals)
// Perform cross-chain transfer
async function crossChainTransfer() {
try {
// 1. Check BSC account balance (for Gas and possible Service Fee)
const balance = await web3.eth.getBalance(account.address);
console.log('BSC Account Balance:', web3.utils.fromWei(balance, 'ether'), 'tBNB');
// 2. Check token balance
const tokenContract = new web3.eth.Contract(erc20ABI, tokenAddress);
const tokenBalance = await tokenContract.methods.balanceOf(account.address).call();
console.log('BSC Token Balance:', web3.utils.fromWei(tokenBalance, 'ether')); // Ensure correct decimals
if (BigInt(tokenBalance) < BigInt(amount)) {
console.error('Error: Insufficient token balance');
return;
}
// 3. Approve token usage (required for lock operation)
const allowance = await tokenContract.methods.allowance(account.address, bridgeBankAddress).call();
console.log('Current Allowance:', web3.utils.fromWei(allowance, 'ether'));
if (BigInt(allowance) < BigInt(amount)) {
console.log('Insufficient allowance, approving...');
// Approve the amount to transfer, or a larger amount, e.g., 100 tokens
// const approvalAmount = amount; // Approve only the required amount
const approvalAmount = web3.utils.toWei('100', 'ether'); // Approve 100 tokens
// const maxApproval = '115792089237316195423570985008687907853269984665640564039457584007913129639935'; // Max approval
const approveGas = await tokenContract.methods.approve(bridgeBankAddress, approvalAmount).estimateGas({ from: account.address });
const approveTx = await tokenContract.methods.approve(bridgeBankAddress, approvalAmount).send({
from: account.address,
gas: (BigInt(approveGas) + BigInt(approveGas) / 2n).toString(), // Add 50% Gas buffer
// gasPrice: web3.utils.toWei('10', 'gwei') // BSC Testnet Gas Price
});
console.log('Approval successful, Tx Hash:', approveTx.transactionHash);
// Waiting for approval confirmation might be safer
await web3.eth.getTransactionReceipt(approveTx.transactionHash);
console.log('Approval confirmed');
} else {
console.log('Sufficient allowance');
}
// 4. Get cross-chain service fee
let fee;
try {
fee = await bridgeBank.methods.bridgeServiceFee().call();
console.log('Service Fee:', web3.utils.fromWei(fee.toString(), 'ether'), 'tBNB'); // Ensure fee is a string
} catch (error) {
console.error('Failed to fetch service fee, using default:', error.message);
fee = web3.utils.toWei('0.001', 'ether'); // Example default fee 0.001 tBNB
}
const feeString = fee.toString();
const amountString = amount.toString();
// 5. Estimate Gas
let estimatedGas;
try {
estimatedGas = await bridgeBank.methods.lock(
receiverAddress,
tokenAddress,
amountString
).estimateGas({ from: account.address, value: feeString });
console.log('Estimated Gas:', estimatedGas.toString());
} catch (error) {
console.error('Gas estimation failed, check parameters, approval, or network status, using default:', error.message);
estimatedGas = 500000n; // Use BigInt as default Gas limit
}
const gasLimit = BigInt(estimatedGas) + (BigInt(estimatedGas) / 2n); // Add 50% Gas buffer
// 6. Call lock function for cross-chain transfer
console.log(`Preparing transaction: lock(${receiverAddress}, ${tokenAddress}, ${amountString}) with value: ${feeString}`);
const tx = await bridgeBank.methods.lock(
receiverAddress,
tokenAddress,
amountString
).send({
from: account.address,
value: feeString,
gas: gasLimit.toString(),
// gasPrice: web3.utils.toWei('10', 'gwei') // BSC Testnet Gas Price
});
console.log('Cross-chain transfer transaction hash:', tx.transactionHash);
console.log('Transaction sent to BSC, please wait for Relayer and Signer to process and complete minting on JuChain...');
} catch (error) {
console.error('Cross-chain transfer failed:', error);
if (error.receipt) {
console.error("Transaction failure receipt:", error.receipt);
}
}
}
crossChainTransfer();
Notes
Testnet Limitations:
These contracts and tokens are only available on testnets and are not suitable for mainnet environments.
Testnets may be reset periodically; do not store valuable assets on them.
Service Fees:
Cross-chain operations require a service fee, which can be queried via the bridgeServiceFee function.
Fees are paid in the source chain’s native token (JU, tBNB, or Holesky ETH) and sent as the value when initiating lock or burnBridgeTokens transactions.
Approval Requirements:
Before calling the lock function to lock ERC20 tokens, users must first call the token contract’s approve function to authorize the BridgeBank contract to use the required amount.
burnBridgeTokens typically does not require approve, as it directly operates on the bridged tokens in the user’s account.
Cross-Chain Delay:
Cross-chain operations are not instantaneous, as they depend on Relayers detecting events, Signers validating them, and Relayers executing actions on the target chain.
Delays depend on network congestion and the processing speed of off-chain components. Be patient and check transaction statuses on block explorers for both source and target chains.
Token Compatibility:
Only whitelisted tokens can be bridged.
Currently supported tokens include USDT, tBNB, and tETH (in bridged form on JuChain or original form on ETH/BSC).
Whether there’s enough token balance and service fee (native token balance).
Review console output for error messages and transaction receipts.
Off-Chain Component Dependency:
The bridge’s proper functioning relies on off-chain Relayers and Signers. While these components are typically designed to be decentralized and resilient (e.g., through multiple nodes, multi-signature, or consensus mechanisms), their availability, correctness, and processing speed are critical to the timely completion of cross-chain transfers.
Cross-Chain Fees
Cross-chain operations require a service fee to cover the Gas costs on the target chain and platform operational costs. The fee is paid in the source chain’s native token when initiating a lock (ETH/BSC to JuChain) or burnBridgeTokens (JuChain to ETH/BSC) transaction.
Fee Structure:
Fees typically consist of two parts:
Network Gas Fee: Covers the transaction execution cost on the target chain (e.g., unlocking or minting). This varies based on the target chain’s real-time Gas price.
Platform Service Fee: Charged by the JuChain platform to maintain the bridge service.
Mainnet Fee Reference (subject to change based on network conditions and token prices):
Charges 0.00005 ETH (network fee) + 0.0002 JU (platform fee)
(Estimated value: network ≈ 0.1 USD, platform ≈ 0.0004 USD)
ETH > JU
ETH
Charges 0.00005 ETH (network fee) + 0.0002 JU (platform fee)
(Estimated value: network ≈ 0.1 USD, platform ≈ 0.0004 USD)
JU > BSC
USDT
Charges 1 JU (platform fee) + 0.002 BNB (estimated network fee)
(Estimated total ≈ 1 USD)
JU > BSC
BNB
Charges 1 JU (platform fee) + 0.002 BNB (estimated network fee)
(Estimated total ≈ 1 USD)
JU > ETH
USDT
Charges 1 JU (platform fee) + 0.00002 ETH (estimated network fee)
(Estimated total ≈ 0.036 USD)
JU > ETH
ETH
Charges 1 JU (platform fee) + 0.00002 ETH (estimated network fee)
(Estimated total ≈ 0.036 USD)
Note: The above value estimates are based on prices at a specific time (e.g., BNB=590 USD, ETH=1800 USD, JU=2 USD). Actual fees and values will fluctuate. The platform consumption portion refers to the cost incurred by the platform to process the transaction.
Fee Receiver Address:
0x8C0641240B418e0349dC52abd3F5cEcc4D4C748A
Recommendation: Before performing a cross-chain operation, you can query the current estimated service fee (typically only the platform fee, excluding target chain Gas) by calling the bridgeServiceFee() method on the source chain’s BridgeBank contract. The total fee paid should be estimated by the user based on current network conditions and target chain Gas prices.