update: mpc nodes

This commit is contained in:
venuswhispers
2024-09-03 00:32:34 +08:00
parent cfa7fa4876
commit 348bd1b805
19 changed files with 3002 additions and 357 deletions
+34 -3
View File
@@ -119,7 +119,23 @@ export const sourceNetworks: Network[] = [
destination_base_fee: 0.99,
is_native: false
},
{
name: "DAI",
asset: "DAI",
logo: "https://cdn.lux.network/bridge/currencies/dai.png",
contract_address: "0x7390C3FA8576a0E9E7c788cc7955c3151c4c1612",
decimals: 18,
status: "active",
is_deposit_enabled: true,
is_withdrawal_enabled: true,
is_refuel_enabled: true,
max_withdrawal_amount: 3000,
deposit_fee: 12.79,
withdrawal_fee: 6.4,
source_base_fee: 0.99,
destination_base_fee: 0.99,
is_native: false
},
{
name: "LRC",
asset: "LRC",
@@ -379,7 +395,6 @@ export const sourceNetworks: Network[] = [
node: "https://eth-mainnet.g.alchemy.com/v2/-z4Zrujiou9ajXAplVapFFWWrPuLPSm7",
currencies: []
},
]
export const sourceNetworks_: Network[] = [
@@ -502,7 +517,23 @@ export const sourceNetworks_: Network[] = [
destination_base_fee: 0.99,
is_native: false
},
{
name: "DAI",
asset: "DAI",
logo: "https://cdn.lux.network/bridge/currencies/dai.png",
contract_address: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
decimals: 18,
status: "active",
is_deposit_enabled: true,
is_withdrawal_enabled: true,
is_refuel_enabled: true,
max_withdrawal_amount: 3000,
deposit_fee: 12.79,
withdrawal_fee: 6.4,
source_base_fee: 0.99,
destination_base_fee: 0.99,
is_native: false
},
{
name: "LRC",
asset: "LRC",
+1 -9
View File
@@ -15,17 +15,9 @@ pragma solidity ^0.8.19;
import "./ERC20B.sol";
contract LuxETH is ERC20B {
contract LETH is ERC20B {
string public constant _name = "Lux ETH";
string public constant _symbol = "LETH";
constructor() ERC20B(_name, _symbol) {}
function mint(address account, uint256 amount) public {
_mint(account, amount);
}
function burn(address account, uint256 amount) public {
_burn(account, amount);
}
}
+1 -11
View File
@@ -16,15 +16,5 @@ contract LUSD is ERC20B {
string public constant _name = "Lux Dollar";
string public constant _symbol = "LUSD";
constructor() ERC20B(_name, _symbol) {
_mint(msg.sender, 10000000000 * 10 ** decimals());
}
function mint(address account, uint256 amount) public {
_mint(account, amount);
}
function burn(address account, uint256 amount) public {
_burn(account, amount);
}
constructor() ERC20B(_name, _symbol) {}
}
+22 -7
View File
@@ -9,30 +9,45 @@ const PRIVATE_KEY: string = process.env.PRIVATE_KEY!;
const SEPOLIA_RPC: string = process.env.SEPOLIA_RPC!;
const BASE_RPC: string = process.env.BASE_RPC!;
const BSC_TESTNET_RPC: string = process.env.BSC_TESTNET_RPC!;
const LUX_MAINNET_RPC: string = process.env.LUX_MAINNET_RPC!;
const config: HardhatUserConfig = {
solidity: "0.8.20",
// defaultNetwork: "sepolia",
networks: {
sepolia: {
url: SEPOLIA_RPC,
accounts: [PRIVATE_KEY],
accounts: [`0x${PRIVATE_KEY}`],
},
bsc_testnet: {
url: BSC_TESTNET_RPC,
accounts: [PRIVATE_KEY]
accounts: [`0x${PRIVATE_KEY}`],
},
base: {
url: BASE_RPC,
accounts: [PRIVATE_KEY]
}
accounts: [`0x${PRIVATE_KEY}`],
},
lux: {
url: LUX_MAINNET_RPC,
accounts: [`0x${PRIVATE_KEY}`],
},
},
etherscan: {
apiKey: {
sepolia: process.env.ETHERSCAN_API_KEY!,
bsc: process.env.BSCSCAN_API_KEY!,
bscTestnet: process.env.BSCSCAN_API_KEY!
}
bscTestnet: process.env.BSCSCAN_API_KEY!,
lux: "your API key" // Leave empty if not applicable
},
customChains: [
{
network: "lux",
chainId: 7777,
urls: {
apiURL: "https://api.lux.network",
browserURL: "https://explore.lux.network"
}
}
]
},
sourcify: {
enabled: true,
+2 -1
View File
@@ -15,7 +15,7 @@
"@types/node": ">=18.0.0",
"chai": "^4.2.0",
"ethers": "^6.4.0",
"hardhat": "^2.22.8",
"hardhat": "^2.22.9",
"hardhat-gas-reporter": "^1.0.8",
"solidity-coverage": "^0.8.0",
"ts-node": ">=8.0.0",
@@ -23,6 +23,7 @@
"typescript": ">=4.5.0"
},
"dependencies": {
"@nomiclabs/hardhat-etherscan": "^3.1.8",
"@openzeppelin/contracts": "^5.0.2",
"dotenv": "^16.4.5"
}
+17
View File
@@ -0,0 +1,17 @@
import { ethers } from "hardhat";
async function main() {
const [deployer] = await ethers.getSigners();
/////LETH
const _signer = await ethers.getContractFactory("LETH");
const token = await _signer.deploy();
console.log("LETH address:", await token.getAddress());
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
+17
View File
@@ -0,0 +1,17 @@
import { ethers } from "hardhat";
async function main() {
const [deployer] = await ethers.getSigners();
/////LETH
const _signer = await ethers.getContractFactory("LUSD");
const token = await _signer.deploy();
console.log("LUSD address:", await token.getAddress());
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
+15 -5
View File
@@ -1,5 +1,15 @@
smTimeOutBound=20
sign_client_name="gg18_sign_client"
node_number=0
sign_sm_manager="http://127.0.0.1:8000"
PORT=6000
smTimeOutBound=
sign_client_name=
node_number=
sign_sm_manager=
PORT=
# This was inserted by `prisma init`:
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
POSTGRES_PRISMA_URL=
POSTGRES_URL_NON_POOLING=
+3 -1
View File
@@ -36,6 +36,7 @@
"typescript": "^5.5.4"
},
"dependencies": {
"@prisma/client": "^5.19.1",
"axios": "^1.7.4",
"child_process": "^1.0.2",
"cors": "^2.8.5",
@@ -43,6 +44,7 @@
"ethers": "^6.13.2",
"express": "^4.19.2",
"find-process": "^1.4.7",
"prisma": "^5.19.1",
"web3": "^4.11.1"
}
}
}
Binary file not shown.
@@ -0,0 +1,20 @@
-- CreateTable
CREATE TABLE "Teleporter" (
"id" SERIAL NOT NULL,
"txId" TEXT NOT NULL,
"chainType" TEXT NOT NULL,
"amount" TEXT NOT NULL,
"signature" TEXT NOT NULL,
"hashedTxId" TEXT NOT NULL,
CONSTRAINT "Teleporter_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Teleporter_txId_key" ON "Teleporter"("txId");
-- CreateIndex
CREATE UNIQUE INDEX "Teleporter_signature_key" ON "Teleporter"("signature");
-- CreateIndex
CREATE UNIQUE INDEX "Teleporter_hashedTxId_key" ON "Teleporter"("hashedTxId");
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
@@ -0,0 +1,35 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("POSTGRES_PRISMA_URL") // uses connection pooling
directUrl = env("POSTGRES_URL_NON_POOLING") // uses a direct connection
}
// datasource db {
// provider = "sqlite"
// url = "file:dev.sqlite?socket_timeout=50000" // uses connection pooling
// directUrl = "file:dev.sqlite?socket_timeout=50000" // uses a direct connection
// }
// pnpx prisma generate
// npx prisma db push
// npx prisma studio
// prisma migrate dev
model Teleporter {
id Int @id @default(autoincrement())
txId String @unique
chainType String
amount String
signature String @unique
hashedTxId String @unique
}
@@ -1,36 +1,62 @@
export const settings = {
Vault: "0x568BF299E115D78a1fBa57BafdAe0fD8A26BFb7e",
RPC: {
11155111: "https://sepolia.infura.io/v3/2e58a899d3c64eccb6955c2f33fc8a88",
97: "https://data-seed-prebsc-1-s1.bnbchain.org:8545"
1: "https://lb.drpc.org/ogrpc?network=ethereum&dkey=AqFR4xsp40v0ugTBeYcYdFLf2rXfQTYR76p2UgWAgP__",
7777: "https://api.lux.network"
},
LuxETH: {
Ethereum: "0x42449554b0c7D85EbD488e14D7D48c6A78D3F9Be",
Lux: "0x41e51eFcdA08fDFB84f4b1caa4b7f03c67FA431b"
},
LuxBTC: {
Ethereum: "0x526903Ee6118de6737D11b37f82fC7f69B13685D",
Lux: "0x8c07F93A76213c0BE738ded6110403b6d0ceE286"
},
// WSHM: {
// "Ethereum": "0xc1Dd1e835F4cdFF9fc12Cb1750428E345A3d3d5f",
// "Lux": "0x2d6Bce32Ea63F2e2CDfCAd49999e89a00389Df4C",
// },
Teleporter: {
Ethereum: "0x5519582dde6eb1f53F92298622c2ecb39A64369A",
LETH: {
Ethereum: "",
Sepolia: "",
Lux: "0x42449554b0c7D85EbD488e14D7D48c6A78D3F9Be"
},
LUSD: {
Ethereum: "",
Sepolia: "",
Lux: "0xc16ECFE3cB80e142d7110b97a442d4caAA203ABf"
},
Teleporter: {
Ethereum: "",
Sepolia: "0x568BF299E115D78a1fBa57BafdAe0fD8A26BFb7e",
Lux: "0xB2237fb7DBB19Ff09BBD64029064eC05B3C369Ac"
},
USDT: {
Ethereum: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
Sepolia: "0xa92e09451140d645a2fe262c9631dd808439dded"
},
USDC: {
Ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
Sepolia: "0xB587bAb3d507d720625D30544C2889D661446BF7"
},
DAI: {
Ethereum: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
Sepolia: "0x7390C3FA8576a0E9E7c788cc7955c3151c4c1612"
},
ETH: {
Ethereum: "0x0000000000000000000000000000000000000000",
Sepolia: "0x0000000000000000000000000000000000000000"
},
NetNames: {
"11155111": "Ethereum",
"97": "Lux"
"11155111": "Sepolia",
"1": "Ethereum",
"7777": "Lux"
},
DECIMALS: {
ETH: "18",
USDT: "6",
USDC: "6",
DAI: "18"
},
Msg: "Sign to prove you are initiator of transaction.",
DupeListLimit: "200",
SMTimeout: "1",
NewSigAllowed: false,
SigningManagers: ["http://127.0.0.1:8000", "http://127.0.0.1:8000"],
MPCPeers: {
0: "mnode.wpkt.cash, 139.162.85.205",
1: "mnode.odapp.io, 172.104.108.56"
},
KeyStore: "keys.store"
}
export const swapMappings: Record<string, string[] | undefined> = {
// LETH swap available tokens (native ETH or wrapped ETH)
"0xa684c5721e54B871111CE1F1E206d669a7e7F0a5": ["ETH", "WETH"],
// LUSD swap available tokens (stable coins)
"0xa684c5721e54B871111CE1F1E206d669a7e7F0a6": ["USDT", "USDC", "DAI", "BUSD", "TUSD", "USDP", "HUSD", "SUSD", "USDN"]
}
@@ -1,42 +0,0 @@
{
"RPC": {
"11155111": "https://sepolia.infura.io/v3/2e58a899d3c64eccb6955c2f33fc8a88",
"97": "https://data-seed-prebsc-1-s1.bnbchain.org:8545"
},
"LuxETH": {
"Ethereum": "0x42449554b0c7D85EbD488e14D7D48c6A78D3F9Be",
"Lux": "0xFa005D52398B4BdAAf73C4e2E3AF8095533D790E"
},
"LuxBTC": {
"Ethereum": "0x526903Ee6118de6737D11b37f82fC7f69B13685D",
"Lux": "0x8c07F93A76213c0BE738ded6110403b6d0ceE286"
},
// "WSHM": {
// "Ethereum": "0xc1Dd1e835F4cdFF9fc12Cb1750428E345A3d3d5f",
// "Lux": "0x2d6Bce32Ea63F2e2CDfCAd49999e89a00389Df4C",
// },
"Teleporter": {
"Ethereum": "0x5519582dde6eb1f53F92298622c2ecb39A64369A",
"Lux": "0x42449554b0c7D85EbD488e14D7D48c6A78D3F9Be"
},
"NetNames": {
"11155111": "Ethereum",
"97": "Lux"
},
"DB": "<change to somePassthatisSUperDuperPrivate123AbcBlahBlahBlah>",
"Msg": "Sign to prove you are initiator of transaction.",
"DupeListLimit": "200",
"SMTimeout": "1",
"NewSigAllowed": false,
"SigningManagers": {
"0": "http://mpc.wpkt.cash",
"1": "http://mpc.odapp.io"
},
"MPCPeers": {
"0": "mnode.wpkt.cash, 139.162.85.205",
"1": "mnode.odapp.io, 172.104.108.56"
},
"KeyStore": {
"0": "keys2.store"
}
}
+354 -179
View File
@@ -13,11 +13,14 @@ import Web3 from "web3"
import { Interface } from "ethers"
import { promisify } from "util"
import { exec as childExec } from "child_process"
import { settings } from "./config"
import { settings, swapMappings } from "./config"
import { RegisteredSubscription } from "web3/lib/commonjs/eth.exports"
import { hashAndSignTx, signClient, sleep } from "./utils"
import { PrismaClient } from "@prisma/client"
const exec = promisify(childExec)
const prisma = new PrismaClient()
/* Settings Mapping */
const settingsMap = new Map()
@@ -30,6 +33,8 @@ for (const [key, value] of Object.entries(settings)) {
const rpcList = settings.RPC
/* Networks (ie. chains) */
const networkName = settings.NetNames
/* DECIMAL LIST */
const DECIMALS = settings.DECIMALS
/* Signature Re-signing flag */
const newSigAllowed = settings.NewSigAllowed
/* Signing MSG */
@@ -41,15 +46,39 @@ let dupeList = new Map()
/* Bridge contracts for Teleport Bridge */
const list = settings.Teleporter
function getWeb3ForId(toNetId: number) {
return new Web3(new Web3.providers.HttpProvider(rpcList[toNetId]))
/**
* get WEB3 object by given network id
* @param networkId
* @returns
*/
function getWeb3ForId(networkId: number) {
try {
const _web3 = new Web3(new Web3.providers.HttpProvider(rpcList[networkId]))
return Promise.resolve(_web3)
} catch (err) {
return Promise.reject("error to build web3 object")
}
}
/* Given network id returns the appropriate contract to talk to as array of values */
const getNetworkAddresses = (toNetId: number, tokenName: string) => {
const web3 = getWeb3ForId(toNetId)
const chainName: string = networkName[toNetId]
return [settingsMap.get(tokenName.toString())[chainName], web3, list[chainName]]
/**
* Given network id returns the appropriate contract to talk to as array of values
* @param networkId
* @param tokenName
* @returns boject { fromTokenConAddr, w3From, frombridgeConAddr }
*/
const getNetworkInfo = async (networkId: number, tokenName: string) => {
try {
const web3 = await getWeb3ForId(networkId)
const chainName: string = networkName[networkId]
if (!chainName) throw "no chain"
return {
fromTokenConAddr: settingsMap.get(tokenName.toString())[chainName],
w3From: web3,
frombridgeConAddr: list[chainName]
}
} catch (err) {
return null
}
}
const Exp = /((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+[0-9a-z]+$/i
@@ -72,233 +101,379 @@ app.get("/", async (req: express.Request, res: express.Response) => {
* Parameters specific to where funds are being moved / minted to, are hashed, such that only the user has knowledge of
* mint destination. Effectively, the transaction is teleported stealthily.
*/
app.get(
"/api/v1/getsig/txid/:txid/fromNetId/:fromNetId/toNetIdHash/:toNetIdHash/tokenName/:tokenName/tokenAddrHash/:tokenAddrHash/msgSig/:msgSig/toTargetAddrHash/:toTargetAddrHash/nonce/:nonce",
async (req: express.Request, res: express.Response) => {
// Checking inputs
const stealthMode = true // Stealth overrride by default, for now.
let sig: string = ""
let evmTxHash: string = ""
let output: any = null
app.get("/api/v1/getsig/txid/:txid/fromNetId/:fromNetId/toNetIdHash/:toNetIdHash/tokenName/:tokenName/tokenAddr/:tokenAddr/msgSig/:msgSig/toTargetAddrHash/:toTargetAddrHash/nonce/:nonce", async (req: express.Request, res: express.Response) => {
// Checking inputs
const stealthMode = true // Stealth overrride by default, for now.
const txid = req.params.txid.trim()
if (!(txid.length > 0) && !txid.match(Exp)) {
output = "NullTransactionError: bad transaction hash"
return res.send(output)
const txid = req.params.txid.trim()
if (!(txid.length > 0) && !txid.match(Exp)) {
const output = {
status: false,
msg: "NullTransactionError: bad transaction hash"
}
res.json(output)
return
}
// Dupelist reset if time has elapsed.
const dupeStop = new Date() //time now
if ((Number(dupeStop) - dupeStart) / 1000 >= 2400) {
dupeList = new Map() //Clear dupeList
dupeStart = new Date().getTime()
}
// Dupelist reset if time has elapsed.
const dupeStop = new Date() //time now
if ((Number(dupeStop) - dupeStart) / 1000 >= 2400) {
dupeList = new Map() //Clear dupeList
dupeStart = new Date().getTime()
}
// Limit on checks here, else you go into graylist.
if (dupeList.get(txid.toString()) == undefined) {
//First time
dupeList.set(txid.toString(), 0) // init
} else {
if (dupeList.get(txid.toString()) >= dupeListLimit) {
// temp blacklist - pm2 cron will reset graylist after 12hrs
console.log("Dupe transaction request limit hit")
output = "DupeTransactionError: Too many transaction requests. Try back later."
res.send(output)
return
// Limit on checks here, else you go into graylist.
if (dupeList.get(txid.toString()) == undefined) {
//First time
dupeList.set(txid.toString(), 0) // init
} else {
if (dupeList.get(txid.toString()) >= dupeListLimit) {
// temp blacklist - pm2 cron will reset graylist after 12hrs
console.log("Dupe transaction request limit hit")
const output = {
status: false,
msg: "DupeTransactionError: Too many transaction requests. Try back later."
}
dupeList.set(txid.toString(), dupeList.get(txid.toString()) + 1)
}
const fromNetId = req.params.fromNetId.trim()
console.log("fromNetId:", fromNetId)
if (!fromNetId) {
output = "NullFromNetIDError: No from netId sent."
res.send(output)
res.json(output)
return
}
dupeList.set(txid.toString(), dupeList.get(txid.toString()) + 1)
}
const toNetIdHash = req.params.toNetIdHash.trim()
if (!toNetIdHash) {
output = "NullToNetIDHashError: No to netId sent."
res.send(output)
return
const fromNetId = req.params.fromNetId.trim()
if (!fromNetId) {
const output = {
status: false,
msg: "NullFromNetIDError: No from netId sent."
}
res.json(output)
return
}
const tokenName = req.params.tokenName.trim()
if (!tokenName) {
output = "NullTokenNameError: No token name sent."
res.send(output)
return
const toNetIdHash = req.params.toNetIdHash.trim()
if (!toNetIdHash) {
const output = {
status: false,
msg: "NullToNetIDHashError: No to netId sent."
}
res.json(output)
return
}
const tokenAddrHash = req.params.tokenAddrHash.trim()
console.log("TokenAddrHash:", tokenAddrHash)
if (!tokenAddrHash) {
output = "NullTokenAddressHashError: No token address hash sent."
res.send(output)
return
const tokenName = req.params.tokenName.trim()
if (!tokenName) {
const output = {
status: false,
msg: "NullTokenNameError: No token name sent."
}
res.json(output)
return
}
let toTargetAddrHash = req.params.toTargetAddrHash.trim()
if (!toTargetAddrHash) {
output = "NullToTargetAddrHashError: No target address hash sent."
res.send(output)
return
const tokenAddr = req.params.tokenAddr.trim()
if (!tokenAddr) {
const output = {
status: false,
msg: "NullTokenAddressHashError: No token address sent."
}
res.json(output)
return
}
const msgSig = req.params.msgSig.trim()
if (!msgSig) {
output = "NullMessageSignatureError: Challenge message signature not sent."
res.send(output)
return
let toTargetAddrHash = req.params.toTargetAddrHash.trim()
if (!toTargetAddrHash) {
const output = {
status: false,
msg: "NullToTargetAddrHashError: No target address hash sent."
}
res.json(output)
return
}
const nonce = req.params.nonce.trim()
if (!nonce) {
output = "NullNonceError: No nonce sent."
res.send(output)
return
const msgSig = req.params.msgSig.trim()
if (!msgSig) {
const output = {
status: false,
msg: "NullMessageSignatureError: Challenge message signature not sent."
}
res.json(output)
return
}
const fromNetArr = getNetworkAddresses(Number(fromNetId), tokenName)
// console.log("fromNetArr:", fromNetArr)
evmTxHash = Web3.utils.soliditySha3(txid)
const txidNonce = stealthMode ? evmTxHash + nonce : txid + nonce
const txStub = stealthMode ? evmTxHash : txid
const txInfo = [txStub, fromNetId, toNetIdHash, tokenName, tokenAddrHash, toTargetAddrHash, msgSig, nonce]
// console.log("txidNonce:", txidNonce)
// console.log("txProcMapX:", txProcMap.get(txidNonce.toString()), "TX MAP:", txProcMap)
if (fromNetArr.length !== 3) {
console.log("FromNetArrLengthError:", fromNetArr.length)
output = "Unknown error occurred."
res.send(output)
return
const nonce = req.params.nonce.trim()
if (!nonce) {
const output = {
status: false,
msg: "NullNonceError: No nonce sent."
}
const fromTokenConAddr: string = fromNetArr[0]
const frombridgeConAddr: string = fromNetArr[2]
const w3From: Web3<RegisteredSubscription> = fromNetArr[1]
const cnt = 0
res.json(output)
return
}
// TODO: check swap available according to source tokenName and destination token address
const _availableTokensByDesTokenAddress = swapMappings[tokenAddr]
if (!_availableTokensByDesTokenAddress) {
const output = {
status: false,
msg: "NullDestinationTokenError: No destination Token."
}
res.json(output)
return
}
if (!_availableTokensByDesTokenAddress.includes(tokenName)) {
const output = {
status: false,
msg: "NullDestinationTokenError: No Swap Permitted Token."
}
res.json(output)
return
}
const transaction = await getEVMTx(txid, w3From)
if (!transaction) {
output = { error: "NullTransactionError: bad transaction hash, no transaction on chain" }
res.send(output)
const tokenAddrHash = Web3.utils.keccak256(tokenAddr) // hash the destinationTokenAddress
const evmTxHash = Web3.utils.soliditySha3(txid)
// check replay attack
const { status, data } = await checkStealthSignature(evmTxHash)
if (status) {
const output = {
status: false,
msg: "DuplicatedTxHashError: TxHash already exists."
}
res.json(output)
return
}
const txidNonce = stealthMode ? evmTxHash + nonce : txid + nonce
const txStub = stealthMode ? evmTxHash : txid
const txInfo = [txStub, fromNetId, toNetIdHash, tokenName, tokenAddrHash, toTargetAddrHash, msgSig, nonce]
// console.log("txidNonce:", txidNonce)
// console.log("txProcMapX:", txProcMap.get(txidNonce.toString()), "TX MAP:", txProcMap)
// get network settings
const fromNetArr = await getNetworkInfo(Number(fromNetId), tokenName)
if (!fromNetArr) {
console.log("FromNetArrLengthError:")
const output = {
status: false,
msg: "Invalid source network settings."
}
res.json(output)
return
}
const { fromTokenConAddr, w3From, frombridgeConAddr } = fromNetArr
// get transaction info according to chains
const transaction = await getEVMTx(txid, w3From)
if (!transaction) {
const output = {
status: false,
msg: "NullTransactionError: bad transaction hash, no transaction on chain"
}
res.json(output)
return
} else {
const { teleporter, from, token: fromTokenContract, value: amount, eventName } = transaction
let vault = false
if (!eventName) {
const output = {
status: false,
msg: "NotVaultedOrBurnedError: No tokens were vaulted or burned."
}
res.json(output)
return
} else {
console.log("Transaction:", transaction)
const { contractTo: contract, from, addressTo: fromTokenContract, value: amt, log: eventName } = transaction
let vault = false
if (!eventName) {
output = "NotVaultedOrBurnedError: No tokens were vaulted or burned."
res.send(output)
return
} else {
console.log("Event:", eventName)
if (eventName.toString() === "BridgeBurned") {
vault = false
} else if (eventName.toString() === "VaultDeposit") {
vault = true
}
if (eventName.toString() === "BridgeBurned") {
vault = false
} else if (eventName.toString() === "VaultDeposit") {
vault = true
}
// To prove user signed we recover signer for (msg, sig) using testSig which rtrns address which must == toTargetAddr or return error
let signerAddress = ""
}
// To prove user signed we recover signer for (msg, sig) using testSig which rtrns address which must == toTargetAddr or return error
let signerAddress = ""
try {
signerAddress = w3From.eth.accounts.recover(msg, msgSig) //best on server
} catch (err) {
const output = {
status: false,
msg: "can not recover signer from msgSig"
}
res.json(output)
return
}
console.log("signerAddress:", signerAddress.toString().toLowerCase(), "From Address:", from.toString().toLowerCase())
// Bad signer (test transaction signer must be same as burn transaction signer) => exit, front run attempt
let signerOk = false
if (signerAddress.toString().toLowerCase() != from.toString().toLowerCase()) {
console.log("*** Possible front run attempt, message signer not transaction sender ***")
const output = {
status: false,
msg: "NullToNetIDHashError: No to netId sent."
}
res.json(output)
return
} else {
signerOk = true
}
// If signerOk we use the toTargetAddrHash provided, else we hash the from address.
toTargetAddrHash = signerOk ? toTargetAddrHash : Web3.utils.keccak256(from)
// console.log("token contract:", fromTokenContract.toLowerCase(), "fromTokenConAddr", fromTokenConAddr.toLowerCase(), "contract", contract.toLowerCase(), "frombridgeConAddr", frombridgeConAddr.toLowerCase())
// Token was burned.
const _decimals = DECIMALS[tokenName] ?? "18"
if (fromTokenContract.toLowerCase() === fromTokenConAddr.toLowerCase() && teleporter.toLowerCase() === frombridgeConAddr.toLowerCase()) {
// console.log("fromTokenConAddr", fromTokenConAddr, "tokenAddrHash", tokenAddrHash)
//Produce signature for minting approval.
try {
signerAddress = w3From.eth.accounts.recover(msg, msgSig) //best on server
const { signature, mpcSigner } = await hashAndSignTx(amount.toString(), w3From, vault, txInfo, _decimals)
//NOTE: For private transactions, store only the sig.
const output = {
fromTokenContractAddress: fromTokenContract,
contract: teleporter,
from: toTargetAddrHash,
tokenAmt: amount,
signature: signature,
mpcSigner: mpcSigner,
hashedTxId: evmTxHash,
tokenAddrHash: tokenAddrHash,
vault: vault
}
await saveEvmTxHash({
chainType: "evm",
txId: txid,
amount: amount.toString(),
signature,
hashedTxId: evmTxHash
})
res.status(200).json({
status: true,
data: output
})
return
} catch (err) {
console.log("cannt recover signer from msgSig")
return res.status(400).json("cannt recover signer from msgSig")
}
console.log("signerAddress:", signerAddress.toString().toLowerCase(), "From Address:", from.toString().toLowerCase())
// Bad signer (test transaction signer must be same as burn transaction signer) => exit, front run attempt
let signerOk = false
if (signerAddress.toString().toLowerCase() != from.toString().toLowerCase()) {
console.log("*** Possible front run attempt, message signer not transaction sender ***")
output = "NullToNetIDHashError: No to netId sent."
res.send(output)
return
} else {
signerOk = true
}
// If signerOk we use the toTargetAddrHash provided, else we hash the from address.
toTargetAddrHash = signerOk ? toTargetAddrHash : Web3.utils.keccak256(from)
// console.log("token contract:", fromTokenContract.toLowerCase(), "fromTokenConAddr", fromTokenConAddr.toLowerCase(), "contract", contract.toLowerCase(), "frombridgeConAddr", frombridgeConAddr.toLowerCase())
// Token was burned.
if (fromTokenContract.toLowerCase() === fromTokenConAddr.toLowerCase() && contract.toLowerCase() === frombridgeConAddr.toLowerCase()) {
// console.log("fromTokenConAddr", fromTokenConAddr, "tokenAddrHash", tokenAddrHash)
//Produce signature for minting approval.
try {
sig = (await hashAndSignTx(w3From.utils.toWei(amt.toString(), "ether"), w3From, vault, txInfo)) as string
//NOTE: For private transactions, store only the sig.
output = { fromTokenContractAddress: fromTokenContract, contract: contract, from: toTargetAddrHash, tokenAmt: amt, signature: sig, hashedTxId: txid, tokenAddrHash: tokenAddrHash, vault: vault }
return res.send(output)
} catch (err) {
if (err === "AlreadyMintedError") {
console.log(err)
output = { error: err + " It appears these coins were already bridged." }
} else if (err === "GasTooLowError") {
console.log(err)
output = { error: err + " Try setting higher gas prices. Do you have enough tokens to pay for fees?" }
} else {
console.log(err)
output = { error: err }
}
res.json(output)
return
let output: any
if (err === "AlreadyMintedError") {
console.log(err)
output = { status: false, msg: err + " It appears these coins were already bridged." }
} else if (err === "GasTooLowError") {
console.log(err)
output = { status: false, msg: err + " Try setting higher gas prices. Do you have enough tokens to pay for fees?" }
} else {
console.log(err)
output = { status: false, msg: err }
}
} else {
output = { error: "ContractMisMatchError: bad token or bridge contract address." }
res.json(output)
return
}
} else {
const output = {
status: false,
msg: "ContractMisMatchError: bad token or bridge contract address."
}
res.json(output)
return
}
}
)
})
const getEVMTx = async (txh: string, w3From: Web3<RegisteredSubscription>) => {
// console.log("In getEVMTx", "txid:", txh)
/**
* check relay attack
* @param evmTxHash
* @returns object { status, data }
*/
const checkStealthSignature = async (evmTxHash: string) => {
console.log("Searching for txid:", evmTxHash)
try {
const transaction = await w3From.eth.getTransaction(txh)
const transactionReceipt = await w3From.eth.getTransactionReceipt(txh)
const data = await prisma.teleporter.findUnique({
where: { hashedTxId: evmTxHash }
})
console.log("Find Stealth Hash: ", data)
if (data) {
return Promise.resolve({ status: true, data: data })
} else {
// not a replay
return Promise.resolve({ status: false, data })
}
} catch (err) {
return Promise.resolve({ status: false, data: null })
}
}
/**
* save Tx info to db
* @param data
*/
const saveEvmTxHash = async (data: { chainType: string; txId: string; amount: string; signature: string; hashedTxId: string }) => {
try {
const _tx = await prisma.teleporter.findUnique({
where: { hashedTxId: data.hashedTxId }
})
if (_tx) {
console.log("Already Existed Tx")
} else {
await prisma.teleporter.create({ data })
}
} catch (err) {
console.log(err)
console.log("Failed to save tx to db")
}
}
/**
* get EVM transaction
* @param txHash
* @param w3From
* @returns
*/
const getEVMTx = async (txHash: string, w3From: Web3<RegisteredSubscription>) => {
try {
const transaction = await w3From.eth.getTransaction(txHash)
const transactionReceipt = await w3From.eth.getTransactionReceipt(txHash)
if (transaction != null && transactionReceipt != null && transaction != undefined && transactionReceipt != undefined) {
// console.log("Transaction:", transaction, "Transaction Receipt:", transactionReceipt)
const tx = { ...transaction, ...transactionReceipt }
if (transactionReceipt.logs.length === 0) {
throw new Error("getEVMTXError: there was a problem with the transaction receipt.")
}
const addrTo = transactionReceipt.logs.length > 0 ? transactionReceipt.logs[0].address : transactionReceipt.to
let tokenAmt = parseInt(transaction.input.slice(74, 138), 16) / 10 ** 18
tokenAmt -= tokenAmt * 0.008
const contractTo = transaction.to
// console.log({ transaction })
// const addrTo = transactionReceipt.logs.length > 0 ? transactionReceipt.logs[0].address : transactionReceipt.to
const teleporter = String(transactionReceipt?.to)
const _addressOfLog = String(transactionReceipt?.logs[0]?.address)
const _addressToken = _addressOfLog.toLowerCase() === teleporter.toLowerCase() ? "0x0000000000000000000000000000000000000000" : _addressOfLog
console.log("fromTokenAddress: =======>", _addressToken)
// console.log({ addrTo, log0: transactionReceipt.logs[0] })
// let tokenAmt = parseInt(transaction.input.slice(74, 138), 16)
// console.log(tokenAmt)
// tokenAmt -= tokenAmt * 0.008
const from = transaction.from
let amount: number = 0
const abi = ["event BridgeBurned(address caller, uint256 amt)", "event VaultDeposit(address depositor, uint256 amt)"]
const iface = new Interface(abi)
const eventLog = tx.logs
let logName: string = ""
let _eventName: string = ""
// console.log("Log Length:", eventLog.length)
for (const _event of eventLog) {
try {
//@ts-expect-error "_event type not matching"
const log = iface.parseLog(_event)
// console.log("log", log.name)
logName = log.name
_eventName = log.name
} catch (error) {
console.log("EventNotFoundError in log number:", _event)
// console.log(`EventNotFoundError in log number:`, _event)
}
}
amount = transactionReceipt.logs.length > 0 && transactionReceipt.logs[0].data == "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ? 0 : Number(transactionReceipt.logs[0].data)
amount = Number(Web3.utils.fromWei(amount.toLocaleString("fullwide", { useGrouping: false }).toString(), "ether"))
// amount = transactionReceipt.logs.length > 0 && transactionReceipt.logs[0].data == "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ? 0 : Number(transactionReceipt.logs[0].data)
// amount = Number(Web3.utils.fromWei(amount.toLocaleString("fullwide", { useGrouping: false }).toString(), "ether"))
const _tokenAmount = transactionReceipt.logs.length === 1 ? String(transactionReceipt.logs[0].data).substring(66) : String(transactionReceipt.logs[1].data).substring(66)
// const amount = transactionReceipt.logs.length === 1 ? Number('0x' + String(transactionReceipt.logs[0]).substring(66)) : Number('0x' + String(transactionReceipt.logs[1]).substring(66))
const _amount = Number("0x" + _tokenAmount)
const transactionObj = {
contractTo,
addressTo: addrTo,
teleporter, // source teleporter address
token: _addressToken,
from,
tokenAmount: tokenAmt,
log: logName,
value: amount
// tokenAmount: tokenAmt,
eventName: _eventName,
value: _amount
}
return transactionObj
}
+35 -36
View File
@@ -36,9 +36,8 @@ export const signClient = async (i: number, msgHash: string, txInfo: string[]) =
const [txid, fromNetId, toNetIdHash, tokenName, tokenAddrHash, toTargetAddrHash, msgSig, nonce] = txInfo
const txidNonce = txid + nonce
const list = await find("name", `${signClientName} ${signSmManager}`)
console.log("list", list);
if (list.length > 0) {
console.log("clientAlreadyRunning", list)
console.log("clientAlreadyRunning:::", list)
try {
const x = list.length === 1 ? 0 : 1
const uptimeCmd = "ps -p " + list[x].pid + " -o etime"
@@ -59,7 +58,7 @@ export const signClient = async (i: number, msgHash: string, txInfo: string[]) =
await killSigner(String(p.pid))
}
const cmd = `./target/release/examples/${signClientName} ${signSmManager} ${keyStore} ${msgHash}`
await exec(cmd, { cwd: __dirname + "/multiparty", shell: '/bin/bash' }) // Make sure it"s dead
await exec(cmd, { cwd: __dirname + "/multiparty", shell: "/bin/bash" }) // Make sure it"s dead
} catch (err) {
console.log("Partial signature process may not have exited:", err)
resolve(signClient(i, msgHash, txInfo))
@@ -145,32 +144,32 @@ export const signClient = async (i: number, msgHash: string, txInfo: string[]) =
* @param txProcMap
* @returns
*/
//will become MPC
export const signMsg = async (message: string, web3: Web3<RegisteredSubscription>, txInfo: string[]) => {
try {
// const sig = web3.eth.accounts.sign(message, "0xb0bffa4504c56ae708e1fed516aa433f8926fd1dfd667ebd33667611ab02ac0f")
// const { signature, messageHash, r, s, v } = sig
// console.log("MPC Address:", web3.eth.accounts.recover(message, signature))
// console.log("MPC Address:", web3.eth.accounts.recover(message, v, r, s))
// const myMsgHashAndPrefix = web3.eth.accounts.hashMessage(message)
// const netSigningMsg = message
// return signature
const sig = web3.eth.accounts.sign(message, "0xb0bffa4504c56ae708e1fed516aa433f8926fd1dfd667ebd33667611ab02ac0f")
const { signature, messageHash, r, s, v } = sig
const myMsgHashAndPrefix = web3.eth.accounts.hashMessage(message)
const netSigningMsg = myMsgHashAndPrefix.substr(2)
const i = 0
try {
const { signature, r, s, v } = (await signClient(i, netSigningMsg, txInfo)) as any
console.log("SIGNATURE =================> ", { netSigningMsg, signature, r, s, v })
try {
console.log("MPC Address:", recoverAddress(myMsgHashAndPrefix, signature))
} catch (err) {
console.log("err: ", err)
}
return Promise.resolve(signature)
} catch (err) {
console.log("Error:", err)
return Promise.reject("signClientError:")
}
const signer = recoverAddress(myMsgHashAndPrefix, signature)
console.log("MPC Address:", signer)
return Promise.resolve({ signature, signer })
// const myMsgHashAndPrefix = web3.eth.accounts.hashMessage(message)
// const netSigningMsg = myMsgHashAndPrefix.substr(2)
// const i = 0
// try {
// const { signature, r, s, v } = (await signClient(i, netSigningMsg, txInfo)) as any
// let signer = ""
// try {
// signer = recoverAddress(myMsgHashAndPrefix, signature)
// console.log("MPC Address:", signer)
// } catch (err) {
// console.log("err: ", err)
// }
// return Promise.resolve({ signature, signer })
// } catch (err) {
// console.log("Error:", err)
// return Promise.reject("signClientError:")
// }
} catch (err) {
console.log("Error:", err)
return Promise.reject(err)
@@ -187,8 +186,8 @@ export const signMsg = async (message: string, web3: Web3<RegisteredSubscription
* @param vault
* @returns string
*/
export const concatMsg = (amt: string, targetAddressHash: string, txid: string, toContractAddress: string, toChainIdHash: string, vault: boolean) => {
return amt + targetAddressHash + txid + toContractAddress + toChainIdHash + vault
export const concatMsg = (amount: string, targetAddressHash: string, txid: string, toContractAddress: string, toChainIdHash: string, decimals: string, vault: boolean) => {
return amount + targetAddressHash + txid + toContractAddress + toChainIdHash + decimals + vault
}
/**
@@ -196,23 +195,23 @@ export const concatMsg = (amt: string, targetAddressHash: string, txid: string,
* @param web3
* @param vault
* @param txInfo
* @param txProcMap
* @param _decimals
*/
export const hashAndSignTx = async (amt: string, web3: Web3<RegisteredSubscription>, vault: boolean, txInfo: string[]) => {
export const hashAndSignTx = async (amount: string, web3: Web3<RegisteredSubscription>, vault: boolean, txInfo: string[], _decimals: string) => {
try {
const toTargetAddrHash = txInfo[5]
const txid = txInfo[0]
const txHash = txInfo[0]
const toChainIdHash = txInfo[2]
const toContractAddress = txInfo[4]
const nonce = txInfo[7]
const message = concatMsg(amt, toTargetAddrHash, txid, toContractAddress, toChainIdHash, vault)
// console.log("Message:", message)
const message = concatMsg(amount, toTargetAddrHash, txHash, toContractAddress, toChainIdHash, _decimals, vault)
const hash = web3.utils.soliditySha3(message)
const sig = await signMsg(hash, web3, txInfo)
return Promise.resolve(sig)
const { signature, signer } = await signMsg(hash, web3, txInfo)
return Promise.resolve({ signature, mpcSigner: signer })
} catch (err) {
if (err.toString().includes("invalid point")) {
hashAndSignTx(amt, web3, vault, txInfo)
hashAndSignTx(amount, web3, vault, txInfo, _decimals)
} else {
console.log(err)
return Promise.reject(err)
+2395 -41
View File
File diff suppressed because it is too large Load Diff