Adding configuration files in same repo

This commit is contained in:
Godmode Galactus 2023-06-26 12:37:08 +02:00
parent c2c52c25f9
commit 8972afb236
No known key found for this signature in database
GPG Key ID: A04142C71ABB0DEA
37 changed files with 3711 additions and 28 deletions

View File

@ -1,41 +1,48 @@
# Mango Simulation - test solana cluster by simulating mango markets
This project is use to stress a solana cluster like devnet, testnet or local solana cluster by simulating mango markets. This code requires ids.json which describe mango group for a cluster and accounts.json file which has preconfigured user accounts in mango.
This project is use to stress a solana cluster like devnet, testnet or local
solana cluster by simulating mango markets. This code requires ids.json which
describe mango group for a cluster and accounts.json file which has
preconfigured user accounts in mango.
To create a new configuration for mango for your cluster please check the following project:
<https://github.com/godmodegalactus/configure_mango>
The code then will create transaction request (q) requests per seconds for (n)
seconds per perp market perp user. Each transaction request will contains remove
following instruction CancelAllPerpOrders and two PlacePerpOrder (one for bid
and another for ask).
The code then will create transaction request (q) requests per seconds for (n) seconds per perp market perp user. Each transaction request will contains remove following instruction CancelAllPerpOrders and two PlacePerpOrder (one for bid and another for ask).
For the best results to avoid limits by quic it is better to fill the argument "identity" of a valid staked validator for the cluster you are testing with.
For the best results to avoid limits by quic it is better to fill the argument
"identity" of a valid staked validator for the cluster you are testing with.
## Build
Install configure-mango
```sh
git clone https://github.com/godmodegalactus/configure_mango.git
cd configure_mango
yarn install
sh scripts/configure_local.sh
# open a new terminal as the previous one will continue running a solana validator
# this command will hang for around a minute, just wait for it to finish
NB_USERS=50 yarn ts-node index.ts
```
Install mango-simulation
```sh
git clone https://github.com/blockworks-foundation/mango-simulation.git
cd mango-simulation
cargo build
cargo build --release
```
# copy over files from configure_mango while you wait for the build to finish
mkdir -p localnet
cp ../configure_mango/ids.json localnet
cp ../configure_mango/accounts.json localnet
cp ../configure_mango/authority.json localnet
cp ../configure_mango/config/validator-identity.json localnet
## Configure cluster
Install all nodejs dependencies
```sh
yarn install
```
To start a local cluster. This will start a validator with all the data in
config directory.
```sh
sh configure_cluster/scripts/configure_local.sh
```
To create a configuration. This will create two files `ids.json` which contain
cluster information and `account.json` which contains user information.
```sh
# open a new terminal as the previous one will continue running a solana validator
# this command will hang for around a minute, just wait for it to finish
NB_USERS=50 yarn ts-node configure_cluster/configure_mango_v3.ts
```
## Run
@ -43,7 +50,7 @@ cp ../configure_mango/config/validator-identity.json localnet
To run against your local validator:
```sh
cargo run --bin mango-simulation -- -u http://127.0.0.1:8899 --identity localnet/validator-identity.json --keeper-authority localnet/authority.json --accounts localnet/accounts.json --mango localnet/ids.json --mango-cluster localnet --duration 10 -q 2 --transaction-save-file tlog.csv --block-data-save-file blog.csv
cargo run --bin mango-simulation -- -u http://127.0.0.1:8899 --identity config/validator-identity.json --keeper-authority authority.json --accounts accounts.json --mango ids.json --mango-cluster localnet --duration 10 -q 2 --transaction-save-file tlog.csv --block-data-save-file blog.csv
```
You can also run the simulation against testnet, but you will need to run configure_mango

View File

@ -0,0 +1,46 @@
# Configure Mango - A faster and easier way to configure Mango Markets on a cluster
This code can be used to configure mango group, tokens, spot market, perp markets and oracles on local solana validator and create 50 mango user accounts.
It will create authority.json which is keypair of authority file, accounts.json which contains all user data and ids.json which contains info about mango group.
The project also contains necessary binary files. You can always update the binary files by compiling from source and replacing the binaries locally. You have to apply bin/mango.patch to your mango repository to use it to create a local cluster.
From the root directory of this project do:
## How to use
To install all the dependencies :
```sh
yarn install
```
To start a solana test validator
```sh
sh scripts/start_test_validator.sh
```
Or
To start a local solana validator
```sh
sh scripts/configure_local.sh
```
To configure mango
```sh
yarn ts-node index.ts
```
To run mango keeper (deprecated)
```sh
yarn ts-node keeper.ts
```
To create 50 users and store in the file accounts.json
```sh
ts-node create-users.ts 50 accounts.json
```
To refund users in account file with some sols
```sh
ts-node refund_users.ts accounts.json
```
Pyth oracle is a mock it is a program which will just rewrite an account with a binary data.

View File

@ -0,0 +1,13 @@
{
"pubkey": "MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac",
"account": {
"lamports": 2951730457,
"data": [
"AAAAAAtrwmw9oIkyCiWWCwghgfcii0hdoI/YwMv3ysQDw2Rnv7V4NXnDEQAGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"base64"
],
"owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"executable": false,
"rentEpoch": 354
}
}

View File

@ -0,0 +1 @@
[58,245,175,8,29,172,112,246,82,58,159,33,67,70,96,142,201,49,206,2,141,176,61,126,194,219,211,186,64,157,154,80,182,57,102,252,249,87,92,208,23,30,135,230,135,224,66,83,33,200,183,24,186,60,88,107,147,124,205,29,21,90,177,190]

BIN
configure_cluster/bin/mango.so Executable file

Binary file not shown.

View File

@ -0,0 +1 @@
[7,63,139,28,171,130,217,59,234,218,62,230,18,110,107,250,248,157,38,140,144,249,132,3,227,171,69,83,143,133,221,200,79,71,58,223,237,15,99,138,254,212,98,142,85,62,107,155,14,192,247,103,98,211,182,228,4,150,79,174,78,235,4,240]

Binary file not shown.

View File

