5 tokens (#6)
* Get token decimals by index * Update default mango group * Update readme * Add account filtering behind feature flag * prettier; changed the default loading for targets * updated version number and fixed updating of min coll ratio account Co-authored-by: Riordan Panayides <riordan@panayid.es> Co-authored-by: dd <dafydd.durairaj@gmail.com>
This commit is contained in:
parent
39b5ab3b4f
commit
ba1a874ebb
21
README.md
21
README.md
|
@ -4,7 +4,7 @@
|
|||
### Prerequisites
|
||||
To run the liquidator you will need:
|
||||
* A Solana account with some SOL deposited to cover transaction fees
|
||||
* Token accounts for each currency in the Mango Group (e.g. BTC, ETH, USDT)
|
||||
* Token accounts for each currency in the Mango Group (e.g. BTC, ETH, SOL, SRM, USDT)
|
||||
* Roughly equal deposits for each token. You will need base currencies to liquidate shorts, and quote currency to liquidate longs.
|
||||
* Serum Dex OpenOrders accounts associated with your account. This is required for balance wallets functionality.
|
||||
* The easiest way to set these up is by placing an order on Serum Dex for each currency pair then immediately cancelling it.
|
||||
|
@ -15,16 +15,19 @@ export CLUSTER="mainnet-beta"
|
|||
export CLUSTER_URL="https://solana-api.projectserum.com"
|
||||
export KEYPAIR=~/.config/solana/id.json
|
||||
export NODE_ENV=production
|
||||
export TARGETS="0.1 2"
|
||||
export GROUP_NAME="BTC_ETH_USDT"
|
||||
export TARGETS="0.1 2.0 100.0 500.0"
|
||||
export GROUP_NAME="BTC_ETH_SOL_SRM_USDT"
|
||||
export CHECK_INTERVAL="1000.0"
|
||||
export FILTER_ACCOUNTS=true
|
||||
```
|
||||
|
||||
TARGETS represents the BTC and ETH amounts the partial liquidator should try to maintain
|
||||
TARGETS represents the amounts of each token the partial liquidator should try to maintain
|
||||
in the liquidator's wallet. Any excess of that amount in the wallet will be market sold on Serum DEX.
|
||||
|
||||
CHECK_INTERVAL is the amount of milliseconds to wait between querying all margin accounts
|
||||
|
||||
FILTER_ACCOUNTS uses a more efficient method of querying marginAccounts by only returning accounts with open borrows. This is only supported on Mango Groups released after 'BTC_ETH_SOL_SRM_USDT'. Disabled by default.
|
||||
|
||||
### Run
|
||||
```
|
||||
yarn install
|
||||
|
@ -32,16 +35,6 @@ source .env
|
|||
yarn partialLiquidate
|
||||
```
|
||||
|
||||
## Setup Full Liquidator [DEPRECATED]
|
||||
Make sure to edit the .env file to look something like this:
|
||||
```
|
||||
export CLUSTER="mainnet-beta"
|
||||
export CLUSTER_URL="https://solana-api.projectserum.com"
|
||||
export KEYPAIR=~/.config/solana/id.json
|
||||
export NODE_ENV=production
|
||||
export GROUP_NAME="BTC_ETH_USDT"
|
||||
```
|
||||
|
||||
### Run
|
||||
```
|
||||
yarn install
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@mango/liquidator",
|
||||
"version": "0.0.1",
|
||||
"version": "2.0.0",
|
||||
"description": "Library for interacting with the serum dex",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
@ -32,7 +32,7 @@
|
|||
"typescript": "^4.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blockworks-foundation/mango-client": "^0.1.19",
|
||||
"@blockworks-foundation/mango-client": "^2.0.0",
|
||||
"@project-serum/serum": "^0.13.20",
|
||||
"@solana/spl-token": "0.0.13",
|
||||
"@solana/web3.js": "^0.90.0",
|
||||
|
|
|
@ -13,7 +13,7 @@ async function setupMarginAccounts() {
|
|||
const clusterIds = IDS[cluster]
|
||||
|
||||
const connection = new Connection(IDS.cluster_urls[cluster], 'singleGossip')
|
||||
const mangoGroupPk = new PublicKey(clusterIds.mango_groups.BTC_ETH_USDC.mango_group_pk);
|
||||
const mangoGroupPk = new PublicKey(clusterIds.mango_groups['BTC_ETH_USDC'].mango_group_pk);
|
||||
const mangoProgramId = new PublicKey(clusterIds.mango_program_id);
|
||||
const dexProgramId = new PublicKey(clusterIds.dex_program_id)
|
||||
|
||||
|
@ -139,7 +139,7 @@ async function testing() {
|
|||
const dexProgramId = new PublicKey(IDS[cluster].dex_program_id)
|
||||
|
||||
// Address of the MangoGroup
|
||||
const mangoGroupPk = new PublicKey(IDS[cluster].mango_groups.BTC_ETH_USDC.mango_group_pk)
|
||||
const mangoGroupPk = new PublicKey(IDS[cluster].mango_groups['BTC_ETH_USDC'].mango_group_pk)
|
||||
|
||||
|
||||
// TODO fetch these automatically
|
||||
|
|
|
@ -69,7 +69,7 @@ async function drainAccount(
|
|||
for (let i = 0; i < NUM_TOKENS - 1; i++) {
|
||||
const marketIndex = netValues[i][0]
|
||||
const market = markets[marketIndex]
|
||||
const tokenDecimals = tokenToDecimals[marketIndex === 0 ? 'BTC' : 'ETH']
|
||||
const tokenDecimals = mangoGroup.getTokenDecimals(marketIndex)
|
||||
const tokenDecimalAdj = Math.pow(10, tokenDecimals)
|
||||
|
||||
if (netValues[i][1] > 0) { // sell to close
|
||||
|
@ -106,7 +106,7 @@ async function drainAccount(
|
|||
async function runLiquidator() {
|
||||
const client = new MangoClient()
|
||||
const cluster = process.env.CLUSTER || 'mainnet-beta'
|
||||
const group_name = process.env.GROUP_NAME || 'BTC_ETH_USDT'
|
||||
const group_name = process.env.GROUP_NAME || 'BTC_ETH_SOL_SRM_USDT'
|
||||
const clusterUrl = process.env.CLUSTER_URL || IDS.cluster_urls[cluster]
|
||||
const connection = new Connection(clusterUrl, 'singleGossip')
|
||||
|
||||
|
@ -146,8 +146,11 @@ async function runLiquidator() {
|
|||
while (true) {
|
||||
try {
|
||||
mangoGroup = await client.getMangoGroup(connection, mangoGroupPk)
|
||||
let [marginAccounts, prices, vaultAccs, liqorAccs] = await Promise.all([
|
||||
client.getAllMarginAccounts(connection, programId, mangoGroup),
|
||||
let marginAccounts = process.env.FILTER_ACCOUNTS ?
|
||||
await client.getAllMarginAccountsWithBorrows(connection, programId, mangoGroup) :
|
||||
await client.getAllMarginAccounts(connection, programId, mangoGroup)
|
||||
|
||||
let [prices, vaultAccs, liqorAccs] = await Promise.all([
|
||||
mangoGroup.getPrices(connection),
|
||||
getMultipleAccounts(connection, mangoGroup.vaults),
|
||||
getMultipleAccounts(connection, tokenWallets),
|
||||
|
|
507
src/partial.ts
507
src/partial.ts
|
@ -3,7 +3,8 @@ import {
|
|||
getMultipleAccounts,
|
||||
IDS,
|
||||
MangoClient,
|
||||
MangoGroup, MarginAccount,
|
||||
MangoGroup,
|
||||
MarginAccount,
|
||||
nativeToUi,
|
||||
NUM_MARKETS,
|
||||
NUM_TOKENS,
|
||||
|
@ -11,7 +12,13 @@ import {
|
|||
parseTokenAccountData,
|
||||
tokenToDecimals,
|
||||
} from '@blockworks-foundation/mango-client';
|
||||
import { Account, Commitment, Connection, PublicKey, Transaction } from '@solana/web3.js';
|
||||
import {
|
||||
Account,
|
||||
Commitment,
|
||||
Connection,
|
||||
PublicKey,
|
||||
Transaction,
|
||||
} from '@solana/web3.js';
|
||||
import { homedir } from 'os';
|
||||
import fs from 'fs';
|
||||
import { notify, sleep } from './utils';
|
||||
|
@ -22,7 +29,6 @@ import {
|
|||
} from '@blockworks-foundation/mango-client/lib/instruction';
|
||||
import BN = require('bn.js');
|
||||
|
||||
|
||||
/*
|
||||
After a liquidation, the amounts in each wallet become unbalanced
|
||||
Make sure to sell or buy quantities different from the target on base currencies
|
||||
|
@ -37,69 +43,95 @@ async function balanceWallets(
|
|||
liqorWallets: PublicKey[],
|
||||
liqorValuesUi: number[],
|
||||
liqorOpenOrdersKeys: PublicKey[],
|
||||
targets: number[]
|
||||
targets: number[],
|
||||
) {
|
||||
|
||||
// Retrieve orders from order book by owner
|
||||
const liqorOrders = await Promise.all(markets.map((m) =>
|
||||
m.loadOrdersForOwner(connection, liqor.publicKey)));
|
||||
const liqorOrders = await Promise.all(
|
||||
markets.map((m) => m.loadOrdersForOwner(connection, liqor.publicKey)),
|
||||
);
|
||||
|
||||
// Cancel all
|
||||
const cancelTransactions: Promise<string>[] = []
|
||||
const cancelTransactions: Promise<string>[] = [];
|
||||
for (let i = 0; i < NUM_MARKETS; i++) {
|
||||
for (let order of liqorOrders[i]) {
|
||||
console.log(`Cancelling liqor order on market ${i} size=${order.size} price=${order.price}`)
|
||||
console.log(
|
||||
`Cancelling liqor order on market ${i} size=${order.size} price=${order.price}`,
|
||||
);
|
||||
cancelTransactions.push(markets[i].cancelOrder(connection, liqor, order));
|
||||
}
|
||||
}
|
||||
await Promise.all(cancelTransactions)
|
||||
await Promise.all(cancelTransactions);
|
||||
|
||||
// Load open orders accounts
|
||||
const liqorOpenOrders = await Promise.all(liqorOpenOrdersKeys.map((pk) => OpenOrders.load(connection, pk, mangoGroup.dexProgramId)))
|
||||
const liqorOpenOrders = await Promise.all(
|
||||
liqorOpenOrdersKeys.map((pk) =>
|
||||
OpenOrders.load(connection, pk, mangoGroup.dexProgramId),
|
||||
),
|
||||
);
|
||||
|
||||
// Settle all
|
||||
const settleTransactions: Promise<string>[] = []
|
||||
const settleTransactions: Promise<string>[] = [];
|
||||
for (let i = 0; i < NUM_MARKETS; i++) {
|
||||
const oo = liqorOpenOrders[i]
|
||||
if (parseFloat(oo.quoteTokenTotal.toString()) > 0 || parseFloat(oo.baseTokenTotal.toString()) > 0) {
|
||||
console.log(`Settling funds to liqor wallet ${i} quote:${oo.quoteTokenTotal.toString()} base:${oo.baseTokenTotal.toString()}`)
|
||||
settleTransactions.push(markets[i].settleFunds(connection, liqor, oo, liqorWallets[i], liqorWallets[NUM_TOKENS-1]))
|
||||
const oo = liqorOpenOrders[i];
|
||||
if (
|
||||
parseFloat(oo.quoteTokenTotal.toString()) > 0 ||
|
||||
parseFloat(oo.baseTokenTotal.toString()) > 0
|
||||
) {
|
||||
console.log(
|
||||
`Settling funds to liqor wallet ${i} quote:${oo.quoteTokenTotal.toString()} base:${oo.baseTokenTotal.toString()}`,
|
||||
);
|
||||
settleTransactions.push(
|
||||
markets[i].settleFunds(
|
||||
connection,
|
||||
liqor,
|
||||
oo,
|
||||
liqorWallets[i],
|
||||
liqorWallets[NUM_TOKENS - 1],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
await Promise.all(settleTransactions)
|
||||
await Promise.all(settleTransactions);
|
||||
|
||||
// Account wallets should instantly update
|
||||
const liqorWalletAccounts = await getMultipleAccounts(connection, liqorWallets, 'processed' as Commitment)
|
||||
liqorValuesUi = liqorWalletAccounts.map(
|
||||
(a, i) => nativeToUi(parseTokenAccountData(a.accountInfo.data).amount, mangoGroup.mintDecimals[i])
|
||||
)
|
||||
const liqorWalletAccounts = await getMultipleAccounts(
|
||||
connection,
|
||||
liqorWallets,
|
||||
'processed' as Commitment,
|
||||
);
|
||||
liqorValuesUi = liqorWalletAccounts.map((a, i) =>
|
||||
nativeToUi(
|
||||
parseTokenAccountData(a.accountInfo.data).amount,
|
||||
mangoGroup.mintDecimals[i],
|
||||
),
|
||||
);
|
||||
|
||||
const diffs: number[] = []
|
||||
const netValues: [number, number][] = []
|
||||
const diffs: number[] = [];
|
||||
const netValues: [number, number][] = [];
|
||||
// Go to each base currency and see if it's above or below target
|
||||
for (let i = 0; i < NUM_TOKENS - 1; i++) {
|
||||
const diff = liqorValuesUi[i] - targets[i]
|
||||
diffs.push(diff)
|
||||
netValues.push([i, diff * prices[i]])
|
||||
const diff = liqorValuesUi[i] - targets[i];
|
||||
diffs.push(diff);
|
||||
netValues.push([i, diff * prices[i]]);
|
||||
}
|
||||
|
||||
// Sort in decreasing order so you sell first then buy
|
||||
netValues.sort((a, b) => (b[1] - a[1]))
|
||||
netValues.sort((a, b) => b[1] - a[1]);
|
||||
for (let i = 0; i < NUM_TOKENS - 1; i++) {
|
||||
const marketIndex = netValues[i][0]
|
||||
const market = markets[marketIndex]
|
||||
const tokenDecimals = tokenToDecimals[marketIndex === 0 ? 'BTC' : 'ETH'] // TODO make this mapping allow arbitrary mango groups
|
||||
const tokenDecimalAdj = Math.pow(10, tokenDecimals)
|
||||
if (netValues[i][1] > 0) { // sell to close
|
||||
const price = prices[marketIndex] * 0.95
|
||||
const size = Math.floor(diffs[marketIndex] * tokenDecimalAdj) / tokenDecimalAdj // round down the size
|
||||
const marketIndex = netValues[i][0];
|
||||
const market = markets[marketIndex];
|
||||
const tokenDecimals = mangoGroup.getTokenDecimals(marketIndex);
|
||||
const tokenDecimalAdj = Math.pow(10, tokenDecimals);
|
||||
if (netValues[i][1] > 0) {
|
||||
// sell to close
|
||||
const price = prices[marketIndex] * 0.95;
|
||||
const size =
|
||||
Math.floor(diffs[marketIndex] * tokenDecimalAdj) / tokenDecimalAdj; // round down the size
|
||||
if (size === 0) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
console.log(`Sell to close ${marketIndex} ${size} @ ${price}`)
|
||||
let txid = await market.placeOrder(
|
||||
connection,
|
||||
{
|
||||
console.log(`Sell to close ${marketIndex} ${size} @ ${price}`);
|
||||
let txid = await market.placeOrder(connection, {
|
||||
owner: liqor,
|
||||
payer: liqorWallets[marketIndex],
|
||||
side: 'sell',
|
||||
|
@ -107,199 +139,257 @@ async function balanceWallets(
|
|||
size,
|
||||
orderType: 'ioc',
|
||||
openOrdersAddressKey: liqorOpenOrdersKeys[marketIndex],
|
||||
feeDiscountPubkey: null // TODO find liqor's SRM fee account
|
||||
}
|
||||
)
|
||||
feeDiscountPubkey: null, // TODO find liqor's SRM fee account
|
||||
});
|
||||
// TODO add a SettleFunds instruction to this transaction
|
||||
console.log(`Place order successful: ${txid}; Settling funds`)
|
||||
await market.settleFunds(connection, liqor, liqorOpenOrders[marketIndex], liqorWallets[marketIndex], liqorWallets[NUM_TOKENS-1])
|
||||
|
||||
} else if (netValues[i][1] < 0) { // buy to close
|
||||
const price = prices[marketIndex] * 1.05 // buy at up to 5% higher than oracle price
|
||||
const size = Math.ceil(-diffs[marketIndex] * tokenDecimalAdj) / tokenDecimalAdj
|
||||
|
||||
console.log(`Buy to close ${marketIndex} ${size} @ ${price}`)
|
||||
let txid = await market.placeOrder(
|
||||
console.log(`Place order successful: ${txid}; Settling funds`);
|
||||
await market.settleFunds(
|
||||
connection,
|
||||
{
|
||||
liqor,
|
||||
liqorOpenOrders[marketIndex],
|
||||
liqorWallets[marketIndex],
|
||||
liqorWallets[NUM_TOKENS - 1],
|
||||
);
|
||||
} else if (netValues[i][1] < 0) {
|
||||
// buy to close
|
||||
const price = prices[marketIndex] * 1.05; // buy at up to 5% higher than oracle price
|
||||
const size =
|
||||
Math.ceil(-diffs[marketIndex] * tokenDecimalAdj) / tokenDecimalAdj;
|
||||
|
||||
console.log(`Buy to close ${marketIndex} ${size} @ ${price}`);
|
||||
let txid = await market.placeOrder(connection, {
|
||||
owner: liqor,
|
||||
payer: liqorWallets[NUM_TOKENS-1],
|
||||
payer: liqorWallets[NUM_TOKENS - 1],
|
||||
side: 'buy',
|
||||
price,
|
||||
size,
|
||||
orderType: 'ioc',
|
||||
openOrdersAddressKey: liqorOpenOrdersKeys[marketIndex],
|
||||
feeDiscountPubkey: null
|
||||
}
|
||||
)
|
||||
console.log(`Place order successful: ${txid}; Settling funds`)
|
||||
await market.settleFunds(connection, liqor, liqorOpenOrders[marketIndex], liqorWallets[marketIndex], liqorWallets[NUM_TOKENS-1])
|
||||
feeDiscountPubkey: null,
|
||||
});
|
||||
console.log(`Place order successful: ${txid}; Settling funds`);
|
||||
await market.settleFunds(
|
||||
connection,
|
||||
liqor,
|
||||
liqorOpenOrders[marketIndex],
|
||||
liqorWallets[marketIndex],
|
||||
liqorWallets[NUM_TOKENS - 1],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function runPartialLiquidator() {
|
||||
const client = new MangoClient()
|
||||
const cluster = process.env.CLUSTER || 'mainnet-beta'
|
||||
const group_name = process.env.GROUP_NAME || 'BTC_ETH_USDT'
|
||||
const clusterUrl = process.env.CLUSTER_URL || IDS.cluster_urls[cluster]
|
||||
const targetsStr = process.env.TARGETS || "0.1 2"
|
||||
const checkInterval = parseFloat(process.env.CHECK_INTERVAL || "1000.0")
|
||||
const targets = targetsStr.split(' ').map((s) => parseFloat(s))
|
||||
const connection = new Connection(clusterUrl, 'singleGossip')
|
||||
const client = new MangoClient();
|
||||
const cluster = process.env.CLUSTER || 'mainnet-beta';
|
||||
const group_name = process.env.GROUP_NAME || 'BTC_ETH_SOL_SRM_USDC';
|
||||
const clusterUrl = process.env.CLUSTER_URL || IDS.cluster_urls[cluster];
|
||||
const targetsStr = process.env.TARGETS || '0.1 2.0 100.0 1000.0';
|
||||
const checkInterval = parseFloat(process.env.CHECK_INTERVAL || '1000.0');
|
||||
const targets = targetsStr.split(' ').map((s) => parseFloat(s));
|
||||
const connection = new Connection(clusterUrl, 'singleGossip');
|
||||
|
||||
// The address of the Mango Program on the blockchain
|
||||
const programId = new PublicKey(IDS[cluster].mango_program_id)
|
||||
const programId = new PublicKey(IDS[cluster].mango_program_id);
|
||||
|
||||
// The address of the serum dex program on the blockchain: https://github.com/project-serum/serum-dex
|
||||
const dexProgramId = new PublicKey(IDS[cluster].dex_program_id)
|
||||
const dexProgramId = new PublicKey(IDS[cluster].dex_program_id);
|
||||
|
||||
// Address of the MangoGroup
|
||||
const mangoGroupPk = new PublicKey(IDS[cluster].mango_groups[group_name].mango_group_pk)
|
||||
const mangoGroupPk = new PublicKey(
|
||||
IDS[cluster].mango_groups[group_name].mango_group_pk,
|
||||
);
|
||||
|
||||
// liquidator's keypair
|
||||
const keyPairPath = process.env.KEYPAIR || homedir() + '/.config/solana/id.json'
|
||||
const payer = new Account(JSON.parse(fs.readFileSync(keyPairPath, 'utf-8')))
|
||||
const keyPairPath =
|
||||
process.env.KEYPAIR || homedir() + '/.config/solana/id.json';
|
||||
const payer = new Account(JSON.parse(fs.readFileSync(keyPairPath, 'utf-8')));
|
||||
|
||||
notify(`partial liquidator launched cluster=${cluster} group=${group_name}`);
|
||||
|
||||
let mangoGroup = await client.getMangoGroup(connection, mangoGroupPk)
|
||||
let mangoGroup = await client.getMangoGroup(connection, mangoGroupPk);
|
||||
|
||||
const tokenWallets = (await Promise.all(
|
||||
mangoGroup.tokens.map(
|
||||
(mint) => findLargestTokenAccountForOwner(connection, payer.publicKey, mint).then(
|
||||
(response) => response.publicKey
|
||||
)
|
||||
)
|
||||
))
|
||||
const tokenWallets = await Promise.all(
|
||||
mangoGroup.tokens.map((mint) =>
|
||||
findLargestTokenAccountForOwner(connection, payer.publicKey, mint).then(
|
||||
(response) => response.publicKey,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// load all markets
|
||||
const markets = await Promise.all(mangoGroup.spotMarkets.map(
|
||||
(pk) => Market.load(connection, pk, {skipPreflight: true, commitment: 'singleGossip'}, dexProgramId)
|
||||
))
|
||||
const markets = await Promise.all(
|
||||
mangoGroup.spotMarkets.map((pk) =>
|
||||
Market.load(
|
||||
connection,
|
||||
pk,
|
||||
{ skipPreflight: true, commitment: 'singleGossip' },
|
||||
dexProgramId,
|
||||
),
|
||||
),
|
||||
);
|
||||
// TODO handle failures in any of the steps
|
||||
// Find a way to get all margin accounts without querying fresh--get incremental updates to margin accounts
|
||||
const liqorOpenOrdersKeys: PublicKey[] = []
|
||||
const liqorOpenOrdersKeys: PublicKey[] = [];
|
||||
|
||||
for (let i = 0; i < NUM_MARKETS; i++) {
|
||||
let openOrdersAccounts: OpenOrders[] = await markets[i].findOpenOrdersAccountsForOwner(connection, payer.publicKey)
|
||||
if(openOrdersAccounts.length) {
|
||||
liqorOpenOrdersKeys.push(openOrdersAccounts[0].publicKey)
|
||||
let openOrdersAccounts: OpenOrders[] = await markets[
|
||||
i
|
||||
].findOpenOrdersAccountsForOwner(connection, payer.publicKey);
|
||||
if (openOrdersAccounts.length) {
|
||||
liqorOpenOrdersKeys.push(openOrdersAccounts[0].publicKey);
|
||||
} else {
|
||||
console.log(`No OpenOrders account found for market ${markets[i].publicKey.toBase58()}`)
|
||||
console.log(
|
||||
`No OpenOrders account found for market ${markets[
|
||||
i
|
||||
].publicKey.toBase58()}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if(liqorOpenOrdersKeys.length != NUM_MARKETS) {
|
||||
console.log('Warning: Missing OpenOrders accounts. Wallet balancing has been disabled.')
|
||||
if (liqorOpenOrdersKeys.length != NUM_MARKETS) {
|
||||
console.log(
|
||||
'Warning: Missing OpenOrders accounts. Wallet balancing has been disabled.',
|
||||
);
|
||||
}
|
||||
|
||||
const cancelLimit = 5
|
||||
const cancelLimit = 5;
|
||||
while (true) {
|
||||
try {
|
||||
mangoGroup = await client.getMangoGroup(connection, mangoGroupPk)
|
||||
mangoGroup = await client.getMangoGroup(connection, mangoGroupPk);
|
||||
let marginAccounts = process.env.FILTER_ACCOUNTS
|
||||
? await client.getAllMarginAccountsWithBorrows(
|
||||
connection,
|
||||
programId,
|
||||
mangoGroup,
|
||||
)
|
||||
: await client.getAllMarginAccounts(connection, programId, mangoGroup);
|
||||
|
||||
let [marginAccounts, prices, vaultAccs, liqorAccs] = await Promise.all([
|
||||
client.getAllMarginAccounts(connection, programId, mangoGroup),
|
||||
let [prices, vaultAccs, liqorAccs] = await Promise.all([
|
||||
mangoGroup.getPrices(connection),
|
||||
getMultipleAccounts(connection, mangoGroup.vaults),
|
||||
getMultipleAccounts(connection, tokenWallets, 'processed' as Commitment),
|
||||
])
|
||||
getMultipleAccounts(
|
||||
connection,
|
||||
tokenWallets,
|
||||
'processed' as Commitment,
|
||||
),
|
||||
]);
|
||||
|
||||
const vaultValues = vaultAccs.map(
|
||||
(a, i) => nativeToUi(parseTokenAccountData(a.accountInfo.data).amount, mangoGroup.mintDecimals[i])
|
||||
)
|
||||
const vaultValues = vaultAccs.map((a, i) =>
|
||||
nativeToUi(
|
||||
parseTokenAccountData(a.accountInfo.data).amount,
|
||||
mangoGroup.mintDecimals[i],
|
||||
),
|
||||
);
|
||||
const liqorTokenValues = liqorAccs.map(
|
||||
(a) => parseTokenAccount(a.accountInfo.data).amount
|
||||
)
|
||||
const liqorTokenUi = liqorAccs.map(
|
||||
(a, i) => nativeToUi(parseTokenAccountData(a.accountInfo.data).amount, mangoGroup.mintDecimals[i])
|
||||
)
|
||||
(a) => parseTokenAccount(a.accountInfo.data).amount,
|
||||
);
|
||||
const liqorTokenUi = liqorAccs.map((a, i) =>
|
||||
nativeToUi(
|
||||
parseTokenAccountData(a.accountInfo.data).amount,
|
||||
mangoGroup.mintDecimals[i],
|
||||
),
|
||||
);
|
||||
|
||||
console.log(prices)
|
||||
console.log(vaultValues)
|
||||
console.log(liqorTokenUi)
|
||||
console.log(prices);
|
||||
console.log(vaultValues);
|
||||
console.log(liqorTokenUi);
|
||||
|
||||
// FIXME: added bias to collRatio to allow other liquidators to step in for testing
|
||||
let coll_bias = 0
|
||||
let coll_bias = 0;
|
||||
if (process.env.COLL_BIAS) {
|
||||
coll_bias = parseFloat(process.env.COLL_BIAS)
|
||||
coll_bias = parseFloat(process.env.COLL_BIAS);
|
||||
}
|
||||
|
||||
let maxBorrAcc: MarginAccount | undefined = undefined;
|
||||
let maxBorrVal = 0;
|
||||
let minCollAcc: MarginAccount | undefined = undefined;
|
||||
let minCollVal = 99999;
|
||||
for (let ma of marginAccounts) { // parallelize this if possible
|
||||
for (let ma of marginAccounts) {
|
||||
// parallelize this if possible
|
||||
|
||||
let description = ''
|
||||
let description = '';
|
||||
try {
|
||||
const assetsVal = ma.getAssetsVal(mangoGroup, prices)
|
||||
const liabsVal = ma.getLiabsVal(mangoGroup, prices)
|
||||
const assetsVal = ma.getAssetsVal(mangoGroup, prices);
|
||||
const liabsVal = ma.getLiabsVal(mangoGroup, prices);
|
||||
if (liabsVal > maxBorrVal) {
|
||||
maxBorrVal = liabsVal
|
||||
maxBorrAcc = ma
|
||||
maxBorrVal = liabsVal;
|
||||
maxBorrAcc = ma;
|
||||
}
|
||||
|
||||
if (liabsVal < 0.1) { // too small of an account; number precision may cause errors
|
||||
continue
|
||||
if (liabsVal < 0.1) {
|
||||
// too small of an account; number precision may cause errors
|
||||
continue;
|
||||
}
|
||||
let collRatio = assetsVal / liabsVal;
|
||||
if (collRatio < minCollVal) {
|
||||
minCollVal = collRatio;
|
||||
minCollAcc = ma;
|
||||
}
|
||||
|
||||
if (!ma.beingLiquidated) {
|
||||
let collRatio = (assetsVal / liabsVal)
|
||||
|
||||
const deficit = liabsVal * mangoGroup.initCollRatio - assetsVal
|
||||
if (deficit < 0.1) { // too small of an account; number precision may cause errors
|
||||
continue
|
||||
}
|
||||
|
||||
if (collRatio < minCollVal)
|
||||
{
|
||||
minCollVal = collRatio
|
||||
minCollAcc = ma
|
||||
const deficit = liabsVal * mangoGroup.initCollRatio - assetsVal;
|
||||
if (deficit < 0.1) {
|
||||
// too small of an account; number precision may cause errors
|
||||
continue;
|
||||
}
|
||||
|
||||
if (collRatio + coll_bias >= mangoGroup.maintCollRatio) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
description = ma.toPrettyString(mangoGroup, prices)
|
||||
console.log(`Liquidatable\n${description}\nbeingLiquidated: ${ma.beingLiquidated}`)
|
||||
notify(`Liquidatable\n${description}\nbeingLiquidated: ${ma.beingLiquidated}`)
|
||||
description = ma.toPrettyString(mangoGroup, prices);
|
||||
console.log(
|
||||
`Liquidatable\n${description}\nbeingLiquidated: ${ma.beingLiquidated}`,
|
||||
);
|
||||
notify(
|
||||
`Liquidatable\n${description}\nbeingLiquidated: ${ma.beingLiquidated}`,
|
||||
);
|
||||
|
||||
// find the market with the most value in OpenOrdersAccount
|
||||
let maxMarketIndex = -1
|
||||
let maxMarketVal = 0
|
||||
let maxMarketIndex = -1;
|
||||
let maxMarketVal = 0;
|
||||
for (let i = 0; i < NUM_MARKETS; i++) {
|
||||
const openOrdersAccount = ma.openOrdersAccounts[i]
|
||||
const openOrdersAccount = ma.openOrdersAccounts[i];
|
||||
if (openOrdersAccount === undefined) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
const marketVal = openOrdersAccount.quoteTokenTotal.toNumber() + openOrdersAccount.baseTokenTotal.toNumber() * prices[i]
|
||||
const marketVal =
|
||||
openOrdersAccount.quoteTokenTotal.toNumber() +
|
||||
openOrdersAccount.baseTokenTotal.toNumber() * prices[i];
|
||||
if (marketVal > maxMarketVal) {
|
||||
maxMarketIndex = i
|
||||
maxMarketVal = marketVal
|
||||
maxMarketIndex = i;
|
||||
maxMarketVal = marketVal;
|
||||
}
|
||||
}
|
||||
const transaction = new Transaction()
|
||||
|
||||
// Cancel orders on the market with the most value in open orders
|
||||
const transaction = new Transaction();
|
||||
if (maxMarketIndex !== -1) {
|
||||
// force cancel orders on this particular market
|
||||
const spotMarket = markets[maxMarketIndex]
|
||||
const [bids, asks] = await Promise.all([spotMarket.loadBids(connection), spotMarket.loadAsks(connection)])
|
||||
const openOrdersAccount = ma.openOrdersAccounts[maxMarketIndex]
|
||||
const spotMarket = markets[maxMarketIndex];
|
||||
const [bids, asks] = await Promise.all([
|
||||
spotMarket.loadBids(connection),
|
||||
spotMarket.loadAsks(connection),
|
||||
]);
|
||||
const openOrdersAccount = ma.openOrdersAccounts[maxMarketIndex];
|
||||
if (openOrdersAccount === undefined) {
|
||||
console.log('error state')
|
||||
continue
|
||||
console.log('error state');
|
||||
continue;
|
||||
}
|
||||
let numOrders = spotMarket.filterForOpenOrders(bids, asks, [openOrdersAccount]).length
|
||||
let numOrders = spotMarket.filterForOpenOrders(bids, asks, [
|
||||
openOrdersAccount,
|
||||
]).length;
|
||||
const dexSigner = await PublicKey.createProgramAddress(
|
||||
[
|
||||
spotMarket.publicKey.toBuffer(),
|
||||
spotMarket['_decoded'].vaultSignerNonce.toArrayLike(Buffer, 'le', 8)
|
||||
spotMarket['_decoded'].vaultSignerNonce.toArrayLike(
|
||||
Buffer,
|
||||
'le',
|
||||
8,
|
||||
),
|
||||
],
|
||||
spotMarket.programId
|
||||
)
|
||||
spotMarket.programId,
|
||||
);
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const instruction = makeForceCancelOrdersInstruction(
|
||||
|
@ -308,7 +398,7 @@ async function runPartialLiquidator() {
|
|||
payer.publicKey,
|
||||
ma.publicKey,
|
||||
mangoGroup.vaults[maxMarketIndex],
|
||||
mangoGroup.vaults[NUM_TOKENS-1],
|
||||
mangoGroup.vaults[NUM_TOKENS - 1],
|
||||
spotMarket.publicKey,
|
||||
spotMarket.bidsAddress,
|
||||
spotMarket.asksAddress,
|
||||
|
@ -320,35 +410,36 @@ async function runPartialLiquidator() {
|
|||
spotMarket.programId,
|
||||
ma.openOrders,
|
||||
mangoGroup.oracles,
|
||||
new BN(cancelLimit)
|
||||
)
|
||||
transaction.add(instruction)
|
||||
numOrders -= cancelLimit
|
||||
new BN(cancelLimit),
|
||||
);
|
||||
transaction.add(instruction);
|
||||
numOrders -= cancelLimit;
|
||||
if (numOrders <= 0) {
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the market with the highest borrows and lowest deposits
|
||||
const deposits = ma.getAssets(mangoGroup)
|
||||
const borrows = ma.getLiabs(mangoGroup)
|
||||
let minNet = 0
|
||||
let minNetIndex = -1
|
||||
let maxNet = 0
|
||||
let maxNetIndex = NUM_TOKENS-1
|
||||
// Find the market with the highest borrows and lowest assets
|
||||
const deposits = ma.getAssets(mangoGroup);
|
||||
const borrows = ma.getLiabs(mangoGroup);
|
||||
let minNet = 0;
|
||||
let minNetIndex = -1;
|
||||
let maxNet = 0;
|
||||
let maxNetIndex = NUM_TOKENS - 1;
|
||||
for (let i = 0; i < NUM_TOKENS; i++) {
|
||||
const netDeposit = (deposits[i] - borrows[i]) * prices[i]
|
||||
const netDeposit = (deposits[i] - borrows[i]) * prices[i];
|
||||
if (netDeposit < minNet) {
|
||||
minNet = netDeposit
|
||||
minNetIndex = i
|
||||
minNet = netDeposit;
|
||||
minNetIndex = i;
|
||||
} else if (netDeposit > maxNet) {
|
||||
maxNet = netDeposit
|
||||
maxNetIndex = i
|
||||
maxNet = netDeposit;
|
||||
maxNetIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
transaction.add(makePartialLiquidateInstruction(
|
||||
transaction.add(
|
||||
makePartialLiquidateInstruction(
|
||||
programId,
|
||||
mangoGroup.publicKey,
|
||||
payer.publicKey,
|
||||
|
@ -360,18 +451,33 @@ async function runPartialLiquidator() {
|
|||
mangoGroup.signerKey,
|
||||
ma.openOrders,
|
||||
mangoGroup.oracles,
|
||||
liqorTokenValues[minNetIndex]
|
||||
))
|
||||
liqorTokenValues[minNetIndex],
|
||||
),
|
||||
);
|
||||
|
||||
await client.sendTransaction(connection, transaction, payer, [])
|
||||
await sleep(2000)
|
||||
ma = await client.getMarginAccount(connection, ma.publicKey, dexProgramId)
|
||||
console.log(`Successful partial liquidation\n${ma.toPrettyString(mangoGroup, prices)}\nbeingLiquidated: ${ma.beingLiquidated}`)
|
||||
notify(`Successful partial liquidation\n${ma.toPrettyString(mangoGroup, prices)}\nbeingLiquidated: ${ma.beingLiquidated}`)
|
||||
break // This is so wallets get balanced
|
||||
await client.sendTransaction(connection, transaction, payer, []);
|
||||
await sleep(2000);
|
||||
ma = await client.getMarginAccount(
|
||||
connection,
|
||||
ma.publicKey,
|
||||
dexProgramId,
|
||||
);
|
||||
console.log(
|
||||
`Successful partial liquidation\n${ma.toPrettyString(
|
||||
mangoGroup,
|
||||
prices,
|
||||
)}\nbeingLiquidated: ${ma.beingLiquidated}`,
|
||||
);
|
||||
notify(
|
||||
`Successful partial liquidation\n${ma.toPrettyString(
|
||||
mangoGroup,
|
||||
prices,
|
||||
)}\nbeingLiquidated: ${ma.beingLiquidated}`,
|
||||
);
|
||||
break; // This is so wallets get balanced
|
||||
} catch (e) {
|
||||
if (!e.timeout) {
|
||||
throw e
|
||||
throw e;
|
||||
} else {
|
||||
notify(`unknown error: ${e}`);
|
||||
console.error(e);
|
||||
|
@ -379,28 +485,45 @@ async function runPartialLiquidator() {
|
|||
}
|
||||
}
|
||||
|
||||
const maxBorrAccPk = maxBorrAcc ? maxBorrAcc.publicKey.toBase58() : ""
|
||||
const maxBorrAccCr = maxBorrAcc ? maxBorrAcc.getCollateralRatio(mangoGroup, prices) : 0
|
||||
const minCollAccPk = minCollAcc ? minCollAcc.publicKey.toBase58() : ""
|
||||
const minCollBorrVal = minCollAcc ? minCollAcc.getLiabsVal(mangoGroup, prices) : 0
|
||||
const maxBorrAccPk = maxBorrAcc ? maxBorrAcc.publicKey.toBase58() : '';
|
||||
const maxBorrAccCr = maxBorrAcc
|
||||
? maxBorrAcc.getCollateralRatio(mangoGroup, prices)
|
||||
: 0;
|
||||
const minCollAccPk = minCollAcc ? minCollAcc.publicKey.toBase58() : '';
|
||||
const minCollBorrVal = minCollAcc
|
||||
? minCollAcc.getLiabsVal(mangoGroup, prices)
|
||||
: 0;
|
||||
|
||||
console.log(`Max Borrow Account: ${maxBorrAccPk} | Borrow Val: ${maxBorrVal} | CR: ${maxBorrAccCr}`)
|
||||
console.log(`Min Coll-Ratio Account: ${minCollAccPk} | Borrow Val: ${minCollBorrVal} | CR: ${minCollVal}`)
|
||||
if(liqorOpenOrdersKeys.length == NUM_MARKETS) {
|
||||
await balanceWallets(connection, mangoGroup, prices, markets, payer, tokenWallets, liqorTokenUi, liqorOpenOrdersKeys, targets)
|
||||
console.log(
|
||||
`Max Borrow Account: ${maxBorrAccPk} | Borrow Val: ${maxBorrVal} | CR: ${maxBorrAccCr}`,
|
||||
);
|
||||
console.log(
|
||||
`Min Coll-Ratio Account: ${minCollAccPk} | Borrow Val: ${minCollBorrVal} | CR: ${minCollVal}`,
|
||||
);
|
||||
if (liqorOpenOrdersKeys.length == NUM_MARKETS) {
|
||||
await balanceWallets(
|
||||
connection,
|
||||
mangoGroup,
|
||||
prices,
|
||||
markets,
|
||||
payer,
|
||||
tokenWallets,
|
||||
liqorTokenUi,
|
||||
liqorOpenOrdersKeys,
|
||||
targets,
|
||||
);
|
||||
} else {
|
||||
console.log('Could not balance wallets due to missing OpenOrders account')
|
||||
console.log(
|
||||
'Could not balance wallets due to missing OpenOrders account',
|
||||
);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
notify(`unknown error: ${e}`);
|
||||
console.error(e);
|
||||
} finally {
|
||||
await sleep(checkInterval)
|
||||
await sleep(checkInterval);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
runPartialLiquidator()
|
||||
|
||||
runPartialLiquidator();
|
||||
|
|
|
@ -38,7 +38,7 @@ async function genMarginAccounts() {
|
|||
const dexProgramId = new PublicKey(IDS[cluster].dex_program_id)
|
||||
|
||||
// Address of the MangoGroup
|
||||
const mangoGroupPk = new PublicKey(IDS[cluster].mango_groups.BTC_ETH_USDC.mango_group_pk)
|
||||
const mangoGroupPk = new PublicKey(IDS[cluster].mango_groups['BTC_ETH_USDC'].mango_group_pk)
|
||||
|
||||
const keyPairPath = '/home/dd/.config/solana/id.json'
|
||||
const payer = new Account(JSON.parse(fs.readFileSync(keyPairPath, 'utf-8')))
|
||||
|
|
133
yarn.lock
133
yarn.lock
|
@ -267,7 +267,7 @@
|
|||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.12.13"
|
||||
|
||||
"@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1":
|
||||
"@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
|
||||
integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==
|
||||
|
@ -310,15 +310,16 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@blockworks-foundation/mango-client@^0.1.19":
|
||||
version "0.1.19"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-0.1.19.tgz#f138712a9a9d3db7beded1e20be1e6ac0ebbdbb0"
|
||||
integrity sha512-w7oPJU84uYZOUAsZBQ7WoGWLV83sQElOwHkhna58+hxFFBVo9B5siuAwLf8vN+m9n/mf4p+t8PZR9tnpp7+63A==
|
||||
"@blockworks-foundation/mango-client@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-2.0.0.tgz#2040497f7137d97aebd8ef459a887ddcef4ef9e0"
|
||||
integrity sha512-lDRjARyXVuBbsohllnrAfkZd2xCY8iOA8xbimAGe9qmYeQIJDqN6SSwd5dcCkIFmihIQrObEym54MvaRAQaWVg==
|
||||
dependencies:
|
||||
"@project-serum/common" "^0.0.1-beta.3"
|
||||
"@project-serum/serum" "^0.13.20"
|
||||
"@project-serum/sol-wallet-adapter" "^0.1.4"
|
||||
"@solana/spl-token" "^0.0.13"
|
||||
"@solana/web3.js" "^0.90.0"
|
||||
"@solana/spl-token" "0.0.13"
|
||||
"@solana/web3.js" "^0.95.0"
|
||||
bn.js "^5.1.2"
|
||||
borsh "https://github.com/defactojob/borsh-js#field-mapper"
|
||||
buffer-layout "^1.2.0"
|
||||
|
@ -554,6 +555,15 @@
|
|||
"@nodelib/fs.scandir" "2.1.4"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@project-serum/common@^0.0.1-beta.3":
|
||||
version "0.0.1-beta.3"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/common/-/common-0.0.1-beta.3.tgz#53586eaff9d9fd7e8938b1e12080c935b8b6ad07"
|
||||
integrity sha512-gnQE/eUydTtto5okCgLWj1M97R9RRPJqnhKklikYI7jP/pnNhDmngSXC/dmfzED2GXSJEIKNIlxVw1k+E2Aw3w==
|
||||
dependencies:
|
||||
"@project-serum/serum" "^0.13.21"
|
||||
bn.js "^5.1.2"
|
||||
superstruct "0.8.3"
|
||||
|
||||
"@project-serum/serum@^0.13.20":
|
||||
version "0.13.34"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.34.tgz#c76477c27e14d975afa38b6c352b3abe92af6e52"
|
||||
|
@ -563,6 +573,15 @@
|
|||
bn.js "^5.1.2"
|
||||
buffer-layout "^1.2.0"
|
||||
|
||||
"@project-serum/serum@^0.13.21":
|
||||
version "0.13.38"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.38.tgz#ef50a0f50bd69fd7b51309fbb44ad995a1e6e210"
|
||||
integrity sha512-TOph1Hxoi5kOUg72tWbbNGviqBw29SrP4BH70gXtqUDiFTQJLrE2yfS5HC7p0JaU8p9WdrYGnxcFKCddZJ3ing==
|
||||
dependencies:
|
||||
"@solana/web3.js" "^0.90.0"
|
||||
bn.js "^5.1.2"
|
||||
buffer-layout "^1.2.0"
|
||||
|
||||
"@project-serum/sol-wallet-adapter@^0.1.4":
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/sol-wallet-adapter/-/sol-wallet-adapter-0.1.8.tgz#90c6c1da793d32ed4ba3c67c5702a5bc804ef197"
|
||||
|
@ -585,7 +604,7 @@
|
|||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
|
||||
"@solana/spl-token@0.0.13", "@solana/spl-token@^0.0.13":
|
||||
"@solana/spl-token@0.0.13":
|
||||
version "0.0.13"
|
||||
resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.0.13.tgz#5e0b235b1f8b34643280401dbfddeb34d13d1acd"
|
||||
integrity sha512-WT8M9V/hxURR5jLbhr3zgwVsgcY6m8UhHtK045w7o+jx8FJ9MKARkj387WBFU7mKiFq0k8jw/8YL7XmnIUuH8Q==
|
||||
|
@ -643,6 +662,25 @@
|
|||
tweetnacl "^1.0.0"
|
||||
ws "^7.0.0"
|
||||
|
||||
"@solana/web3.js@^0.95.0":
|
||||
version "0.95.0"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-0.95.0.tgz#c028c800bf078bec90945240370df51026f8bdab"
|
||||
integrity sha512-jdjAESYTChAYI1aZwH1RggcoT22BxdB9tDKBOt2Iw9SSpZ1Bb81ydrP+7eksE1AwQj6lpzrgtVt2d3sFosqupQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
bn.js "^5.0.0"
|
||||
bs58 "^4.0.1"
|
||||
buffer "6.0.1"
|
||||
buffer-layout "^1.2.0"
|
||||
crypto-hash "^1.2.2"
|
||||
jayson "^3.4.4"
|
||||
js-sha3 "^0.8.0"
|
||||
node-fetch "^2.6.1"
|
||||
rpc-websockets "^7.4.2"
|
||||
secp256k1 "^4.0.2"
|
||||
superstruct "^0.14.2"
|
||||
tweetnacl "^1.0.0"
|
||||
|
||||
"@tsconfig/node14@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.0.tgz#5bd046e508b1ee90bc091766758838741fdefd6e"
|
||||
|
@ -696,9 +734,9 @@
|
|||
"@types/node" "*"
|
||||
|
||||
"@types/express-serve-static-core@^4.17.9":
|
||||
version "4.17.19"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz#00acfc1632e729acac4f1530e9e16f6dd1508a1d"
|
||||
integrity sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz#a427278e106bca77b83ad85221eae709a3414d42"
|
||||
integrity sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
"@types/qs" "*"
|
||||
|
@ -744,19 +782,19 @@
|
|||
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
|
||||
|
||||
"@types/lodash@^4.14.159":
|
||||
version "4.14.169"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.169.tgz#83c217688f07a4d9ef8f28a3ebd1d318f6ff4cbb"
|
||||
integrity sha512-DvmZHoHTFJ8zhVYwCLWbQ7uAbYQEk52Ev2/ZiQ7Y7gQGeV9pjBqjnQpECMHfKS1rCYAhMI7LHVxwyZLZinJgdw==
|
||||
version "4.14.170"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6"
|
||||
integrity sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==
|
||||
|
||||
"@types/node@*":
|
||||
version "15.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.3.tgz#ee09fcaac513576474c327da5818d421b98db88a"
|
||||
integrity sha512-/WbxFeBU+0F79z9RdEOXH4CsDga+ibi5M8uEYr91u3CkT/pdWcV8MCook+4wDPnZBexRdwWS+PiVZ2xJviAzcQ==
|
||||
version "15.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.1.tgz#9b60797dee1895383a725f828a869c86c6caa5c2"
|
||||
integrity sha512-zyxJM8I1c9q5sRMtVF+zdd13Jt6RU4r4qfhTd7lQubyThvLfx6yYekWSQjGCGV2Tkecgxnlpl/DNlb6Hg+dmEw==
|
||||
|
||||
"@types/node@^12.12.54":
|
||||
version "12.20.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.13.tgz#e743bae112bd779ac9650f907197dd2caa7f0364"
|
||||
integrity sha512-1x8W5OpxPq+T85OUsHRP6BqXeosKmeXRtjoF39STcdf/UWLqUsoehstZKOi0CunhVqHG17AyZgpj20eRVooK6A==
|
||||
version "12.20.14"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.14.tgz#9caf7eea0df08b406829889cc015256a6d81ab10"
|
||||
integrity sha512-iFJOS5Q470FF+r4Ol2pSley7/wCNVqf+jgjhtxLLaJcDs+To2iCxlXIkJXrGLD9w9G/oJ9ibySu7z92DCwr7Pg==
|
||||
|
||||
"@types/normalize-package-data@^2.4.0":
|
||||
version "2.4.0"
|
||||
|
@ -1201,9 +1239,9 @@ boolbase@~1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||
|
||||
"borsh@https://github.com/defactojob/borsh-js#field-mapper":
|
||||
"borsh@git+https://github.com/defactojob/borsh-js.git#field-mapper":
|
||||
version "0.3.1"
|
||||
resolved "https://github.com/defactojob/borsh-js#33a0d24af281112c0a48efb3fa503f3212443de9"
|
||||
resolved "git+https://github.com/defactojob/borsh-js.git#33a0d24af281112c0a48efb3fa503f3212443de9"
|
||||
dependencies:
|
||||
"@types/bn.js" "^4.11.5"
|
||||
bn.js "^5.0.0"
|
||||
|
@ -1293,6 +1331,14 @@ buffer-layout@^1.2.0:
|
|||
resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.1.tgz#17b6db7abea303cab35eebd77919b89de026297d"
|
||||
integrity sha512-RUTGEYG1vX0Zp1dStQFl8yeU/LEBPXVtHwzzDbPWkE5zq+Prt9fkFLKNiwmaeHg6BBiRMcQAgj4cynazO6eekw==
|
||||
|
||||
buffer@6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.1.tgz#3cbea8c1463e5a0779e30b66d4c88c6ffa182ac2"
|
||||
integrity sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.2.1"
|
||||
|
||||
buffer@^5.4.3:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||
|
@ -2923,10 +2969,10 @@ istanbul-reports@^3.0.2:
|
|||
html-escaper "^2.0.0"
|
||||
istanbul-lib-report "^3.0.0"
|
||||
|
||||
jayson@^3.0.1:
|
||||
version "3.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.6.2.tgz#e551e25abf2efe333051a6ed88b10f08c5288f50"
|
||||
integrity sha512-hbl+x2xH6FT7nckw+Pq3lKOIJaMBKOgNJEVfvloDBWB8iSfzn/1U2igj1A5rplqNMFN/OnnaTNw8qPKVmoq83Q==
|
||||
jayson@^3.0.1, jayson@^3.4.4:
|
||||
version "3.6.3"
|
||||
resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.6.3.tgz#b0bb8d2e37e34e39e68044ab925fd92f103f1bd9"
|
||||
integrity sha512-H/JuWKaJwU8FbwofPHROvtGoMF6R3DB0GGPpYyIaRzXU50Ser/4likFVfo/bpTGe0csB7n/+uybxJpBvX40VOQ==
|
||||
dependencies:
|
||||
"@types/connect" "^3.4.33"
|
||||
"@types/express-serve-static-core" "^4.17.9"
|
||||
|
@ -3313,6 +3359,11 @@ jest@^26.4.0:
|
|||
import-local "^3.0.2"
|
||||
jest-cli "^26.6.3"
|
||||
|
||||
js-sha3@^0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
|
||||
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
|
@ -3784,7 +3835,7 @@ node-addon-api@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32"
|
||||
integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==
|
||||
|
||||
node-fetch@^2.2.0:
|
||||
node-fetch@^2.2.0, node-fetch@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
@ -4361,16 +4412,16 @@ rimraf@^3.0.0, rimraf@^3.0.2:
|
|||
glob "^7.1.3"
|
||||
|
||||
rpc-websockets@^7.4.2:
|
||||
version "7.4.11"
|
||||
resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.4.11.tgz#ac8105116f1a888fbc3dd6b8394ea135ee59499c"
|
||||
integrity sha512-/6yKCkRrEEb+TlJb6Q/pNBD4WdO/tFxE22rQYBl1YyIgz3SpzQDQ/0qAMWWksjFkDayiq3xVxmkP8e/tL422ZA==
|
||||
version "7.4.12"
|
||||
resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.4.12.tgz#6a187a772cbe9ee48ed04e001b6d9c29b8e69bae"
|
||||
integrity sha512-WxZRM4443SiYbJhsLwVJc6P/VAQJIkeDS89CQAuHqyMt/GX8GEplWZezcLw6MMGemzA6Kp32kz7CbQppMTLQxA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.11.2"
|
||||
assert-args "^1.2.1"
|
||||
circular-json "^0.5.9"
|
||||
eventemitter3 "^4.0.7"
|
||||
uuid "^8.3.0"
|
||||
ws "^7.3.1"
|
||||
ws "^7.4.5"
|
||||
optionalDependencies:
|
||||
bufferutil "^4.0.1"
|
||||
utf-8-validate "^5.0.2"
|
||||
|
@ -4753,6 +4804,19 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
|
|||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
superstruct@0.8.3:
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.8.3.tgz#fb4d8901aca3bf9f79afab1bbab7a7f335cc4ef2"
|
||||
integrity sha512-LbtbFpktW1FcwxVIJlxdk7bCyBq/GzOx2FSFLRLTUhWIA1gHkYPIl3aXRG5mBdGZtnPNT6t+4eEcLDCMOuBHww==
|
||||
dependencies:
|
||||
kind-of "^6.0.2"
|
||||
tiny-invariant "^1.0.6"
|
||||
|
||||
superstruct@^0.14.2:
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b"
|
||||
integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==
|
||||
|
||||
superstruct@^0.8.3:
|
||||
version "0.8.4"
|
||||
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.8.4.tgz#478a19649f6b02c6319c02044db6a1f5863c391f"
|
||||
|
@ -5249,7 +5313,12 @@ write-file-atomic@^3.0.0:
|
|||
signal-exit "^3.0.2"
|
||||
typedarray-to-buffer "^3.1.5"
|
||||
|
||||
ws@^7.0.0, ws@^7.3.1, ws@^7.4.4:
|
||||
ws@^7.0.0, ws@^7.4.5:
|
||||
version "7.4.6"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
|
||||
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
|
||||
|
||||
ws@^7.4.4:
|
||||
version "7.4.5"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1"
|
||||
integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==
|
||||
|
|
Loading…
Reference in New Issue