接入okx的waas服务

This commit is contained in:
CounterFire2023 2023-11-07 17:33:11 +08:00
parent 9835e55675
commit 7dcf3a956a
8 changed files with 532 additions and 2 deletions

View File

@ -49,3 +49,8 @@ WALLET_CLIENT_SK='38d9baa24aaea6f87a1caa51f588b0c9578368a1cb00b1639eb9f450b6cada
# 检查guest能否绑定平台账号
GAME_CHECK_RELATION_URL='https://game2006api-test.kingsome.cn/webapp/index.php?c=AccountVerify&a=canBind'
OKX_API_KEY='5cda794d-b2af-479c-bd75-af1eb877d4ef'
OKX_PROJECT_ID='17c69bdda138a6342f9bece529030cbb'
OKX_PASS='7654321Cf_'
OKX_SECRET_KEY='AF7F4CEE2A10715F9709D38452CE0BFD'

View File

@ -44,7 +44,7 @@ export class ApiServer {
expiresIn: config.api.token_expiresIn,
})
if (process.env.NODE_ENV !== 'production') {
mongoose.set('debug', true)
// mongoose.set('debug', true)
this.server.register(require('@fastify/cors'), {})
}
}

View File

@ -0,0 +1,156 @@
import BaseController, { ROLE_ANON } from 'common/base.controller'
import { ZError } from 'common/ZError'
import { role, router } from 'decorators/router'
import logger from 'logger/logger'
import { TranRecord } from 'modules/TranRecord'
import { Wallet } from 'modules/Wallet'
import {
addCoin,
createWallet,
ensureTxhash,
getAddresses,
getGasPrice,
getSignInfo,
queryCoin,
queryTranDetail,
queryTranHistory,
sendTran,
} from 'service/okx.svr'
const DEFAULT_CHAINID = 42161
class OkxController extends BaseController {
// @role(ROLE_ANON)
// @router('post /wallet/okx/gasprice')
async getPrice(req, res) {
let { chain } = req.params
chain = chain || DEFAULT_CHAINID
let { data } = await getGasPrice(chain)
if (data.code) {
throw new ZError(data.code, data.message)
}
if (!data.data || data.data.length === 0) {
throw new ZError(100, 'no data')
}
return data.data[0]
}
// @role(ROLE_ANON)
// @router('post /wallet/okx/signinfo')
async signInfo(req, res) {
let { signData } = req.params
let { data } = await getSignInfo(signData)
return data
}
// @role(ROLE_ANON)
// @router('post /wallet/okx/addcoin')
async addCustomerCoin(req, res) {
let { chainId, address } = req.params
let reqData = {
coins: [
{
chainId: chainId,
tokenAddress: address,
},
],
}
let { data } = await addCoin(reqData)
return data
}
// @router('post /wallet/okx/bindwallet')
async bindWallet(req, res) {
let user = req.user
let { chainId, address, walletId } = req.params
chainId = chainId || DEFAULT_CHAINID
let data = {
addresses: [
{
chainId: chainId,
address: address,
},
],
walletId,
}
let rep = await createWallet(data)
return rep.data
}
@router('post /wallet/okx/walletaddress')
async walletAddresses(req, res) {
let { walletId } = req.params
let result = await getAddresses(walletId)
return result.data
}
@router('post /wallet/okx/walletassets')
async queryWalletCoin(req, res) {
let { coinIds, chainIds, walletId } = req.params
let reqData: any = {
walletId,
}
if (coinIds && coinIds.length > 0) {
reqData.coinIds = coinIds
}
chainIds = chainIds || [DEFAULT_CHAINID]
reqData.chainIds = chainIds
let result = await queryCoin(reqData)
return result.data
}
@router('post /wallet/okx/transhistory')
async queryWalletTransHistory(req, res) {
let { chainIds, walletId } = req.params
let reqData: any = {
walletId,
limit: 10,
}
chainIds = chainIds || [DEFAULT_CHAINID]
reqData.chainIds = chainIds
let result = await queryTranHistory(reqData)
return result.data
}
@router('post /wallet/okx/transinfo')
async queryWalletTransInfo(req, res) {
let { chainId, walletId, orderId } = req.params
chainId = chainId || DEFAULT_CHAINID
let result = await queryTranDetail({walletId, orderId, chainId})
return result.data
}
@router('post /wallet/okx/sendtran')
async sendWalletTran(req, res) {
let { data } = req.params
let user = req.user;
let record = await Wallet.findOne({ account: user.id })
if (!record || !record.address) {
throw new ZError(11, 'no wallet found')
}
data.chainId = data.chainId || DEFAULT_CHAINID
data.walletId = record.id;
console.log('send trans: ' + JSON.stringify(data));
let result = await sendTran(data)
if (result.status !== 200) {
throw new ZError(result.status, result.statusText)
}
if (result.data.code) {
throw new ZError(result.data.code, result.data.msg)
}
if (result.data?.data) {
setImmediate(async () => {
try {
await TranRecord.insertOrUpdate({
transactionHash: data.txHash,
}, {
okxOrderId: result.data.data.orderId,
})
logger.log(`success update log: ${data.txHash}`)
} catch (err) {
logger.log(`error update log: ${data.txHash} with error: ${err}`)
}
});
}
return {txHash: data.txHash}
}
}