@ -0,0 +1,133 @@
diff --git a/program/src/ids.rs b/program/src/ids.rs
index 7b4caae..e69de29 100644
--- a/program/src/ids.rs
+++ b/program/src/ids.rs
@@ -1,28 +0,0 @@
-pub mod srm_token {
- use solana_program::declare_id;
- #[cfg(feature = "devnet")]
- declare_id!("AvtB6w9xboLwA145E221vhof5TddhqsChYcx7Fy3xVMH");
- #[cfg(not(feature = "devnet"))]
- declare_id!("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt");
-}
-
-pub mod msrm_token {
- use solana_program::declare_id;
- #[cfg(feature = "devnet")]
- declare_id!("8DJBo4bF4mHNxobjdax3BL9RMh5o71Jf8UiKsf5C5eVH");
- #[cfg(not(feature = "devnet"))]
- declare_id!("MSRMcoVyrFxnSgo5uXwone5SKcGhT1KEJMFEkMEWf9L");
-}
-
-pub mod mngo_token {
- use solana_program::declare_id;
- #[cfg(feature = "devnet")]
- declare_id!("Bb9bsTQa1bGEtQ5KagGkvSHyuLqDWumFUcRqFusFNJWC");
- #[cfg(not(feature = "devnet"))]
- declare_id!("MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac");
-}
-
-pub mod luna_pyth_oracle {
- use solana_program::declare_id;
- declare_id!("5bmWuR1dgP4avtGYMNKLuxumZTVKGgoN2BCMXWDNL9nY");
-}
diff --git a/program/src/matching.rs b/program/src/matching.rs
index 16088cf..35b4862 100644
--- a/program/src/matching.rs
+++ b/program/src/matching.rs
@@ -19,7 +19,6 @@ use mango_logs::{mango_emit_stack, ReferralFeeAccrualLog};
use mango_macro::{Loadable, Pod};
use crate::error::{check_assert, MangoError, MangoErrorCode, MangoResult, SourceFileId};
-use crate::ids::mngo_token;
use crate::queue::{EventQueue, FillEvent, OutEvent};
use crate::state::{
DataType, MangoAccount, MangoCache, MangoGroup, MetaData, PerpMarket, PerpMarketCache,
@@ -2063,10 +2062,7 @@ fn determine_ref_vars<'a>(
referrer_mango_account_ai: Option<&'a AccountInfo>,
now_ts: u64,
) -> MangoResult<(I80F48, Option<RefMut<'a, MangoAccount>>)> {
- let mngo_index = match mango_group.find_token_index(&mngo_token::id()) {
- None => return Ok((ZERO_I80F48, None)),
- Some(i) => i,
- };
+ let mngo_index = 0;
let mngo_cache = &mango_cache.root_bank_cache[mngo_index];
let tier_2_enabled = mango_group.ref_surcharge_centibps_tier_2 != 0
diff --git a/program/src/processor.rs b/program/src/processor.rs
index 3181140..7bc9789 100644
--- a/program/src/processor.rs
+++ b/program/src/processor.rs
@@ -35,7 +35,6 @@ use mango_logs::{
};
use crate::error::{check_assert, MangoError, MangoErrorCode, MangoResult, SourceFileId};
-use crate::ids::{luna_pyth_oracle, msrm_token, srm_token};
use crate::instruction::MangoInstruction;
use crate::matching::{Book, BookSide, ExpiryType, OrderType, Side};
use crate::oracle::{determine_oracle_type, OracleType, StubOracle, STUB_MAGIC};
@@ -129,7 +128,6 @@ impl Processor {
check!(msrm_vault.delegate.is_none(), MangoErrorCode::InvalidVault)?;
check!(msrm_vault.close_authority.is_none(), MangoErrorCode::InvalidVault)?;
check_eq!(msrm_vault.owner, mango_group.signer_key, MangoErrorCode::InvalidVault)?;
- check_eq!(&msrm_vault.mint, &msrm_token::ID, MangoErrorCode::InvalidVault)?;
check_eq!(msrm_vault_ai.owner, &spl_token::ID, MangoErrorCode::InvalidVault)?;
mango_group.msrm_vault = *msrm_vault_ai.key;
}
@@ -508,14 +506,6 @@ impl Processor {
mango_group.tokens[QUOTE_INDEX].mint.to_aligned_bytes(),
MangoErrorCode::Default
)?;
-
- // TODO - what if quote currency is mngo, srm or msrm
- // if mint is SRM set srm_vault
-
- if mint_ai.key == &srm_token::ID {
- check!(mango_group.srm_vault == Pubkey::default(), MangoErrorCode::Default)?;
- mango_group.srm_vault = *vault_ai.key;
- }
Ok(())
}
@@ -8155,11 +8145,6 @@ pub fn read_oracle(
conf.to_num::<f64>()
);
- // For luna, to prevent market from getting stuck, just continue using last known price in cache
- if oracle_ai.key == &luna_pyth_oracle::ID {
- return Ok(last_known_price_in_cache);
- }
-
return Err(throw_err!(MangoErrorCode::InvalidOraclePrice));
}
diff --git a/program/src/state.rs b/program/src/state.rs
index 2b79ab7..c06b046 100644
--- a/program/src/state.rs
+++ b/program/src/state.rs
@@ -24,7 +24,6 @@ use mango_common::Loadable;
use mango_macro::{Loadable, Pod, TriviallyTransmutable};
use crate::error::{check_assert, MangoError, MangoErrorCode, MangoResult, SourceFileId};
-use crate::ids::mngo_token;
use crate::matching::{Book, LeafNode, OrderType, Side};
use crate::queue::{EventQueue, EventType, FillEvent};
use crate::utils::{
@@ -1197,7 +1196,7 @@ impl HealthCache {
let taker_quote_native =
I80F48::from_num(info.quote_lot_size.checked_mul(taker_quote.abs()).unwrap());
let mut market_fees = info.taker_fee * taker_quote_native;
- if let Some(mngo_index) = mango_group.find_token_index(&mngo_token::id()) {
+ if let Some(mngo_index) = Some(0) {
let mngo_cache = &mango_cache.root_bank_cache[mngo_index];
let mngo_deposits = mango_account.get_native_deposit(mngo_cache, mngo_index)?;
let ref_mngo_req = I80F48::from_num(mango_group.ref_mngo_required);
@@ -2235,7 +2234,6 @@ impl PerpMarket {
check!(vault.owner == mango_group.signer_key, MangoErrorCode::InvalidOwner)?;
check!(vault.delegate.is_none(), MangoErrorCode::InvalidVault)?;
check!(vault.close_authority.is_none(), MangoErrorCode::InvalidVault)?;
- check!(vault.mint == mngo_token::ID, MangoErrorCode::InvalidVault)?;
check!(mngo_vault_ai.owner == &spl_token::ID, MangoErrorCode::InvalidOwner)?;
state.mngo_vault = *mngo_vault_ai.key;

View File

@ -0,0 +1 @@
[247,81,41,82,59,37,100,159,77,210,53,136,212,197,48,156,35,235,118,52,60,249,192,135,247,217,123,52,53,60,223,78,205,15,251,191,21,149,104,160,69,71,75,237,133,4,137,222,132,215,169,167,197,234,74,145,175,129,228,68,34,166,170,187]

Binary file not shown.

View File

@ -0,0 +1 @@
[127,193,75,90,107,91,27,214,61,161,202,125,83,195,77,34,68,138,67,157,106,248,191,233,146,76,25,54,166,46,176,8,37,48,54,71,239,206,113,208,127,201,25,167,143,65,171,203,221,82,164,96,94,240,221,57,154,153,99,238,4,215,240,109]

Binary file not shown.

View File

@ -0,0 +1 @@
--bpf-program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA BPFLoader2111111111111111111111111111111111 spl_token-3.5.0.so --bpf-program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo BPFLoader1111111111111111111111111111111111 spl_memo-1.0.0.so --bpf-program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr BPFLoader2111111111111111111111111111111111 spl_memo-3.0.0.so --bpf-program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL BPFLoader2111111111111111111111111111111111 spl_associated-token-account-1.1.1.so --bpf-program Feat1YXHhH6t1juaWF74WLcfv4XoNocjXA6sPWHNgAse BPFLoader2111111111111111111111111111111111 spl_feature-proposal-1.0.0.so

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
configure_cluster/bin/spl_noop.so Executable file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,110 @@
import { MangoUtils } from "./utils/mango_utils";
import { Connection, Keypair, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
import { sleep, Cluster } from "@blockworks-foundation/mango-client";
import { getProgramMap } from "./utils/config"
import * as fs from 'fs';
export async function main() {
// cluster should be in 'devnet' | 'mainnet' | 'localnet' | 'testnet'
const cluster = (process.env.CLUSTER || 'localnet') as Cluster;
const programNameToId = getProgramMap(cluster);
const endpoint = process.env.ENDPOINT_URL || 'http://0.0.0.0:8899';
const connection = new Connection(endpoint, 'confirmed');
const nbUsers = Number(process.env.NB_USERS || "1");
console.log('Connecting to cluster ' + endpoint)
if (!fs.existsSync('authority.json')) {
//create an authority.json
const new_authority = Keypair.generate();
fs.writeFileSync('authority.json', '[' + new_authority.secretKey.toString() + ']');
}
const authority = Keypair.fromSecretKey(
Uint8Array.from(
JSON.parse(
process.env.KEYPAIR ||
fs.readFileSync('authority.json', 'utf-8'),
),
),
);
const do_log_str = process.env.LOG || "false";
const do_log = do_log_str === "true";
console.log('Configuring authority')
const balance = await connection.getBalance(authority.publicKey)
console.log('Authority balance is : ' + balance + ' lamports');
if (balance < (nbUsers + 100) * LAMPORTS_PER_SOL) {
console.log('Balance too low airdropping ' + (nbUsers + 100) + ' SOLs')
const signature = await connection.requestAirdrop(authority.publicKey, (nbUsers + 100) * LAMPORTS_PER_SOL);
await connection.confirmTransaction(signature, 'confirmed');
}
const beginSlot = await connection.getSlot();
console.log('Creating Mango Cookie')
const mangoProgramId = new PublicKey(programNameToId['mango'])
const dexProgramId = new PublicKey(programNameToId['serum_dex']);
const pythProgramId = new PublicKey(programNameToId['pyth_mock']);
let logId = 0
if (do_log) {
logId = connection.onLogs(mangoProgramId, (log, ctx) => {
if (log.err != null) {
console.log("mango error : ", log.err)
}
else {
for (const l of log.logs) {
console.log("mango log : " + l)
}
}
});
}
try {
const mangoUtils = new MangoUtils(connection, authority, mangoProgramId, dexProgramId, pythProgramId);
const cookie = await mangoUtils.createMangoCookie(['MNGO', 'SOL', 'BTC', 'ETH', 'AVAX', 'SRM', 'FTT', 'RAY', 'MNGO', 'BNB', 'GMT', 'ADA'])
console.log('Creating ids.json');
const json = mangoUtils.convertCookie2Json(cookie, cluster)
fs.writeFileSync('ids.json', JSON.stringify(json, null, 2));
console.log('Mango cookie created successfully')
console.log('Creating ' + nbUsers + ' Users');
const users = (await mangoUtils.createAndMintUsers(cookie, nbUsers, authority)).map(x => {
const info = {};
info['publicKey'] = x.kp.publicKey.toBase58();
info['secretKey'] = Array.from(x.kp.secretKey);
info['mangoAccountPks'] = [x.mangoAddress.toBase58()];
return info;
})
console.log('created ' + nbUsers + ' Users');
fs.writeFileSync('accounts.json', JSON.stringify(users));
} finally {
if (logId) {
// to log mango logs
await sleep(5000)
const endSlot = await connection.getSlot();
const blockSlots = await connection.getBlocks(beginSlot, endSlot);
console.log("\n\n===============================================")
for (let blockSlot of blockSlots) {
const block = await connection.getBlock(blockSlot);
for (let i = 0; i < block.transactions.length; ++i) {
if (block.transactions[i].meta.logMessages) {
for (const msg of block.transactions[i].meta.logMessages) {
console.log('solana_message : ' + msg);
}
}
}
}
connection.removeOnLogsListener(logId);
}
}
}
main().then(x => {
console.log('finished sucessfully')
}).catch(e => {
console.log('caught an error : ' + e)
})

View File

@ -0,0 +1,61 @@
import { Cluster, Config } from "@blockworks-foundation/mango-client";
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
import { readFileSync, writeFileSync } from 'fs';
import { MangoUtils } from "./utils/mango_utils";
import { getProgramMap } from "./utils/config"
if (process.argv.length < 4) {
console.log("please enter arguments as follows\n `ts-node create_users number_of_users output_file cluster_config_file(o)`");
}
export async function main(
file: any,
nbUsers: number,
authority: Keypair,
outFile: string,
) {
const cluster = (process.env.CLUSTER || 'localnet') as Cluster;
const programNameToId = getProgramMap(cluster);
const endpoint = process.env.ENDPOINT_URL || 'http://0.0.0.0:8899';
const connection = new Connection(endpoint, 'confirmed');
console.log('Connecting to cluster ' + endpoint)
const mangoProgramId = new PublicKey(programNameToId['mango'])
const dexProgramId = new PublicKey(programNameToId['serum_dex']);
const pythProgramId = new PublicKey(programNameToId['pyth_mock']);
const json = JSON.parse(file);
try {
const mangoUtils = new MangoUtils(connection, authority, mangoProgramId, dexProgramId, pythProgramId);
let mangoCookie = await mangoUtils.json2Cookie(json, cluster);
const users = (await mangoUtils.createAndMintUsers(mangoCookie, nbUsers, authority)).map(x => {
const info = {};
info['publicKey'] = x.kp.publicKey.toBase58();
info['secretKey'] = Array.from(x.kp.secretKey);
info['mangoAccountPks'] = [x.mangoAddress.toBase58()];
return info;
})
console.log('created ' + nbUsers + ' Users');
writeFileSync(outFile, JSON.stringify(users));
}
catch (e) {
console.log('failed to create error ' + e);
}
}
const configFile = process.argv.length >= 5 ? process.argv[4] : 'ids.json';
const file = readFileSync(configFile, 'utf-8');
const authority = Keypair.fromSecretKey(
Uint8Array.from(
JSON.parse(
process.env.KEYPAIR ||
readFileSync('authority.json', 'utf-8'),
),
),
);
let nbUsers = +process.argv[2];
let outFile = process.argv[3];
main(file, nbUsers, authority, outFile).then(x=> {
console.log("finished");
})

379
configure_cluster/keeper.ts Normal file
View File

@ -0,0 +1,379 @@
/**
This will probably move to its own repo at some point but easier to keep it here for now
*/
import * as os from 'os';
import * as fs from 'fs';
import {
Keypair,
Commitment,
Connection,
PublicKey,
Transaction,
ComputeBudgetProgram
} from '@solana/web3.js';
import {
MangoClient,
makeCachePerpMarketsInstruction,
makeCachePricesInstruction,
makeCacheRootBankInstruction,
makeUpdateFundingInstruction,
makeUpdateRootBankInstruction,
getMultipleAccounts,
zeroKey,
Cluster,
Config,
PerpEventQueueLayout,
MangoGroup, PerpMarket, promiseUndef,
PerpEventQueue,
sleep,
makeConsumeEventsInstruction
} from "@blockworks-foundation/mango-client";
import BN from 'bn.js';
let lastRootBankCacheUpdate = 0;
const groupName = process.env.GROUP || 'localnet';
const updateCacheInterval = parseInt(
process.env.UPDATE_CACHE_INTERVAL || '2000',
);
const updateRootBankCacheInterval = parseInt(
process.env.UPDATE_ROOT_BANK_CACHE_INTERVAL || '3000',
);
const processKeeperInterval = parseInt(
process.env.PROCESS_KEEPER_INTERVAL || '3000',
);
const consumeEventsInterval = parseInt(
process.env.CONSUME_EVENTS_INTERVAL || '100',
);
const maxUniqueAccounts = parseInt(process.env.MAX_UNIQUE_ACCOUNTS || '24');
const consumeEventsLimit = new BN(process.env.CONSUME_EVENTS_LIMIT || '20');
const consumeEvents = process.env.CONSUME_EVENTS ? process.env.CONSUME_EVENTS === 'true' : true;
const skipPreflight = process.env.SKIP_PREFLIGHT ? process.env.SKIP_PREFLIGHT === 'true' : true;
const cluster = (process.env.CLUSTER || 'localnet') as Cluster;
import configFile from './ids.json';
const config = new Config(configFile);
const groupIds = config.getGroup(cluster, groupName);
if (!groupIds) {
throw new Error(`Group ${groupName} not found`);
}
const mangoProgramId = groupIds.mangoProgramId;
const mangoGroupKey = groupIds.publicKey;
const payer = Keypair.fromSecretKey(
Uint8Array.from(
JSON.parse(
process.env.KEYPAIR ||
fs.readFileSync('authority.json', 'utf-8'),
),
),
);
const connection = new Connection(
process.env.ENDPOINT_URL || config.cluster_urls[cluster],
'processed' as Commitment,
);
const client = new MangoClient(connection, mangoProgramId, {
timeout: 10000,
prioritizationFee: 10000, // number of micro lamports
});
async function main() {
if (!groupIds) {
throw new Error(`Group ${groupName} not found`);
}
const mangoGroup = await client.getMangoGroup(mangoGroupKey);
const perpMarkets = await Promise.all(
groupIds.perpMarkets.map((m) => {
return mangoGroup.loadPerpMarket(
connection,
m.marketIndex,
m.baseDecimals,
m.quoteDecimals,
);
}),
);
const do_log_str = process.env.LOG || "false";
const do_log = do_log_str === "true";
let logId = 0
if (do_log) {
console.log("LOGGING ON");
logId = connection.onLogs(mangoProgramId, (log, ctx) => {
if (log.err != null) {
console.log("mango error : ", log.err)
}
else {
for (const l of log.logs) {
console.log("mango log : " + l)
}
}
});
}
const beginSlot = await connection.getSlot();
try {
processUpdateCache(mangoGroup);
processKeeperTransactions(mangoGroup, perpMarkets);
if (consumeEvents) {
processConsumeEvents(mangoGroup, perpMarkets);
}
} finally {
if (logId) {
// to log mango logs
await sleep(5000)
const endSlot = await connection.getSlot();
const blockSlots = await connection.getBlocks(beginSlot, endSlot);
console.log("\n\n===============================================")
for (let blockSlot of blockSlots) {
const block = await connection.getBlock(blockSlot);
for (let i = 0; i < block.transactions.length; ++i) {
if (block.transactions[i].meta.logMessages) {
for (const msg of block.transactions[i].meta.logMessages) {
console.log('solana_message : ' + msg);
}
}
}
}
connection.removeOnLogsListener(logId);
}
}
}
console.time('processUpdateCache');
console.time('processKeeperTransactions');
console.time('processConsumeEvents');
async function processUpdateCache(mangoGroup: MangoGroup) {
try {
const batchSize = 8;
const promises: Promise<string>[] = [];
const rootBanks = mangoGroup.tokens
.map((t) => t.rootBank)
.filter((t) => !t.equals(zeroKey));
const oracles = mangoGroup.oracles.filter((o) => !o.equals(zeroKey));
const perpMarkets = mangoGroup.perpMarkets
.filter((pm) => !pm.isEmpty())
.map((pm) => pm.perpMarket);
const nowTs = Date.now();
let shouldUpdateRootBankCache = false;
if (nowTs - lastRootBankCacheUpdate > updateRootBankCacheInterval) {
shouldUpdateRootBankCache = true;
lastRootBankCacheUpdate = nowTs;
}
for (let i = 0; i < Math.ceil(rootBanks.length / batchSize); i++) {
const startIndex = i * batchSize;
const endIndex = Math.min(i * batchSize + batchSize, rootBanks.length);
const cacheTransaction = new Transaction();
cacheTransaction.add(
makeCachePricesInstruction(
mangoProgramId,
mangoGroup.publicKey,
mangoGroup.mangoCache,
oracles.slice(startIndex, endIndex),
),
);
if (shouldUpdateRootBankCache) {
cacheTransaction.add(
makeCacheRootBankInstruction(
mangoProgramId,
mangoGroup.publicKey,
mangoGroup.mangoCache,
rootBanks.slice(startIndex, endIndex),
),
);
}
cacheTransaction.add(
makeCachePerpMarketsInstruction(
mangoProgramId,
mangoGroup.publicKey,
mangoGroup.mangoCache,
perpMarkets.slice(startIndex, endIndex),
),
);
if (cacheTransaction.instructions.length > 0) {
promises.push(connection.sendTransaction(cacheTransaction, [payer], {skipPreflight}));
}
}
Promise.all(promises).catch((err) => {
console.error('Error updating cache', err);
});
} catch (err) {
console.error('Error in processUpdateCache', err);
} finally {
console.timeLog('processUpdateCache');
setTimeout(processUpdateCache, updateCacheInterval, mangoGroup);
}
}
async function processConsumeEvents(
mangoGroup: MangoGroup,
perpMarkets: PerpMarket[],
) {
let eventsConsumed = [];
try {
const eventQueuePks = perpMarkets.map((mkt) => mkt.eventQueue);
const eventQueueAccts = await getMultipleAccounts(
connection,
eventQueuePks,
);
const perpMktAndEventQueue = eventQueueAccts.map(
({ publicKey, accountInfo }) => {
const parsed = PerpEventQueueLayout.decode(accountInfo?.data);
const eventQueue = new PerpEventQueue(parsed);
const perpMarket = perpMarkets.find((mkt) =>
mkt.eventQueue.equals(publicKey),
);
if (!perpMarket) {
throw new Error('PerpMarket not found');
}
return { perpMarket, eventQueue };
},
);
const promises: Promise<string | void>[] = perpMktAndEventQueue.map(
({ perpMarket, eventQueue }) => {
const events = eventQueue.getUnconsumedEvents();
if (events.length === 0) {
// console.log('No events to consume', perpMarket.publicKey.toString(), perpMarket.eventQueue.toString());
return promiseUndef();
}
const accounts: Set<string> = new Set();
for (const event of events) {
if (event.fill) {
accounts.add(event.fill.maker.toBase58());
accounts.add(event.fill.taker.toBase58());
} else if (event.out) {
accounts.add(event.out.owner.toBase58());
}
// Limit unique accounts to first 20 or 21
if (accounts.size >= maxUniqueAccounts) {
break;
}
}
const consumeEventsInstruction = makeConsumeEventsInstruction(
mangoProgramId,
mangoGroup.publicKey,
mangoGroup.mangoCache,
perpMarket.publicKey,
perpMarket.eventQueue,
Array.from(accounts)
.map((s) => new PublicKey(s))
.sort(),
consumeEventsLimit,
);
const transaction = new Transaction();
transaction.add(consumeEventsInstruction);
eventsConsumed.push(perpMarket.eventQueue.toString());
return connection.sendTransaction(transaction, [payer], {skipPreflight})
.catch((err) => {
console.error('Error consuming events', err);
});
},
);
Promise.all(promises).catch((err) => {
console.error('Error consuming events', err);
});
} catch (err) {
console.error('Error in processConsumeEvents', err);
} finally {
console.timeLog('processConsumeEvents', eventsConsumed);
setTimeout(
processConsumeEvents,
consumeEventsInterval,
mangoGroup,
perpMarkets,
);
}
}
async function processKeeperTransactions(
mangoGroup: MangoGroup,
perpMarkets: PerpMarket[],
) {
try {
if (!groupIds) {
throw new Error(`Group ${groupName} not found`);
}
const batchSize = 8;
const promises: Promise<string>[] = [];
const filteredPerpMarkets = perpMarkets.filter(
(pm) => !pm.publicKey.equals(zeroKey),
);
for (let i = 0; i < groupIds.tokens.length / batchSize; i++) {
const startIndex = i * batchSize;
const endIndex = i * batchSize + batchSize;
const updateRootBankTransaction = new Transaction();
groupIds.tokens.slice(startIndex, endIndex).forEach((token) => {
updateRootBankTransaction.add(
makeUpdateRootBankInstruction(
mangoProgramId,
mangoGroup.publicKey,
mangoGroup.mangoCache,
token.rootKey,
token.nodeKeys,
),
);
});
const updateFundingTransaction = new Transaction();
filteredPerpMarkets.slice(startIndex, endIndex).forEach((market) => {
if (market) {
updateFundingTransaction.add(
makeUpdateFundingInstruction(
mangoProgramId,
mangoGroup.publicKey,
mangoGroup.mangoCache,
market.publicKey,
market.bids,
market.asks,
),
);
}
});
if (updateRootBankTransaction.instructions.length > 0) {
promises.push(
connection.sendTransaction(updateRootBankTransaction, [payer], {skipPreflight}),
);
}
if (updateFundingTransaction.instructions.length > 0) {
promises.push(
connection.sendTransaction(updateFundingTransaction, [payer], {skipPreflight}),
);
}
}
Promise.all(promises).catch((err) => {
console.error('Error processing keeper instructions', err);
});
} catch (err) {
console.error('Error in processKeeperTransactions', err);
} finally {
console.timeLog('processKeeperTransactions');
setTimeout(
processKeeperTransactions,
processKeeperInterval,
mangoGroup,
perpMarkets,
);
}
}
process.on('unhandledRejection', (err: any, p: any) => {
console.error(`Unhandled rejection: ${err} promise: ${p})`);
});
main();

View File

@ -0,0 +1,110 @@
import { web3 } from "@project-serum/anchor";
import { Cluster, Connection, Keypair, LAMPORTS_PER_SOL, SystemProgram } from "@solana/web3.js";
import { sleep } from "@blockworks-foundation/mango-client";
if (process.argv.length < 3) {
console.log("please enter user file name as argument");
}
import { readFileSync } from 'fs';
let fileName = process.argv[2];
interface Users {
publicKey: string;
secretKey: any;
mangoAccountPks: string,
}
enum Result {
SUCCESS = 0,
FAILURE = 1
};
export async function main(users: Users[],
authority: Keypair,
targetBalance: number,
n_try: number) {
// cluster should be in 'devnet' | 'mainnet' | 'localnet' | 'testnet'
const endpoint = process.env.ENDPOINT_URL || 'http://localhost:8899';
const connection = new Connection(endpoint, 'confirmed');
let accounts_to_fund = new Array<[Users, number]>();
try {
for (let i = 0; i <= n_try; i++) {
if (i > 0) {
await sleep(2000);
}
// add all accounts which have balance less than threshold to set
{
let promises : Promise<number>[]= []
for (let curAccount of users) {
let curPubkey = new web3.PublicKey(curAccount.publicKey)
promises.push(connection.getBalance(curPubkey));
}
const balance = await Promise.all(promises);
users.forEach( (cur_account, index) => {
if (balance[index] < LAMPORTS_PER_SOL * targetBalance) {
accounts_to_fund.push([cur_account, balance[index]]);
}
});
if (accounts_to_fund.length == 0) {
return Result.SUCCESS;
}
}
// fund all these accounts
{
let promises : Promise<String>[]= []
let blockHash = await connection.getLatestBlockhash();
for (const [user, balance] of accounts_to_fund) {
let userPubkey = new web3.PublicKey(user.publicKey)
const ix = SystemProgram.transfer({
fromPubkey: authority.publicKey,
lamports: LAMPORTS_PER_SOL * targetBalance - balance,
toPubkey: userPubkey,
})
let tx = new web3.Transaction().add(ix);
tx.recentBlockhash = blockHash.blockhash;
console.log("Fund for " + (LAMPORTS_PER_SOL * targetBalance - balance)/LAMPORTS_PER_SOL + "");
promises.push(connection.sendTransaction(tx, [authority]))
}
try {
const result = await Promise.all(promises);
} catch(e) {
console.log('While sending transactions caught an error : ' + e + ". Will try again.")
}
}
accounts_to_fund.length = 0;
}
} catch(e) {
console.log('caught an error : ' + e)
}
return Result.FAILURE;
}
const targetBalance = parseFloat(process.env.REFUND_TARGET_SOL || '1.0');
const nTry = parseFloat(process.env.NUMBER_TRY_REFUNG || '3');
const file = readFileSync(fileName, 'utf-8');
const users : Users[] = JSON.parse(file);
if (users === undefined) {
console.log("cannot read users list")
}
const authority = Keypair.fromSecretKey(
Uint8Array.from(
JSON.parse(
process.env.KEYPAIR ||
readFileSync('authority.json', 'utf-8'),
),
),
);
console.log('refunding to have up to ' + targetBalance + ' sol for ' + users.length + ' users')
main(users, authority, targetBalance, nTry).then(x => {
if (x == Result.SUCCESS) {
console.log('finished sucessfully')
process.exit(0);
} else {
console.log('failed')
process.exit(1);
}
});

View File

@ -0,0 +1,100 @@
#!/usr/bin/env bash
#
# Run a minimal Solana cluster. Ctrl-C to exit.
#
# Before running this script ensure standard Solana programs are available
# in the PATH, or that `cargo build` ran successfully
#
# Prefer possible `cargo build` binaries over PATH binaries
# ctrl-c trap to stop child processes
trap ctrl_c INT
function ctrl_c() {
echo "Kill them all"
pkill -P $$
exit 1
}
outDir=$PWD
export RUST_LOG=${RUST_LOG:-solana=info,solana_runtime::message_processor=debug} # if RUST_LOG is unset, default to info
export RUST_BACKTRACE=1
dataDir=$outDir/config
ledgerDir=$outDir/config/ledger
binDir=$(dirname $0)/../bin
set -x
if ! solana address; then
echo Generating default keypair
solana-keygen new --no-passphrase
fi
validator_identity="$dataDir/validator-identity.json"
if [[ -e $validator_identity ]]; then
echo "Use existing validator keypair"
else
solana-keygen new --no-passphrase -so "$validator_identity"
fi
validator_vote_account="$dataDir/validator-vote-account.json"
if [[ -e $validator_vote_account ]]; then
echo "Use existing validator vote account keypair"
else
solana-keygen new --no-passphrase -so "$validator_vote_account"
fi
validator_stake_account="$dataDir/validator-stake-account.json"
if [[ -e $validator_stake_account ]]; then
echo "Use existing validator stake account keypair"
else
solana-keygen new --no-passphrase -so "$validator_stake_account"
fi
if [[ -e "$ledgerDir"/genesis.bin || -e "$ledgerDir"/genesis.tar.bz2 ]]; then
echo "Use existing genesis"
else
echo $SPL_GENESIS_ARGS
# shellcheck disable=SC2086
solana-genesis \
--hashes-per-tick sleep \
--faucet-lamports 500000000000000000 \
--bootstrap-validator \
"$validator_identity" \
"$validator_vote_account" \
"$validator_stake_account" \
--ledger "$ledgerDir" \
--cluster-type "development" \
--bpf-program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA BPFLoader2111111111111111111111111111111111 $binDir/spl_token-3.5.0.so \
--bpf-program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL BPFLoader2111111111111111111111111111111111 $binDir/spl_associated-token-account-1.1.1.so \
--bpf-program DGKy8w8RtRsWB48qHa4yCd3AeP5uv4m3Qn7LU8z93RWV BPFLoader2111111111111111111111111111111111 $binDir/mango.so \
--bpf-program 3WAiypER8fm6vHjUPRiigGifq6ueSY645aYGH5Jj14pU BPFLoader2111111111111111111111111111111111 $binDir/serum_dex.so \
--bpf-program EoUiQKGpM4jsdb5oRnYnuWMaE4Gcey72QjEBbFxhk23C BPFLoader2111111111111111111111111111111111 $binDir/pyth_mock.so \
--bpf-program noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV BPFLoader2111111111111111111111111111111111 $binDir/spl_noop.so \
--bpf-program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo BPFLoader1111111111111111111111111111111111 $binDir/spl_memo-1.0.0.so \
--bpf-program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr BPFLoader2111111111111111111111111111111111 $binDir/spl_memo-3.0.0.so \
$SOLANA_RUN_SH_GENESIS_ARGS
fi
solana-faucet &
faucet=$!
args=(
--identity "$validator_identity"
--vote-account "$validator_vote_account"
--ledger "$ledgerDir"
--gossip-port 8001
--full-rpc-api
--rpc-port 8899
--rpc-faucet-address 127.0.0.1:9900
--log "$dataDir/validator.log"
--enable-rpc-transaction-history
--enable-extended-tx-metadata-storage
--init-complete-file "$dataDir"/init-completed
--snapshot-compression none
--require-tower
--no-wait-for-vote-to-start-leader
--no-os-network-limits-test
--allow-private-addr
)
# shellcheck disable=SC2086
solana-validator "${args[@]}" $SOLANA_RUN_SH_VALIDATOR_ARGS &
validator=$!
wait "$validator"

View File

@ -0,0 +1 @@
--bpf-program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA BPFLoader2111111111111111111111111111111111 ${binDir}/spl_token-3.5.0.so --bpf-program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo BPFLoader1111111111111111111111111111111111 $binDir/spl_memo-1.0.0.so --bpf-program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr BPFLoader2111111111111111111111111111111111 $binDir/spl_memo-3.0.0.so --bpf-program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL BPFLoader2111111111111111111111111111111111 $binDir/spl_associated-token-account-1.1.1.so --bpf-program Feat1YXHhH6t1juaWF74WLcfv4XoNocjXA6sPWHNgAse BPFLoader2111111111111111111111111111111111 $binDir/spl_feature-proposal-1.0.0.so --bpf-program DGKy8w8RtRsWB48qHa4yCd3AeP5uv4m3Qn7LU8z93RWV BPFLoader2111111111111111111111111111111111 $binDir/mango.so --bpf-program 3WAiypER8fm6vHjUPRiigGifq6ueSY645aYGH5Jj14pU BPFLoader2111111111111111111111111111111111 $binDir/serum_dex.so --bpf-program EoUiQKGpM4jsdb5oRnYnuWMaE4Gcey72QjEBbFxhk23C BPFLoader2111111111111111111111111111111111 $binDir/pyth_mock.so --bpf-program noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV BPFLoader2111111111111111111111111111111111 $binDir/spl_noop.so

View File

@ -0,0 +1,23 @@
#!/bin/bash
binDir=$(dirname $0)/../bin
solana-test-validator --account MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac $binDir/mango-mint.json --bpf-program noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV $binDir/spl_noop.so &
pid="$!"
# handle ctrl-c
trap cleanup INT EXIT KILL 2
cleanup()
{
echo "cleanup $pid"
kill -9 $pid
}
sleep 5
solana program deploy bin/mango.so -ul --program-id $binDir/mango.json
solana program deploy bin/serum_dex.so -ul --program-id $binDir/serum_dex.json
solana program deploy bin/pyth_mock.so -ul --program-id $binDir/pyth_mock.json
# idle waiting for abort
wait $pid

View File

@ -0,0 +1,27 @@
#!/usr/bin/env bash
URL_OR_MONIKER="${URL_OR_MONIKER:=testnet}"
# spl programs are already deployed to testnet
spl_soFiles=("spl_token-3.5.0.so" "spl_memo-1.0.0.so" "spl_memo-3.0.0.so" "spl_associated-token-account-1.1.1.so" "spl_feature-proposal-1.0.0.so")
soFiles=("mango.so" "serum_dex.so" "pyth_mock.so")
DEPLOY_PROGRAM="solana program deploy -u ${URL_OR_MONIKER} --use-quic"
mkdir -p program-keypairs
output=()
for program in ${soFiles[@]}; do
programName="${program%.*}"
keypair="./program-keypairs/${programName}.json"
solana-keygen new --no-passphrase -so ${keypair}
${DEPLOY_PROGRAM} --program-id ${keypair} bin/${program}
pubkey=$(solana-keygen pubkey ${keypair})
output+=("\"${programName}\":\"${pubkey}\"")
done
mapFile="testnet-program-name-to-id.json"
echo "{" > ${mapFile}
last=$((${#output[@]}-1))
echo $last
for v in ${output[@]:0:$last}; do
echo "$v," >> ${mapFile}
done
echo ${output[-1]} >> ${mapFile}
echo "}" >> ${mapFile}

View File

@ -0,0 +1,99 @@
import { GroupConfig, OracleConfig, PerpMarketConfig, SpotMarketConfig, TokenConfig, Cluster } from '@blockworks-foundation/mango-client';
import { readFileSync } from 'fs';
export function getProgramMap(cluster : Cluster): Map<String, String> {
var file = "";
if (cluster == "testnet") {
file = readFileSync('./configure_cluster/utils/testnet-program-name-to-id.json', 'utf-8');
} else {
file = readFileSync('./configure_cluster/utils/genesis-program-name-to-id.json', 'utf-8');
};
return JSON.parse(file);
}
export class Config {
public cluster_urls: Record<Cluster, string>;
public groups: GroupConfig[];
constructor(cluster_urls: Record<Cluster, string>, groups : GroupConfig[]) {
this.cluster_urls = cluster_urls;
this.groups = groups;
}
oracleConfigToJson(o: OracleConfig): any {
return {
...o,
publicKey: o.publicKey.toBase58(),
};
}
perpMarketConfigToJson(p: PerpMarketConfig): any {
return {
...p,
publicKey: p.publicKey.toBase58(),
bidsKey: p.bidsKey.toBase58(),
asksKey: p.asksKey.toBase58(),
eventsKey: p.eventsKey.toBase58(),
};
}
spotMarketConfigToJson(p: SpotMarketConfig): any {
return {
...p,
publicKey: p.publicKey.toBase58(),
bidsKey: p.bidsKey.toBase58(),
asksKey: p.asksKey.toBase58(),
eventsKey: p.eventsKey.toBase58(),
};
}
tokenConfigToJson(t: TokenConfig): any {
return {
...t,
mintKey: t.mintKey.toBase58(),
rootKey: t.rootKey.toBase58(),
nodeKeys: t.nodeKeys.map((k) => k.toBase58()),
};
}
groupConfigToJson(g: GroupConfig): any {
return {
...g,
publicKey: g.publicKey.toBase58(),
mangoProgramId: g.mangoProgramId.toBase58(),
serumProgramId: g.serumProgramId.toBase58(),
oracles: g.oracles.map((o) => this.oracleConfigToJson(o)),
perpMarkets: g.perpMarkets.map((p) => this.perpMarketConfigToJson(p)),
spotMarkets: g.spotMarkets.map((p) => this.spotMarketConfigToJson(p)),
tokens: g.tokens.map((t) => this.tokenConfigToJson(t)),
};
}
public toJson(): any {
return {
...this,
groups: this.groups.map((g) => this.groupConfigToJson(g)),
};
}
public getGroup(cluster: Cluster, name: string) {
return this.groups.find((g) => g.cluster === cluster && g.name === name);
}
public getGroupWithName(name: string) {
return this.groups.find((g) => g.name === name);
}
public storeGroup(group: GroupConfig) {
const _group = this.getGroup(group.cluster, group.name);
if (_group) {
Object.assign(_group, group);
} else {
this.groups.unshift(group);
}
}
}

View File

@ -0,0 +1,5 @@
{
"mango": "DGKy8w8RtRsWB48qHa4yCd3AeP5uv4m3Qn7LU8z93RWV",
"serum_dex": "3WAiypER8fm6vHjUPRiigGifq6ueSY645aYGH5Jj14pU",
"pyth_mock" : "EoUiQKGpM4jsdb5oRnYnuWMaE4Gcey72QjEBbFxhk23C"
}

View File

@ -0,0 +1,811 @@
import * as anchor from '@project-serum/anchor';
import { Market, OpenOrders } from "@project-serum/serum";
import * as mango_client_v3 from '@blockworks-foundation/mango-client';
import { Connection, LAMPORTS_PER_SOL } from '@solana/web3.js';
import * as splToken from '@solana/spl-token';
import {
NATIVE_MINT,
Mint,
TOKEN_PROGRAM_ID,
MintLayout,
} from "@solana/spl-token";
import {
PublicKey,
Keypair,
Transaction,
SystemProgram,
sendAndConfirmTransaction,
Signer,
} from '@solana/web3.js';
import { SerumUtils, } from "./serum_utils";
import { MintUtils, TokenData, } from './mint_utils';
import { BN } from 'bn.js';
import {
GroupConfig,
OracleConfig,
PerpMarketConfig,
SpotMarketConfig,
TokenConfig,
Cluster,
PerpEventQueueHeaderLayout,
PerpEventLayout,
BookSideLayout,
makeCreatePerpMarketInstruction,
I80F48,
PerpMarketLayout,
makeAddPerpMarketInstruction,
makeDepositInstruction,
Config as MangoConfig,
sleep
} from '@blockworks-foundation/mango-client';
import { token } from '@project-serum/anchor/dist/cjs/utils';
import { Config } from './config';
export interface PerpMarketData {
publicKey: PublicKey,
asks: PublicKey,
bids: PublicKey,
eventQ: PublicKey,
}
export interface MangoTokenData extends TokenData {
rootBank: PublicKey,
nodeBank: PublicKey,
marketIndex: number,
perpMarket: PerpMarketData,
}
export interface MangoCookie {
mangoGroup: PublicKey,
signerKey: PublicKey,
mangoCache: PublicKey,
usdcRootBank: PublicKey,
usdcNodeBank: PublicKey,
usdcVault: PublicKey,
usdcMint: PublicKey,
tokens: Array<[String, MangoTokenData]>,
MSRM: PublicKey,
}
export interface MangoUser {
kp: Keypair,
mangoAddress: PublicKey,
}
export class MangoUtils {
private conn: Connection;
private serumUtils: SerumUtils;
private mintUtils: MintUtils;
private mangoClient: mango_client_v3.MangoClient;
private authority: Keypair;
private mangoProgramId: PublicKey;
private dexProgramId: PublicKey;
constructor(conn: Connection, authority: Keypair, mangoProgramId: PublicKey, dexProgramId: PublicKey, pythProgramId: PublicKey) {
this.conn = conn;
this.authority = authority;
this.mangoProgramId = mangoProgramId;
this.dexProgramId = dexProgramId;
this.serumUtils = new SerumUtils(conn, authority, dexProgramId);
this.mintUtils = new MintUtils(conn, authority, dexProgramId, pythProgramId);
this.mangoClient = new mango_client_v3.MangoClient(conn, mangoProgramId);
}
async createAccountForMango(size: number): Promise<PublicKey> {
const lamports = await this.conn.getMinimumBalanceForRentExemption(size);
let address = Keypair.generate();
const transaction = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: this.authority.publicKey,
newAccountPubkey: address.publicKey,
lamports,
space: size,
programId: this.mangoProgramId,
}))
transaction.feePayer = this.authority.publicKey;
let hash = await this.conn.getRecentBlockhash();
transaction.recentBlockhash = hash.blockhash;
// Sign transaction, broadcast, and confirm
await sendAndConfirmTransaction(
this.conn,
transaction,
[this.authority, address],
{ commitment: 'confirmed' },
);
// airdrop all mongo accounts 1000 SOLs
// const signature = await this.conn.requestAirdrop(address.publicKey, LAMPORTS_PER_SOL * 1000);
// const blockHash = await this.conn.getRecentBlockhash('confirmed');
// const blockHeight = await this.conn.getBlockHeight('confirmed')
// await this.conn.confirmTransaction({signature: signature, blockhash: blockHash.blockhash, lastValidBlockHeight: blockHeight});
return address.publicKey;
}
public async createMangoCookie(tokensList: Array<String>): Promise<MangoCookie> {
const size = mango_client_v3.MangoGroupLayout.span;
let group_address = await this.createAccountForMango(size);
let root_bank_address = await this.createAccountForMango(mango_client_v3.RootBankLayout.span);
let node_bank_address = await this.createAccountForMango(mango_client_v3.NodeBankLayout.span);
let mango_cache = await this.createAccountForMango(mango_client_v3.MangoCacheLayout.span);
const { signerKey, signerNonce } = await mango_client_v3.createSignerKeyAndNonce(
this.mangoProgramId,
group_address,
);
let mangoCookie: MangoCookie = {
mangoGroup: null,
signerKey,
mangoCache: null,
usdcRootBank: null,
usdcNodeBank: null,
usdcVault: null,
tokens: new Array<[String, MangoTokenData]>(),
usdcMint: await this.mintUtils.createMint(6),
MSRM: await this.mintUtils.createMint(6),
};
let usdc_vault = await this.mintUtils.createTokenAccount(mangoCookie.usdcMint, this.authority, signerKey);
splToken.mintTo(this.conn, this.authority, mangoCookie.usdcMint, usdc_vault, this.authority, 1000000 * 1000000);
mangoCookie.usdcVault = usdc_vault;
let insurance_vault = await this.mintUtils.createTokenAccount(mangoCookie.usdcMint, this.authority, signerKey);
splToken.mintTo(this.conn, this.authority, mangoCookie.usdcMint, insurance_vault, this.authority, 1000000 * 1000000);
let fee_vault = await this.mintUtils.createTokenAccount(mangoCookie.usdcMint, this.authority, TOKEN_PROGRAM_ID);
splToken.mintTo(this.conn, this.authority, mangoCookie.usdcMint, fee_vault, this.authority, 1000000 * 1000000);
let msrm_vault = await this.mintUtils.createTokenAccount(mangoCookie.MSRM, this.authority, signerKey);
mangoCookie.usdcRootBank = root_bank_address;
mangoCookie.usdcNodeBank = node_bank_address;
console.log('mango program id : ' + this.mangoProgramId)
console.log('serum program id : ' + this.dexProgramId)
let ix = mango_client_v3.makeInitMangoGroupInstruction(
this.mangoProgramId,
group_address,
signerKey,
this.authority.publicKey,
mangoCookie.usdcMint,
usdc_vault,
node_bank_address,
root_bank_address,
insurance_vault,
PublicKey.default,
fee_vault,
mango_cache,
this.dexProgramId,
new anchor.BN(signerNonce),
new anchor.BN(10),
mango_client_v3.I80F48.fromNumber(0.7),
mango_client_v3.I80F48.fromNumber(0.06),
mango_client_v3.I80F48.fromNumber(1.5),
);
let ixCacheRootBank = mango_client_v3.makeCacheRootBankInstruction(this.mangoProgramId,
group_address,
mango_cache,
[root_bank_address]);
let ixupdateRootBank = mango_client_v3.makeUpdateRootBankInstruction(this.mangoProgramId,
group_address,
mango_cache,
root_bank_address,
[node_bank_address]);
await this.processInstruction(ix, [this.authority]);
await this.processInstruction(ixCacheRootBank, [this.authority]);
await this.processInstruction(ixupdateRootBank, [this.authority]);
mangoCookie.mangoGroup = group_address;
mangoCookie.mangoCache = mango_cache;
console.log('Mango group created, creating tokens')
// create mngo
const mngoData = await this.createMangoToken(mangoCookie, tokensList[0], 0, 6, 100);
let tokenData = await Promise.all(tokensList.filter((a,b)=> b > 0).map((tokenStr, tokenIndex) => this.createMangoToken(mangoCookie, tokenStr, tokenIndex+1, 6, 100)));
tokenData.push(mngoData)
await this.mangoClient.cachePrices(mangoCookie.mangoGroup, mangoCookie.mangoCache, tokenData.map(x=>x.priceOracle.publicKey), this.authority);
//tokensList.map((tokenStr, tokenIndex) => this.createMangoToken(mangoCookie, tokenStr, tokenIndex, 6, 100));
return mangoCookie;
}
public async createMangoToken(mangoCookie: MangoCookie, tokenName: String, tokenIndex: number, nbDecimals, startingPrice): Promise<MangoTokenData> {
console.log('Creating token ' + tokenName + " at index " + tokenIndex)
const tokenData = await this.mintUtils.createNewToken(mangoCookie.usdcMint, nbDecimals, startingPrice);
let mangoTokenData: MangoTokenData = {
market: tokenData.market,
marketIndex: tokenIndex,
mint: tokenData.mint,
priceOracle: tokenData.priceOracle,
nbDecimals: tokenData.nbDecimals,
startingPrice: startingPrice,
nodeBank: await this.createAccountForMango(mango_client_v3.NodeBankLayout.span),
rootBank: await this.createAccountForMango(mango_client_v3.RootBankLayout.span),
perpMarket: null,
};
// add oracle to mango
let add_oracle_ix = mango_client_v3.makeAddOracleInstruction(
this.mangoProgramId,
mangoCookie.mangoGroup,
mangoTokenData.priceOracle.publicKey,
this.authority.publicKey,
);
// add oracle
{
const transaction = new Transaction();
transaction.add(add_oracle_ix);
transaction.feePayer = this.authority.publicKey;
let hash = await this.conn.getRecentBlockhash();
transaction.recentBlockhash = hash.blockhash;
// Sign transaction, broadcast, and confirm
await this.mangoClient.sendTransaction(
transaction,
this.authority,
[],
3600,
'confirmed',
);
}
await this.initSpotMarket(mangoCookie, mangoTokenData);
const group = await this.mangoClient.getMangoGroup(mangoCookie.mangoGroup);
mangoTokenData.marketIndex = group.getTokenIndex(mangoTokenData.mint)
mangoTokenData.perpMarket = await this.initPerpMarket(mangoCookie, mangoTokenData);
mangoCookie.tokens.push([tokenName, mangoTokenData]);
return mangoTokenData;
}
public async initSpotMarket(mangoCookie: MangoCookie, token: MangoTokenData) {
const vault = await this.mintUtils.createTokenAccount(token.mint, this.authority, mangoCookie.signerKey);
// add spot market to mango
let add_spot_ix = mango_client_v3.makeAddSpotMarketInstruction(
this.mangoProgramId,
mangoCookie.mangoGroup,
token.priceOracle.publicKey,
token.market.address,
this.dexProgramId,
token.mint,
token.nodeBank,
vault,
token.rootBank,
this.authority.publicKey,
mango_client_v3.I80F48.fromNumber(10),
mango_client_v3.I80F48.fromNumber(5),
mango_client_v3.I80F48.fromNumber(0.05),
mango_client_v3.I80F48.fromNumber(0.7),
mango_client_v3.I80F48.fromNumber(0.06),
mango_client_v3.I80F48.fromNumber(1.5),
);
let ixCacheRootBank = mango_client_v3.makeCacheRootBankInstruction(this.mangoProgramId,
mangoCookie.mangoGroup,
mangoCookie.mangoCache,
[token.rootBank]);
let ixupdateRootBank = mango_client_v3.makeUpdateRootBankInstruction(this.mangoProgramId,
mangoCookie.mangoGroup,
mangoCookie.mangoCache,
token.rootBank,
[token.nodeBank]);
const transaction = new Transaction();
transaction.add(add_spot_ix);
transaction.add(ixCacheRootBank);
transaction.add(ixupdateRootBank);
transaction.feePayer = this.authority.publicKey;
let hash = await this.conn.getRecentBlockhash();
transaction.recentBlockhash = hash.blockhash;
await sendAndConfirmTransaction(
this.conn,
transaction,
[this.authority],
{ commitment: 'confirmed', maxRetries: 100 }
)
}
public async getMangoGroup(mangoCookie: MangoCookie) {
return this.mangoClient.getMangoGroup(mangoCookie.mangoGroup)
}
private async initPerpMarket(mangoCookie: MangoCookie, token: MangoTokenData) {
const maxNumEvents = 256;
// const perpMarketPk = await this.createAccountForMango(
// PerpMarketLayout.span,
// );
const [perpMarketPk] = await PublicKey.findProgramAddress(
[
mangoCookie.mangoGroup.toBytes(),
new Buffer('PerpMarket', 'utf-8'),
token.priceOracle.publicKey.toBytes(),
],
this.mangoProgramId,
);
const eventQ = await this.createAccountForMango(
PerpEventQueueHeaderLayout.span + maxNumEvents * PerpEventLayout.span,
);
const bids = await this.createAccountForMango(
BookSideLayout.span,
);
const mangoMint = token.marketIndex == 0 ? token.mint : mangoCookie.tokens[0][1].mint;
const asks = await this.createAccountForMango(
BookSideLayout.span,
);
const [mngoVaultPk] = await PublicKey.findProgramAddress(
[
perpMarketPk.toBytes(),
TOKEN_PROGRAM_ID.toBytes(),
mangoMint.toBytes(),
],
this.mangoProgramId,
);
const instruction = await makeCreatePerpMarketInstruction(
this.mangoProgramId,
mangoCookie.mangoGroup,
token.priceOracle.publicKey,
perpMarketPk,
eventQ,
bids,
asks,
mangoMint,
mngoVaultPk,
this.authority.publicKey,
mangoCookie.signerKey,
I80F48.fromNumber(10),
I80F48.fromNumber(5),
I80F48.fromNumber(0.05),
I80F48.fromNumber(0),
I80F48.fromNumber(0.005),
new BN(1000),
new BN(1000),
I80F48.fromNumber(1),
I80F48.fromNumber(200),
new BN(3600),
new BN(0),
new BN(2),
new BN(2),
new BN(0),
new BN(6)
)
const transaction = new Transaction();
transaction.add(instruction);
await this.mangoClient.sendTransaction(transaction, this.authority, [], 3600, 'confirmed');
//await sendAndConfirmTransaction(this.conn, transaction, additionalSigners);
const perpMarketData: PerpMarketData = {
publicKey: perpMarketPk,
asks: asks,
bids: bids,
eventQ: eventQ,
}
return perpMarketData;
}
public async createAndInitPerpMarket(mangoCookie: MangoCookie, token: MangoTokenData): Promise<PerpMarketData> {
const blockHash = await this.conn.getLatestBlockhashAndContext('confirmed')
const signature = await this.mangoClient.addPerpMarket(
await this.getMangoGroup(mangoCookie),
token.priceOracle.publicKey,
token.mint,
this.authority,
10,
5,
0.05,
0,
0.0005,
1000,
1000,
256,
1,
200,
3600,
0,
2,
);
this.conn.confirmTransaction({ blockhash: blockHash.value.blockhash, lastValidBlockHeight: blockHash.value.lastValidBlockHeight, signature: signature }, 'confirmed')
const mangoGroup = await this.mangoClient.getMangoGroup(mangoCookie.mangoGroup);
const tokenIndex = token.marketIndex;
const perpMarketPk = mangoGroup.perpMarkets[tokenIndex].perpMarket
const perpMarket = await this.mangoClient.getPerpMarket(perpMarketPk, token.nbDecimals, 6);
// const perpMarketData : PerpMarketData = {
// publicKey: perpMarketPk,
// asks: perpMarket.asks,
// bids: perpMarket.bids,
// eventQ: perpMarket.eventQueue,
// }
const perpMarketData: PerpMarketData = {
publicKey: PublicKey.default,
asks: PublicKey.default,
bids: PublicKey.default,
eventQ: PublicKey.default,
}
return perpMarketData
}
public async refreshTokenCache(mangoCookie: MangoCookie, tokenData: MangoTokenData) {
let ixupdateRootBank = mango_client_v3.makeUpdateRootBankInstruction(this.mangoProgramId,
mangoCookie.mangoGroup,
mangoCookie.mangoCache,
tokenData.rootBank,
[tokenData.nodeBank]);
const transaction = new Transaction();
transaction.add(ixupdateRootBank);
transaction.feePayer = this.authority.publicKey;
let hash = await this.conn.getRecentBlockhash();
transaction.recentBlockhash = hash.blockhash;
// Sign transaction, broadcast, and confirm
await sendAndConfirmTransaction(
this.conn,
transaction,
[this.authority],
{ commitment: 'confirmed' },
);
}
public async refreshRootBankCache(mangoContext: MangoCookie) {
let ixCacheRootBank = mango_client_v3.makeCacheRootBankInstruction(this.mangoProgramId,
mangoContext.mangoGroup,
mangoContext.mangoCache,
Array.from(mangoContext.tokens).map(x => x[1].rootBank));
const transaction = new Transaction();
transaction.add(ixCacheRootBank);
transaction.feePayer = this.authority.publicKey;
let hash = await this.conn.getRecentBlockhash();
transaction.recentBlockhash = hash.blockhash;
// Sign transaction, broadcast, and confirm
await sendAndConfirmTransaction(
this.conn,
transaction,
[this.authority],
{ commitment: 'confirmed' },
);
}
async refreshAllTokenCache(mangoContext: MangoCookie) {
await this.refreshRootBankCache(mangoContext);
await Promise.all(
Array.from(mangoContext.tokens).map(x => this.refreshTokenCache(mangoContext, x[1]))
);
}
async createSpotOpenOrdersAccount(mangoContext: MangoCookie, mangoAccount: PublicKey, owner: Keypair, tokenData: TokenData): Promise<PublicKey> {
let mangoGroup = await this.mangoClient.getMangoGroup(mangoContext.mangoGroup);
const marketIndex = new BN(mangoGroup.tokens.findIndex(x => x.mint.equals(tokenData.mint)));
const [spotOpenOrdersAccount, _bump] = await PublicKey.findProgramAddress(
[
mangoAccount.toBuffer(),
marketIndex.toBuffer("le", 8),
Buffer.from("OpenOrders"),
], this.mangoProgramId);
const space = OpenOrders.getLayout(this.dexProgramId).span;
//await this.createAccount( spotOpenOrdersAccount, owner, DEX_ID, space);
const lamports = await this.conn.getMinimumBalanceForRentExemption(space);
let ix2 = mango_client_v3.makeCreateSpotOpenOrdersInstruction(
this.mangoProgramId,
mangoContext.mangoGroup,
mangoAccount,
owner.publicKey,
this.dexProgramId,
spotOpenOrdersAccount,
tokenData.market.address,
mangoContext.signerKey,
)
await this.processInstruction(ix2, [owner]);
return spotOpenOrdersAccount;
}
async processInstruction(ix: anchor.web3.TransactionInstruction, signers: Array<Signer>) {
const transaction = new Transaction();
transaction.add(ix);
transaction.feePayer = this.authority.publicKey;
signers.push(this.authority);
let hash = await this.conn.getRecentBlockhash();
transaction.recentBlockhash = hash.blockhash;
// Sign transaction, broadcast, and confirm
try {
await this.mangoClient.sendTransaction(transaction,
this.authority,
[],
3600,
'confirmed');
}
catch (ex) {
const ext = ex as anchor.web3.SendTransactionError;
if (ext != null) {
console.log("Error processing instruction : " + ext.message)
}
throw ex;
}
}
convertCookie2Json(mangoCookie: MangoCookie, cluster: Cluster) {
const oracles = Array.from(mangoCookie.tokens).map(x => {
const oracle: OracleConfig = {
publicKey: x[1].priceOracle.publicKey,
symbol: x[0] + 'USDC'
}
return oracle;
})
const perpMarkets = Array.from(mangoCookie.tokens).map(x => {
const perpMarket: PerpMarketConfig = {
publicKey: x[1].perpMarket.publicKey,
asksKey: x[1].perpMarket.asks,
bidsKey: x[1].perpMarket.bids,
eventsKey: x[1].perpMarket.eventQ,
marketIndex: x[1].marketIndex,
baseDecimals: x[1].nbDecimals,
baseSymbol: x[0].toString(),
name: (x[0] + 'USDC PERP'),
quoteDecimals: 6,
}
return perpMarket
})
const spotMarkets = Array.from(mangoCookie.tokens).map(x => {
const spotMarket: SpotMarketConfig = {
name: x[0] + 'USDC spot Market',
marketIndex: x[1].marketIndex,
publicKey: x[1].market.address,
asksKey: x[1].market.asksAddress,
bidsKey: x[1].market.bidsAddress,
baseDecimals: x[1].nbDecimals,
baseSymbol: x[0].toString(),
eventsKey: x[1].market.decoded.eventQueue,
quoteDecimals: 6,
}
return spotMarket;
})
const tokenConfigs = Array.from(mangoCookie.tokens).map(x => {
const tokenConfig: TokenConfig = {
decimals: x[1].nbDecimals,
mintKey: x[1].mint,
nodeKeys: [x[1].nodeBank],
rootKey: x[1].rootBank,
symbol: x[0].toString(),
}
return tokenConfig
})
// quote token config
tokenConfigs.push(
{
decimals: 6,
mintKey: mangoCookie.usdcMint,
rootKey: mangoCookie.usdcRootBank,
nodeKeys: [mangoCookie.usdcNodeBank],
symbol: "USDC",
}
)
const groupConfig: GroupConfig = {
cluster,
mangoProgramId: this.mangoProgramId,
name: cluster,
publicKey: mangoCookie.mangoGroup,
quoteSymbol: "USDC",
oracles: oracles,
serumProgramId: this.dexProgramId,
perpMarkets: perpMarkets,
spotMarkets: spotMarkets,
tokens: tokenConfigs,
}
groupConfig["cacheKey"] = mangoCookie.mangoCache
const groupConfigs: GroupConfig[] = [groupConfig]
const cluster_urls: Record<Cluster, string> = {
"devnet": "https://mango.devnet.rpcpool.com",
"localnet": "http://127.0.0.1:8899",
"mainnet": "https://mango.rpcpool.com/946ef7337da3f5b8d3e4a34e7f88",
"testnet": "http://api.testnet.rpcpool.com"
};
const config = new Config(cluster_urls, groupConfigs);
return config.toJson();
}
async json2Cookie(json: any, cluster: Cluster) : Promise<MangoCookie> {
const clusterConfig = new MangoConfig(json)
const group = clusterConfig.getGroup(cluster, cluster);
let mangoCookie: MangoCookie = {
mangoGroup: null,
signerKey: null,
mangoCache: null,
usdcRootBank: null,
usdcNodeBank: null,
usdcVault: null,
tokens: new Array<[String, MangoTokenData]>(),
usdcMint: null,
MSRM: null,
};
const mangoGroup = await this.mangoClient.getMangoGroup(group.publicKey);
let quoteToken = mangoGroup.getQuoteTokenInfo();
const quoteRootBankIndex = await mangoGroup.getRootBankIndex(quoteToken.rootBank);
const rootBanks = await (await mangoGroup.loadRootBanks(this.conn));
const quoteRootBank = rootBanks[quoteRootBankIndex];
const quoteNodeBanks = await quoteRootBank.loadNodeBanks(this.conn);
mangoCookie.mangoGroup = group.publicKey;
mangoCookie.mangoCache = mangoGroup.mangoCache;
mangoCookie.usdcRootBank = quoteToken.rootBank;
mangoCookie.usdcNodeBank = quoteNodeBanks[0].publicKey;
mangoCookie.usdcVault = quoteNodeBanks[0].vault;
mangoCookie.usdcMint = mangoGroup.getQuoteTokenInfo().mint;
const rootBanksF = rootBanks.filter(x => x != undefined);
mangoCookie.tokens = mangoGroup.tokens.map((x, index) => {
if (x.rootBank.equals(PublicKey.default)) {
return ["", null]
}
let rootBank = rootBanksF.find(y => y.publicKey.equals(x.rootBank))
let tokenData : MangoTokenData = {
market: null,
marketIndex: index,
mint: x.mint,
nbDecimals: 6,
rootBank: x.rootBank,
nodeBank: rootBank.nodeBanks[0],
perpMarket: null,
priceOracle: null,
startingPrice: 0
};
return ["", tokenData]
})
return mangoCookie
}
async createUser(
mangoCookie,
mangoGroup: mango_client_v3.MangoGroup,
usdcAcc: PublicKey,
rootBanks : mango_client_v3.RootBank[],
nodeBanks : mango_client_v3.NodeBank[],
fundingAccounts : PublicKey[],
authority: Keypair,
): Promise<MangoUser> {
const user = Keypair.generate();
// transfer 1 sol to the user
{
const ix = SystemProgram.transfer({
fromPubkey: authority.publicKey,
lamports : LAMPORTS_PER_SOL,
programId: anchor.web3.SystemProgram.programId,
toPubkey: user.publicKey,
})
await anchor.web3.sendAndConfirmTransaction( this.conn, new Transaction().add(ix), [authority]);
}
const mangoAcc = await this.mangoClient.createMangoAccount(
mangoGroup,
user,
1,
user.publicKey,
);
//const mangoAccount = await this.mangoClient.getMangoAccount(user.mangoAddress, this.dexProgramId)
const depositUsdcIx = makeDepositInstruction(
this.mangoProgramId,
mangoCookie.mangoGroup,
this.authority.publicKey,
mangoGroup.mangoCache,
mangoAcc,
mangoCookie.usdcRootBank,
mangoCookie.usdcNodeBank,
mangoCookie.usdcVault,
usdcAcc,
new BN(10_000_000_000),
);
const blockHashInfo = await this.conn.getLatestBlockhashAndContext();
const transaction = new Transaction()
.add(depositUsdcIx)
transaction.recentBlockhash = blockHashInfo.value.blockhash;
transaction.feePayer = this.authority.publicKey;
await this.mangoClient.sendTransaction(transaction, this.authority, [], 3600, 'confirmed')
for (const tokenIte of mangoCookie.tokens)
{
if (tokenIte[1] === null)
{
continue;
}
const marketIndex = tokenIte[1].marketIndex;
const deposit = makeDepositInstruction(
this.mangoProgramId,
mangoCookie.mangoGroup,
this.authority.publicKey,
mangoGroup.mangoCache,
mangoAcc,
rootBanks[marketIndex].publicKey,
nodeBanks[marketIndex].publicKey,
nodeBanks[marketIndex].vault,
fundingAccounts[marketIndex],
new BN(1_000_000_000),
);
const blockHashInfo = await this.conn.getLatestBlockhashAndContext();
const transaction = new Transaction()
.add(deposit)
transaction.recentBlockhash = blockHashInfo.value.blockhash;
transaction.feePayer = this.authority.publicKey;
await this.mangoClient.sendTransaction(transaction, this.authority, [], 3600, 'confirmed')
}
return { kp: user, mangoAddress: mangoAcc };
}
public async createAndMintUsers(mangoCookie: MangoCookie, nbUsers: number, authority : Keypair): Promise<MangoUser[]> {
const mangoGroup = await this.getMangoGroup(mangoCookie)
const rootBanks = await mangoGroup.loadRootBanks(this.conn)
const nodeBanksList = await Promise.all(rootBanks.map(x => x != undefined ? x.loadNodeBanks(this.conn): Promise.resolve(undefined)))
const nodeBanks = nodeBanksList.map(x => x!=undefined ? x[0] : undefined);
const usdcAcc = await this.mintUtils.createTokenAccount(mangoCookie.usdcMint, this.authority, this.authority.publicKey);
await splToken.mintTo(
this.conn,
this.authority,
mangoCookie.usdcMint,
usdcAcc,
this.authority,
10_000_000_000 * nbUsers,
)
const tmpAccounts : PublicKey []= new Array(mangoCookie.tokens.length)
for (const tokenIte of mangoCookie.tokens) {
if (tokenIte[1] === null)
{
continue;
}
const acc = await this.mintUtils.createTokenAccount(tokenIte[1].mint, this.authority, this.authority.publicKey);
await splToken.mintTo(
this.conn,
this.authority,
tokenIte[1].mint,
acc,
this.authority,
1_000_000_000 * nbUsers,
)
tmpAccounts[tokenIte[1].marketIndex] = acc
}
// create in batches of 10
let users : MangoUser[] = [];
for (let i=0; i<Math.floor((nbUsers/10)); ++i) {
let users_batch = await Promise.all( [...Array(10)].map(_x => this.createUser(mangoCookie, mangoGroup, usdcAcc, rootBanks, nodeBanks, tmpAccounts, authority)));
users = users.concat(users_batch)
}
if (nbUsers%10 != 0) {
let last_batch = await Promise.all( [...Array(nbUsers%10)].map(_x => this.createUser(mangoCookie, mangoGroup, usdcAcc, rootBanks, nodeBanks, tmpAccounts, authority)));
users = users.concat(last_batch)
}
return users;
}
}

View File

@ -0,0 +1,88 @@
import * as splToken from "@solana/spl-token";
import {
PublicKey,
Connection,
Keypair,
} from "@solana/web3.js";
import { Market, OpenOrders } from "@project-serum/serum";
import { PythUtils } from "./pyth_utils";
import { SerumUtils } from "./serum_utils";
export interface TokenData {
mint : PublicKey,
market : Market | undefined,
startingPrice : number,
nbDecimals: number,
priceOracle: Keypair | undefined,
}
export class MintUtils {
private conn: Connection;
private authority: Keypair;
private recentBlockhash: string;
private pythUtils : PythUtils;
private serumUtils : SerumUtils;
constructor(conn: Connection, authority: Keypair, dexProgramId: PublicKey, pythProgramId: PublicKey) {
this.conn = conn;
this.authority = authority;
this.recentBlockhash = "";
this.pythUtils = new PythUtils(conn, authority, pythProgramId);
this.serumUtils = new SerumUtils(conn, authority, dexProgramId);
}
async createMint(nb_decimals = 6) : Promise<PublicKey> {
const kp = Keypair.generate();
return await splToken.createMint(this.conn,
this.authority,
this.authority.publicKey,
this.authority.publicKey,
nb_decimals,
kp)
}
public async updateTokenPrice(tokenData: TokenData, newPrice: number) {
this.pythUtils.updatePriceAccount(tokenData.priceOracle,
{
exponent: tokenData.nbDecimals,
aggregatePriceInfo: {
price: BigInt(newPrice),
conf: BigInt(newPrice * 0.01),
},
});
}
public async createNewToken(quoteToken: PublicKey, nbDecimals = 6, startingPrice = 1_000_000) {
const mint = await this.createMint(nbDecimals);
const tokenData : TokenData = {
mint: mint,
market : await this.serumUtils.createMarket({
baseToken : mint,
quoteToken: quoteToken,
baseLotSize : 1000,
quoteLotSize : 1000,
feeRateBps : 0,
}),
startingPrice : startingPrice,
nbDecimals: nbDecimals,
priceOracle : await this.pythUtils.createPriceAccount(),
};
await this.updateTokenPrice(tokenData, startingPrice)
return tokenData;
}
public async createTokenAccount(mint: PublicKey, payer: Keypair, owner: PublicKey) {
const account = Keypair.generate();
return splToken.createAccount(
this.conn,
payer,
mint,
owner,
account
)
}
}

View File

@ -0,0 +1,322 @@
import * as anchor from "@project-serum/anchor";
import * as pyth from "@pythnetwork/client";
import {
Connection,
Keypair,
PublicKey,
SystemProgram,
Transaction,
sendAndConfirmTransaction,
} from "@solana/web3.js";
const PRICE_ACCOUNT_SIZE = 3312;
export interface Price {
version?: number;
type?: number;
size?: number;
priceType?: string;
exponent?: number;
currentSlot?: bigint;
validSlot?: bigint;
twap?: Ema;
productAccountKey?: PublicKey;
nextPriceAccountKey?: PublicKey;
aggregatePriceUpdaterAccountKey?: PublicKey;
aggregatePriceInfo?: PriceInfo;
priceComponents?: PriceComponent[];
}
export interface PriceInfo {
price?: bigint;
conf?: bigint;
status?: string;
corpAct?: string;
pubSlot?: bigint;
}
export interface PriceComponent {
publisher?: PublicKey;
agg?: PriceInfo;
latest?: PriceInfo;
}
export interface Product {
version?: number;
atype?: number;
size?: number;
priceAccount?: PublicKey;
attributes?: Record<string, string>;
}
export interface Ema {
valueComponent?: bigint;
numerator?: bigint;
denominator?: bigint;
}
export class PythUtils {
conn: Connection;
authority: Keypair;
pythProgramId: PublicKey;
async createAccount(size : number) : Promise<Keypair> {
const lamports = await this.conn.getMinimumBalanceForRentExemption(size);
let address = Keypair.generate();
const transaction = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: this.authority.publicKey,
newAccountPubkey: address.publicKey,
lamports,
space: size,
programId : this.pythProgramId,
}))
transaction.feePayer = this.authority.publicKey;
let hash = await this.conn.getRecentBlockhash();
transaction.recentBlockhash = hash.blockhash;
// Sign transaction, broadcast, and confirm
await sendAndConfirmTransaction(
this.conn,
transaction,
[this.authority, address],
{ commitment: 'confirmed' },
);
return address;
}
async store(account: Keypair, offset: number, input: Buffer) {
let keys = [
{ isSigner: true, isWritable: true, pubkey: account.publicKey },
];
let offsetBN = new anchor.BN(offset);
//const instructionData = Buffer.from([235, 116, 91, 200, 206, 170, 144, 120]);
const data = Buffer.concat([offsetBN.toBuffer("le", 8), input]);
const transaction = new anchor.web3.Transaction().add(
new anchor.web3.TransactionInstruction({
keys,
programId : this.pythProgramId,
data
}
),
);
transaction.feePayer = this.authority.publicKey;
let hash = await this.conn.getRecentBlockhash();
transaction.recentBlockhash = hash.blockhash;
// Sign transaction, broadcast, and confirm
const signature = await anchor.web3.sendAndConfirmTransaction(
this.conn,
transaction,
[this.authority, account],
{ commitment: 'confirmed' },
);
}
constructor(conn: Connection, authority: Keypair, pythProgramId: PublicKey) {
this.conn = conn;
this.authority = authority;
this.pythProgramId = pythProgramId;
}
async createPriceAccount(): Promise<Keypair> {
return this.createAccount(PRICE_ACCOUNT_SIZE);
}
async createProductAccount(): Promise<Keypair> {
return this.createPriceAccount();
}
async updatePriceAccount(account: Keypair, data: Price) {
const buf = Buffer.alloc(512);
const d = getPriceDataWithDefaults(data);
if (!d.aggregatePriceInfo || !d.twap)
{
return;
}
d.aggregatePriceInfo = getPriceInfoWithDefaults(d.aggregatePriceInfo);
d.twap = getEmaWithDefaults(d.twap);
writePriceBuffer(buf, 0, d);
await this.store(account, 0, buf);
}
async updateProductAccount(account: Keypair, data: Product) {
const buf = Buffer.alloc(512);
const d = getProductWithDefaults(data);
writeProductBuffer(buf, 0, d);
await this.store(account, 0, buf);
}
}
function writePublicKeyBuffer(buf: Buffer, offset: number, key: PublicKey) {
buf.write(key.toBuffer().toString("binary"), offset, "binary");
}
function writePriceBuffer(buf: Buffer, offset: number, data: Price) {
buf.writeUInt32LE(pyth.Magic, offset + 0);
buf.writeUInt32LE(data.version, offset + 4);
buf.writeUInt32LE(data.type, offset + 8);
buf.writeUInt32LE(data.size, offset + 12);
buf.writeUInt32LE(convertPriceType(data.priceType), offset + 16);
buf.writeInt32LE(data.exponent, offset + 20);
buf.writeUInt32LE(data.priceComponents.length, offset + 24);
buf.writeBigUInt64LE(data.currentSlot, offset + 32);
buf.writeBigUInt64LE(data.validSlot, offset + 40);
buf.writeBigInt64LE(data.twap.valueComponent, offset + 48);
buf.writeBigInt64LE(data.twap.numerator, offset + 56);
buf.writeBigInt64LE(data.twap.denominator, offset + 64);
writePublicKeyBuffer(buf, offset + 112, data.productAccountKey);
writePublicKeyBuffer(buf, offset + 144, data.nextPriceAccountKey);
writePublicKeyBuffer(
buf,
offset + 176,
data.aggregatePriceUpdaterAccountKey
);
writePriceInfoBuffer(buf, 208, data.aggregatePriceInfo);
let pos = offset + 240;
for (const component of data.priceComponents) {
writePriceComponentBuffer(buf, pos, component);
pos += 96;
}
}
function writePriceInfoBuffer(buf: Buffer, offset: number, info: PriceInfo) {
buf.writeBigInt64LE(info.price, offset + 0);
buf.writeBigUInt64LE(info.conf, offset + 8);
buf.writeUInt32LE(convertPriceStatus(info.status), offset + 16);
buf.writeBigUInt64LE(info.pubSlot, offset + 24);
}
function writePriceComponentBuffer(
buf: Buffer,
offset: number,
component: PriceComponent
) {
component.publisher.toBuffer().copy(buf, offset);
writePriceInfoBuffer(buf, offset + 32, component.agg);
writePriceInfoBuffer(buf, offset + 64, component.latest);
}
function writeProductBuffer(buf: Buffer, offset: number, product: Product) {
let accountSize = product.size;
if (!accountSize) {
accountSize = 48;
for (const key in product.attributes) {
accountSize += 1 + key.length;
accountSize += 1 + product.attributes[key].length;
}
}
buf.writeUInt32LE(pyth.Magic, offset + 0);
buf.writeUInt32LE(product.version, offset + 4);
buf.writeUInt32LE(product.atype, offset + 8);
buf.writeUInt32LE(accountSize, offset + 12);
writePublicKeyBuffer(buf, offset + 16, product.priceAccount);
let pos = offset + 48;
for (const key in product.attributes) {
buf.writeUInt8(key.length, pos);
buf.write(key, pos + 1);
pos += 1 + key.length;
const value = product.attributes[key];
buf.writeUInt8(value.length, pos);
buf.write(value, pos + 1);
}
}
function convertPriceType(type: string): number {
return 1;
}
function convertPriceStatus(status: string): number {
return 1;
}
function getPriceDataWithDefaults({
version = pyth.Version2,
type = 3,
size = PRICE_ACCOUNT_SIZE,
priceType = "price",
exponent = 0,
currentSlot = 0n,
validSlot = 0n,
twap = {},
productAccountKey = PublicKey.default,
nextPriceAccountKey = PublicKey.default,
aggregatePriceUpdaterAccountKey = PublicKey.default,
aggregatePriceInfo = {},
priceComponents = [],
}: Price): Price {
return {
version,
type,
size,
priceType,
exponent,
currentSlot,
validSlot,
twap,
productAccountKey,
nextPriceAccountKey,
aggregatePriceUpdaterAccountKey,
aggregatePriceInfo,
priceComponents,
};
}
function getPriceInfoWithDefaults({
price = 0n,
conf = 0n,
status = "trading",
corpAct = "no_corp_act",
pubSlot = 0n,
}: PriceInfo): PriceInfo {
return {
price,
conf,
status,
corpAct,
pubSlot,
};
}
function getEmaWithDefaults({
valueComponent = 0n,
denominator = 0n,
numerator = 0n,
}: Ema): Ema {
return {
valueComponent,
denominator,
numerator,
};
}
function getProductWithDefaults({
version = pyth.Version2,
atype = 2,
size = 0,
priceAccount = PublicKey.default,
attributes = {},
}: Product): Product {
return {
version,
atype,
size,
priceAccount,
attributes,
};
}

View File

@ -0,0 +1,360 @@
import { Market, DexInstructions, OpenOrders, } from "@project-serum/serum";
import {
Connection,
Keypair,
LAMPORTS_PER_SOL,
PublicKey,
SystemProgram,
TransactionInstruction,
Transaction,
sendAndConfirmTransaction,
Signer,
} from "@solana/web3.js";
import { BN } from "@project-serum/anchor";
import * as splToken from "@solana/spl-token";
export class SerumUtils {
private authority: Keypair;
private payer : PublicKey;
private conn: Connection;
private dexProgramId : PublicKey;
constructor(
conn: Connection,
authority: Keypair,
dexProgramId : PublicKey) {
this.conn = conn;
this.authority = authority;
this.payer = authority.publicKey;
this.dexProgramId = dexProgramId;
}
private async createAccountIx(
account: PublicKey,
space: number,
programId: PublicKey
): Promise<TransactionInstruction> {
return SystemProgram.createAccount({
newAccountPubkey: account,
fromPubkey: this.payer,
lamports: await this.conn.getMinimumBalanceForRentExemption(space),
space,
programId,
});
}
async createAccount( owner : Keypair, pid : PublicKey, space: number): Promise<Keypair> {
const newAccount = new Keypair();
const createTx = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: owner.publicKey,
newAccountPubkey: newAccount.publicKey,
programId: pid,
lamports: await this.conn.getMinimumBalanceForRentExemption(
space
),
space,
})
);
await sendAndConfirmTransaction(this.conn, createTx, [
owner,
newAccount,
]);
return newAccount;
}
async createTokenAccount(mint: PublicKey, payer: Keypair, owner: PublicKey) : Promise<PublicKey> {
return splToken.createAccount(this.conn,
payer,
mint,
owner,)
}
async transaction(): Promise<Transaction> {
return new Transaction({
feePayer: this.authority.publicKey,
recentBlockhash: (await this.conn.getRecentBlockhash()).blockhash,
});
}
/**
* Create a new Serum market
*/
public async createMarket(info: CreateMarketInfo): Promise<Market> {
const owner = this.authority;
const market = await this.createAccount( owner, this.dexProgramId, Market.getLayout(this.dexProgramId).span,);
const requestQueue = await this.createAccount( owner, this.dexProgramId, 5132);
const eventQueue = await this.createAccount( owner, this.dexProgramId, 262156);
const bids = await this.createAccount( owner, this.dexProgramId, 65548);
const asks = await this.createAccount( owner, this.dexProgramId, 65548);
const quoteDustThreshold = new BN(100);
const [vaultOwner, vaultOwnerBump] = await this.findVaultOwner(
market.publicKey
);
const [baseVault, quoteVault] = await Promise.all([
splToken.createAccount(this.conn, this.authority, info.baseToken, vaultOwner, Keypair.generate()),
splToken.createAccount(this.conn, this.authority, info.quoteToken, vaultOwner, Keypair.generate()),
]);
const initMarketTx = (await this.transaction()).add(
DexInstructions.initializeMarket(
toPublicKeys({
market,
requestQueue,
eventQueue,
bids,
asks,
baseVault,
quoteVault,
baseMint: info.baseToken,
quoteMint: info.quoteToken,
baseLotSize: new BN(info.baseLotSize),
quoteLotSize: new BN(info.quoteLotSize),
feeRateBps: info.feeRateBps,
vaultSignerNonce: vaultOwnerBump,
quoteDustThreshold,
programId: this.dexProgramId,
})
)
);
await sendAndConfirmTransaction(this.conn, initMarketTx, [this.authority]);
let mkt = await Market.load(
this.conn,
market.publicKey,
{ commitment: "recent" },
this.dexProgramId
);
return mkt;
}
async createWallet(lamports: number): Promise<Keypair> {
const wallet = Keypair.generate();
const fundTx = new Transaction().add(
SystemProgram.transfer({
fromPubkey: this.authority.publicKey,
toPubkey: wallet.publicKey,
lamports,
})
);
await sendAndConfirmTransaction(this.conn, fundTx, [this.authority]);
return wallet;
}
public async getMarket(market: PublicKey) {
return Market.load(this.conn, market, {commitment: "confirmed"}, this.dexProgramId);
}
public async createMarketMaker(
lamports: number,
tokens: [PublicKey, BN][]
): Promise<MarketMaker> {
const account = await this.createWallet(lamports);
const tokenAccounts = {};
const transactions = [];
for (const [token, amount] of tokens) {
const publicKey = await this.createTokenAccount(
token,
this.authority,
account.publicKey,
);
splToken.mintTo( this.conn, this.authority, token, publicKey, this.authority, amount.toNumber())
tokenAccounts[token.toBase58()] = publicKey;
}
return new MarketMaker(account, tokenAccounts);
}
public async createAndMakeMarket(baseToken: PublicKey, quoteToken: PublicKey, marketPrice: number, exp : number): Promise<Market> {
const market = await this.createMarket({
baseToken,
quoteToken,
baseLotSize: 1000,
quoteLotSize: 100,
feeRateBps: 0,
});
let nb = Math.floor(40000/marketPrice);
{
const marketMaker = await this.createMarketMaker(
1 * LAMPORTS_PER_SOL,
[
[baseToken, new BN(nb * 10)],
[quoteToken, new BN(nb * 10)],
]
);
const bids = MarketMaker.makeOrders([[marketPrice * 0.995, nb]]);
const asks = MarketMaker.makeOrders([[marketPrice * 1.005, nb]]);
await marketMaker.placeOrders(this.conn, market, bids, asks);
}
return market;
}
public async findVaultOwner(market: PublicKey): Promise<[PublicKey, BN]> {
const bump = new BN(0);
while (bump.toNumber() < 255) {
try {
const vaultOwner = await PublicKey.createProgramAddress(
[market.toBuffer(), bump.toArrayLike(Buffer, "le", 8)],
this.dexProgramId
);
return [vaultOwner, bump];
} catch (_e) {
bump.iaddn(1);
}
}
throw new Error("no seed found for vault owner");
}
}
export interface CreateMarketInfo {
baseToken: PublicKey;
quoteToken: PublicKey;
baseLotSize: number;
quoteLotSize: number;
feeRateBps: number;
}
export interface Order {
price: number;
size: number;
}
export class MarketMaker {
public account: Keypair;
public tokenAccounts: { [mint: string]: PublicKey };
constructor(
account: Keypair,
tokenAccounts: { [mint: string]: PublicKey }
) {
this.account = account;
this.tokenAccounts = tokenAccounts;
}
static makeOrders(orders: [number, number][]): Order[] {
return orders.map(([price, size]) => ({ price, size }));
}
async placeOrders(connection : Connection, market: Market, bids: Order[], asks: Order[]) {
await connection.confirmTransaction(
await connection.requestAirdrop(this.account.publicKey, 20 * LAMPORTS_PER_SOL),
"confirmed"
);
const baseTokenAccount =
this.tokenAccounts[market.baseMintAddress.toBase58()];
const quoteTokenAccount =
this.tokenAccounts[market.quoteMintAddress.toBase58()];
const askOrderTxs = [];
const bidOrderTxs = [];
const placeOrderDefaultParams = {
owner: this.account.publicKey,
clientId: undefined,
openOrdersAddressKey: undefined,
openOrdersAccount: undefined,
feeDiscountPubkey: null,
};
for (const entry of asks) {
const { transaction, signers } =
await market.makePlaceOrderTransaction(
connection,
{
payer: baseTokenAccount,
side: "sell",
price: entry.price,
size: entry.size,
orderType: "limit",
selfTradeBehavior: "decrementTake",
...placeOrderDefaultParams,
}
);
askOrderTxs.push([transaction, [this.account, ...signers]]);
}
for (const entry of bids) {
const { transaction, signers } =
await market.makePlaceOrderTransaction(
connection,
{
payer: quoteTokenAccount,
side: "buy",
price: entry.price,
size: entry.size,
orderType: "limit",
selfTradeBehavior: "decrementTake",
...placeOrderDefaultParams,
}
);
bidOrderTxs.push([transaction, [this.account, ...signers]]);
}
await this.sendAndConfirmTransactionSet(
connection,
...askOrderTxs,
...bidOrderTxs
);
}
async sendAndConfirmTransactionSet(
connection: Connection,
...transactions: [Transaction, Signer[]][]
): Promise<string[]> {
const signatures = await Promise.all(
transactions.map(([t, s]) =>
connection.sendTransaction(t, s)
)
);
const result = await Promise.all(
signatures.map((s) => connection.confirmTransaction(s))
);
const failedTx = result.filter((r) => r.value.err != null);
if (failedTx.length > 0) {
throw new Error(`Transactions failed: ${failedTx}`);
}
return signatures;
}
}
export function toPublicKeys(
obj: Record<string, string | PublicKey | HasPublicKey | any>
): any {
const newObj = {};
for (const key in obj) {
const value = obj[key];
if (typeof value == "string") {
newObj[key] = new PublicKey(value);
} else if (typeof value == "object" && "publicKey" in value) {
newObj[key] = value.publicKey;
} else {
newObj[key] = value;
}
}
return newObj;
}
interface HasPublicKey {
publicKey: PublicKey;
}

