Skip to content

Commit

Permalink
feat: map Uniswap token list to Gnosis chain (#359)
Browse files Browse the repository at this point in the history
* feat: map Uniswap token list to Gnosis chain

* chore: remove node-fetch

* chore: rename generateGnosisChainList

* chore: fix scripts
  • Loading branch information
shoom3301 authored Mar 28, 2024
1 parent 274f348 commit 30b3006
Show file tree
Hide file tree
Showing 19 changed files with 1,802 additions and 764 deletions.
19 changes: 19 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Editor configuration, see http://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf

[*.md]
max_line_length = off
trim_trailing_whitespace = false

[{*.ts,*.tsx}]
ij_typescript_force_quote_style = true
ij_typescript_use_double_quotes = false
ij_typescript_use_semicolon_after_statement = false
14 changes: 8 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,27 @@
"type": "module",
"main": "src/scripts/coingecko.js",
"scripts": {
"build": "npm run public && npm run coingecko",
"coingecko": "node src/scripts/coingecko.js",
"downloadImages": "node --experimental-json-modules src/scripts/downloadImages",
"cowFi:tokens": "node ./src/scripts/cowFi-tokens.js",
"build": "npm run generateGnosisChainList && npm run public && npm run coingecko",
"coingecko": "yarn run-script src/scripts/coingecko.js",
"downloadImages": "yarn run-script src/scripts/downloadImages",
"cowFi:tokens": "yarn run-script ./src/scripts/cowFi-tokens.js",
"generateGnosisChainList": "yarn run-script src/scripts/generateGnosisChainList.ts",
"public": "copyfiles src/public/*.json build/lists -f",
"workflowHelper": "python3 src/scripts/workflow_helper.py",
"validate": "ajv -s node_modules/@uniswap/token-lists/dist/tokenlist.schema.json -d src/public/CowSwap.json -c ajv-formats --errors text",
"fetchPermitInfo": "ts-node src/permitInfo/fetchPermitInfo.ts",
"fetchPermitInfo": "yarn run-script src/permitInfo/fetchPermitInfo.ts",
"fetchPermitInfo:mainnet": "yarn run fetchPermitInfo -- 1",
"fetchPermitInfo:gnosis": "yarn run fetchPermitInfo -- 100",
"fetchPermitInfo:sepolia": "yarn run fetchPermitInfo -- 11155111",
"recheckPermitInfo:mainnet": "yarn run fetchPermitInfo -- 1 '' '' true",
"recheckPermitInfo:gnosis": "yarn run fetchPermitInfo -- 100 '' '' true",
"recheckPermitInfo:sepolia": "yarn run fetchPermitInfo -- 11155111 '' '' true",
"run-script": "node --loader ts-node/esm --experimental-json-modules --experimental-specifier-resolution=node",
"test": "node --test"
},
"license": "(MIT OR Apache-2.0)",
"dependencies": {
"@cowprotocol/cow-sdk": "^5.1.0",
"@cowprotocol/permit-utils": "^0.1.2",
"@uniswap/token-lists": "^1.0.0-beta.33",
"ajv": "^8.12.0",
Expand All @@ -31,7 +34,6 @@
"axios": "^1.0.0",
"exponential-backoff": "^3.1.1",
"lodash": "^4.17.21",
"node-fetch": "^3.3.0",
"p-retry": "^6.1.0",
"p-throttle": "^5.1.0",
"ts-node": "^10.9.1"
Expand Down
50 changes: 50 additions & 0 deletions src/abi/multicallAbi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
export const MULTICALL_ABI = [
{
'inputs': [
{
'internalType': 'bool',
'name': 'requireSuccess',
'type': 'bool'
},
{
'components': [
{
'internalType': 'address',
'name': 'target',
'type': 'address'
},
{
'internalType': 'bytes',
'name': 'callData',
'type': 'bytes'
}
],
'internalType': 'struct Multicall3.Call[]',
'name': 'calls',
'type': 'tuple[]'
}
],
'name': 'tryAggregate',
'outputs': [
{
'components': [
{
'internalType': 'bool',
'name': 'success',
'type': 'bool'
},
{
'internalType': 'bytes',
'name': 'returnData',
'type': 'bytes'
}
],
'internalType': 'struct Multicall3.Result[]',
'name': 'returnData',
'type': 'tuple[]'
}
],
'stateMutability': 'payable',
'type': 'function'
}
]
7 changes: 7 additions & 0 deletions src/abi/omnibridgeAbi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const OMNIBRIDGE_CONTRACT_ABI = [{
'inputs': [{'internalType': 'address', 'name': '_nativeToken', 'type': 'address'}],
'name': 'bridgedTokenAddress',
'outputs': [{'internalType': 'address', 'name': '', 'type': 'address'}],
'stateMutability': 'view',
'type': 'function'
}]
9 changes: 5 additions & 4 deletions src/permitInfo/const.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { env } from 'node:process'
import { join } from 'node:path'
import {SupportedChainId} from '@cowprotocol/cow-sdk'

// CoW protocol contract address. Could be any address in theory for checking the token is permittable
export const SPENDER_ADDRESS = '0xC92E8bdf79f0507f65a392b0ab4667716BFE0110'

export const DEFAULT_RPC_URLS: Record<number, string> = {
1: 'https://mainnet.infura.io/v3/' + env.INFURA_API_KEY,
100: 'https://rpc.gnosischain.com',
11155111: 'https://ethereum-sepolia.publicnode.com',
export const DEFAULT_RPC_URLS: Record<SupportedChainId, string> = {
[SupportedChainId.MAINNET]: 'https://mainnet.infura.io/v3/' + env.INFURA_API_KEY,
[SupportedChainId.GNOSIS_CHAIN]: 'https://rpc.gnosischain.com',
[SupportedChainId.SEPOLIA]: 'https://ethereum-sepolia.publicnode.com',
}

export const BASE_PATH = join('..', 'public')
26 changes: 15 additions & 11 deletions src/permitInfo/fetchPermitInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ import {
import * as path from 'node:path'
import { readFileSync, writeFileSync } from 'node:fs'
import { JsonRpcProvider } from '@ethersproject/providers'
import { argv, chdir, exit } from 'node:process'
import { BASE_PATH, SPENDER_ADDRESS } from './const.ts'
import { sortPermitInfo } from './utils/sortPermitInfo.ts'
import { getProvider } from './utils/getProvider.ts'
import { Token } from './types.ts'
import { getTokensFromTokenList } from './utils/getTokensFromTokenList.ts'
import { getUnsupportedTokensFromPermitInfo } from './utils/getUnsupportedTokensFromPermitInfo.ts'
import { argv, chdir, env, exit } from 'node:process'
import { BASE_PATH, SPENDER_ADDRESS } from './const'
import { sortPermitInfo } from './utils/sortPermitInfo'
import { getProvider } from './utils/getProvider'
import { Token } from './types'
import { getTokensFromTokenList } from './utils/getTokensFromTokenList'
import { getUnsupportedTokensFromPermitInfo } from './utils/getUnsupportedTokensFromPermitInfo'
import { SupportedChainId } from '@cowprotocol/cow-sdk'

// TODO: maybe make the args nicer?
// Get args from cli: chainId, optional token lists path, optional rpcUrl, optional recheckUnsupported flag
Expand All @@ -59,7 +60,7 @@ if (!chainId) {
chdir(path.dirname(scriptPath))

async function fetchPermitInfo(
chainId: number,
chainId: SupportedChainId,
tokenListPath: string | undefined,
rpcUrl: string | undefined,
recheckUnsupported: boolean = false,
Expand All @@ -81,6 +82,10 @@ async function fetchPermitInfo(
}
}

if (!rpcUrl && chainId === 1 && !env.INFURA_API_KEY) {
throw new Error(`INFURA_API_KEY is required`)
}

// Build provider instance
const provider = getProvider(chainId, rpcUrl)

Expand Down Expand Up @@ -132,7 +137,7 @@ const throttle = pThrottle({
const throttledGetTokenPermitInfo = throttle(getTokenPermitInfo)

async function _fetchPermitInfo(
chainId: number,
chainId: SupportedChainId,
provider: JsonRpcProvider,
token: Token,
existing: PermitInfo | undefined,
Expand All @@ -151,7 +156,6 @@ async function _fetchPermitInfo(
provider,
spender: SPENDER_ADDRESS,
tokenAddress: token.address,
tokenName: token.name,
})

if ('error' in response) {
Expand All @@ -169,6 +173,6 @@ async function _fetchPermitInfo(
}

// Execute the script
fetchPermitInfo(+chainId, tokenListPath, rpcUrl, !!recheckUnsupported, !!forceRecheck).then(() =>
fetchPermitInfo(+chainId as SupportedChainId, tokenListPath, rpcUrl, !!recheckUnsupported, !!forceRecheck).then(() =>
console.info(`Done 🏁`),
)
10 changes: 3 additions & 7 deletions src/permitInfo/utils/getProvider.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { JsonRpcProvider } from '@ethersproject/providers'
import { DEFAULT_RPC_URLS } from '../const.ts'
import { env } from 'node:process'
import { DEFAULT_RPC_URLS } from '../const'
import { ethers } from 'ethers'
import {SupportedChainId} from '@cowprotocol/cow-sdk'

export function getProvider(chainId: number, rpcUrl: string | undefined): JsonRpcProvider {
const rpcEndpoint = rpcUrl ? rpcUrl : DEFAULT_RPC_URLS[chainId]
const rpcEndpoint = rpcUrl ? rpcUrl : DEFAULT_RPC_URLS[chainId as SupportedChainId]

if (!rpcEndpoint) {
throw new Error(`No RPC found for network ${chainId}`)
}

if (!rpcUrl && chainId === 1 && !env.INFURA_API_KEY) {
throw new Error(`INFURA_API_KEY is required`)
}

return new ethers.providers.JsonRpcProvider(rpcEndpoint)
}
15 changes: 8 additions & 7 deletions src/permitInfo/utils/getTokensFromTokenList.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { readFileSync } from 'node:fs'
import { BASE_PATH } from '../const.ts'
import { Token } from '../types.ts'
import { BASE_PATH } from '../const'
import { Token } from '../types'
import { join } from 'node:path'
import { SupportedChainId } from '@cowprotocol/cow-sdk'

const tokenListsByNetwork: Record<number, string> = {
1: 'CowSwap.json',
100: 'CowSwap.json',
11155111: 'CowSwapSepolia.json',
const tokenListsByNetwork: Record<SupportedChainId, string> = {
[SupportedChainId.MAINNET]: 'CowSwap.json',
[SupportedChainId.GNOSIS_CHAIN]: 'CowSwap.json',
[SupportedChainId.SEPOLIA]: 'CowSwapSepolia.json',
}

export function getTokensFromTokenList(chainId: number, tokenListPath: string | undefined): Array<Token> {
export function getTokensFromTokenList(chainId: SupportedChainId, tokenListPath: string | undefined): Array<Token> {
const filePath = tokenListPath
? tokenListPath
: join(BASE_PATH, tokenListsByNetwork[chainId])
Expand Down
4 changes: 2 additions & 2 deletions src/permitInfo/utils/getUnsupportedTokensFromPermitInfo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isSupportedPermitInfo, PermitInfo } from '@cowprotocol/permit-utils'
import { Token } from '../types.js'
import { Token } from '../types'

export function getUnsupportedTokensFromPermitInfo(
chainId: number,
Expand All @@ -9,7 +9,7 @@ export function getUnsupportedTokensFromPermitInfo(

for (const [k, v] of Object.entries(allPermitInfo)) {
if (!isSupportedPermitInfo(v)) {
tokens.push({ address: k, name: v?.name, chainId })
tokens.push({ address: k, name: (v as PermitInfo)?.name, chainId })
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/permitInfo/utils/sortPermitInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function sortPermitInfo(allPermitInfo: Record<string, PermitInfo>): Recor
// Otherwise, supported tokens go on top
return isSupportedPermitInfo(pb) ? 1 : -1
})
.reduce((acc, address) => {
.reduce<{[address: string]: PermitInfo}>((acc, address) => {
// Create a new object with the keys in the sorted order
acc[address] = allPermitInfo[address]

Expand Down
Loading

0 comments on commit 30b3006

Please sign in to comment.