Skip to content

Commit 95b789a

Browse files
alfetopitoLeandro BoscariolanxolinVelenir
authored
978/config file (gnosis#987)
* Bumping dex-js to include Tcr contract bindings * Addeding config types * Moving TCR config to global config file * Added TheGraph and DexPriceEstimator configs * Add config loader * Move jest config to it's own file and add CONFIG global * Add config as a yaml file * Refactor apis to use global config * Add config tests * Make TCR optional * Expose global config using webpack * Disable annoying error of eslint analyising JS files * Update yarn lock * Do not fail with global * Fix issues in config test * Rename app name * Fix issue with globals * Fix issue with globals * Small hack to make it work, however added some fixmes to review later! * Use appName * Simplify set creation Co-authored-by: Velenir <[email protected]> * Apply Dimas suggestions to fix issues with the declarations * Use interface instead of type * Simplify parsing config * Make app name mandatory * Rename default config * Add a WARN to not override * Improve extenssion checking * Don't read a file if the extenssion is unsupported * Improve definition of tcrApi * Simplify config extraction * Removing uncessary config from webpack * Moving env vars from Define to Environment plugin * Allowing custom config files of different formats * Do not load custom config when testing * Added logoPath as config option * Added test for new config `logoPath` * Checking for files with given extension instead of checking every file on custom folder * Adding templatePath to default config * Added Eth node config * fix type inference * Removing unused import after cherry-pick Co-authored-by: Leandro Boscariol <[email protected]> Co-authored-by: Anxo Rodriguez <[email protected]> Co-authored-by: Velenir <[email protected]> Co-authored-by: Velenir <[email protected]>
1 parent ea991d3 commit 95b789a

24 files changed

+526
-204
lines changed

.eslintrc.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ module.exports = {
2626
'react-hooks/rules-of-hooks': 'error',
2727
'react-hooks/exhaustive-deps': 'warn',
2828
},
29+
overrides: [
30+
{
31+
files: ['**/*.js'],
32+
rules: {
33+
'@typescript-eslint/explicit-function-return-type': 0,
34+
'@typescript-eslint/no-var-requires': 0,
35+
},
36+
},
37+
],
2938
plugins: ['react-hooks'],
3039
settings: {
3140
react: {

README.md

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,41 @@ import About from 'pages/About'
109109
// will try resolving from custom/ first and failing that from src/
110110
```
111111

112-
If you also need to change contents of `index.html`, you will have to modify `webpack.config.babel.js`:
112+
## Config
113113

114-
```js
115-
new HtmlWebPackPlugin({
116-
template: './src/html/index.html',
117-
title: ...
114+
### TCR (Optional)
115+
116+
Tokens are dynamically loaded from the contract, but it might not be desirable to display everything in the interface.
117+
118+
To dynamically control which tokens are displayed without the need of a redeployment, it's possible to use a Token Curated Registry (TCR) contract per network.
119+
120+
The only requirement is that this contract implements the following method:
121+
122+
```sol
123+
function getTokens(uint256 _listId) public view returns (address[] memory)
124+
```
125+
126+
For a sample implementation, refer to [dxDAO's TCR](https://github.com/nicoelzer/dxDAO-Token-Registry/blob/master/contracts/dxTokenRegistry.sol).
127+
128+
<!-- TODO: use a central place for all configs https://github.com/gnosis/dex-react/issues/978 -->
129+
130+
Add the relevant config to [this file](./src/api/index.ts).
131+
132+
Config format:
133+
134+
```ts
135+
{
136+
<networkId>: {
137+
listId: 4
138+
contractAddress: '0xa2d...'
139+
}
140+
}
118141
```
119142

120-
A good idea would be to conditionally change `template` path based on external config or an env variable.
143+
Where:
144+
145+
- `<networkId>` is a number, such as `1` for Mainnet, `4` for Rinkeby and so on.
146+
- `listId` is optional and defaults to `0`
147+
- `contractAddress` the address of the contract deployed in network `<networkId>`
148+
149+
**Note**: For networks where a TCR contract is not provided, the tokens will not be filtered.

config-default.yaml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#######################################################################################
2+
## DEFAULT CONFIG ##
3+
#######################################################################################
4+
# - Defines the default configuration
5+
# - **WARNING**: Do not modify this file
6+
# - For custom config create a new file "custom/config.yaml"
7+
# - You can override in the custom file any config that is defined here
8+
# - For redefining components, or for more information, follow the instructions in Github:
9+
# https://github.com/gnosis/dex-react
10+
11+
# TODO: Rename Dapp into Gnosis Protocol DApp
12+
name: Mesa - Gnosis Protocol DApp
13+
14+
logoPath: './src/assets/img/logo.svg'
15+
16+
templatePath: './src/html/index.html'
17+
18+
# Whitelisted tokens from Gnosis Contract retrieved from a smart contract
19+
tcr:
20+
type: 'multi-tcr' # json-file, kleros, multi-tcr, none (for full list, it's not the default)
21+
config:
22+
lists:
23+
- networkId: 1
24+
listId: 1
25+
contractAddress: '0x1854dae560abb0f399d8badca456663ca5c309d0'
26+
27+
- networkId: 4
28+
contractAddress: '0xBb840456546496E7640DC09ba9fE06E67C157E1b'
29+
30+
# Gets a price estimation for a given volume
31+
dexPriceEstimator:
32+
type: 'dex-price-estimator' # dex-price-estimator, the-graph
33+
config:
34+
- networkId: 1
35+
url: https://dex-price-estimator.gnosis.io
36+
37+
- networkId: 4
38+
url: https://dex-price-estimator.rinkeby.gnosis.io
39+
40+
# Subgraph abstraction, used for getting the last price
41+
theGraphApi:
42+
type: 'the-graph' # json-file, kleros, multi-tcr, none (for full list, it's not the default)
43+
config:
44+
- networkId: 1
45+
url: https://api.thegraph.com/subgraphs/name/gnosis/protocol
46+
47+
- networkId: 4
48+
url: https://api.thegraph.com/subgraphs/name/gnosis/protocol-rinkeby
49+
50+
# Eth node config
51+
defaultProviderConfig:
52+
type: 'infura' # Choices: infura | url
53+
config:
54+
# It'll be appended to `ethNodeUrl`
55+
infuraId: 607a7dfcb1ad4a0b83152e30ce20cfc5
56+
# Eth node websocket url to use.
57+
# When `infuraId` not set, is used as is
58+
infuraEndpoint: wss://mainnet.infura.io/ws/v3/
59+
#
60+
# Example for type `url`
61+
# type: 'url'
62+
# config:
63+
# ethNodeUrl: <local eth node>

jest.config.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const config = require('./src/loadConfig')(true)
2+
3+
module.exports = {
4+
roots: ['<rootDir>/test'],
5+
transform: {
6+
'^.+\\.tsx?$': 'ts-jest',
7+
},
8+
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
9+
moduleDirectories: ['node_modules', 'src'],
10+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
11+
snapshotSerializers: ['enzyme-to-json/serializer'],
12+
setupFilesAfterEnv: ['<rootDir>/test/setupEnzyme.ts'],
13+
moduleNameMapper: {
14+
'\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
15+
'<rootDir>/test/mocks/fileMock.js',
16+
'\\.(css|less)$': '<rootDir>/test/mocks/fileMock.js',
17+
},
18+
globalSetup: '<rootDir>/test/globalSetup.ts',
19+
globals: {
20+
CONFIG: config,
21+
'ts-jest': {
22+
diagnostics: {
23+
warnOnly: true,
24+
},
25+
},
26+
},
27+
}

package.json

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,38 +21,6 @@
2121
"pre-commit": "npm run verify"
2222
}
2323
},
24-
"jest": {
25-
"roots": [
26-
"<rootDir>/test"
27-
],
28-
"transform": {
29-
"^.+\\.tsx?$": "ts-jest"
30-
},
31-
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
32-
"moduleDirectories": [
33-
"node_modules",
34-
"src"
35-
],
36-
"moduleFileExtensions": [
37-
"ts",
38-
"tsx",
39-
"js",
40-
"jsx",
41-
"json",
42-
"node"
43-
],
44-
"snapshotSerializers": [
45-
"enzyme-to-json/serializer"
46-
],
47-
"setupFilesAfterEnv": [
48-
"<rootDir>/test/setupEnzyme.ts"
49-
],
50-
"moduleNameMapper": {
51-
"\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test/mocks/fileMock.js",
52-
"\\.(css|less)$": "<rootDir>/test/mocks/fileMock.js"
53-
},
54-
"globalSetup": "<rootDir>/test/globalSetup.ts"
55-
},
5624
"author": "",
5725
"dependencies": {
5826
"@amcharts/amcharts4": "^4.9.12",
@@ -154,6 +122,7 @@
154122
"webpack": "^4.41.6",
155123
"webpack-bundle-analyzer": "^3.6.0",
156124
"webpack-cli": "^3.3.10",
157-
"webpack-dev-server": "^3.10.1"
125+
"webpack-dev-server": "^3.10.1",
126+
"yaml": "^1.9.2"
158127
}
159128
}

src/api/dexPriceEstimator/DexPriceEstimatorApi.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import BigNumber from 'bignumber.js'
22
import { assert, TEN_BIG_NUMBER, ONE_BIG_NUMBER } from '@gnosis.pm/dex-js'
3-
import { Network } from 'types'
43
import { ORDER_BOOK_HOPS_DEFAULT, ORDER_BOOK_HOPS_MAX } from 'const'
54

65
export interface DexPriceEstimatorApi {
@@ -42,32 +41,23 @@ interface GetPriceResponse {
4241
sellAmountInQuote: string
4342
}
4443

45-
export interface Params {
46-
networkIds: number[]
44+
export interface PriceEstimatorEndpoint {
45+
networkId: number
46+
url: string
4747
}
4848

49-
function getDexPriceEstimatorUrl(networkId: number): string {
50-
const basePath = 'api/v1/'
49+
export type DexPriceEstimatorParams = PriceEstimatorEndpoint[]
5150

52-
switch (networkId) {
53-
case Network.Mainnet:
54-
return `https://dex-price-estimator.gnosis.io/${basePath}`
55-
case Network.Rinkeby:
56-
return `https://dex-price-estimator.rinkeby.gnosis.io/${basePath}`
57-
default:
58-
throw new Error(`dex-price-estimator not available for network ${networkId}`)
59-
}
51+
function getDexPriceEstimatorUrl(baseUlr: string): string {
52+
return `${baseUlr}${baseUlr.endsWith('/') ? '' : '/'}api/v1/`
6053
}
6154

6255
export class DexPriceEstimatorApiImpl implements DexPriceEstimatorApi {
63-
private urlsByNetwork: { [networkId: number]: string }
64-
65-
public constructor(params: Params) {
66-
const { networkIds } = params
56+
private urlsByNetwork: { [networkId: number]: string } = {}
6757

68-
this.urlsByNetwork = {}
69-
networkIds.forEach(networkId => {
70-
this.urlsByNetwork[networkId] = getDexPriceEstimatorUrl(networkId)
58+
public constructor(params: DexPriceEstimatorParams) {
59+
params.forEach(endpoint => {
60+
this.urlsByNetwork[endpoint.networkId] = getDexPriceEstimatorUrl(endpoint.url)
7161
})
7262
}
7363

src/api/dexPriceEstimator/DexPriceEstimatorApiProxy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { CacheMixin } from 'api/proxy'
22
import { PRICES_CACHE_TIME } from 'const'
3-
import { DexPriceEstimatorApiImpl, Params, DexPriceEstimatorApi } from './DexPriceEstimatorApi'
3+
import { DexPriceEstimatorApiImpl, DexPriceEstimatorParams, DexPriceEstimatorApi } from './DexPriceEstimatorApi'
44

55
export class DexPriceEstimatorApiProxy extends DexPriceEstimatorApiImpl {
66
private cache: CacheMixin
77

8-
public constructor(params: Params) {
8+
public constructor(params: DexPriceEstimatorParams) {
99
super(params)
1010

1111
this.cache = new CacheMixin()

src/api/index.ts

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { TheGraphApiProxy } from './thegraph/TheGraphApiProxy'
1717
import { DexPriceEstimatorApi } from './dexPriceEstimator/DexPriceEstimatorApi'
1818
import { DexPriceEstimatorApiProxy } from './dexPriceEstimator/DexPriceEstimatorApiProxy'
1919
import { TcrApi } from './tcr/TcrApi'
20-
import { TcrApiProxy } from './tcr/TcrApiProxy'
20+
import { MultiTcrApiProxy } from './tcr/MultiTcrApiProxy'
2121
import {
2222
tokenList,
2323
exchangeBalanceStates,
@@ -114,39 +114,54 @@ function createTokenListApi(): TokenList {
114114
}
115115

116116
function createTheGraphApi(): TheGraphApi {
117-
const urls = {
118-
[Network.Mainnet]: 'https://api.thegraph.com/subgraphs/name/gnosis/protocol',
119-
[Network.Rinkeby]: 'https://api.thegraph.com/subgraphs/name/gnosis/protocol-rinkeby',
117+
const { type, config } = CONFIG.theGraphApi
118+
let theGraphApi: TheGraphApi
119+
switch (type) {
120+
case 'the-graph':
121+
theGraphApi = new TheGraphApiProxy(config)
122+
break
123+
124+
default:
125+
throw new Error('Unknown implementation for TheGraphApi: ' + type)
120126
}
121127

122-
const theGraphApi = new TheGraphApiProxy({ urls })
123-
124128
window['theGraphApi'] = theGraphApi
125-
126129
return theGraphApi
127130
}
128131

129132
function createDexPriceEstimatorApi(): DexPriceEstimatorApi {
130-
const networkIds = [Network.Mainnet, Network.Rinkeby]
131-
132-
const dexPriceEstimatorApi = new DexPriceEstimatorApiProxy({ networkIds })
133+
const { type, config } = CONFIG.dexPriceEstimator
134+
let dexPriceEstimatorApi: DexPriceEstimatorApi
135+
switch (type) {
136+
case 'dex-price-estimator':
137+
dexPriceEstimatorApi = new DexPriceEstimatorApiProxy(config)
138+
break
139+
140+
default:
141+
throw new Error('Unknown implementation for DexPriceEstimatorApi: ' + type)
142+
}
133143

134144
window['dexPriceEstimatorApi'] = dexPriceEstimatorApi
135-
136145
return dexPriceEstimatorApi
137146
}
138147

139-
function createTcrApi(web3: Web3): TcrApi {
140-
// TODO: load config from config file
141-
const config = {
142-
1: { listId: 1, contractAddress: '0x1854dae560abb0f399d8badca456663ca5c309d0' },
143-
4: { contractAddress: '0xBb840456546496E7640DC09ba9fE06E67C157E1b' },
148+
function createTcrApi(web3: Web3): TcrApi | undefined {
149+
const { type } = CONFIG.tcr
150+
let tcrApi: TcrApi | undefined
151+
switch (CONFIG.tcr.type) {
152+
case 'none':
153+
tcrApi = undefined
154+
break
155+
case 'multi-tcr':
156+
const multiTcrApiConfig = CONFIG.tcr
157+
tcrApi = new MultiTcrApiProxy({ web3, ...multiTcrApiConfig.config })
158+
break
159+
160+
default:
161+
throw new Error('Unknown implementation for DexPriceEstimatorApi: ' + type)
144162
}
145163

146-
const tcrApi = new TcrApiProxy({ web3, config })
147-
148164
window['tcrApi'] = tcrApi
149-
150165
return tcrApi
151166
}
152167

@@ -165,4 +180,4 @@ export const exchangeApi: ExchangeApi = createExchangeApi(erc20Api, injectedDepe
165180
export const tokenListApi: TokenList = createTokenListApi()
166181
export const theGraphApi: TheGraphApi = createTheGraphApi()
167182
export const dexPriceEstimatorApi: DexPriceEstimatorApi = createDexPriceEstimatorApi()
168-
export const tcrApi: TcrApi = createTcrApi(web3)
183+
export const tcrApi: TcrApi | undefined = createTcrApi(web3)

0 commit comments

Comments
 (0)