Skip to content

Commit

Permalink
refactor subgraph to support multiple chains (#233)
Browse files Browse the repository at this point in the history
* refactor subgraph to support multiple chains

* fix(lint): auto-fix [ci]

---------

Co-authored-by: mzywang <[email protected]>
  • Loading branch information
mzywang and mzywang authored Jun 21, 2024
1 parent cb859f1 commit 99a325a
Show file tree
Hide file tree
Showing 25 changed files with 1,746 additions and 338 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,17 @@ Pending Changes at same URL
2. Install postgres: `brew install postgresql`
3. `yarn run build:docker`
4. `yarn run test`

### Adding New Chains

1. Create a new subgraph config in `src/utils/chains.ts`. This will require adding a new `<NETWORK_NAME>_NETWORK_NAME` const for the corresponding network.
2. Add a new entry in `networks.json` for the new chain. The network name should be derived from the CLI Name in The Graph's [supported networks documenation](https://thegraph.com/docs/en/developing/supported-networks/). The factory address can be derived from Uniswap's [deployments documentation](https://docs.uniswap.org/contracts/v3/reference/deployments/ethereum-deployments).
3. To deploy to Alchemy, run the following command:

```
yarn run deploy:alchemy --
<SUBGRAPH_NAME>
--version-label <VERSION_LABEL>
--deploy-key <DEPLOYMENT_KEY>
--network <NETWORK_NAME>
```
56 changes: 56 additions & 0 deletions networks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"arbitrum-one": {
"Factory": {
"address": "0x1F98431c8aD98523631AE4a59f267346ea31F984",
"startBlock": 165
}
},
"avalanche": {
"Factory": {
"address": "0x740b1c1de25031C31FF4fC9A62f554A55cdC1baD",
"startBlock": 27832971
}
},
"base": {
"Factory": {
"address": "0x33128a8fC17869897dcE68Ed026d694621f6FDfD",
"startBlock": 2009445
}
},
"blast-mainnet": {
"Factory": {
"address": "0x792edAdE80af5fC680d96a2eD80A44247D2Cf6Fd",
"startBlock": 400903
}
},
"bsc": {
"Factory": {
"address": "0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7",
"startBlock": 12369621
}
},
"celo": {
"Factory": {
"address": "0xAfE208a311B21f13EF87E33A90049fC17A7acDEc",
"startBlock": 13916355
}
},
"mainnet": {
"Factory": {
"address": "0x1F98431c8aD98523631AE4a59f267346ea31F984",
"startBlock": 12369621
}
},
"matic": {
"Factory": {
"address": "0x1F98431c8aD98523631AE4a59f267346ea31F984",
"startBlock": 22757547
}
},
"optimism": {
"Factory": {
"address": "0x1F98431c8aD98523631AE4a59f267346ea31F984",
"startBlock": 0
}
}
}
4 changes: 3 additions & 1 deletion schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ type Token @entity {
totalValueLockedUSD: BigDecimal!
# TVL derived in USD untracked
totalValueLockedUSDUntracked: BigDecimal!
# derived price in ETH
# Note: for chains where ETH is not the native token, this will be the derived
# price of that chain's native token, effectively, this should be renamed
# derivedNative
derivedETH: BigDecimal!
# pools token is in that are white listed for USD pricing
whitelistPools: [Pool!]!
Expand Down
127 changes: 127 additions & 0 deletions src/backfill/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { Address, BigInt, ethereum } from '@graphprotocol/graph-ts'

import { ERC20 } from '../types/Factory/ERC20'
import { Pool as PoolABI } from '../types/Factory/Pool'
import { Pool, Token } from '../types/schema'
import { Pool as PoolTemplate } from '../types/templates'
import { convertTokenToDecimal } from '../utils'
import { ZERO_BD, ZERO_BI } from '../utils/constants'
import { StaticTokenDefinition } from '../utils/staticTokenDefinition'
import { fetchTokenDecimals, fetchTokenName, fetchTokenSymbol, fetchTokenTotalSupply } from '../utils/token'

function populateToken(tokenAddress: string, tokenOverrides: StaticTokenDefinition[]): void {
let token = Token.load(tokenAddress)
if (token != null) {
return
}
token = new Token(tokenAddress)
token.symbol = fetchTokenSymbol(Address.fromString(tokenAddress), tokenOverrides)
token.name = fetchTokenName(Address.fromString(tokenAddress), tokenOverrides)
token.totalSupply = fetchTokenTotalSupply(Address.fromString(tokenAddress))
const decimals = fetchTokenDecimals(Address.fromString(tokenAddress), tokenOverrides)
if (decimals === null) {
return
}
token.decimals = decimals
token.derivedETH = ZERO_BD
token.volume = ZERO_BD
token.volumeUSD = ZERO_BD
token.feesUSD = ZERO_BD
token.untrackedVolumeUSD = ZERO_BD
token.totalValueLocked = ZERO_BD
token.totalValueLockedUSD = ZERO_BD
token.totalValueLockedUSDUntracked = ZERO_BD
token.txCount = ZERO_BI
token.poolCount = ZERO_BI
token.whitelistPools = []
token.save()
}

/**
* Create entries in store for hard-coded pools and tokens. This is only
* used for generating optimism pre-regenesis data.
*/
export function populateEmptyPools(
event: ethereum.Event,
poolMappings: Array<Address[]>,
whitelistTokens: string[],
tokenOverrides: StaticTokenDefinition[],
): void {
const length = poolMappings.length
for (let i = 0; i < length; ++i) {
const poolMapping = poolMappings[i]
const newAddress = poolMapping[1]
const token0Address = poolMapping[2]
const token1Address = poolMapping[3]

const poolContract = PoolABI.bind(newAddress)
const pool = new Pool(newAddress.toHexString()) as Pool
pool.createdAtBlockNumber = event.block.number
pool.createdAtTimestamp = event.block.timestamp
pool.token0 = token0Address.toHexString()
pool.token1 = token1Address.toHexString()
pool.liquidity = poolContract.liquidity()
pool.sqrtPrice = ZERO_BI
pool.token0Price = ZERO_BD
pool.token1Price = ZERO_BD
pool.observationIndex = ZERO_BI
pool.liquidityProviderCount = ZERO_BI
pool.txCount = ZERO_BI
pool.totalValueLockedToken0 = ZERO_BD
pool.totalValueLockedToken1 = ZERO_BD
pool.totalValueLockedETH = ZERO_BD
pool.totalValueLockedUSD = ZERO_BD
pool.totalValueLockedUSDUntracked = ZERO_BD
pool.volumeToken0 = ZERO_BD
pool.volumeToken1 = ZERO_BD
pool.volumeUSD = ZERO_BD
pool.untrackedVolumeUSD = ZERO_BD
pool.feesUSD = ZERO_BD
pool.collectedFeesToken0 = ZERO_BD
pool.collectedFeesToken1 = ZERO_BD
pool.collectedFeesUSD = ZERO_BD

// need fee tier
const feeTier = poolContract.fee()
pool.feeTier = BigInt.fromI32(feeTier)

// create token entities if needed
populateToken(token0Address.toHexString(), tokenOverrides)
populateToken(token1Address.toHexString(), tokenOverrides)
const token0 = Token.load(token0Address.toHexString())
const token1 = Token.load(token1Address.toHexString())

if (token0 && token1) {
if (whitelistTokens.includes(pool.token0)) {
const newPools = token1.whitelistPools
newPools.push(pool.id)
token1.whitelistPools = newPools
}

if (whitelistTokens.includes(token1.id)) {
const newPools = token0.whitelistPools
newPools.push(pool.id)
token0.whitelistPools = newPools
}

// populate the TVL by call contract balanceOf
const token0Contract = ERC20.bind(Address.fromString(pool.token0))
const tvlToken0Raw = token0Contract.balanceOf(Address.fromString(pool.id))
const tvlToken0Adjusted = convertTokenToDecimal(tvlToken0Raw, token0.decimals)
pool.totalValueLockedToken0 = tvlToken0Adjusted
token0.totalValueLocked = tvlToken0Adjusted

const token1Contract = ERC20.bind(Address.fromString(pool.token1))
const tvlToken1Raw = token1Contract.balanceOf(Address.fromString(pool.id))
const tvlToken1Adjusted = convertTokenToDecimal(tvlToken1Raw, token1.decimals)
pool.totalValueLockedToken1 = tvlToken1Adjusted
token1.totalValueLocked = tvlToken1Adjusted

// add pool to tracked address and store entities
PoolTemplate.create(Address.fromString(pool.id))
token0.save()
token1.save()
pool.save()
}
}
}
Loading

0 comments on commit 99a325a

Please sign in to comment.