diff --git a/package.json b/package.json index 897a6c94..84cba88b 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "docgen": "hardhat docgen", "prepare": "husky install", "generate-abis": "yarn wagmi generate", - "postinstall": "yarn generate-abis", + "postinstall": "yarn generate-abis && yarn generate-subgraph-types", "generate-subgraph-types": "rm -rf /subgraph-client/.graphclient && npx graphclient build --dir ./subgraph-client" }, "keywords": [], diff --git a/src/config/clients.ts b/src/config/clients.ts deleted file mode 100644 index 478a6a42..00000000 --- a/src/config/clients.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { HttpTransport, PublicClient, WalletClient, createPublicClient, createWalletClient, http } from "viem"; -import { PrivateKeyAccount, privateKeyToAccount } from "viem/accounts"; - -import { chains } from "./chains"; -import type { SUPPORTED_CHAINS } from "./chains"; - -export const getPublicClient = ( - chainName: SUPPORTED_CHAINS, -): PublicClient => { - return createPublicClient({ - chain: chains[chainName], - transport: http(process.env[`LIVE_NETWORK_${chainName}`]), - }); -}; - -const readPrivateKeyFromEnv = (chainName: string): PrivateKeyAccount => { - const key = process.env[`PRIVATE_KEY_${chainName}`]; - if (key?.startsWith("0x")) { - return privateKeyToAccount(key as `0x${string}`); - } - throw new Error(`Invalid private key for ${chainName}. Please specify PRIVATE_KEY_${chainName} env variable.`); -}; - -export const getWalletClient = ( - chainName: SUPPORTED_CHAINS, -): WalletClient => { - return createWalletClient({ - chain: chains[chainName], - transport: http(process.env[`LIVE_NETWORK_${chainName}`]), - account: readPrivateKeyFromEnv(chainName), - }); -}; diff --git a/src/config/clients/publicClient.ts b/src/config/clients/publicClient.ts new file mode 100644 index 00000000..5064e6b1 --- /dev/null +++ b/src/config/clients/publicClient.ts @@ -0,0 +1,14 @@ +import { HttpTransport, PublicClient, createPublicClient, http } from "viem"; + +import { chains } from "../chains"; +import type { SUPPORTED_CHAINS } from "../chains"; + +export const getPublicClient = (): PublicClient => { + const chainName = process.env.FORKED_NETWORK as SUPPORTED_CHAINS; + return createPublicClient({ + chain: chains[chainName], + transport: http(process.env[`LIVE_NETWORK_${chainName}`]), + }); +}; + +export default getPublicClient(); diff --git a/src/config/clients/walletClient.ts b/src/config/clients/walletClient.ts new file mode 100644 index 00000000..09916988 --- /dev/null +++ b/src/config/clients/walletClient.ts @@ -0,0 +1,24 @@ +import { HttpTransport, WalletClient, createWalletClient, http } from "viem"; +import { PrivateKeyAccount, privateKeyToAccount } from "viem/accounts"; + +import { chains } from "../chains"; +import type { SUPPORTED_CHAINS } from "../chains"; + +const readPrivateKeyFromEnv = (chainName: string): PrivateKeyAccount => { + const key = process.env[`PRIVATE_KEY_${chainName}`]; + if (key?.startsWith("0x")) { + return privateKeyToAccount(key as `0x${string}`); + } + throw new Error(`Invalid private key for ${chainName}. Please specify PRIVATE_KEY_${chainName} env variable.`); +}; + +export const getWalletClient = (): WalletClient => { + const chainName = process.env.FORKED_NETWORK as SUPPORTED_CHAINS; + return createWalletClient({ + chain: chains[chainName], + transport: http(process.env[`LIVE_NETWORK_${chainName}`]), + account: readPrivateKeyFromEnv(chainName), + }); +}; + +export default getWalletClient(); diff --git a/src/converter-bot/tokenConverterBot.ts b/src/converter-bot/TokenConverter.ts similarity index 82% rename from src/converter-bot/tokenConverterBot.ts rename to src/converter-bot/TokenConverter.ts index 2aac7943..55d5eaa7 100644 --- a/src/converter-bot/tokenConverterBot.ts +++ b/src/converter-bot/TokenConverter.ts @@ -1,34 +1,30 @@ import { Currency, CurrencyAmount, Token, TradeType } from "@pancakeswap/sdk"; import { BaseRoute, Pool, QuoteProvider, SmartRouter, SmartRouterTrade, V3Pool } from "@pancakeswap/smart-router/evm"; import { Client as UrqlClient, createClient } from "urql/core"; -import { Address, parseAbi } from "viem"; -import { Hex, encodePacked } from "viem"; +import { Hex, encodePacked, Address, parseAbi, erc20Abi} from "viem"; import { tokenConverterOperatorAbi } from "../config/abis/generated"; import addresses from "../config/addresses"; import type { SUPPORTED_CHAINS } from "../config/chains"; import { chains } from "../config/chains"; -import { getPublicClient, getWalletClient } from "../config/clients"; +import publicClient from "../config/clients/publicClient"; +import walletClient from "../config/clients/walletClient"; import { Path } from "./path"; const REVERT_IF_NOT_MINED_AFTER = 60n; // seconds - -export const NETWORK_IDS: Record = { - bsctestnet: 97, - bscmainnet: 56, -}; +const MAX_HOPS = 5; export const getOutputCurrency = (pool: V3Pool, inputToken: Token): Currency => { const { token0, token1 } = pool; return token0.equals(inputToken) ? token1 : token0; }; -export class TokenConverterBot { +export class TokenConverter { private chainName: SUPPORTED_CHAINS; private operator: { address: Address; abi: typeof tokenConverterOperatorAbi }; private addresses: typeof addresses; - private _walletClient?: ReturnType; - private _publicClient?: ReturnType; + private _walletClient?: typeof walletClient; + private _publicClient?: typeof publicClient; private v3SubgraphClient: UrqlClient; private quoteProvider: QuoteProvider; private tokens: Map; @@ -50,11 +46,11 @@ export class TokenConverterBot { } get publicClient() { - return (this._publicClient ||= getPublicClient(this.chainName)); + return (this._publicClient ||= publicClient); } get walletClient() { - return (this._walletClient ||= getWalletClient(this.chainName)); + return (this._walletClient ||= walletClient); } getToken = async (address: Address): Promise => { @@ -65,18 +61,18 @@ export class TokenConverterBot { contracts: [ { address, - abi: parseAbi(["function decimals() external pure returns (uint8)"]), + abi: erc20Abi, functionName: "decimals", }, { address, - abi: parseAbi(["function symbol() external pure returns (string memory)"]), + abi: erc20Abi, functionName: "symbol", }, ], }); if (decimals && symbol) { - const token = new Token(NETWORK_IDS[this.chainName], address, decimals, symbol); + const token = new Token(chains[this.chainName].id, address, decimals, symbol); this.tokens.set(address, token); return token; } @@ -114,7 +110,7 @@ export class TokenConverterBot { TradeType.EXACT_OUTPUT, { gasPriceWei: () => this.publicClient.getGasPrice(), - maxHops: 1, + maxHops: MAX_HOPS, maxSplits: 0, poolProvider: SmartRouter.createStaticPoolProvider(candidatePools), @@ -164,7 +160,7 @@ export class TokenConverterBot { return encodePacked(types.reverse(), path.reverse()); } - async arbitrage(converterAddress: Address, path: Path, amount: bigint, minIncome: bigint) { + async arbitrage(converterAddress: Address, trade: SmartRouterTrade, amount: bigint, minIncome: bigint) { const beneficiary = this.walletClient.account.address; const chain = chains[this.chainName]; @@ -172,11 +168,11 @@ export class TokenConverterBot { if (minIncome < 0n) { await this.walletClient.writeContract({ - address: path.end, + address: trade.outputAmount.address, chain, abi: parseAbi(["function approve(address,uint256)"]), functionName: "approve", - args: [this.operator.address, -minIncome], // + amount + args: [this.operator.address, -minIncome], }); } try { @@ -187,12 +183,12 @@ export class TokenConverterBot { args: [ { beneficiary, - tokenToReceiveFromConverter: path.end, + tokenToReceiveFromConverter: trade.outputAmount.address, amount, minIncome, - tokenToSendToConverter: path.start, + tokenToSendToConverter: trade.inputAmount.address, converter: converterAddress, - path: path.hex, + path: this.encodeExactOutputPath(trade.route), deadline: block.timestamp + REVERT_IF_NOT_MINED_AFTER, }, ], @@ -200,8 +196,7 @@ export class TokenConverterBot { } catch (e) { console.error("Conversion failed", { converterAddress, - assetIn: path.start, - assetOut: path.end, + trade, amount, minIncome, }); @@ -209,4 +204,4 @@ export class TokenConverterBot { } } -export default TokenConverterBot; +export default TokenConverter; diff --git a/src/converter-bot/index.ts b/src/converter-bot/index.ts index 580b2d57..3dbcca4f 100644 --- a/src/converter-bot/index.ts +++ b/src/converter-bot/index.ts @@ -1,123 +1,43 @@ import "dotenv/config"; -import { Abi, Address } from "viem"; - -import subgraphClient from "../../subgraph-client"; -import { - coreComptrollerAbi, - coreVTokenAbi, - protocolShareReserveAbi, - tokenConverterAbi, - vBnbAdminAbi, -} from "../config/abis/generated"; +import { Address } from "viem"; + +import { coreVTokenAbi, protocolShareReserveAbi, tokenConverterAbi, vBnbAdminAbi } from "../config/abis/generated"; import addresses, { underlyingToVTokens } from "../config/addresses"; import { SUPPORTED_CHAINS } from "../config/chains"; -import { getPublicClient, getWalletClient } from "../config/clients"; -import TokenConverterBot from "./TokenConverterBot"; -import formatTokenConverterConfigs from "./formatTokenConverterConfigs"; +import publicClient from "../config/clients/publicClient"; +import walletClient from "../config/clients/walletClient"; +import TokenConverter from "./TokenConverter"; import { parsePath } from "./path"; - -interface BalanceResult { - tokenConverter: Address; - assetIn: { address: Address; balance: bigint }; - assetOut: { address: Address; balance: bigint }; -} +import readCoreMarkets from "./queries/read/readCoreMarkets"; +import readIsolatedMarkets from "./queries/read/readIsolatedMarkets"; +import readTokenConverterConfigs from "./queries/read/readTokenConverterConfigs"; +import readTokenConvertersTokenBalances, { BalanceResult } from "./queries/read/readTokenConvertersTokenBalances"; +import type { Pool } from "./types"; const network = process.env.FORKED_NETWORK as SUPPORTED_CHAINS; -const client = getPublicClient(network); -const wallet = getWalletClient(network); - -const getCoreMarkets = async () => { - const markets = await client.readContract({ - address: addresses.Unitroller as Address, - abi: coreComptrollerAbi, - functionName: "getAllMarkets", - }); - return markets; -}; - -const formatResults = ( - results: ( - | { - error: Error; - result?: undefined; - status: "failure"; - } - | { - error?: undefined; - result: unknown; - status: "success"; - } - )[], - assetIn: Address, - assetOut: Address, - tokenConverters: Address[], -) => { - const chunkSize = 2; - for (let i = 0; i < results.length; i += chunkSize) { - const index = (i + chunkSize) / chunkSize - 1; - const tokenConverter = tokenConverters[index]; - - const curr = results.slice(i, i + chunkSize); - - return { - tokenConverter, - assetIn: { address: assetIn, balance: curr[0].result as bigint }, - assetOut: { address: assetOut, balance: curr[1].result as bigint }, - }; - } -}; +const isFulfilled = (input: PromiseSettledResult): input is PromiseFulfilledResult => + input.status === "fulfilled"; -const getBalances = async (assetIn: Address, assetOut: Address, tokenConverters: Address[]) => { - const corePoolMarkets = await getCoreMarkets(); - const results = await client.multicall({ - contracts: [ - { - address: addresses.ProtocolShareReserve as Address, - abi: protocolShareReserveAbi, - functionName: "releaseFunds", - args: [addresses.Unitroller, corePoolMarkets], - }, - ...tokenConverters.reduce((acc, curr) => { - acc = acc.concat( - { - address: assetIn as Address, - abi: coreVTokenAbi, - functionName: "balanceOf", - args: [curr], - }, - { - address: assetOut as Address, - abi: coreVTokenAbi, - functionName: "balanceOf", - args: [curr], - }, - ); - return acc; - }, [] as { address: Address; abi: Abi; functionName: string; args?: readonly unknown[] | undefined }[]), - ], - }); - // Release funds will be empty - results.shift(); - const formattedResults = formatResults(results, assetIn, assetOut, tokenConverters); - return formattedResults; -}; +const checkForTrades = async (values: PromiseSettledResult[]) => { + const trades = values.reduce((acc, curr) => { + if (isFulfilled(curr)) { + acc = acc.concat(curr.value.filter(v => v.assetOut.balance > 0)); + } + return acc; + }, [] as BalanceResult[]); -const checkForTrade = async (values: { value: BalanceResult }[]) => { - const trades = values.filter(v => { - return v.value.assetOut.balance > 0; - }); return trades; }; const executeTrade = async (t: BalanceResult) => { - const bot = new TokenConverterBot(network); + const tokenConverter = new TokenConverter(network); const vTokens = underlyingToVTokens[t.assetOut.address]; if (vTokens.core) { - await wallet.writeContract({ + await walletClient.writeContract({ address: addresses.ProtocolShareReserve as Address, abi: protocolShareReserveAbi, functionName: "releaseFunds", @@ -126,7 +46,7 @@ const executeTrade = async (t: BalanceResult) => { } if (vTokens.isolated) { - await wallet.writeContract({ + await walletClient.writeContract({ address: addresses.ProtocolShareReserve as Address, abi: protocolShareReserveAbi, functionName: "releaseFunds", @@ -134,53 +54,41 @@ const executeTrade = async (t: BalanceResult) => { }); } - const { result: amountIn } = await client.simulateContract({ + const { result: amountIn } = await publicClient.simulateContract({ address: t.tokenConverter as Address, abi: tokenConverterAbi, functionName: "getUpdatedAmountIn", args: [t.assetOut.balance, t.assetIn.address, t.assetOut.address], }); - await bot.sanityCheck(); - const trade = await bot.getBestTrade(t.assetIn.address, t.assetOut.address, amountIn[1]); + const trade = await tokenConverter.getBestTrade(t.assetIn.address, t.assetOut.address, amountIn[1]); const fee = BigInt(trade.routes[0].pools[0].fee); - const flashPayment = trade.inputAmount.numerator * (fee / 100000n + 1n); - const minIncome = t.assetOut.balance - flashPayment; - const path = parsePath([t.assetIn.address, fee, t.assetOut.address]); - await bot.arbitrage(t.tokenConverter, path, amountIn[1], minIncome); -}; + const minIncome = t.assetOut.balance - trade.inputAmount.numerator; -const main = async () => { - const { - data: { tokenConverters }, - } = await subgraphClient.getTokenConverters(); - const corePoolMarkets = await getCoreMarkets(); - await Promise.allSettled( - corePoolMarkets.map(async market => { - await wallet.writeContract({ - address: market, - abi: coreVTokenAbi, - functionName: "accrueInterest", - }); - }), - ); + await tokenConverter.arbitrage(t.tokenConverter, trade, amountIn[1], minIncome); +}; - const totalReserves = await client.readContract({ +/** + * Checks total reserves and available cash and then uses the vBNB admin + * to send the reserves ProtocolShare Reserve to be distributed + */ +const reduceReserves = async () => { + const totalReserves = await publicClient.readContract({ address: addresses.vBNB as Address, abi: coreVTokenAbi, functionName: "totalReserves", }); - const cash = await client.readContract({ + const cash = await publicClient.readContract({ address: addresses.vBNB as Address, abi: coreVTokenAbi, functionName: "getCash", }); if (cash > 0) { - await wallet.writeContract({ + await walletClient.writeContract({ address: addresses.VBNBAdmin as Address, abi: vBnbAdminAbi, functionName: "reduceReserves", @@ -189,13 +97,41 @@ const main = async () => { } else { console.error("Unable to reduce reservers vBNB Admin is out of cash."); } +}; + +const accrueInterest = async (allPools: Pool[]) => { + const allMarkets = allPools.reduce((acc, curr) => { + acc.concat(curr[1]); + return acc; + }, [] as Address[]); + + // Accrue Interest in all markets + await Promise.allSettled( + allMarkets.map(async market => { + await walletClient.writeContract({ + address: market, + abi: coreVTokenAbi, + functionName: "accrueInterest", + }); + }), + ); +}; + +const main = async () => { + const tokenConverterConfigs = await readTokenConverterConfigs(); + const corePoolMarkets = await readCoreMarkets(); + const isolatedPoolsMarkets = await readIsolatedMarkets(); + const allPools = [...corePoolMarkets, ...isolatedPoolsMarkets]; + + await accrueInterest(allPools); + await reduceReserves(); - const tokenConverterConfigs = formatTokenConverterConfigs(tokenConverters); const results = await Promise.allSettled( Object.keys(tokenConverterConfigs).map(async tokenIn => { const results = await Promise.allSettled( Object.keys(tokenConverterConfigs[tokenIn as Address]).map(async tokenOut => { - const result = await getBalances( + const result = await readTokenConvertersTokenBalances( + allPools, tokenIn as Address, tokenOut as Address, tokenConverterConfigs[tokenIn as Address][tokenOut as Address], @@ -208,15 +144,13 @@ const main = async () => { ); for (const r of results) { - // @ts-ignore - const trades = await checkForTrade(r?.value); - for (const t of trades) { - await executeTrade(t.value); + if (isFulfilled[]>(r)) { + const trades = await checkForTrades(r.value); + for (const t of trades) { + await executeTrade(t); + } } } }; -main().catch(error => { - console.error(error); - process.exit(1); -}); +export default main(); diff --git a/src/converter-bot/queries/read/readCoreMarkets.ts b/src/converter-bot/queries/read/readCoreMarkets.ts new file mode 100644 index 00000000..ec5fe76f --- /dev/null +++ b/src/converter-bot/queries/read/readCoreMarkets.ts @@ -0,0 +1,17 @@ +import { Address } from "viem"; + +import { coreComptrollerAbi } from "../../../config/abis/generated"; +import addresses from "../../../config/addresses"; +import publicClient from "../../../config/clients/publicClient"; +import type { Pool } from "../../types"; + +const readCoreMarkets = async (): Promise => { + const markets = await publicClient.readContract({ + address: addresses.Unitroller as Address, + abi: coreComptrollerAbi, + functionName: "getAllMarkets", + }); + return [[addresses.Unitroller as Address, markets]]; +}; + +export default readCoreMarkets; diff --git a/src/converter-bot/queries/read/readIsolatedMarkets.ts b/src/converter-bot/queries/read/readIsolatedMarkets.ts new file mode 100644 index 00000000..9cc9c26f --- /dev/null +++ b/src/converter-bot/queries/read/readIsolatedMarkets.ts @@ -0,0 +1,18 @@ +import { Address } from "viem"; + +import { poolLensAbi } from "../../../config/abis/generated"; +import addresses from "../../../config/addresses"; +import publicClient from "../../../config/clients/publicClient"; +import type { Pool } from "../../types"; + +const readIsolatedMarkets = async (): Promise => { + const markets = await publicClient.readContract({ + address: addresses.PoolLens as Address, + abi: poolLensAbi, + functionName: "getAllPools", + args: [addresses.PoolRegistry as Address], + }); + return markets.map((pool): [Address, Address[]] => [pool.comptroller, pool.vTokens.map(vToken => vToken.vToken)]); +}; + +export default readIsolatedMarkets; diff --git a/src/converter-bot/formatTokenConverterConfigs.ts b/src/converter-bot/queries/read/readTokenConverterConfigs.ts similarity index 53% rename from src/converter-bot/formatTokenConverterConfigs.ts rename to src/converter-bot/queries/read/readTokenConverterConfigs.ts index 08637591..983a2dfe 100644 --- a/src/converter-bot/formatTokenConverterConfigs.ts +++ b/src/converter-bot/queries/read/readTokenConverterConfigs.ts @@ -1,6 +1,7 @@ import { Address } from "viem"; -import { TokenConvertersQuery } from "../../subgraph-client/.graphclient"; +import subgraphClient from "../../../../subgraph-client"; +import { TokenConvertersQuery } from "../../../../subgraph-client/.graphclient"; const formatTokenConverterConfigs = (data: TokenConvertersQuery["tokenConverters"]) => data.reduce((acc, curr) => { @@ -14,4 +15,12 @@ const formatTokenConverterConfigs = (data: TokenConvertersQuery["tokenConverters return acc; }, {} as Record>); -export default formatTokenConverterConfigs; +const readTokenConverterConfigs = async () => { + const { + data: { tokenConverters }, + } = await subgraphClient.getTokenConverters(); + const tokenConverterConfigs = formatTokenConverterConfigs(tokenConverters); + return tokenConverterConfigs; +}; + +export default readTokenConverterConfigs; diff --git a/src/converter-bot/queries/read/readTokenConvertersTokenBalances.ts b/src/converter-bot/queries/read/readTokenConvertersTokenBalances.ts new file mode 100644 index 00000000..eacfe892 --- /dev/null +++ b/src/converter-bot/queries/read/readTokenConvertersTokenBalances.ts @@ -0,0 +1,90 @@ +import "dotenv/config"; + +import { Abi, Address } from "viem"; + +import { coreVTokenAbi, protocolShareReserveAbi } from "../../../config/abis/generated"; +import addresses from "../../../config/addresses"; +import publicClient from "../../../config/clients/publicClient"; + +export interface BalanceResult { + tokenConverter: Address; + assetIn: { address: Address; balance: bigint }; + assetOut: { address: Address; balance: bigint }; +} + +const formatResults = ( + results: ( + | { + error: Error; + result?: undefined; + status: "failure"; + } + | { + error?: undefined; + result: unknown; + status: "success"; + } + )[], + assetIn: Address, + assetOut: Address, + tokenConverters: Address[], +): BalanceResult[] => { + const chunkSize = 2; + const balances: BalanceResult[] = []; + for (let i = 0; i < results.length; i += chunkSize) { + const index = (i + chunkSize) / chunkSize - 1; + const tokenConverter = tokenConverters[index]; + + const curr = results.slice(i, i + chunkSize); + + balances.push({ + tokenConverter, + assetIn: { address: assetIn, balance: curr[0].result as bigint }, + assetOut: { address: assetOut, balance: curr[1].result as bigint }, + }); + } + return balances; +}; + +const readTokenConvertersTokenBalances = async ( + pools: [Address, readonly Address[]][], + assetIn: Address, + assetOut: Address, + tokenConverters: Address[], +): Promise => { + const releaseFundsCalls = pools.map(pool => ({ + address: addresses.ProtocolShareReserve as Address, + abi: protocolShareReserveAbi, + functionName: "releaseFunds", + args: pool, + })); + const results = await publicClient.multicall({ + contracts: [ + ...releaseFundsCalls, + ...tokenConverters.reduce((acc, curr) => { + acc = acc.concat([ + { + address: assetIn as Address, + abi: coreVTokenAbi, + functionName: "balanceOf", + args: [curr], + }, + { + address: assetOut as Address, + abi: coreVTokenAbi, + functionName: "balanceOf", + args: [curr], + }, + ]); + return acc; + }, [] as { address: Address; abi: Abi; functionName: string; args?: readonly unknown[] | undefined }[]), + ], + }); + + // Release funds will be empty + results.splice(0, releaseFundsCalls.length); + const formattedResults = formatResults(results, assetIn, assetOut, tokenConverters); + return formattedResults; +}; + +export default readTokenConvertersTokenBalances; diff --git a/src/converter-bot/types.ts b/src/converter-bot/types.ts new file mode 100644 index 00000000..cf03fb92 --- /dev/null +++ b/src/converter-bot/types.ts @@ -0,0 +1,3 @@ +import { Address } from "viem"; + +export type Pool = [Address, readonly Address[]]; diff --git a/subgraph-client/.graphclientrc.yml b/subgraph-client/.graphclientrc.yml index 99d495e1..5ed282c6 100644 --- a/subgraph-client/.graphclientrc.yml +++ b/subgraph-client/.graphclientrc.yml @@ -5,4 +5,4 @@ sources: endpoint: https://api.thegraph.com/subgraphs/name/venusprotocol/venus-protocol-reserve-chapel documents: - - "../**/*.graphql" + - "./queries/*.graphql" diff --git a/src/queries/TokenConvertersQuery.graphql b/subgraph-client/queries/TokenConvertersQuery.graphql similarity index 88% rename from src/queries/TokenConvertersQuery.graphql rename to subgraph-client/queries/TokenConvertersQuery.graphql index dbf929a3..5145bf39 100644 --- a/src/queries/TokenConvertersQuery.graphql +++ b/subgraph-client/queries/TokenConvertersQuery.graphql @@ -13,6 +13,5 @@ query TokenConverters { incentive access } - paused(filter: Boolean): true } }