Raydium CLMM (#7)

* raydium_clamm in mango-router

---------

Co-authored-by: Maximilian Schneider <mail@maximilianschneider.net>
This commit is contained in:
Britt Cyr 2023-08-29 14:08:04 -04:00 committed by GitHub
parent 36a28e979e
commit 20c4c2bebd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 379 additions and 8 deletions

View File

@ -15,12 +15,15 @@
},
"dependencies": {
"@blockworks-foundation/mango-v4": "^0.9.5",
"@noble/hashes": "^1.3.1",
"@orca-so/common-sdk": "^0.1.12",
"@orca-so/whirlpools-sdk": "^0.8.2",
"@project-serum/anchor": "^0.26.0",
"@raydium-io/raydium-sdk": "^1.3.1-beta.8",
"@solana/web3.js": "^1.70.1",
"@types/bn.js": "^5.1.1",
"@types/node": "^18.11.18",
"bs58": "^5.0.0",
"cors": "^2.8.5",
"express": "^4.18.2",
"express-prom-bundle": "^6.6.0",

View File

@ -16,8 +16,12 @@ import {
swapQuoteByOutputToken,
} from "@orca-so/whirlpools-sdk";
import { AnchorProvider, BorshAccountsCoder, Idl } from "@project-serum/anchor";
import { PublicKey, TransactionInstruction } from "@solana/web3.js";
import { AmmV3, AmmV3PoolInfo, ApiAmmV3PoolsItem, MAINNET_PROGRAM_ID, PoolInfoLayout, ReturnTypeComputeAmountOut, ReturnTypeComputeAmountOutBaseOut, ReturnTypeFetchMultipleMintInfos, ReturnTypeFetchMultiplePoolInfos, ReturnTypeFetchMultiplePoolTickArrays, SqrtPriceMath, fetchMultipleMintInfos } from "@raydium-io/raydium-sdk";
import { Connection, EpochInfo, PublicKey, TransactionInstruction } from "@solana/web3.js";
import { sha256 } from "@noble/hashes/sha256";
import BN from "bn.js";
import bs58 from 'bs58';
export interface DepthResult {
label: string;
@ -199,6 +203,158 @@ class WhirlpoolEdge implements Edge {
}
}
class RaydiumEdge implements Edge {
constructor(
public label: string,
public inputMint: PublicKey,
public outputMint: PublicKey,
public poolPk: PublicKey,
public raydiumCache: RaydiumCache,
) {}
static pairFromPool(poolInfo: AmmV3PoolInfo, raydiumCache: RaydiumCache): Edge[] {
const label = "raydium: " + poolInfo.id;
const fwd = new RaydiumEdge(
label,
new PublicKey(poolInfo.mintA.mint),
new PublicKey(poolInfo.mintB.mint),
new PublicKey(poolInfo.id),
raydiumCache,
);
const bwd = new RaydiumEdge(
label,
new PublicKey(poolInfo.mintB.mint),
new PublicKey(poolInfo.mintA.mint),
new PublicKey(poolInfo.id),
raydiumCache,
);
return [fwd, bwd];
}
async swap(
amount: BN,
otherAmountThreshold: BN,
mode: SwapMode,
slippage: number
): Promise<SwapResult> {
try {
let ok: boolean = false;
let fee: BN;
let maxAmtIn: BN;
let minAmtOut: BN;
let feeRate: number;
if (mode === SwapMode.ExactIn) {
let amountOut: ReturnTypeComputeAmountOut = AmmV3.computeAmountOut(
{
poolInfo: this.raydiumCache.poolInfos[this.poolPk.toBase58()].state,
tickArrayCache: this.raydiumCache.tickArrayByPoolIds[this.poolPk.toBase58()],
baseMint: this.inputMint,
token2022Infos: this.raydiumCache.mintInfos,
epochInfo: this.raydiumCache.epochInfo,
amountIn: amount,
slippage: slippage,
}
);
ok = otherAmountThreshold.lte(amountOut.amountOut.amount);
fee = amountOut.fee;
maxAmtIn = amountOut.realAmountIn.amount;
feeRate = fee.toNumber() / maxAmtIn.toNumber();
minAmtOut = amountOut.minAmountOut.amount;
} else {
let amountIn: ReturnTypeComputeAmountOutBaseOut = AmmV3.computeAmountIn(
{
poolInfo: this.raydiumCache.poolInfos[this.poolPk.toBase58()].state,
tickArrayCache: this.raydiumCache.tickArrayByPoolIds[this.poolPk.toBase58()],
baseMint: this.outputMint,
token2022Infos: this.raydiumCache.mintInfos,
epochInfo: this.raydiumCache.epochInfo,
amountOut: amount,
slippage: slippage,
}
);
ok = otherAmountThreshold.lte(amountIn.amountIn.amount);
fee = amountIn.fee;
maxAmtIn = amountIn.maxAmountIn.amount;
feeRate = fee.toNumber() / maxAmtIn.toNumber();
minAmtOut = amountIn.realAmountOut.amount;
}
let instructions = async (wallet: PublicKey) => {
const tokenIn = await getAssociatedTokenAddress(this.inputMint, wallet);
const tokenOut = await getAssociatedTokenAddress(
this.outputMint,
wallet
);
const swapIx = AmmV3.makeSwapBaseInInstructions({
poolInfo: this.raydiumCache.poolInfos[this.poolPk.toBase58()].state,
ownerInfo: {
wallet: wallet,
tokenAccountA: tokenIn,
tokenAccountB: tokenOut,
},
inputMint: this.inputMint,
amountIn: amount,
amountOutMin: otherAmountThreshold,
sqrtPriceLimitX64: new BN(slippage),
remainingAccounts: [],
});
return swapIx.innerTransaction.instructions;
};
return {
ok: ok,
instructions,
label: this.poolPk.toString(),
marketInfos: [
{
label: "Raydium",
fee: {
amount: fee,
mint: this.inputMint,
rate: feeRate,
},
},
],
maxAmtIn: maxAmtIn,
minAmtOut: minAmtOut,
mints: [this.inputMint, this.outputMint],
};
} catch (err) {
return {
ok: false,
label: "",
marketInfos: [],
maxAmtIn: amount,
minAmtOut: otherAmountThreshold,
mints: [this.inputMint, this.outputMint],
instructions: async () => [],
};
}
}
}
export class RaydiumCache {
epochInfo: EpochInfo;
mintInfos: ReturnTypeFetchMultipleMintInfos;
poolInfos: ReturnTypeFetchMultiplePoolInfos;
tickArrayByPoolIds: ReturnTypeFetchMultiplePoolTickArrays;
constructor(
epochInfo: EpochInfo,
mintInfos: ReturnTypeFetchMultipleMintInfos,
poolInfos: ReturnTypeFetchMultiplePoolInfos,
tickArrayByPoolIds: ReturnTypeFetchMultiplePoolTickArrays,
) {
this.epochInfo = epochInfo;
this.mintInfos = mintInfos;
this.poolInfos = poolInfos;
this.tickArrayByPoolIds = tickArrayByPoolIds;
}
}
export class Router {
minTvl: number;
routes: Map<string, Map<string, Edge[]>>;
@ -206,12 +362,17 @@ export class Router {
whirlpoolClient: WhirlpoolClient;
whirlpoolSub?: number;
connection: Connection;
raydiumCache?: RaydiumCache;
raydiumPoolInfoSub?: number;
constructor(anchorProvider: AnchorProvider, minTvl: number) {
this.minTvl = minTvl;
this.routes = new Map();
this.whirlpoolClient = buildWhirlpoolClient(
WhirlpoolContext.withProvider(anchorProvider, ORCA_WHIRLPOOL_PROGRAM_ID)
);
this.connection = anchorProvider.connection;
}
public async start(): Promise<void> {
@ -235,6 +396,60 @@ export class Router {
},
"processed"
);
await this.indexRaydium();
// Only the poolInfo is worth updating. tickArray and mintInfos should not change.
const poolInfoDiscriminator = Buffer.from(
sha256("account:PoolState")
).slice(0, 8);
this.raydiumPoolInfoSub = this.connection.onProgramAccountChange(
MAINNET_PROGRAM_ID.CLMM,
(p) => {
const key = p.accountId.toBase58();
const accountData = p.accountInfo.data;
const layoutAccountInfo = PoolInfoLayout.decode(accountData);
// Cache only holds those filtered with enough TVL.
if (!(key in this.raydiumCache!.poolInfos)) {
return;
}
// Most of these fields dont matter, but update anyways.
this.raydiumCache!.poolInfos[key] = {
state: {
...this.raydiumCache!.poolInfos[key].state,
observationId: layoutAccountInfo.observationId,
creator: layoutAccountInfo.creator,
version: 6,
tickSpacing: layoutAccountInfo.tickSpacing,
liquidity: layoutAccountInfo.liquidity,
sqrtPriceX64: layoutAccountInfo.sqrtPriceX64,
currentPrice: SqrtPriceMath.sqrtPriceX64ToPrice(
layoutAccountInfo.sqrtPriceX64,
layoutAccountInfo.mintDecimalsA,
layoutAccountInfo.mintDecimalsB
),
tickCurrent: layoutAccountInfo.tickCurrent,
observationIndex: layoutAccountInfo.observationIndex,
observationUpdateDuration:
layoutAccountInfo.observationUpdateDuration,
feeGrowthGlobalX64A: layoutAccountInfo.feeGrowthGlobalX64A,
feeGrowthGlobalX64B: layoutAccountInfo.feeGrowthGlobalX64B,
protocolFeesTokenA: layoutAccountInfo.protocolFeesTokenA,
protocolFeesTokenB: layoutAccountInfo.protocolFeesTokenB,
swapInAmountTokenA: layoutAccountInfo.swapInAmountTokenA,
swapOutAmountTokenB: layoutAccountInfo.swapOutAmountTokenB,
swapInAmountTokenB: layoutAccountInfo.swapInAmountTokenB,
swapOutAmountTokenA: layoutAccountInfo.swapOutAmountTokenA,
tickArrayBitmap: layoutAccountInfo.tickArrayBitmap,
startTime: layoutAccountInfo.startTime.toNumber(),
},
};
},
"processed",
[{ memcmp: { offset: 0, bytes: bs58.encode(poolInfoDiscriminator) } }]
);
}
public async stop(): Promise<void> {
@ -243,6 +458,9 @@ export class Router {
.getContext()
.connection.removeProgramAccountChangeListener(this.whirlpoolSub);
}
if (this.raydiumPoolInfoSub) {
await this.connection.removeProgramAccountChangeListener(this.raydiumPoolInfoSub);
}
}
addEdge(edge: Edge) {
@ -267,6 +485,66 @@ export class Router {
}
}
async indexRaydium(): Promise<void> {
const response = await fetch('https://api.raydium.io/v2/ammV3/ammPools', {
method: 'GET'
});
const poolData = (await response.json()).data as ApiAmmV3PoolsItem[];
// TODO: Do not trust the tvl and instead look it up like with jupiter prices
const poolsFilteredByTvl = poolData.filter((p: ApiAmmV3PoolsItem) => {
return p.tvl > this.minTvl;
});
console.log(
"found",
poolData.length,
"raydium pools.",
poolsFilteredByTvl.length,
"of those with TVL >",
this.minTvl,
"USD"
);
this.routes = new Map();
const poolInfos = await AmmV3.fetchMultiplePoolInfos(
{
connection: this.connection,
poolKeys: poolsFilteredByTvl,
ownerInfo: undefined,
chainTime: 0,
batchRequest: false,
updateOwnerRewardAndFee: true
}
);
const poolTickArrays = await AmmV3.fetchMultiplePoolTickArrays(
{
connection: this.connection,
poolKeys: poolsFilteredByTvl.map((p) => poolInfos[p.id].state),
batchRequest: false
}
);
const mints = poolsFilteredByTvl.map((p) => [new PublicKey(p.mintA), new PublicKey(p.mintB)]).flat();
const mintInfos = await fetchMultipleMintInfos(
{
connection: this.connection,
mints: mints,
}
);
this.raydiumCache = new RaydiumCache(
await this.connection.getEpochInfo(),
mintInfos,
poolInfos,
poolTickArrays,
)
for (const pool of poolsFilteredByTvl) {
const poolInfo = poolInfos[pool.id].state;
this.addEdges(RaydiumEdge.pairFromPool(poolInfo, this.raydiumCache));
}
}
async indexWhirpools(): Promise<void> {
const poolsPks = (
await this.whirlpoolClient.getContext().program.account.whirlpool.all()
@ -339,7 +617,7 @@ export class Router {
console.log(
"found",
poolsPks.length,
"pools.",
"orca pools.",
filtered.length,
"of those with TVL >",
this.minTvl,

102
yarn.lock
View File

@ -108,6 +108,11 @@
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.5.tgz#1a0377f3b9020efe2fae03290bd2a12140c95c11"
integrity sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==
"@noble/hashes@^1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9"
integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==
"@noble/secp256k1@^1.6.3":
version "1.7.0"
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.0.tgz#d15357f7c227e751d90aa06b05a0e5cf993ba8c1"
@ -245,6 +250,22 @@
"@coral-xyz/anchor" "^0.26.0"
buffer "^6.0.1"
"@raydium-io/raydium-sdk@^1.3.1-beta.8":
version "1.3.1-beta.8"
resolved "https://registry.yarnpkg.com/@raydium-io/raydium-sdk/-/raydium-sdk-1.3.1-beta.8.tgz#f3af2e6c04cfe36677310e7a3cb37d2b2ec9e2f6"
integrity sha512-emBu9sxyKOgUIdy1706+6VxqhXA7BmTjG+jhF+G+BDiuu6olYJ3hFoceMHzNMlNYbWkGBo3GSKKyzjaX0u6D5w==
dependencies:
"@solana/buffer-layout" "^4.0.1"
"@solana/spl-token" "^0.3.7"
axios "^1.2.6"
big.js "^6.2.1"
bn.js "^5.2.1"
decimal.js "^10.4.3"
decimal.js-light "^2.5.1"
fecha "^4.2.3"
lodash "^4.17.21"
toformat "^2.0.0"
"@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"
@ -255,7 +276,7 @@
bigint-buffer "^1.1.5"
bignumber.js "^9.0.1"
"@solana/buffer-layout@^4.0.0":
"@solana/buffer-layout@^4.0.0", "@solana/buffer-layout@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15"
integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==
@ -283,6 +304,15 @@
"@solana/buffer-layout-utils" "^0.2.0"
buffer "^6.0.3"
"@solana/spl-token@^0.3.7":
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.66.0":
version "1.66.0"
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.66.0.tgz#ab76e59b369815fc6114c784a48e91726c1ae6c5"
@ -516,6 +546,20 @@ array-flatten@1.1.1:
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
axios@^1.2.6:
version "1.4.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f"
integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
base-x@^3.0.2:
version "3.0.9"
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320"
@ -533,7 +577,7 @@ base64-js@^1.3.1, base64-js@^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:
big.js@^6.1.1, big.js@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.2.1.tgz#7205ce763efb17c2e41f26f121c420c6a7c2744f"
integrity sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ==
@ -575,7 +619,7 @@ bintrees@1.0.2:
resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8"
integrity sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==
bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2, bn.js@^5.2.0:
bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1:
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==
@ -672,6 +716,13 @@ camelcase@^6.3.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
commander@^2.20.3:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@ -743,7 +794,12 @@ debug@4, debug@^4.1.0:
dependencies:
ms "2.1.2"
decimal.js@^10.3.1:
decimal.js-light@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934"
integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==
decimal.js@^10.3.1, decimal.js@^10.4.3:
version "10.4.3"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
@ -753,6 +809,11 @@ delay@^5.0.0:
resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d"
integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
@ -883,6 +944,11 @@ fast-stable-stringify@^1.0.0:
resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313"
integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==
fecha@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd"
integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==
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"
@ -908,6 +974,20 @@ find@^0.3.0:
dependencies:
traverse-chain "~0.1.0"
follow-redirects@^1.15.0:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@ -1051,7 +1131,7 @@ lodash.zipobject@^4.1.3:
resolved "https://registry.yarnpkg.com/lodash.zipobject/-/lodash.zipobject-4.1.3.tgz#b399f5aba8ff62a746f6979bf20b214f964dbef8"
integrity sha512-A9SzX4hMKWS25MyalwcOnNoplyHbkNVsjidhTp8ru0Sj23wY9GWBKS8gAIGDSAqeWjIjvE4KBEl24XXAs+v4wQ==
lodash@^4.17.20:
lodash@^4.17.20, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -1088,7 +1168,7 @@ mime-db@1.52.0:
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@~2.1.24, mime-types@~2.1.34:
mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
@ -1206,6 +1286,11 @@ proxy-addr@~2.0.7:
forwarded "0.2.0"
ipaddr.js "1.9.1"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
qs@6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
@ -1362,6 +1447,11 @@ tiny-invariant@^1.2.0:
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642"
integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==
toformat@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/toformat/-/toformat-2.0.0.tgz#7a043fd2dfbe9021a4e36e508835ba32056739d8"
integrity sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ==
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"