View File

@ -7,10 +7,26 @@ import { Wallet } from 'modules/Wallet'
import { WalletBackup } from 'modules/WalletBackup'
import { WalletExt } from 'modules/WalletExt'
import { customAlphabet } from 'nanoid'
import { createWallet } from 'service/okx.svr'
import { genRandomString, sha3_256, sha512 } from 'utils/security.util'
const nanoid = customAlphabet('1234567890abcdef', 10)
const DEFAULT_CHAINID = 42161
const bindOkx = async (record: any) => {
console.log('bindOkx: ', record.address)
let res = await createWallet({
addresses: [{ chainId: DEFAULT_CHAINID, address: record.address}],
walletId: record.id
})
console.log('bind result: '+ JSON.stringify(res.data));
if (!res.data.code || res.data.code == 81102) {
record.toOkx = true
await record.save()
}
}
class WalletController extends BaseController {
@router('get /wallet/info')
async getWalletInfo(req, res) {
@ -24,6 +40,11 @@ class WalletController extends BaseController {
record.nweRecord = false
await record.save()
}
if (!record.toOkx && record.address) {
setImmediate(async () => {
await bindOkx(record)
});
}
Object.assign(data, record.toJson())
return data
}
@ -66,6 +87,13 @@ class WalletController extends BaseController {
}
record.address = address
await record.save()
if (!record.toOkx) {
if (!record.toOkx && record.address) {
setImmediate(async () => {
await bindOkx(record)
});
}
}
return {}
}

View File