View File

@ -0,0 +1,5 @@
{
"mango":"97Jd7kcwULGAdqsWTsdYQrqt6gQdYeowR767iTS4JFpD",
"serum_dex":"JE56RNCPMmb5x8QUo9FFpPyBjPesE3YZhFajDk4g5g7h",
"pyth_mock":"DwRHy1GcMxVtZJTcbMTe4TyKNRy8Dsb5hSiEVr2hRKZG"
}

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"devDependencies": {
"typescript": "^4.8.3",
"ts-node": "^10.9.1"
},
"dependencies": {
"@blockworks-foundation/mango-client": "^3.6.20",
"@project-serum/anchor": "^0.25.0",
"@project-serum/serum": "^0.13.65",
"@pythnetwork/client": "^2.8.0",
"@solana/spl-token": "^0.3.5",
"@solana/web3.js": "^1.70.1",
"yarn": "^1.22.19"
}
}

100
tsconfig.json Executable file
View File

@ -0,0 +1,100 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
// "incremental": true, /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
"resolveJsonModule": true, /* Enable importing .json files */
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

863
yarn.lock Normal file
View File

@ -0,0 +1,863 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec"
integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==
dependencies:
regenerator-runtime "^0.13.11"
"@blockworks-foundation/mango-client@^3.6.20":
version "3.6.20"
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.6.20.tgz#66ed0ae4545959fdc31ca1918de0f931de579d41"
integrity sha512-Te0i52KUyp5e8jQQZlIMsTy9fKIfefPHvkA8+NRGIH80kQcnJKKfzw3T1NxaDsc3KFMZwpuN3m4afDNpKTuF0g==
dependencies:
"@project-serum/anchor" "^0.21.0"
"@project-serum/serum" "^0.13.65"
"@project-serum/sol-wallet-adapter" "^0.2.0"
"@solana/spl-token" "^0.1.6"
"@solana/web3.js" "^1.43.5"
big.js "^6.1.1"
bn.js "^5.1.0"
buffer-layout "^1.2.1"
cross-fetch "^3.1.5"
dotenv "^10.0.0"
toformat "^2.0.0"
yargs "^17.0.1"
"@coral-xyz/anchor@^0.28.1-beta.1":
version "0.28.1-beta.1"
resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.28.1-beta.1.tgz#0f71a3b62e38527f50d4a80e1fb4d20ba95926e2"
integrity sha512-JdKr4IQqY719wts0wzhHIffpAo4fdJ25gPLfFN75d6LMfCKvwPupyDUigaT+ac6UWTw/bDdBbJFY6QSRvlTnrA==
dependencies:
"@coral-xyz/borsh" "^0.28.0"
"@solana/web3.js" "^1.68.0"
base64-js "^1.5.1"
bn.js "^5.1.2"
bs58 "^4.0.1"
buffer-layout "^1.2.2"
camelcase "^6.3.0"
cross-fetch "^3.1.5"
crypto-hash "^1.3.0"
eventemitter3 "^4.0.7"
js-sha256 "^0.9.0"
pako "^2.0.3"
snake-case "^3.0.4"
superstruct "^0.15.4"
toml "^3.0.0"
"@coral-xyz/borsh@^0.28.0":
version "0.28.0"
resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.28.0.tgz#fa368a2f2475bbf6f828f4657f40a52102e02b6d"
integrity sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ==
dependencies:
bn.js "^5.1.2"
buffer-layout "^1.2.0"
"@cspotcode/source-map-support@^0.8.0":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
dependencies:
"@jridgewell/trace-mapping" "0.3.9"
"@jridgewell/resolve-uri@^3.0.3":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
"@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.15"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@jridgewell/trace-mapping@0.3.9":
version "0.3.9"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
dependencies:
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@noble/curves@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d"
integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==
dependencies:
"@noble/hashes" "1.3.1"
"@noble/hashes@1.3.1", "@noble/hashes@^1.3.0":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9"
integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==
"@project-serum/anchor@^0.11.1":
version "0.11.1"
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.11.1.tgz#155bff2c70652eafdcfd5559c81a83bb19cec9ff"
integrity sha512-oIdm4vTJkUy6GmE6JgqDAuQPKI7XM4TPJkjtoIzp69RZe0iAD9JP2XHx7lV1jLdYXeYHqDXfBt3zcq7W91K6PA==
dependencies:
"@project-serum/borsh" "^0.2.2"
"@solana/web3.js" "^1.17.0"
base64-js "^1.5.1"
bn.js "^5.1.2"
bs58 "^4.0.1"
buffer-layout "^1.2.0"
camelcase "^5.3.1"
crypto-hash "^1.3.0"
eventemitter3 "^4.0.7"
find "^0.3.0"
js-sha256 "^0.9.0"
pako "^2.0.3"
snake-case "^3.0.4"
toml "^3.0.0"
"@project-serum/anchor@^0.21.0":
version "0.21.0"
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.21.0.tgz#ad5fb33744991ec1900cdb2fd22707c908b12b5f"
integrity sha512-flRuW/F+iC8mitNokx82LOXyND7Dyk6n5UUPJpQv/+NfySFrNFlzuQZaBZJ4CG5g9s8HS/uaaIz1nVkDR8V/QA==
dependencies:
"@project-serum/borsh" "^0.2.4"
"@solana/web3.js" "^1.17.0"
base64-js "^1.5.1"
bn.js "^5.1.2"
bs58 "^4.0.1"
buffer-layout "^1.2.2"
camelcase "^5.3.1"
cross-fetch "^3.1.5"
crypto-hash "^1.3.0"
eventemitter3 "^4.0.7"
find "^0.3.0"
js-sha256 "^0.9.0"
pako "^2.0.3"
snake-case "^3.0.4"
toml "^3.0.0"
"@project-serum/anchor@^0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.25.0.tgz#88ee4843336005cf5a64c80636ce626f0996f503"
integrity sha512-E6A5Y/ijqpfMJ5psJvbw0kVTzLZFUcOFgs6eSM2M2iWE1lVRF18T6hWZVNl6zqZsoz98jgnNHtVGJMs+ds9A7A==
dependencies:
"@project-serum/borsh" "^0.2.5"
"@solana/web3.js" "^1.36.0"
base64-js "^1.5.1"
bn.js "^5.1.2"
bs58 "^4.0.1"
buffer-layout "^1.2.2"
camelcase "^5.3.1"
cross-fetch "^3.1.5"
crypto-hash "^1.3.0"
eventemitter3 "^4.0.7"
js-sha256 "^0.9.0"
pako "^2.0.3"
snake-case "^3.0.4"
superstruct "^0.15.4"
toml "^3.0.0"
"@project-serum/borsh@^0.2.2", "@project-serum/borsh@^0.2.4", "@project-serum/borsh@^0.2.5":
version "0.2.5"
resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663"
integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q==
dependencies:
bn.js "^5.1.2"
buffer-layout "^1.2.0"
"@project-serum/serum@^0.13.65":
version "0.13.65"
resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.65.tgz#6d3cf07912f13985765237f053cca716fe84b0b0"
integrity sha512-BHRqsTqPSfFB5p+MgI2pjvMBAQtO8ibTK2fYY96boIFkCI3TTwXDt2gUmspeChKO2pqHr5aKevmexzAcXxrSRA==
dependencies:
"@project-serum/anchor" "^0.11.1"
"@solana/spl-token" "^0.1.6"
"@solana/web3.js" "^1.21.0"
bn.js "^5.1.2"
buffer-layout "^1.2.0"
"@project-serum/sol-wallet-adapter@^0.2.0":
version "0.2.6"
resolved "https://registry.yarnpkg.com/@project-serum/sol-wallet-adapter/-/sol-wallet-adapter-0.2.6.tgz#b4cd25a566294354427c97c26d716112b91a0107"
integrity sha512-cpIb13aWPW8y4KzkZAPDgw+Kb+DXjCC6rZoH74MGm3I/6e/zKyGnfAuW5olb2zxonFqsYgnv7ev8MQnvSgJ3/g==
dependencies:
bs58 "^4.0.1"
eventemitter3 "^4.0.7"
"@pythnetwork/client@^2.8.0":
version "2.19.0"
resolved "https://registry.yarnpkg.com/@pythnetwork/client/-/client-2.19.0.tgz#4b3b4fccb402d93ff00c01beec633e6f2bac94fe"
integrity sha512-0VSQ0NqBOa5EtloXbOVYZ6Wpu8CLP3oaOKVTaUMSX/HXbB00S6G+xdwF7stxo6emgrAMopotx3icEVug5Lpomg==
dependencies:
"@coral-xyz/anchor" "^0.28.1-beta.1"
"@coral-xyz/borsh" "^0.28.0"
buffer "^6.0.1"
"@solana/buffer-layout-utils@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca"
integrity sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==
dependencies:
"@solana/buffer-layout" "^4.0.0"
"@solana/web3.js" "^1.32.0"
bigint-buffer "^1.1.5"
bignumber.js "^9.0.1"
"@solana/buffer-layout@^4.0.0":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15"
integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==
dependencies:
buffer "~6.0.3"
"@solana/spl-token@^0.1.6":
version "0.1.8"
resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6"
integrity sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ==
dependencies:
"@babel/runtime" "^7.10.5"
"@solana/web3.js" "^1.21.0"
bn.js "^5.1.0"
buffer "6.0.3"
buffer-layout "^1.2.0"
dotenv "10.0.0"
"@solana/spl-token@^0.3.5":
version "0.3.8"
resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.8.tgz#8e9515ea876e40a4cc1040af865f61fc51d27edf"
integrity sha512-ogwGDcunP9Lkj+9CODOWMiVJEdRtqHAtX2rWF62KxnnSWtMZtV9rDhTrZFshiyJmxDnRL/1nKE1yJHg4jjs3gg==
dependencies:
"@solana/buffer-layout" "^4.0.0"
"@solana/buffer-layout-utils" "^0.2.0"
buffer "^6.0.3"
"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.43.5", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.70.1":
version "1.77.3"
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.77.3.tgz#2cbeaa1dd24f8fa386ac924115be82354dfbebab"
integrity sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w==
dependencies:
"@babel/runtime" "^7.12.5"
"@noble/curves" "^1.0.0"
"@noble/hashes" "^1.3.0"
"@solana/buffer-layout" "^4.0.0"
agentkeepalive "^4.2.1"
bigint-buffer "^1.1.5"
bn.js "^5.0.0"
borsh "^0.7.0"
bs58 "^4.0.1"
buffer "6.0.3"
fast-stable-stringify "^1.0.0"
jayson "^4.1.0"
node-fetch "^2.6.7"
rpc-websockets "^7.5.1"
superstruct "^0.14.2"
"@tsconfig/node10@^1.0.7":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
"@tsconfig/node12@^1.0.7":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
"@tsconfig/node14@^1.0.0":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
"@tsconfig/node16@^1.0.2":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
"@types/connect@^3.4.33":
version "3.4.35"
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==
dependencies:
"@types/node" "*"
"@types/node@*":
version "20.3.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe"
integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==
"@types/node@^12.12.54":
version "12.20.55"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240"
integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==
"@types/ws@^7.4.4":
version "7.4.7"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702"
integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==
dependencies:
"@types/node" "*"
JSONStream@^1.3.5:
version "1.3.5"
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==
dependencies:
jsonparse "^1.2.0"
through ">=2.2.7 <3"
acorn-walk@^8.1.1:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
acorn@^8.4.1:
version "8.9.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59"
integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==
agentkeepalive@^4.2.1:
version "4.3.0"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255"
integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==
dependencies:
debug "^4.1.0"
depd "^2.0.0"
humanize-ms "^1.2.1"
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-styles@^4.0.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
color-convert "^2.0.1"
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
base-x@^3.0.2:
version "3.0.9"
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320"
integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==
dependencies:
safe-buffer "^5.0.1"
base64-js@^1.3.1, base64-js@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
big.js@^6.1.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.2.1.tgz#7205ce763efb17c2e41f26f121c420c6a7c2744f"
integrity sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ==
bigint-buffer@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442"
integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==
dependencies:
bindings "^1.3.0"
bignumber.js@^9.0.1:
version "9.1.1"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6"
integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==
bindings@^1.3.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
dependencies:
file-uri-to-path "1.0.0"
bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2, bn.js@^5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70"
integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==
borsh@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a"
integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==
dependencies:
bn.js "^5.2.0"
bs58 "^4.0.0"
text-encoding-utf-8 "^1.0.2"
bs58@^4.0.0, bs58@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==
dependencies:
base-x "^3.0.2"
buffer-layout@^1.2.0, buffer-layout@^1.2.1, buffer-layout@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.2.tgz#b9814e7c7235783085f9ca4966a0cfff112259d5"
integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==
buffer@6.0.3, buffer@^6.0.1, buffer@^6.0.3, buffer@~6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.2.1"
bufferutil@^4.0.1:
version "4.0.7"
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad"
integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==
dependencies:
node-gyp-build "^4.3.0"
camelcase@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
camelcase@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
cliui@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.1"
wrap-ansi "^7.0.0"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
commander@^2.20.3:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-fetch@^3.1.5:
version "3.1.6"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.6.tgz#bae05aa31a4da760969756318feeee6e70f15d6c"
integrity sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==
dependencies:
node-fetch "^2.6.11"
crypto-hash@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247"
integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==
debug@^4.1.0:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
delay@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d"
integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==
depd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
dot-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
dotenv@10.0.0, dotenv@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
es6-promise@^4.0.3:
version "4.2.8"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
es6-promisify@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==
dependencies:
es6-promise "^4.0.3"
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
eventemitter3@^4.0.7:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
eyes@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==
fast-stable-stringify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313"
integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==
file-uri-to-path@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
find@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/find/-/find-0.3.0.tgz#4082e8fc8d8320f1a382b5e4f521b9bc50775cb8"
integrity sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw==
dependencies:
traverse-chain "~0.1.0"
get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
humanize-ms@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
dependencies:
ms "^2.0.0"
ieee754@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
isomorphic-ws@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc"
integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==
jayson@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/jayson/-/jayson-4.1.0.tgz#60dc946a85197317f2b1439d672a8b0a99cea2f9"
integrity sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==
dependencies:
"@types/connect" "^3.4.33"
"@types/node" "^12.12.54"
"@types/ws" "^7.4.4"
JSONStream "^1.3.5"
commander "^2.20.3"
delay "^5.0.0"
es6-promisify "^5.0.0"
eyes "^0.1.8"
isomorphic-ws "^4.0.1"
json-stringify-safe "^5.0.1"
uuid "^8.3.2"
ws "^7.4.5"
js-sha256@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966"
integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==
json-stringify-safe@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
jsonparse@^1.2.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
lower-case@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
dependencies:
tslib "^2.0.3"
make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@^2.0.0:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
no-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
dependencies:
lower-case "^2.0.2"
tslib "^2.0.3"
node-fetch@^2.6.11, node-fetch@^2.6.7:
version "2.6.11"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25"
integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==
dependencies:
whatwg-url "^5.0.0"
node-gyp-build@^4.3.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055"
integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==
pako@^2.0.3:
version "2.1.0"
resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86"
integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==
regenerator-runtime@^0.13.11:
version "0.13.11"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
rpc-websockets@^7.5.1:
version "7.5.1"
resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.5.1.tgz#e0a05d525a97e7efc31a0617f093a13a2e10c401"
integrity sha512-kGFkeTsmd37pHPMaHIgN1LVKXMi0JD782v4Ds9ZKtLlwdTKjn+CxM9A9/gLT2LaOuEcEFGL98h1QWQtlOIdW0w==
dependencies:
"@babel/runtime" "^7.17.2"
eventemitter3 "^4.0.7"
uuid "^8.3.2"
ws "^8.5.0"
optionalDependencies:
bufferutil "^4.0.1"
utf-8-validate "^5.0.2"
safe-buffer@^5.0.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
snake-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
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.15.4:
version "0.15.5"
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.15.5.tgz#0f0a8d3ce31313f0d84c6096cd4fa1bfdedc9dab"
integrity sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==
text-encoding-utf-8@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13"
integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==
"through@>=2.2.7 <3":
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
toformat@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/toformat/-/toformat-2.0.0.tgz#7a043fd2dfbe9021a4e36e508835ba32056739d8"
integrity sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ==
toml@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee"
integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
traverse-chain@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1"
integrity sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg==
ts-node@^10.9.1:
version "10.9.1"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
dependencies:
"@cspotcode/source-map-support" "^0.8.0"
"@tsconfig/node10" "^1.0.7"
"@tsconfig/node12" "^1.0.7"
"@tsconfig/node14" "^1.0.0"
"@tsconfig/node16" "^1.0.2"
acorn "^8.4.1"
acorn-walk "^8.1.1"
arg "^4.1.0"
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
tslib@^2.0.3:
version "2.5.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913"
integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==
typescript@^4.8.3:
version "4.9.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a"
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
utf-8-validate@^5.0.2:
version "5.0.10"
resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2"
integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==
dependencies:
node-gyp-build "^4.3.0"
uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
ws@^7.4.5:
version "7.5.9"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
ws@^8.5.0:
version "8.13.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
yargs-parser@^21.1.1:
version "21.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
yargs@^17.0.1:
version "17.7.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
dependencies:
cliui "^8.0.1"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.3"
y18n "^5.0.5"
yargs-parser "^21.1.1"
yarn@^1.22.19:
version "1.22.19"
resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.19.tgz#4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
integrity sha512-/0V5q0WbslqnwP91tirOvldvYISzaqhClxzyUKXYxs07yUILIs5jx/k6CFe8bvKSkds5w+eiOqta39Wk3WxdcQ==
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==