This repository has been archived by the owner on Jan 24, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 383
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(uniswap-v2): Enable fetching 5000 Uniswap V2 pools (#1900)
- Loading branch information
1 parent
934c525
commit 9ee1020
Showing
4 changed files
with
92 additions
and
190 deletions.
There are no files selected for viewing
44 changes: 0 additions & 44 deletions
44
src/apps/uniswap-v2/ethereum/uniswap-v2.balance-fetcher.ts
This file was deleted.
Oops, something went wrong.
200 changes: 92 additions & 108 deletions
200
src/apps/uniswap-v2/ethereum/uniswap-v2.pool.token-fetcher.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,117 +1,101 @@ | ||
import { Inject } from '@nestjs/common'; | ||
import { gql } from 'graphql-request'; | ||
import { chunk, compact } from 'lodash'; | ||
|
||
import { Register } from '~app-toolkit/decorators'; | ||
import { PositionFetcher } from '~position/position-fetcher.interface'; | ||
import { AppTokenPosition } from '~position/position.interface'; | ||
import { Network } from '~types/network.interface'; | ||
import { drillBalance } from '~app-toolkit'; | ||
import { ZERO_ADDRESS } from '~app-toolkit/constants/address'; | ||
import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; | ||
import { AppTokenPositionBalance, RawAppTokenBalance } from '~position/position-balance.interface'; | ||
import { isAppToken } from '~position/position.interface'; | ||
|
||
import { UniswapFactory, UniswapPair, UniswapV2ContractFactory } from '../contracts'; | ||
import { UniswapV2OnChainTokenDerivationStrategy } from '../helpers/uniswap-v2.on-chain.token-derivation-strategy'; | ||
import { UniswapV2PoolTokenHelper } from '../helpers/uniswap-v2.pool.token-helper'; | ||
import { UniswapV2TheGraphPoolTokenAddressStrategy } from '../helpers/uniswap-v2.the-graph.pool-token-address-strategy'; | ||
import { UniswapV2TheGraphPoolVolumeStrategy } from '../helpers/uniswap-v2.the-graph.pool-volume-strategy'; | ||
import UNISWAP_V2_DEFINITION from '../uniswap-v2.definition'; | ||
import { UniswapV2DefaultPoolSubgraphTemplateTokenFetcher } from '../common/uniswap-v2.default.subgraph.template.token-fetcher'; | ||
import { UniswapV2TokenDataProps } from '../common/uniswap-v2.pool.on-chain.template.token-fetcher'; | ||
|
||
const appId = UNISWAP_V2_DEFINITION.id; | ||
const groupId = UNISWAP_V2_DEFINITION.groups.pool.id; | ||
const network = Network.ETHEREUM_MAINNET; | ||
type UniswapV2BalancesData = { | ||
user?: { | ||
liquidityPositions: { | ||
pair: { | ||
id: string; | ||
}; | ||
}[]; | ||
}; | ||
}; | ||
|
||
@Register.TokenPositionFetcher({ appId, groupId, network }) | ||
export class EthereumUniswapV2PoolTokenFetcher implements PositionFetcher<AppTokenPosition> { | ||
constructor( | ||
@Inject(UniswapV2ContractFactory) | ||
private readonly uniswapV2ContractFactory: UniswapV2ContractFactory, | ||
@Inject(UniswapV2PoolTokenHelper) | ||
private readonly uniswapV2PoolTokenHelper: UniswapV2PoolTokenHelper, | ||
@Inject(UniswapV2OnChainTokenDerivationStrategy) | ||
private readonly uniswapV2OnChainTokenDerivationStrategy: UniswapV2OnChainTokenDerivationStrategy, | ||
@Inject(UniswapV2TheGraphPoolTokenAddressStrategy) | ||
private readonly uniswapV2TheGraphPoolTokenAddressStrategy: UniswapV2TheGraphPoolTokenAddressStrategy, | ||
@Inject(UniswapV2TheGraphPoolVolumeStrategy) | ||
private readonly uniswapV2TheGraphPoolVolumeStrategy: UniswapV2TheGraphPoolVolumeStrategy, | ||
) {} | ||
const UNISWAP_V2_BALANCES_QUERY = gql` | ||
query getBalances($address: String!) { | ||
user(id: $address) { | ||
liquidityPositions { | ||
pair { | ||
id | ||
} | ||
} | ||
} | ||
} | ||
`; | ||
|
||
async getPositions() { | ||
return this.uniswapV2PoolTokenHelper.getTokens<UniswapFactory, UniswapPair>({ | ||
network, | ||
appId, | ||
groupId: UNISWAP_V2_DEFINITION.groups.pool.id, | ||
factoryAddress: '0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f', | ||
hiddenTokens: [ | ||
'0x62359ed7505efc61ff1d56fef82158ccaffa23d7', | ||
'0x35bd01fc9d6d5d81ca9e055db88dc49aa2c699a8', | ||
'0x6adb2e268de2aa1abf6578e4a8119b960e02928f', | ||
], | ||
blockedPools: ['0x9cbfb60a09a9a33a10312da0f39977cbdb7fde23'], // Uniswap V2: SAITAMA - has a transfer fee (not supported by our zap) | ||
resolveFactoryContract: ({ address, network }) => | ||
this.uniswapV2ContractFactory.uniswapFactory({ address, network }), | ||
resolvePoolContract: ({ address, network }) => this.uniswapV2ContractFactory.uniswapPair({ address, network }), | ||
resolvePoolTokenAddresses: this.uniswapV2TheGraphPoolTokenAddressStrategy.build({ | ||
subgraphUrl: 'https://api.thegraph.com/subgraphs/name/ianlapham/uniswapv2', | ||
first: 1000, | ||
orderBy: 'trackedReserveETH', | ||
requiredPools: [ | ||
// pairs that should be tracked but arent due to lag in subgraph | ||
'0x9928e4046d7c6513326ccea028cd3e7a91c7590a', // FEI/TRIBE | ||
'0xcd7989894bc033581532d2cd88da5db0a4b12859', // WBTC/BADGER | ||
'0xe1573b9d29e2183b1af0e743dc2754979a40d237', // FXS/FRAX | ||
'0x709f7b10f22eb62b05913b59b92ddd372d4e2152', // PAXG/USDP | ||
// other pairs tracked for farms, etc. | ||
'0xaad22f5543fcdaa694b68f94be177b561836ae57', // sUSD/$BASED | ||
'0xe98f89a2b3aecdbe2118202826478eb02434459a', // DAI/DEBASE | ||
'0x980a07e4f64d21a0cb2ef8d4af362a79b9f5c0da', // DAI/BSGS | ||
'0xf58d2bacbc68c587730ea0ce5131f6ae7c622a5d', // ORCL5/ETH | ||
'0xc3601f3e1c26d1a47571c559348e4156786d1fec', // DUCK/WETH | ||
'0xcadd30b39f01cfdfb848174b19bbb5b1b7486159', // DSU/ESS | ||
'0x0bf46ba06dc1d33c3bd80ff42497ebff13a88900', // rDPX/ETH | ||
'0xfd0a40bc83c5fae4203dec7e5929b446b07d1c76', // FRAX/ETH | ||
'0x97c4adc5d28a86f9470c70dd91dc6cc2f20d2d4d', // FRAX/USDC | ||
'0x1f249f8b5a42aa78cc8a2b66ee0bb015468a5f43', // wETH/wBAN | ||
], | ||
}), | ||
resolveDerivedUnderlyingToken: this.uniswapV2OnChainTokenDerivationStrategy.build({ | ||
priceDerivationWhitelist: [ | ||
'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH | ||
'0x6b175474e89094c44da98b954eedeac495271d0f', // DAI | ||
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC | ||
'0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT | ||
'0x853d955acef822db058eb8505911ed77f175b99e', // FRAX | ||
'0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', // WBTC | ||
], | ||
resolvePoolAddress: ({ factoryContract, multicall, token0, token1 }) => | ||
multicall.wrap(factoryContract).getPair(token0, token1), | ||
}), | ||
resolvePoolTokenSymbol: ({ multicall, poolContract }) => multicall.wrap(poolContract).symbol(), | ||
resolvePoolTokenSupply: ({ multicall, poolContract }) => multicall.wrap(poolContract).totalSupply(), | ||
resolvePoolReserves: async ({ multicall, poolContract }) => | ||
multicall | ||
.wrap(poolContract) | ||
.getReserves() | ||
.then(v => [v[0], v[1]]), | ||
resolvePoolUnderlyingTokenAddresses: async ({ multicall, poolContract }) => | ||
Promise.all([multicall.wrap(poolContract).token0(), multicall.wrap(poolContract).token1()]), | ||
resolveTokenDisplaySymbol: token => (token.symbol === 'WETH' ? 'ETH' : token.symbol), | ||
resolvePoolVolumes: this.uniswapV2TheGraphPoolVolumeStrategy.build({ | ||
subgraphUrl: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2', | ||
first: 1000, | ||
requiredPools: [ | ||
// pairs that should be tracked but arent due to lag in subgraph | ||
'0x9928e4046d7c6513326ccea028cd3e7a91c7590a', // FEI/TRIBE | ||
'0xcd7989894bc033581532d2cd88da5db0a4b12859', // WBTC/BADGER | ||
'0xe1573b9d29e2183b1af0e743dc2754979a40d237', // FXS/FRAX | ||
'0x709f7b10f22eb62b05913b59b92ddd372d4e2152', // PAXG/USDP | ||
// other pairs tracked for farms, etc. | ||
'0xaad22f5543fcdaa694b68f94be177b561836ae57', // sUSD/$BASED | ||
'0xe98f89a2b3aecdbe2118202826478eb02434459a', // DAI/DEBASE | ||
'0x980a07e4f64d21a0cb2ef8d4af362a79b9f5c0da', // DAI/BSGS | ||
'0xf58d2bacbc68c587730ea0ce5131f6ae7c622a5d', // ORCL5/ETH | ||
'0xc3601f3e1c26d1a47571c559348e4156786d1fec', // DUCK/WETH | ||
'0xcadd30b39f01cfdfb848174b19bbb5b1b7486159', // DSU/ESS | ||
'0x0bf46ba06dc1d33c3bd80ff42497ebff13a88900', // rDPX/ETH | ||
'0xfd0a40bc83c5fae4203dec7e5929b446b07d1c76', // FRAX/ETH | ||
'0x97c4adc5d28a86f9470c70dd91dc6cc2f20d2d4d', // FRAX/USDC | ||
], | ||
@PositionTemplate() | ||
export class EthereumUniswapV2PoolTokenFetcher extends UniswapV2DefaultPoolSubgraphTemplateTokenFetcher { | ||
groupLabel = 'Pools'; | ||
|
||
factoryAddress = '0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f'; | ||
subgraphUrl = 'https://api.thegraph.com/subgraphs/name/ianlapham/uniswapv2'; | ||
first = 5000; | ||
|
||
async getBalances(_address: string): Promise<AppTokenPositionBalance<UniswapV2TokenDataProps>[]> { | ||
const multicall = this.appToolkit.getMulticall(this.network); | ||
const tokenLoader = this.appToolkit.getTokenDependencySelector(); | ||
const address = await this.getAccountAddress(_address); | ||
if (address === ZERO_ADDRESS) return []; | ||
|
||
// Use the subgraph to determine holdings. Later, we'll optimize this to use our own holdings by default. | ||
const data = await this.appToolkit.helpers.theGraphHelper.requestGraph<UniswapV2BalancesData>({ | ||
endpoint: this.subgraphUrl, | ||
query: UNISWAP_V2_BALANCES_QUERY, | ||
variables: { address: address.toLowerCase() }, | ||
}); | ||
|
||
const heldTokenAddresses = data.user?.liquidityPositions.map(v => v.pair.id.toLowerCase()) ?? []; | ||
const heldTokens = await tokenLoader.getMany(heldTokenAddresses.map(t => ({ address: t, network: this.network }))); | ||
const sanitized = compact(heldTokens).filter(isAppToken); | ||
|
||
const balances = await Promise.all( | ||
sanitized.map(async appToken => { | ||
const balanceRaw = await this.getBalancePerToken({ multicall, address, appToken }); | ||
const tokenBalance = drillBalance(appToken, balanceRaw.toString(), { isDebt: this.isDebt }); | ||
return tokenBalance; | ||
}), | ||
); | ||
|
||
return balances as AppTokenPositionBalance<UniswapV2TokenDataProps>[]; | ||
} | ||
|
||
async getRawBalances(_address: string): Promise<RawAppTokenBalance[]> { | ||
const multicall = this.appToolkit.getMulticall(this.network); | ||
const tokenLoader = this.appToolkit.getTokenDependencySelector(); | ||
const address = await this.getAccountAddress(_address); | ||
if (address === ZERO_ADDRESS) return []; | ||
|
||
// Use the subgraph to determine holdings. Later, we'll optimize this to use our own holdings by default. | ||
const data = await this.appToolkit.helpers.theGraphHelper.requestGraph<UniswapV2BalancesData>({ | ||
endpoint: this.subgraphUrl, | ||
query: UNISWAP_V2_BALANCES_QUERY, | ||
variables: { address: address.toLowerCase() }, | ||
}); | ||
|
||
const heldTokenAddresses = data.user?.liquidityPositions.map(v => v.pair.id.toLowerCase()) ?? []; | ||
const heldTokens = await tokenLoader.getMany(heldTokenAddresses.map(t => ({ address: t, network: this.network }))); | ||
const sanitized = compact(heldTokens).filter(isAppToken); | ||
|
||
let results: RawAppTokenBalance[] = []; | ||
for (const batch of chunk(sanitized, 100).values()) { | ||
results = results.concat( | ||
await Promise.all( | ||
batch.map(async appToken => ({ | ||
key: this.appToolkit.getPositionKey(appToken), | ||
balance: (await this.getBalancePerToken({ multicall, address, appToken })).toString(), | ||
})), | ||
), | ||
); | ||
} | ||
return results; | ||
} | ||
} |
33 changes: 0 additions & 33 deletions
33
src/apps/uniswap-v2/helpers/uniswap-v2.the-graph.tvl-helper.ts
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters