优化转账gas计算
This commit is contained in:
parent
13183a8ebb
commit
a71402dcbc
@ -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
|
@ -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() {
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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
60
src/service/gas.svr.ts
Normal 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()
|
||||
})
|
||||
}
|
||||
}
|
@ -33,9 +33,15 @@ export class PriceSvr {
|
||||
let key = `${crypto}_${network}_${fiat}`
|
||||
try {
|
||||
let priceData = await queryPrice(data)
|
||||
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 alchemy error: ' + e.message || e)
|
||||
if (crypto.toLowerCase() === 'eth') {
|
||||
try {
|
||||
let price = await this.queryEthPrice(crypto)
|
||||
this.priceMap.set(key, price + '')
|
||||
@ -44,6 +50,7 @@ export class PriceSvr {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async refreshAll() {
|
||||
for (let key of this.priceMap.keys()) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user