-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e04c2b0
commit b475a6b
Showing
4 changed files
with
1,450 additions
and
1,023 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,381 @@ | ||
/* eslint-disable @typescript-eslint/explicit-function-return-type */ | ||
/* eslint-disable @typescript-eslint/no-misused-promises */ | ||
|
||
import { fetch } from 'cross-fetch' | ||
import { v4 as uuidv4 } from 'uuid' | ||
import fs from 'fs' | ||
import { ChildProcess, spawn } from 'child_process' | ||
import { RegTestFoundationKeys } from '@defichain/jellyfish-network' | ||
|
||
const PORT = 3002 | ||
const ENDPOINT = `http://127.0.0.1:${PORT}` | ||
const SPAWNING_TIME = 120_000 | ||
|
||
interface OceanListQuery { | ||
size: number | ||
next?: string | ||
} | ||
|
||
class DefidOceanApi { | ||
async get (path: string): Promise<any> { | ||
const res = await fetch(`${ENDPOINT}${path}`, { | ||
method: 'GET' | ||
}) | ||
// return await res.json() | ||
return res | ||
} | ||
|
||
async post (path: string, data?: any): Promise<any> { | ||
const res = await fetch(`${ENDPOINT}${path}`, { | ||
method: 'POST', | ||
body: JSON.stringify(data) | ||
}) | ||
// return await res.json() | ||
return res | ||
} | ||
} | ||
|
||
export class DefidOceanController { | ||
protected readonly api: DefidOceanApi = new DefidOceanApi() | ||
|
||
// ======== FeeController ======== | ||
async estimateFee () { | ||
return await this.api.get('/fee/estimate') | ||
} | ||
|
||
// ======== MasternodeController ======== | ||
async listMasternodes (query: OceanListQuery = { size: 30 }) { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/masternodes?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/masternodes?size=${query.size}`) | ||
} | ||
|
||
async getMasternode (id: string) { | ||
return await this.api.get(`/masternodes/${id}`) | ||
} | ||
|
||
// ======== OracleController ======== | ||
async listOracles (query: OceanListQuery = { size: 30 }) { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/oracles?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/oracles?size=${query.size}`) | ||
} | ||
|
||
async getPriceFeed (id: string, key: string) { | ||
return await this.api.get(`/oracles/${id}/${key}/feed`) | ||
} | ||
|
||
async getOracleByAddress (address: string) { | ||
return await this.api.get(`/oracles/${address}`) | ||
} | ||
|
||
// ======== PoolPairController ======== | ||
async listPoolPairs (query: OceanListQuery = { size: 30 }) { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/poolpairs?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/poolpairs?size=${query.size}`) | ||
} | ||
|
||
async getPoolPair (id: string) { | ||
return await this.api.get(`/poolpairs/${id}`) | ||
} | ||
|
||
async listPoolSwaps (id: string, query: OceanListQuery = { size: 30 }) { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/poolpairs/${id}/swaps?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/poolpairs/${id}/swaps?size=${query.size}`) | ||
} | ||
|
||
async listPoolSwapsVerbose (id: string, query: OceanListQuery = { size: 30 }) { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/poolpairs/${id}/swaps/verbose?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/poolpairs/${id}/swaps/verbose?size=${query.size}`) | ||
} | ||
|
||
async listPoolSwapsAggregate (id: string, interval: number, query: OceanListQuery = { size: 30 }) { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/poolpairs/${id}/swaps/aggregate/${interval}?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/poolpairs/${id}/swaps/aggregate/${interval}?size=${query.size}`) | ||
} | ||
|
||
async getSwappableTokens (id: string) { | ||
return await this.api.get(`/poolpairs/paths/swappable/${id}`) | ||
} | ||
|
||
async getBestPath (fromTokenId: string, toTokenId: string) { | ||
return await this.api.get(`/poolpairs/paths/best/from/${fromTokenId}/to/${toTokenId}`) | ||
} | ||
|
||
async listPaths (fromTokenId: string, toTokenId: string) { | ||
return await this.api.get(`/poolpairs/paths/from/${fromTokenId}/to/${toTokenId}`) | ||
} | ||
|
||
async listDexPrices (denomination: string) { | ||
return await this.api.get(`/poolpairs/dexprices?denomination=${denomination}`) | ||
} | ||
|
||
// ======== PriceController ======== | ||
async listPrices (query: OceanListQuery = { size: 30 }) { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/prices?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/prices?size=${query.size}`) | ||
} | ||
|
||
async getPrice (id: string) { | ||
return await this.api.get(`/prices/${id}`) | ||
} | ||
|
||
async getFeedActive (id: string) { | ||
return await this.api.get(`/prices/${id}/feed/active`) | ||
} | ||
|
||
async getFeed (id: string) { | ||
return await this.api.get(`/prices/${id}/feed`) | ||
} | ||
|
||
async getFeedWithInterval (id: string, interval: number) { | ||
return await this.api.get(`/prices/${id}/feed/interval/${interval}`) | ||
} | ||
|
||
async get_oracles (id: string) { | ||
return await this.api.get(`/prices/${id}/oracles`) | ||
} | ||
|
||
// ======== RawTxController ======== | ||
// async sendRawtx() { | ||
// return this.post(`/rawtx/send`) | ||
// } | ||
async testRawtx () { | ||
return await this.api.get('/rawtx/test') | ||
} | ||
|
||
async getRawtx (id: string) { | ||
return await this.api.get(`/rawtx/${id}`) | ||
} | ||
|
||
// ======== StatController ======== | ||
async getStats () { | ||
return await this.api.get('/stats') | ||
} | ||
|
||
async getRewardDistribution () { | ||
return await this.api.get('/stats/reward/distribution') | ||
} | ||
|
||
async getSupply () { | ||
return await this.api.get('/stats/supply') | ||
} | ||
|
||
async getBurn () { | ||
return await this.api.get('/stats/burn') | ||
} | ||
|
||
// ======== TokenController ======== | ||
async listTokens () { | ||
return await this.api.get('/tokens') | ||
} | ||
|
||
async getToken (id: string) { | ||
return await this.api.get(`/tokens/${id}`) | ||
} | ||
|
||
// ======== TransactionController ======== | ||
async getTransaction (id: string) { | ||
return await this.api.get(`/transactions/${id}`) | ||
} | ||
|
||
async getVins (id: string) { | ||
return await this.api.get(`/transactions/${id}/vins`) | ||
} | ||
|
||
async getVouts (id: string) { | ||
return await this.api.get(`/transactions/${id}/vouts`) | ||
} | ||
} | ||
|
||
export class DAddressController extends DefidOceanController { | ||
async getAccountHistory (address: string, height: number, txno: number) { | ||
return await this.api.get(`/address/${address}/history/${height}/${txno}`) | ||
} | ||
|
||
async listAccountHistory (address: string, query: OceanListQuery = { size: 30 }) { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/address/${address}/history?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/address/${address}/history?size=${query.size}`) | ||
} | ||
|
||
async getBalance (address: string) { | ||
return await this.api.get(`/address/${address}/balance`) | ||
} | ||
|
||
async getAggregation (address: string) { | ||
return await this.api.get(`/address/${address}/aggregation`) | ||
} | ||
|
||
async listAddressTokens (address: string, query: OceanListQuery = { size: 30 }) { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/address/${address}/tokens?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/address/${address}/tokens?size=${query.size}`) | ||
} | ||
|
||
async listAddressVaults (address: string, query: OceanListQuery = { size: 30 }) { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/address/${address}/vaults?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/address/${address}/vaults?size=${query.size}`) | ||
} | ||
|
||
async listAddressTransactions (address: string, query: OceanListQuery = { size: 30 }) { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/address/${address}/transactions?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/address/${address}/transactions?size=${query.size}`) | ||
} | ||
|
||
async listAddressTransactionsUnspent (address: string, query: OceanListQuery = { size: 30 }) { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/address/${address}/transactions/unspent?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/address/${address}/transactions/unspent?size=${query.size}`) | ||
} | ||
} | ||
|
||
export class DBlockController extends DefidOceanController { | ||
async list (query: OceanListQuery = { size: 30 }): Promise<any> { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/blocks/transactions?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/blocks/transactions?size=${query.size}`) | ||
} | ||
|
||
async get (id: string): Promise<any> { | ||
return await this.api.get(`/blocks/${id}`) | ||
} | ||
|
||
async getTransactions (hash: string, query: OceanListQuery = { size: 30 }): Promise<any> { | ||
if (query.next !== undefined) { | ||
return await this.api.get(`/blocks/${hash}/transactions?size=${query.size}&next=${query.next}`) | ||
} | ||
return await this.api.get(`/blocks/${hash}/transactions?size=${query.size}`) | ||
} | ||
} | ||
|
||
export class DefidBin { | ||
tmpDir: string = `/tmp/${uuidv4()}` | ||
binary: ChildProcess | null = null | ||
|
||
public constructor ( | ||
readonly addressController: DAddressController, | ||
readonly blockController: DBlockController | ||
) { | ||
} | ||
|
||
async start (): Promise<void> { | ||
fs.mkdirSync(this.tmpDir) | ||
|
||
if (process.env.DEFID === undefined) { | ||
throw new Error('`process.env.DEFID` is required') | ||
} | ||
|
||
const args = [ | ||
`-datadir=${this.tmpDir}`, | ||
'-regtest', | ||
'-printtoconsole', | ||
'-gen=0', | ||
'-rpcuser=test', | ||
'-rpcpassword=test', | ||
'-jellyfish_regtest', | ||
'-logtimemicros', | ||
'-logthreadnames', | ||
'-debug', | ||
`-masternode_operator=${RegTestFoundationKeys[1].operator.address}`, | ||
'-dummypos=0', | ||
'-txnotokens=0', | ||
'-logtimemicros', | ||
'-txindex=1', | ||
'-acindex=1' | ||
] | ||
|
||
const extraArgs = [ | ||
'-amkheight=0', | ||
'-bayfrontheight=1', | ||
'-bayfrontgardensheight=2', | ||
'-clarkequayheight=3', | ||
'-dakotaheight=4', | ||
'-dakotacrescentheight=5', | ||
'-eunosheight=6', | ||
'-eunospayaheight=7', | ||
'-fortcanningheight=8', | ||
'-fortcanningmuseumheight=9', | ||
'-fortcanninghillheight=10', | ||
'-fortcanningroadheight=11', | ||
'-fortcanningcrunchheight=12', | ||
'-fortcanningspringheight=13', | ||
'-fortcanninggreatworldheight=14', | ||
'-fortcanningepilogueheight=15', | ||
'-grandcentralheight=16', | ||
'-grandcentralepilogueheight=17', | ||
'-metachainheight=18' | ||
] | ||
|
||
const binary = spawn(process.env.DEFID, args.concat(extraArgs)) | ||
|
||
binary.on('error', err => { | ||
if ((err as any).errno === 'ENOENT') { | ||
console.error('\x1b[31mMissing Defid binary.\nPlease compile the Defid\x1b[0m') | ||
} else { | ||
console.error(err) | ||
} | ||
process.exit(1) | ||
}) | ||
|
||
const logs: string[] = [] | ||
|
||
await new Promise<void>((resolve, reject) => { | ||
const timer = setTimeout(() => { | ||
console.error('\x1b[31m Failed to start Defid node.\x1b[0m') | ||
console.error(logs.map(chunk => chunk.toString()).join('\n')) | ||
process.exit(1) | ||
}, SPAWNING_TIME - 20_000) | ||
|
||
const onData = async (chunk: any) => { | ||
logs.push(chunk) | ||
|
||
/* eslint-disable-next-line @typescript-eslint/strict-boolean-expressions */ | ||
if (chunk.toString().match(/addcon thread start/)) { | ||
try { | ||
const res = await this.blockController.get('0') | ||
console.log('[DefidBin.start()] blockController.get res: ', res) | ||
} catch (err) { | ||
console.log('[DefidBin.start()] blockController.get err: ', err) | ||
} | ||
|
||
clearTimeout(timer) | ||
|
||
binary.stderr.off('data', onData) | ||
binary.stdout.off('data', onData) | ||
|
||
// import privkey | ||
// setgov | ||
// generate(2) | ||
|
||
resolve() | ||
} | ||
} | ||
|
||
binary.stderr.on('data', onData) | ||
binary.stdout.on('data', onData) | ||
}) | ||
|
||
this.binary = binary | ||
} | ||
} |
Oops, something went wrong.