优化转账gas计算

This commit is contained in:
CounterFire2023 2023-07-16 13:53:07 +08:00
parent 13183a8ebb
commit a71402dcbc
7 changed files with 172 additions and 19 deletions

View File

@ -46,3 +46,9 @@ HASH_SALT='iG4Rpsa)6U31$H#^T85$^^3'
# 游戏服, 支付上报地址
GAME_PAY_CB_URL=https://game2006api-test.kingsome.cn/webapp/index.php
REDIS=redis://127.0.0.1:6379/14
# CHAIN_RPC_URL=https://arb1.arbitrum.io/rpc
CHAIN_RPC_URL=https://arb-mainnet.g.alchemy.com/v2/tFPlLg-MT8uGkM5wKHyoBqEHWJJ0o3Nt
CHAIN_CEG_ADDRESS=0x741482aE1480E552735E44Ff3A733448AcBbeD8d
CHAIN_WALLET_ADDRESS=0x9c257b7830566e889cd2eef906541f130d4a75f6
CHAIN_GAS_BOOST=1.3

View File

@ -9,6 +9,7 @@ import { ConnectOptions } from 'mongoose'
import CodeTaskSchedule from 'schedule/codetask.schedule'
import { PriceSvr } from 'service/price.svr'
import { GooglePaySvr } from 'service/googlepay.svr'
import { GasSvr } from 'service/gas.svr'
const zReqParserPlugin = require('plugins/zReqParser')
@ -90,6 +91,7 @@ export class ApiServer {
initSchedules() {
new CodeTaskSchedule().scheduleAll()
new PriceSvr().scheduleAll()
new GasSvr().scheduleAll()
}
async connectDB() {

View File

@ -1,12 +1,13 @@
import logger from 'logger/logger'
import BaseController from 'common/base.controller'
import BaseController, { ROLE_ANON } from 'common/base.controller'
import { ZError } from 'common/ZError'
import { router } from 'decorators/router'
import { role, router } from 'decorators/router'
import { createOrder, queryFiat, queryPrice, refreshToken } from 'service/alchemy.svr'
import { generateKVStr } from 'utils/net.util'
import { PayRecord, PayStatus } from 'modules/PayRecord'
import { PriceSvr } from 'service/price.svr'
import { reportPayResult } from 'service/game.svr'
import { GasSvr } from 'service/gas.svr'
const CALL_BACK_URL = `${process.env.ALCHEMY_PAY_CB_URL}/pay/out/alchemy/result`
class AlchemyController extends BaseController {
@ -47,6 +48,9 @@ class AlchemyController extends BaseController {
// 根据 `查询数字货币接口` 返回的数据, 预估可获得的数字货币数量
// 用户法币接口做简单的验证
let priceData = await queryPrice({ crypto, network, fiat, amount: fiatAmount, payWayCode, country })
if (!priceData.success) {
throw new ZError(14, priceData.returnMsg || 'fetch price error')
}
logger.debug('pirce data::', JSON.stringify(priceData))
let amountEstimate = priceData.data.cryptoAmount
let record = new PayRecord({
@ -101,19 +105,15 @@ class AlchemyController extends BaseController {
if (!token || !chain) {
throw new ZError(11, 'token or network not found')
}
if (
(chain.toLowerCase() === 'agor' || chain.toLowerCase() === 'eth') &&
(token.toLowerCase() === 'ceg' || token.toLowerCase() === 'cec')
) {
return { price: 1 }
// ceg价格固定为0.1
if ((chain.toLowerCase() === 'agor' || chain.toLowerCase() === 'eth') && token.toLowerCase() === 'ceg') {
return { price: 0.1 }
}
if ((chain.toLowerCase() === 'agor' || chain.toLowerCase() === 'eth') && token.toLowerCase() === 'agor') {
token = 'ETH'
chain = 'ETH'
}
if (token.toLowerCase() === 'ceg') {
return { price: 1 }
chain = 'ARBITRUM'
}
let data = {
crypto: token,
network: chain,
@ -131,4 +131,12 @@ class AlchemyController extends BaseController {
}
return result.data
}
@role(ROLE_ANON)
@router('post /pay/alchemy/estimateGas')
async estimateGas(req, res) {
let { crypto, network } = req.params
let gas = await new GasSvr().estimateGas({ crypto, network })
return { gas }
}
}

View File

@ -9,6 +9,7 @@ import { TransferRecord } from 'modules/TransferRecord'
import { reportPayResult } from 'service/game.svr'
import { PriceSvr } from 'service/price.svr'
import { OrderCacheSvr } from 'service/ordercache.svr'
import { GasSvr } from 'service/gas.svr'
let errorRes = function (msg: string) {
logger.info(`error res: ${msg}`)
@ -108,7 +109,8 @@ class AlchemyOutController extends BaseController {
if (!checkSha1Sign(req.headers)) {
return errorRes('sign error')
}
let gas = 36000 * 0.0000000001
let gas = await new GasSvr().estimateGas({ crypto, network: 'ARBITRUM' })
gas = gas * 0.0000000001
let price = await new PriceSvr().fetchPrice({ crypto: 'ETH', network: 'ARBITRUM', fiat: 'USD' })
let networkFee = gas * parseFloat(price)
let result = {

View File

@ -29,3 +29,71 @@ export async function fetchGasPrice(data) {
}
return axios(reqConfig)
}
/**
success: {
"jsonrpc": "2.0",
"id": 1,
"result": "0x77b49"
}
error: {
"error": {
"code": 3,
"data": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001d45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000",
"message": "execution reverted: ERC20: insufficient allowance"
},
"id": 1,
"jsonrpc": "2.0"
}
*/
export async function erc20TransferGas() {
let url = process.env.CHAIN_RPC_URL
let address = process.env.CHAIN_CEG_ADDRESS
let walletAddress = process.env.CHAIN_WALLET_ADDRESS
let reqData = {
jsonrpc: '2.0',
id: 1,
method: 'eth_estimateGas',
params: [
{
data: '0x23b872dd0000000000000000000000009c257b7830566e889cd2eef906541f130d4a75f600000000000000000000000042448c6a38c08637218d8327b748f213fc2c02310000000000000000000000000000000000000000000000000000000000000000',
from: walletAddress,
to: address,
},
],
}
let reqConfig: any = {
method: 'post',
url,
headers: {
'Content-Type': 'application/json',
},
data: JSON.stringify(reqData),
}
return axios(reqConfig)
}
export async function ethTransferGas() {
let url = process.env.CHAIN_RPC_URL
let walletAddress = process.env.CHAIN_WALLET_ADDRESS
let reqData = {
jsonrpc: '2.0',
id: 1,
method: 'eth_estimateGas',
params: [
{
from: walletAddress,
to: walletAddress,
value: '0x0',
},
],
}
let reqConfig: any = {
method: 'post',
url,
headers: {
'Content-Type': 'application/json',
},
data: JSON.stringify(reqData),
}
return axios(reqConfig)
}

60
src/service/gas.svr.ts Normal file
View File

@ -0,0 +1,60 @@
import axios from 'axios'
import { singleton } from 'decorators/singleton'
import { queryPrice } from './alchemy.svr'
import * as schedule from 'node-schedule'
import logger from 'logger/logger'
import { erc20TransferGas, ethTransferGas } from './chain.svr'
@singleton
export class GasSvr {
private priceMap: Map<string, number> = new Map()
public async estimateGas(data: any) {
let { crypto, network } = data
network = network || 'arbitrum'
let key = `${crypto}_${network}`
let price = this.priceMap.get(key)
if (!price) {
await this.refreshOne({ crypto, network })
price = this.priceMap.get(key)
}
return price
}
private async refreshOne(data) {
let { crypto, network } = data
let key = `${crypto}_${network}`
try {
let res
if (crypto.toLowerCase() === 'ceg') {
res = await erc20TransferGas()
} else if (crypto.toLowerCase() === 'eth' || crypto.toLowerCase() === 'agor') {
res = await ethTransferGas()
}
let priceData = res.data
if (!priceData.error) {
let gas = parseInt(priceData.result) * +process.env.CHAIN_GAS_BOOST
this.priceMap.set(key, gas)
}
} catch (e) {
logger.debug('update eth gas error: ' + e.message || e)
}
}
private async refreshAll() {
for (let key of this.priceMap.keys()) {
let [crypto, chain] = key.split('_')
let data = {
crypto,
network: chain,
}
await this.refreshOne(data)
}
}
scheduleAll() {
const job = schedule.scheduleJob('*/30 * * * * *', async () => {
this.refreshAll()
})
}
}

View File

@ -33,14 +33,21 @@ export class PriceSvr {
let key = `${crypto}_${network}_${fiat}`
try {
let priceData = await queryPrice(data)
this.priceMap.set(key, priceData.data.cryptoPrice + '')
} catch (e) {
logger.debug('update eth price with alchemy error: ' + e.message || e)
try {
if (priceData.success && priceData.data) {
this.priceMap.set(key, priceData.data.cryptoPrice + '')
} else if (crypto.toLowerCase() === 'eth') {
let price = await this.queryEthPrice(crypto)
this.priceMap.set(key, price + '')
} catch (e) {
logger.debug('update eth price with cryptocompare error: ' + e.message || e)
}
} catch (e) {
logger.debug('update eth price with alchemy error: ' + e.message || e)
if (crypto.toLowerCase() === 'eth') {
try {
let price = await this.queryEthPrice(crypto)
this.priceMap.set(key, price + '')
} catch (e) {
logger.debug('update eth price with cryptocompare error: ' + e.message || e)
}
}
}
}