@ -73,6 +73,9 @@ class TranRecordClass extends BaseModule {
@prop()
public confirmTime: number
@prop()
public okxOrderId?: string
public toJson() {
return {
transactionHash: this.transactionHash,

View File

@ -28,6 +28,9 @@ class WalletClass extends BaseModule {
@prop({ required: true, default: true })
public nweRecord: boolean
@prop({default: false})
public toOkx: boolean
public static async findByAccount(this: ReturnModelType<typeof WalletClass>, account: string) {
return this.findOne({ account }).exec()
}

304
src/service/okx.svr.ts Normal file
View File

@ -0,0 +1,304 @@
import axios, { AxiosResponse } from 'axios'
import logger from 'logger/logger'
import { prepareOkxReqCfg } from 'utils/okx.utils';
const OKX_BASE = 'https://www.okx.com/api/v5/waas'
export interface IOkxRes {
code: number
msg: string
data: any
}
// MARK: begin of tranactions
/**
* Get dynamic gas price
* https://www.okx.com/web3/build/docs/waas/api-transaction-get-gas-price
* @param chainId
* @returns
*/
export function getGasPrice(chainId: string|number) {
let config = {
method: 'get',
url: `${OKX_BASE}/transaction/get-gas-price?chainId=${chainId}`
};
config = prepareOkxReqCfg(config);
return axios.request(config)
}
/**
* Get data required for signature
* https://www.okx.com/web3/build/docs/waas/api-transaction-get-sign-info
* @param data
* @returns
*/
export function getSignInfo(data: any) {
if (typeof data === 'object') {
data = JSON.stringify(data)
}
let config = {
method: 'post',
url: `${OKX_BASE}/transaction/get-sign-info`,
data
};
config = prepareOkxReqCfg(config);
return axios.request(config)
}
/**
* Send transaction
* https://www.okx.com/web3/build/docs/waas/api-transaction-send-transaction
* @param data
* @returns
*/
export function sendTran(data: any): Promise<AxiosResponse<IOkxRes>> {
if (typeof data === 'object') {
data = JSON.stringify(data)
}
let config = {
method: 'post',
url: `${OKX_BASE}/transaction/send-transaction`,
data
};
config = prepareOkxReqCfg(config);
return axios.request(config)
}
/**
* Query transaction details
* https://www.okx.com/web3/build/docs/waas/api-transaction-get-transaction-detail
* @param data
* @returns
*/
export function queryTranDetail({walletId, orderId, chainId}
:{walletId: string, orderId: string, chainId: string}) {
let config = {
method: 'get',
url: `${OKX_BASE}/transaction/get-transaction-detail?walletId=${walletId}&orderId=${orderId}&chainId=${chainId}`
};
config = prepareOkxReqCfg(config);
return axios.request(config)
}
export async function ensureTxhash({walletId, orderId, chainId}
:{walletId: string, orderId: string, chainId: string}) {
return new Promise(async (resolve, reject) => {
let interReq = setInterval(async () => {
try {
let res = await queryTranDetail({walletId, orderId, chainId})
let {code, data} = res.data
if (code === 0) {
let {txHash, txStatus} = data
if (txHash && txStatus === 4) {
clearInterval(interReq)
resolve && resolve(txHash)
} else if (txStatus == 3) {
clearInterval(interReq)
reject && reject('trade error')
}
}
} catch (err) {
logger.log('ensureTxhash err', err)
}
}, 1000);
});
}
/**
* Query transaction history
* https://www.okx.com/web3/build/docs/waas/api-transaction-get-transactions-history
* @param data
* @returns
*/
export function queryTranHistory(data: any) {
if (typeof data === 'object') {
data = JSON.stringify(data)
}
let config = {
method: 'post',
url: `${OKX_BASE}/transaction/get-transactions`,
data
};
config = prepareOkxReqCfg(config);
return axios.request(config)
}
// end of tranactions
// begin of Wallet
/**
* Create wallet
* https://www.okx.com/api/v5/waas/wallet/create-wallet
* @param data
{
"addresses": [
{
"chainId": "1",
"address": "0x238193be9e80e68eace3588b45d8cf4a7eae0fa3"
},
{
"chainId": "56",
"address": "0x238193be9e80e68eace3588b45d8cf4a7eae0fa3"
},
{
"chainId": "66",
"address": "0x238193be9e80e68eace3588b45d8cf4a7eae0fa3"
}
],
"walletId": "13886e05-1265-4b79-8ac3-b7ab46211001"
}
* @returns
*/
export function createWallet(data: any) {
if (typeof data === 'object') {
data = JSON.stringify(data)
}
let config = {
method: 'post',
url: `${OKX_BASE}/wallet/create-wallet`,
data
};
config = prepareOkxReqCfg(config);
return axios.request(config)
}
/**
* Sync wallet other chain addresses
* https://www.okx.com/web3/build/docs/waas/api-wallet-synchronize-wallet-address
* @param data
{
"addresses": [
{
"chainId": "1",
"address": "0x238193be9e80e68eace3588b45d8cf4a7eae0fa3"
},
{
"chainId": "56",
"address": "0x238193be9e80e68eace3588b45d8cf4a7eae0fa3"
},
{
"chainId": "66",
"address": "0x238193be9e80e68eace3588b45d8cf4a7eae0fa3"
},
{
"chainId": "137",
"address": "0x238193be9e80e68eace3588b45d8cf4a7eae0fa3"
}
],
"walletId": "13886e05-1265-4b79-8ac3-b7ab46211001"
}
* @returns
*/
export function syncAddress(data: any) {
if (typeof data === 'object') {
data = JSON.stringify(data)
}
let config = {
method: 'post',
url: `${OKX_BASE}/wallet/sync-address`,
data
};
config = prepareOkxReqCfg(config);
return axios.request(config)
}
/**
* Query current wallet address
* https://www.okx.com/web3/build/docs/waas/api-wallet-get-wallet-address
* @returns
*/
export function getAddresses(walletId: string) {
let config = {
method: 'get',
url: `${OKX_BASE}/wallet/get-addresses?walletId=${walletId}`
};
config = prepareOkxReqCfg(config);
return axios.request(config)
}
// end of Wallet
// begin of Assets
/**
* Query all supported coin information
* https://www.okx.com/web3/build/docs/waas/api-asset-get-all-coins
* @param type 0: Platform coin, 1: Custom token
*/
export function getAllCoins(type: string) {
let config = {
method: 'get',
url: `${OKX_BASE}/asset/get-all-coins?type=${type}`
};
config = prepareOkxReqCfg(config);
return axios.request(config)
}
/**
* Add custom coins
* https://www.okx.com/web3/build/docs/waas/api-asset-add-coin
* @param data :
{
"coins":[
{
"chainId":1,
"tokenAddress":"0x4CEdA7906a5Ed2179785Cd3A40A69ee8bc99C466"
}
]
}
* @returns
*/
export function addCoin(data: any) {
if (typeof data === 'object') {
data = JSON.stringify(data)
}
let config = {
method: 'post',
url: `${OKX_BASE}/asset/add-coin`,
data
};
config = prepareOkxReqCfg(config);
return axios.request(config)
}
/**
* Delete custom coin
* https://www.okx.com/web3/build/docs/waas/api-asset-delete-coin
* @param data
{
"chainId": 1,
"tokenAddress": "0x4CEdA7906a5Ed2179785Cd3A40A69ee8bc99C466"
}
* @returns
*/
export function removeCoin(data: any) {
if (typeof data === 'object') {
data = JSON.stringify(data)
}
let config = {
method: 'post',
url: `${OKX_BASE}/asset/del-coin`,
data
};
config = prepareOkxReqCfg(config);
return axios.request(config)
}
/**
* Query wallet token assets
* https://www.okx.com/web3/build/docs/waas/api-asset-get-wallet-asset
* @param data
{
"walletId": "13886e05-1265-4b79-8ac3-b7ab46211001",
"coinIds":["3"]
}
* @returns
*/
export function queryCoin(data: any) {
if (typeof data === 'object') {
data = JSON.stringify(data)
}
let config = {
method: 'post',
url: `${OKX_BASE}/asset/get-assets`,
data
};
config = prepareOkxReqCfg(config);
return axios.request(config)
}
// end of assets

31
src/utils/okx.utils.ts Normal file
View File

@ -0,0 +1,31 @@
import crypto from 'crypto'
const apiKey = process.env.OKX_API_KEY;
const projectId = process.env.OKX_PROJECT_ID;
const pass = process.env.OKX_PASS;
const secretKey = process.env.OKX_SECRET_KEY;
export function prepareOkxReqCfg(config: any) {
let timestamp = new Date().toISOString();
let url = new URL(config.url);
let method = config.method.toUpperCase();
let signStr = timestamp + method + url.pathname;
if (method === 'GET') {
signStr += url.search
} else if (method === 'POST') {
let bodyStr = JSON.stringify(JSON.parse(config.data))
signStr+=bodyStr;
}
const mac = crypto.createHmac('sha256', secretKey);
const sign = mac.update(signStr).digest('base64')
config.headers = {
'OK-ACCESS-PROJECT': projectId,
'OK-ACCESS-KEY': apiKey,
'OK-ACCESS-SIGN': sign,
'OK-ACCESS-TIMESTAMP': timestamp,
'OK-ACCESS-PASSPHRASE': pass,
'Content-Type': 'application/json'
}
return config;
}