接入okx的waas服务
This commit is contained in:
parent
9835e55675
commit
7dcf3a956a
@ -48,4 +48,9 @@ GAME_PAY_CB_URL=https://game2006api-test.kingsome.cn/webapp/index.php?c=Shop&a=b
|
||||
WALLET_CLIENT_SK='38d9baa24aaea6f87a1caa51f588b0c9578368a1cb00b1639eb9f450b6cada00'
|
||||
|
||||
# 检查guest能否绑定平台账号
|
||||
GAME_CHECK_RELATION_URL='https://game2006api-test.kingsome.cn/webapp/index.php?c=AccountVerify&a=canBind'
|
||||
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'
|
||||
|
@ -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'), {})
|
||||
}
|
||||
}
|
||||
|
156
src/controllers/okx.controller.ts
Normal file
156
src/controllers/okx.controller.ts
Normal 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}
|
||||
}
|
||||
}
|
@ -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 {}
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,9 @@ class TranRecordClass extends BaseModule {
|
||||
@prop()
|
||||
public confirmTime: number
|
||||
|
||||
@prop()
|
||||
public okxOrderId?: string
|
||||
|
||||
public toJson() {
|
||||
return {
|
||||
transactionHash: this.transactionHash,
|
||||
|
@ -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
304
src/service/okx.svr.ts
Normal 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
31
src/utils/okx.utils.ts
Normal 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user