├── service ├── .gitignore ├── .eslintignore ├── .prettierignore ├── .ionide │ └── symbolCache.db ├── nodemon.json ├── .prettierrc ├── src │ ├── tests │ │ ├── eth.js │ │ ├── db.js │ │ ├── rpc.js │ │ ├── bsc.js │ │ └── utils.js │ ├── controllers │ │ ├── index.js │ │ ├── BalanceController.js │ │ ├── IndexController.js │ │ └── SubmitController.js │ ├── models │ │ ├── Data.js │ │ ├── Transfer.js │ │ ├── Transaction.js │ │ └── Conversion.js │ ├── modules │ │ ├── eth.js │ │ ├── index.js │ │ ├── data.js │ │ ├── db.js │ │ ├── bsc.js │ │ ├── rpc.js │ │ ├── web3.js │ │ └── chores.js │ ├── server.js │ ├── app.js │ └── utils │ │ ├── config.js │ │ └── index.js ├── Dockerfile ├── package.json └── abi │ └── WBGL.json ├── app ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── src │ ├── assets │ │ ├── images │ │ │ └── logo.png │ │ └── abi │ │ │ └── WBGL.json │ ├── utils │ │ ├── config.js │ │ ├── index.js │ │ └── wallet.js │ ├── reportWebVitals.js │ ├── components │ │ ├── CheckWalletConnection.js │ │ ├── ConnectWallet.js │ │ ├── Header.js │ │ ├── App.js │ │ ├── Footer.js │ │ ├── BglToWbgl.js │ │ └── WbglToBgl.js │ └── index.js ├── .gitignore ├── Dockerfile ├── package.json └── README.md ├── docker ├── BGL.conf └── Dockerfile ├── .github └── FUNDING.yml ├── docker-compose.example.yml ├── README.md └── .gitignore /service/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules 3 | .ionide -------------------------------------------------------------------------------- /service/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json -------------------------------------------------------------------------------- /service/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json -------------------------------------------------------------------------------- /app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BitgesellOfficial/wbgl-bridge/HEAD/app/public/favicon.ico -------------------------------------------------------------------------------- /app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BitgesellOfficial/wbgl-bridge/HEAD/app/public/logo192.png -------------------------------------------------------------------------------- /app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BitgesellOfficial/wbgl-bridge/HEAD/app/public/logo512.png -------------------------------------------------------------------------------- /app/src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BitgesellOfficial/wbgl-bridge/HEAD/app/src/assets/images/logo.png -------------------------------------------------------------------------------- /service/.ionide/symbolCache.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BitgesellOfficial/wbgl-bridge/HEAD/service/.ionide/symbolCache.db -------------------------------------------------------------------------------- /service/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": false, 3 | "watch": ["src"], 4 | "env": { 5 | "NODE_ENV": "development" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "tabWidth": 2, 4 | "printWidth": 80, 5 | "singleQuote": false, 6 | "trailingComma": "all" 7 | } 8 | -------------------------------------------------------------------------------- /docker/BGL.conf: -------------------------------------------------------------------------------- 1 | rpcbind=0.0.0.0 2 | rpcport=8455 3 | rpcallowip=0.0.0.0/0 4 | rpcuser=localuser 5 | rpcpassword=devpass 6 | 7 | txindex=1 8 | fallbackfee=0.0001 9 | -------------------------------------------------------------------------------- /app/src/utils/config.js: -------------------------------------------------------------------------------- 1 | export const appTitle = process.env.REACT_APP_TITLE || 'Bitgesell-WBGL Bridge' 2 | 3 | export const serviceUrl = process.env.REACT_APP_SERVICE_URL 4 | 5 | export const isTest = process.env.NODE_ENV !== 'production' 6 | -------------------------------------------------------------------------------- /service/src/tests/eth.js: -------------------------------------------------------------------------------- 1 | import assert from "assert"; 2 | import { Eth } from "../modules/index.js"; 3 | 4 | describe("instantiate ethereum via Web3Base", () => { 5 | it("should return handle to communicate ethereum", () => { 6 | assert.ok(Eth); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /service/src/controllers/index.js: -------------------------------------------------------------------------------- 1 | import * as IndexController from "./IndexController.js"; 2 | import * as BalanceController from "./BalanceController.js"; 3 | import * as SubmitController from "./SubmitController.js"; 4 | 5 | export { IndexController, BalanceController, SubmitController }; 6 | -------------------------------------------------------------------------------- /service/src/models/Data.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const schema = new mongoose.Schema({ 4 | name: { type: String, required: true, unique: true }, 5 | value: { type: mongoose.Schema.Types.Mixed, required: true }, 6 | }); 7 | 8 | export default mongoose.model("Data", schema); 9 | -------------------------------------------------------------------------------- /service/src/modules/eth.js: -------------------------------------------------------------------------------- 1 | import { confirmations, eth } from "../utils/config.js"; 2 | import Web3Base from "./web3.js"; 3 | 4 | export default new Web3Base( 5 | eth.endpoint, 6 | "eth", 7 | eth.contract, 8 | eth.account, 9 | eth.key, 10 | "nonce", 11 | confirmations.eth, 12 | ); 13 | -------------------------------------------------------------------------------- /service/src/modules/index.js: -------------------------------------------------------------------------------- 1 | import eth from "./eth.js"; 2 | import bsc from "./bsc.js"; 3 | 4 | export * as RPC from "./rpc.js"; 5 | export * as Db from "./db.js"; 6 | export * as Data from "./data.js"; 7 | export * as Chores from "./chores.js"; 8 | 9 | export const Eth = eth; 10 | export const Bsc = bsc; 11 | -------------------------------------------------------------------------------- /service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:buster-slim 2 | RUN apt-get update && apt-get install -y nano 3 | RUN npm install pm2 -g 4 | 5 | ARG NODE_ENV=production 6 | ENV NODE_ENV $NODE_ENV 7 | 8 | WORKDIR /service 9 | 10 | COPY . . 11 | 12 | RUN yarn install 13 | 14 | CMD ["sh", "-c", "yarn ; pm2-runtime src/server.js"] 15 | -------------------------------------------------------------------------------- /service/src/controllers/BalanceController.js: -------------------------------------------------------------------------------- 1 | import { Bsc, Eth, RPC } from "../modules/index.js"; 2 | 3 | export const bgl = async (_req, res) => 4 | res.json(Math.floor(await RPC.getBalance())); 5 | export const eth = async (_req, res) => 6 | res.json(parseInt(await Eth.getWBGLBalance())); 7 | export const bsc = async (_req, res) => 8 | res.json(parseInt(await Bsc.getWBGLBalance())); 9 | -------------------------------------------------------------------------------- /service/src/tests/db.js: -------------------------------------------------------------------------------- 1 | import assert from "assert"; 2 | import { Db } from "../modules/index.js"; 3 | 4 | describe("init()", () => { 5 | it("should return true for database init", async () => { 6 | if (!Db.isConnected()) { 7 | let isconnected = await Db.init(); 8 | assert.equal(isconnected, true); 9 | } else { 10 | assert.equal(Db.isConnected(), true); 11 | } 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /service/src/modules/data.js: -------------------------------------------------------------------------------- 1 | import Data from "../models/Data.js"; 2 | 3 | export async function get(name, defaultValue = null) { 4 | const record = await Data.findOne({ name }).exec(); 5 | return record 6 | ? record["value"] 7 | : defaultValue instanceof Function 8 | ? await defaultValue() 9 | : defaultValue; 10 | } 11 | 12 | export async function set(name, value) { 13 | await Data.updateOne({ name }, { value }, { upsert: true }); 14 | } 15 | -------------------------------------------------------------------------------- /service/src/models/Transfer.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const schema = new mongoose.Schema( 4 | { 5 | id: { type: String, required: true }, 6 | type: { type: String, required: true, enum: ["bgl", "wbgl"] }, 7 | chain: { type: String, enum: ["eth", "bsc"], default: "eth" }, 8 | from: { type: String, required: true }, 9 | to: { type: String, required: true }, 10 | }, 11 | { timestamps: true }, 12 | ); 13 | 14 | export default mongoose.model("Transfer", schema); 15 | -------------------------------------------------------------------------------- /app/Dockerfile: -------------------------------------------------------------------------------- 1 | # pull base image 2 | FROM node:buster-slim 3 | 4 | # set our node environment, either development or production 5 | # defaults to production, compose overrides this to development on build and run 6 | ARG NODE_ENV=production 7 | ENV NODE_ENV $NODE_ENV 8 | 9 | # default to port 19006 for node, and 19001 and 19002 (tests) for debug 10 | ARG PORT=19006 11 | ENV PORT $PORT 12 | EXPOSE $PORT 19001 19002 13 | 14 | WORKDIR /app 15 | ENV PATH /app/.bin:$PATH 16 | COPY ./package.json ./ 17 | RUN yarn install 18 | -------------------------------------------------------------------------------- /service/src/server.js: -------------------------------------------------------------------------------- 1 | import http from "http"; 2 | import app from "./app.js"; 3 | import { port } from "./utils/config.js"; 4 | import { Db, Chores } from "./modules/index.js"; 5 | 6 | const server = http.createServer(app); 7 | server.listen(parseInt(port), () => { 8 | console.log(`listening on *:${port}`); 9 | }); 10 | 11 | process.on("uncaughtException", async (error) => { 12 | console.log("UNCAUGHT EXCEPTION:", error); 13 | await Db.close(); 14 | process.exit(1); 15 | }); 16 | 17 | await Db.init(); 18 | await Chores.init(); 19 | -------------------------------------------------------------------------------- /app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /app/src/components/CheckWalletConnection.js: -------------------------------------------------------------------------------- 1 | import {useMetaMask} from 'metamask-react' 2 | import React from 'react' 3 | import {isChainValid} from '../utils' 4 | import {isTest} from '../utils/config' 5 | 6 | function CheckWalletConnection({children}) { 7 | const {status, chainId} = useMetaMask() 8 | if (status !== 'connected') { 9 | return 'Please connect wallet.' 10 | } else if (!isChainValid(chainId)) { 11 | return `Please connect your wallet to either Ethereum ${isTest ? '(Ropsten)' : ''} or Binance Smart Chain ${isTest ? 'Testnet' : 'Mainnet'}.` 12 | } 13 | return children 14 | } 15 | 16 | export default CheckWalletConnection 17 | -------------------------------------------------------------------------------- /app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import {MetaMaskProvider} from 'metamask-react' 4 | import App from './components/App' 5 | import reportWebVitals from './reportWebVitals' 6 | 7 | ReactDOM.render( 8 | 9 | 10 | 11 | 12 | , 13 | document.getElementById('root'), 14 | ) 15 | 16 | // If you want to start measuring performance in your app, pass a function 17 | // to log results (for example: reportWebVitals(console.log)) 18 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 19 | reportWebVitals() 20 | -------------------------------------------------------------------------------- /service/src/models/Transaction.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const schema = new mongoose.Schema( 4 | { 5 | type: { type: String, required: true }, 6 | chain: { type: String, enum: ["eth", "bsc"] }, 7 | id: { type: String, required: true }, 8 | transfer: { 9 | type: mongoose.Schema.Types.ObjectId, 10 | required: true, 11 | ref: "Transfer", 12 | }, 13 | address: { type: String, required: true }, 14 | amount: { type: Number, required: true }, 15 | blockHash: { type: String, required: true }, 16 | time: { type: Date, required: true }, 17 | }, 18 | { timestamps: true }, 19 | ); 20 | 21 | export default mongoose.model("Transaction", schema); 22 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bullseye-slim 2 | 3 | RUN apt-get update -y \ 4 | && apt-get install curl ca-certificates apt-transport-https bash perl -y \ 5 | && apt-get clean 6 | 7 | RUN curl -L "http://security.ubuntu.com/ubuntu/pool/main/p/perl/perl-modules-5.30_5.30.0-9ubuntu0.2_all.deb" -o "/var/tmp/perl-modules.deb" \ 8 | && curl -L "https://github.com/BitgesellOfficial/bitgesell/releases/download/0.1.7/bitgesell_0.1.7_amd64.deb" -o "/var/tmp/bitgesell.deb" \ 9 | && dpkg -i "/var/tmp/perl-modules.deb" \ 10 | && dpkg -i "/var/tmp/bitgesell.deb" \ 11 | && apt-get install -y -f \ 12 | && rm -rf "/var/tmp/*" 13 | 14 | WORKDIR "/root/.BGL" 15 | 16 | COPY BGL.conf . 17 | 18 | EXPOSE 8455 19 | 20 | VOLUME "/root/.BGL" 21 | 22 | CMD ["BGLd"] 23 | -------------------------------------------------------------------------------- /service/src/modules/db.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import { mongo } from "../utils/config.js"; 3 | 4 | let dbConnected = false; 5 | 6 | export const init = async () => { 7 | try { 8 | await mongoose.connect(mongo.url, { 9 | dbName: mongo.database, 10 | useNewUrlParser: true, 11 | useUnifiedTopology: true, 12 | useCreateIndex: true, 13 | }); 14 | } catch (error) { 15 | setTimeout(() => { 16 | console.log("DB failed to start"); 17 | init(); 18 | }, 10000); 19 | return (dbConnected = false); 20 | } 21 | return (dbConnected = true); 22 | }; 23 | 24 | export const isConnected = () => { 25 | return dbConnected; 26 | }; 27 | 28 | export const close = async () => { 29 | await mongoose.disconnect(); 30 | dbConnected = false; 31 | }; 32 | -------------------------------------------------------------------------------- /app/src/components/ConnectWallet.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {useMetaMask} from 'metamask-react' 3 | import {Button, Chip} from '@material-ui/core' 4 | import {chainLabel, isChainValid} from '../utils' 5 | 6 | function ConnectWallet() { 7 | const {status, chainId, connect} = useMetaMask() 8 | 9 | switch (status) { 10 | case 'initializing': 11 | return (
Synchronizing...
) 12 | 13 | case 'unavailable': 14 | return (
Please install Metamask!
) 15 | 16 | case 'notConnected': 17 | return () 18 | 19 | case 'connecting': 20 | return (
Connecting...
) 21 | 22 | case 'connected': 23 | return () 24 | } 25 | } 26 | 27 | export default ConnectWallet 28 | -------------------------------------------------------------------------------- /service/src/models/Conversion.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const schema = new mongoose.Schema( 4 | { 5 | type: { type: String, required: true }, 6 | chain: { type: String, enum: ["eth", "bsc"], default: "eth" }, 7 | transfer: { 8 | type: mongoose.Schema.Types.ObjectId, 9 | required: true, 10 | ref: "Transfer", 11 | }, 12 | transaction: { 13 | type: mongoose.Schema.Types.ObjectId, 14 | required: true, 15 | ref: "Transaction", 16 | }, 17 | address: { type: String, required: true }, 18 | amount: { type: Number, required: true }, 19 | sendAmount: { type: Number }, 20 | txid: String, 21 | nonce: Number, 22 | receipt: Object, 23 | returnTxid: String, 24 | status: { type: String, default: "pending" }, 25 | txChecks: Number, 26 | }, 27 | { timestamps: true }, 28 | ); 29 | 30 | export default mongoose.model("Conversion", schema); 31 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: vporton # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] -------------------------------------------------------------------------------- /service/src/modules/bsc.js: -------------------------------------------------------------------------------- 1 | import Common from "ethereumjs-common"; 2 | import { bsc, confirmations } from "../utils/config.js"; 3 | import Web3Base from "./web3.js"; 4 | 5 | class Bsc extends Web3Base { 6 | async getNetworkId() { 7 | if (!this.networkId) { 8 | this.networkId = await this.web3.eth.net.getId(); 9 | } 10 | return this.networkId; 11 | } 12 | 13 | async getChainId() { 14 | if (!this.chainId) { 15 | this.chainId = await this.web3.eth.getChainId(); 16 | } 17 | return this.chainId; 18 | } 19 | 20 | async transactionOpts() { 21 | const params = { 22 | name: "bnb", 23 | networkId: await this.getNetworkId(), 24 | chainId: await this.getChainId(), 25 | }; 26 | const common = Common.default.forCustomChain( 27 | "mainnet", 28 | params, 29 | "petersburg", 30 | ); 31 | return { common }; 32 | } 33 | } 34 | 35 | export default new Bsc( 36 | bsc.endpoint, 37 | "bsc", 38 | bsc.contract, 39 | bsc.account, 40 | bsc.key, 41 | "bscNonce", 42 | confirmations.bsc, 43 | ); 44 | -------------------------------------------------------------------------------- /service/src/app.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import cors from "cors"; 3 | import { port } from "./utils/config.js"; 4 | import { 5 | BalanceController, 6 | IndexController, 7 | SubmitController, 8 | } from "./controllers/index.js"; 9 | 10 | const app = express(); 11 | app.set("port", port); 12 | app.use(cors()); 13 | app.use(express.json()); 14 | 15 | app.use(function(req, res, next) { 16 | res.header("Access-Control-Allow-Origin", "*"); 17 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization"); 18 | res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS'); 19 | next(); 20 | }); 21 | 22 | app.get("/", IndexController.healthCheck); 23 | app.get("/state", IndexController.state); 24 | app.get("/contracts", IndexController.contracts); 25 | 26 | app.get("/balance/bgl", BalanceController.bgl); 27 | app.get("/balance/eth", BalanceController.eth); 28 | app.get("/balance/bsc", BalanceController.bsc); 29 | 30 | app.post("/submit/bgl", SubmitController.bglToWbgl); 31 | app.post("/submit/wbgl", SubmitController.wbglToBgl); 32 | 33 | export default app; 34 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bgl-wbgl-bridge", 3 | "version": "0.3.1", 4 | "license": "MIT", 5 | "dependencies": { 6 | "@fontsource/roboto": "^4.3.0", 7 | "@material-ui/core": "^4.11.4", 8 | "@testing-library/jest-dom": "^5.11.4", 9 | "@testing-library/react": "^11.1.0", 10 | "@testing-library/user-event": "^12.1.10", 11 | "metamask-react": "^2.4.0", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2", 14 | "react-hook-form": "^7.6.6", 15 | "react-scripts": "4.0.3", 16 | "swr": "^0.5.6", 17 | "web-vitals": "^1.0.1", 18 | "web3": "^1.7.5" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Box, Container, Link, makeStyles, Typography} from '@material-ui/core' 3 | import {blueGrey} from '@material-ui/core/colors' 4 | 5 | import logo from '../assets/images/logo.png' 6 | import {appTitle} from '../utils/config' 7 | import ConnectWallet from './ConnectWallet' 8 | 9 | function Header() { 10 | const classes = useStyles() 11 | 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | BGL {appTitle} 19 | 20 | 21 | 22 | 23 | 24 | 25 | ) 26 | } 27 | 28 | const bgColor = blueGrey[700] 29 | 30 | const useStyles = makeStyles(theme => ({ 31 | container: {}, 32 | logo: { 33 | marginLeft: 10, 34 | marginRight: 5, 35 | height: 33, 36 | verticalAlign: 'middle', 37 | }, 38 | })) 39 | 40 | export default Header 41 | -------------------------------------------------------------------------------- /app/src/utils/index.js: -------------------------------------------------------------------------------- 1 | import {isTest, serviceUrl} from './config' 2 | 3 | export const url = path => serviceUrl + path 4 | 5 | export const fetcher = url => fetch(url).then(res => res.json()) 6 | 7 | export const post = async (url, data) => { 8 | const response = await fetch(url, { 9 | method: 'POST', 10 | mode: 'cors', 11 | cache: 'no-cache', 12 | headers: { 13 | 'Content-Type': 'application/json', 14 | }, 15 | body: JSON.stringify(data), 16 | }) 17 | return response.json() 18 | } 19 | 20 | export function chainLabel(chainId) { 21 | const names = { 22 | eth: 'Ethereum', 23 | bsc: 'Binance Smart Chain', 24 | } 25 | let chain = chainId 26 | 27 | if (!['eth', 'bsc'].includes(chainId)) { 28 | chain = isChainBsc(chainId) ? 'bsc' : 'eth' 29 | } 30 | 31 | return names[chain] 32 | } 33 | 34 | export function isChainValid(chainId) { 35 | return isTest ? [3, 97, '0x3', '0x61'].includes(chainId) : [1, 56, '0x1', '0x38'].includes(chainId) 36 | } 37 | 38 | export const isChainBsc = chainId => (typeof chainId == 'string' ? ['0x38', '0x61'] : [56, 97]).includes(chainId) 39 | 40 | let contracts 41 | export async function getTokenAddress(chainId) { 42 | if (!contracts) { 43 | contracts = await (await fetch(url('/contracts'))).json() 44 | } 45 | return contracts[isChainBsc(chainId) ? 'bsc' : 'eth'] 46 | } 47 | -------------------------------------------------------------------------------- /service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bgl-wbgl-bridge-service", 3 | "version": "0.3.1", 4 | "description": "BGL-WBGL(ETH) bridge", 5 | "main": "src/server.js", 6 | "license": "MIT", 7 | "type": "module", 8 | "scripts": { 9 | "dev": "nodemon -L --inspect=0.0.0.0:9229 src/server.js", 10 | "test": "mocha src/tests/**/*.js -r dotenv/config --timeout 10000", 11 | "lint:check": "eslint .", 12 | "lint:fix": "eslint --fix", 13 | "prettier:check": "prettier --check .", 14 | "prettier:fix": "prettier --write \"**/*.js\" .prettierrc --config ./.prettierrc", 15 | "format": "npm run lint:fix && npm run prettier:fix" 16 | }, 17 | "dependencies": { 18 | "bitcoin-core": "^3.0.0", 19 | "cors": "^2.8.5", 20 | "dotenv": "^16.0.0", 21 | "ethereumjs-common": "^1.5.2", 22 | "ethereumjs-tx": "^2.1.2", 23 | "express": "^4.17.1", 24 | "mongoose": "^5.12.10", 25 | "web3": "^1.3.6" 26 | }, 27 | "devDependencies": { 28 | "eslint": "^8.12.0", 29 | "eslint-config-prettier": "^8.5.0", 30 | "eslint-plugin-prettier": "^4.0.0", 31 | "mocha": "^9.2.2", 32 | "nodemon": "^2.0.7", 33 | "pre-commit": "^1.2.2", 34 | "prettier": "^2.6.2", 35 | "should": "^13.2.3" 36 | }, 37 | "options": { 38 | "mocha": "--timeout 20000 --recursive --require should" 39 | }, 40 | "pre-commit": [ 41 | "lint" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /app/src/utils/wallet.js: -------------------------------------------------------------------------------- 1 | import {useEffect, useState} from 'react' 2 | import {useMetaMask} from 'metamask-react' 3 | import Web3 from 'web3' 4 | 5 | import abi from '../assets/abi/WBGL.json' 6 | import {getTokenAddress, isChainValid} from './index' 7 | 8 | let web3 9 | let WBGL 10 | let interval 11 | 12 | export function getWeb3(ethereum) { 13 | if (!web3) { 14 | web3 = new Web3(ethereum) 15 | } 16 | return web3 17 | } 18 | 19 | export function useWbglBalance() { 20 | const [balance, setBalance] = useState('') 21 | const {ethereum, account, chainId} = useMetaMask() 22 | 23 | const fetch = async () => { 24 | const current = web3.utils.fromWei(await WBGL.methods['balanceOf'](account).call(), 'ether') 25 | setBalance(current) 26 | } 27 | 28 | useEffect(() => { 29 | if (isChainValid(chainId)) { 30 | getTokenAddress(chainId).then(async contractAddress => { 31 | const web3 = getWeb3(ethereum) 32 | WBGL = new web3.eth.Contract(abi, contractAddress) 33 | 34 | await fetch() 35 | 36 | interval = setInterval(fetch, 30000) 37 | }) 38 | 39 | return () => clearInterval(interval) 40 | } 41 | }, []) 42 | 43 | return balance 44 | } 45 | 46 | export async function sendWbgl(chainId, from, to, amount, ethereum) { 47 | const web3 = getWeb3(ethereum) 48 | const value = web3.utils.toWei(amount, 'ether'); 49 | 50 | WBGL = WBGL || new web3.eth.Contract(abi, await getTokenAddress(chainId)) 51 | 52 | await WBGL.methods.transfer(to, value).send({from}) 53 | } 54 | -------------------------------------------------------------------------------- /app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | %REACT_APP_TITLE% 28 | 29 | 30 | 31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /service/src/modules/rpc.js: -------------------------------------------------------------------------------- 1 | import Client from "bitcoin-core"; 2 | import { rpc, confirmations } from "../utils/config.js"; 3 | 4 | let client; 5 | 6 | export const getClient = () => { 7 | if (!client) { 8 | client = new Client(rpc); 9 | } 10 | return client; 11 | }; 12 | 13 | export const getBlockchainInfo = async () => 14 | await getClient().command("getblockchaininfo"); 15 | 16 | export const getBlockCount = async () => 17 | await getClient().command("getblockcount"); 18 | 19 | export const getBalance = async () => await getClient().command("getbalance"); 20 | 21 | export const validateAddress = async (address) => 22 | (await getClient().command("validateaddress", address)).isvalid; 23 | 24 | export const createAddress = async () => 25 | await getClient().command("getnewaddress"); 26 | 27 | export const tips = async () => await getClient().getChainTips(); 28 | export const generate = async (tip) => await getClient().generate(tip); 29 | 30 | export const listSinceBlock = async ( 31 | blockHash, 32 | confirmation = confirmations.bgl, 33 | ) => { 34 | return await getClient().command( 35 | "listsinceblock", 36 | blockHash ? blockHash : undefined, 37 | confirmation, 38 | ); 39 | }; 40 | 41 | export const getTransactionFromAddress = async (txid) => { 42 | const rawTx = await getClient().command("getrawtransaction", txid, true); 43 | const vin = rawTx["vin"][0]; 44 | const txIn = await getClient().command("getrawtransaction", vin.txid, true); 45 | return txIn["vout"][vin["vout"]]["scriptPubKey"]["address"]; 46 | }; 47 | 48 | export const send = async (address, amount) => 49 | await getClient().command("sendtoaddress", address, amount); 50 | -------------------------------------------------------------------------------- /service/src/utils/config.js: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | dotenv.config(); 3 | 4 | export const env = process.env.NODE_ENV || "development"; 5 | 6 | export const port = process.env.PORT || "8080"; 7 | 8 | const rpcConfig = { 9 | host: process.env.RPC_HOST || "localhost", 10 | port: process.env.RPC_PORT || "8332", 11 | }; 12 | if (process.env.hasOwnProperty("RPC_USER") && process.env.RPC_USER) { 13 | rpcConfig.username = process.env.RPC_USER; 14 | } 15 | if (process.env.hasOwnProperty("RPC_PASSWORD") && process.env.RPC_PASSWORD) { 16 | rpcConfig.password = process.env.RPC_PASSWORD; 17 | } 18 | if (process.env.hasOwnProperty("RPC_WALLET") && process.env.RPC_WALLET) { 19 | rpcConfig.wallet = process.env.RPC_WALLET; 20 | } 21 | export const rpc = rpcConfig; 22 | 23 | export const eth = { 24 | endpoint: process.env.ETH_ENDPOINT, 25 | account: process.env.ETH_ACCOUNT, 26 | key: Buffer.from(process.env.ETH_PRIVKEY, "hex"), 27 | contract: process.env.ETH_CONTRACT_ADDRESS, 28 | }; 29 | 30 | export const bsc = { 31 | endpoint: process.env.BSC_ENDPOINT, 32 | account: process.env.BSC_ACCOUNT, 33 | key: Buffer.from(process.env.BSC_PRIVKEY, "hex"), 34 | contract: process.env.BSC_CONTRACT_ADDRESS, 35 | }; 36 | 37 | export const mongo = { 38 | url: process.env.DB_CONNECTION, 39 | database: process.env.DB_DATABASE || "wbgl_bridge", 40 | }; 41 | 42 | export const confirmations = { 43 | bgl: parseInt(process.env.BGL_MIN_CONFIRMATIONS) || 3, 44 | eth: parseInt(process.env.ETH_MIN_CONFIRMATIONS) || 3, 45 | bsc: parseInt(process.env.BSC_MIN_CONFIRMATIONS) || 3, 46 | }; 47 | 48 | export const feePercentage = process.env.FEE_PERCENTAGE || 1; 49 | 50 | export const nonces = { 51 | bsc: parseInt(process.env.BSC_NONCE) || 0, 52 | eth: parseInt(process.env.ETH__NONCE) || 0, 53 | }; 54 | -------------------------------------------------------------------------------- /app/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react' 2 | import {Box, Container, CssBaseline, makeStyles, Tab, Tabs} from '@material-ui/core' 3 | import Header from './Header' 4 | import Footer from './Footer' 5 | import BglToWbgl from './BglToWbgl' 6 | import WbglToBgl from './WbglToBgl' 7 | 8 | import '@fontsource/roboto' 9 | 10 | function TabPanel(props) { 11 | const {children, value, index, ...other} = props 12 | 13 | return ( 14 | 26 | ) 27 | } 28 | 29 | function App() { 30 | const classes = useStyles() 31 | const [tab, setTab] = useState(0) 32 | const changeTab = (_event, newValue) => { 33 | setTab(newValue) 34 | } 35 | 36 | return ( 37 | 38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |