Skip to content

Commit 75c2bcf

Browse files
Merge pull request #24 from uniswap-integration/feature/custom-network
Feature/custom network
2 parents 8bebd9a + e737fa7 commit 75c2bcf

14 files changed

Lines changed: 312 additions & 85 deletions

README.md

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,34 @@ export interface CloneUniswapContractDetails {
129129
v3Override?: CloneUniswapContractDetailsV3 | undefined;
130130
}
131131

132+
export interface Token {
133+
chainId: ChainId;
134+
contractAddress: string;
135+
decimals: number;
136+
symbol: string;
137+
name: string;
138+
}
139+
140+
export interface NativeCurrencyInfo {
141+
name: string;
142+
symbol: string;
143+
}
144+
145+
export interface CustomNetwork {
146+
nameNetwork: string;
147+
multicallContractAddress: string;
148+
nativeCurrency: NativeCurrencyInfo;
149+
nativeWrappedTokenInfo: Token;
150+
// defined your base tokens here!
151+
baseTokens?: {
152+
usdt?: Token | undefined;
153+
dai?: Token | undefined;
154+
comp?: Token | undefined;
155+
usdc?: Token | undefined;
156+
wbtc?: Token | undefined;
157+
};
158+
}
159+
132160
export class UniswapPairSettings {
133161
slippage: number;
134162
deadlineMinutes: number;
@@ -137,6 +165,8 @@ export class UniswapPairSettings {
137165
gasSettings?: GasSettings = undefined;
138166
// can be used to pass in a fork of uniswap contract details
139167
cloneUniswapContractDetails?: CloneUniswapContractDetails = undefined;
168+
// can be used to define unsupported networks
169+
customNetwork?: CustomNetwork = undefined;
140170

141171
constructor(settings?: {
142172
slippage?: number | undefined;
@@ -145,12 +175,14 @@ export class UniswapPairSettings {
145175
uniswapVersions?: UniswapVersion[] | undefined;
146176
gasSettings?: GasSettings | undefined;
147177
cloneUniswapContractDetails?: CloneUniswapContractDetails | undefined;
178+
customNetwork?: CustomNetwork | undefined;
148179
}) {
149180
this.slippage = settings?.slippage || 0.005;
150181
this.deadlineMinutes = settings?.deadlineMinutes || 20;
151182
this.disableMultihops = settings?.disableMultihops || false;
152183
this.gasSettings = settings?.gasSettings;
153184
this.cloneUniswapContractDetails = settings?.cloneUniswapContractDetails;
185+
this.customNetwork = settings?.customNetwork;
154186

155187
if (
156188
Array.isArray(settings?.uniswapVersions) &&
@@ -173,9 +205,9 @@ export class UniswapPairSettings {
173205
}
174206
```
175207
176-
### Ethereum provider
208+
### With only the chainId
177209
178-
This will use your ethereum provider you pass in. This will work with any web3 provider, ethers provider or custom provider. For example when using MetaMask you can pass in the window.ethereum and it work. You must supply the ethereum address and the wallet be approved to use for the dApp and unlocked before passing it in. The uniswap sdk makes those assumptions without them it will not work as MetaMask is not allowed access to your dApp. Any change of network or ethereum address change you will need to handle in your dApp and regenerate the uniswap pair context. Most the time the contract addresses for your tokens are different anyway.
210+
This will use a infura endpoint without you having to pass in a node
179211
180212
```ts
181213
import { UniswapPair, ChainId, UniswapVersion, ETH } from 'simple-uniswap-sdk';
@@ -187,7 +219,7 @@ const uniswapPair = new UniswapPair({
187219
toTokenContractAddress: '0x1985365e9f78359a9B6AD760e32412f4a445E862',
188220
// the ethereum address of the user using this part of the dApp
189221
ethereumAddress: '0xB1E6079212888f0bE0cf55874B2EB9d7a5e02cD9',
190-
ethereumProvider: YOUR_WEB3_ETHERS_OR_CUSTOM_ETHEREUM_PROVIDER,
222+
chainId: ChainId.MAINNET,
191223
settings: new UniswapPairSettings({
192224
// if not supplied it will use `0.005` which is 0.5%
193225
// please pass it in as a full number decimal so 0.7%
@@ -210,9 +242,9 @@ const uniswapPair = new UniswapPair({
210242
const uniswapPairFactory = await uniswapPair.createFactory();
211243
```
212244
213-
### With only the chainId
245+
### With your own provider url
214246
215-
This will use a infura endpoint without you having to pass in a node
247+
This will use your node you pass in you must pass us the chainId as well, this stops the ethers instance calling pointless `JSONRPC` calls to get the chain id before every `JSONRPC` call.
216248
217249
```ts
218250
import { UniswapPair, ChainId, UniswapVersion, ETH } from 'simple-uniswap-sdk';
@@ -225,6 +257,7 @@ const uniswapPair = new UniswapPair({
225257
// the ethereum address of the user using this part of the dApp
226258
ethereumAddress: '0xB1E6079212888f0bE0cf55874B2EB9d7a5e02cD9',
227259
chainId: ChainId.MAINNET,
260+
providerUrl: YOUR_PROVIDER_URL,
228261
settings: new UniswapPairSettings({
229262
// if not supplied it will use `0.005` which is 0.5%
230263
// please pass it in as a full number decimal so 0.7%
@@ -247,9 +280,9 @@ const uniswapPair = new UniswapPair({
247280
const uniswapPairFactory = await uniswapPair.createFactory();
248281
```
249282
250-
### With your own provider url
283+
### Custom Ethereum provider
251284
252-
This will use your node you pass in you must pass us the chainId as well, this stops the ethers instance calling pointless `JSONRPC` calls to get the chain id before every `JSONRPC` call.
285+
This will use your ethereum provider you pass in. This will work with any web3 provider, ethers provider or custom provider. For example when using MetaMask you can pass in the window.ethereum and it work. You must supply the ethereum address and the wallet be approved to use for the dApp and unlocked before passing it in. The uniswap sdk makes those assumptions without them it will not work as MetaMask is not allowed access to your dApp. Any change of network or ethereum address change you will need to handle in your dApp and regenerate the uniswap pair context. Most the time the contract addresses for your tokens are different anyway.
253286
254287
```ts
255288
import { UniswapPair, ChainId, UniswapVersion, ETH } from 'simple-uniswap-sdk';
@@ -261,8 +294,7 @@ const uniswapPair = new UniswapPair({
261294
toTokenContractAddress: '0x1985365e9f78359a9B6AD760e32412f4a445E862',
262295
// the ethereum address of the user using this part of the dApp
263296
ethereumAddress: '0xB1E6079212888f0bE0cf55874B2EB9d7a5e02cD9',
264-
chainId: ChainId.MAINNET,
265-
providerUrl: YOUR_PROVIDER_URL,
297+
ethereumProvider: YOUR_WEB3_ETHERS_OR_CUSTOM_ETHEREUM_PROVIDER,
266298
settings: new UniswapPairSettings({
267299
// if not supplied it will use `0.005` which is 0.5%
268300
// please pass it in as a full number decimal so 0.7%

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "simple-uniswap-sdk",
3-
"version": "3.5.0",
3+
"version": "3.6.0",
44
"description": "Simple easy to understand SDK for uniswap which looks over best v2 and v3 to find you the best swap quote",
55
"main": "dist/cjs/index.js",
66
"module": "./dist/esm/index.js",

src/common/tokens/eth.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ChainId } from '../../enums/chain-id';
2+
import { NativeCurrencyInfo } from '../../factories/pair/models/custom-network';
23
import { Token } from '../../factories/token/models/token';
34
import { ErrorCodes } from '../errors/error-codes';
45
import { UniswapError } from '../errors/uniswap-error';
@@ -24,12 +25,20 @@ export const isNativeEth = (contractAddress: string): boolean => {
2425
return contractAddress.includes(ETH_PREFIX);
2526
};
2627

27-
export const turnTokenIntoEthForResponse = (token: Token): Token => {
28+
export const turnTokenIntoEthForResponse = (
29+
token: Token,
30+
nativeCurrencyInfo: NativeCurrencyInfo | undefined
31+
): Token => {
2832
const clone = deepClone(token);
2933
// clear down contract address
3034
clone.contractAddress = 'NO_CONTRACT_ADDRESS';
31-
clone.symbol = ETH_SYMBOL;
32-
clone.name = ETH_NAME;
35+
if (nativeCurrencyInfo) {
36+
clone.symbol = nativeCurrencyInfo.symbol;
37+
clone.name = nativeCurrencyInfo.name;
38+
} else {
39+
clone.symbol = ETH_SYMBOL;
40+
clone.name = ETH_NAME;
41+
}
3342

3443
return clone;
3544
};
@@ -102,7 +111,18 @@ export class ETH {
102111
* Get ETH token info by chain id
103112
* @param chainId The chain id
104113
*/
105-
public static info(chainId: ChainId | number): Token {
114+
public static info(
115+
chainId: ChainId | number,
116+
customNetworkNativeWrappedTokenInfo: Token | undefined = undefined
117+
): Token {
118+
if (customNetworkNativeWrappedTokenInfo) {
119+
return {
120+
...customNetworkNativeWrappedTokenInfo,
121+
contractAddress: appendEthToContractAddress(
122+
customNetworkNativeWrappedTokenInfo.contractAddress
123+
),
124+
};
125+
}
106126
switch (chainId) {
107127
case ChainId.MAINNET:
108128
return this.MAINNET();

src/common/utils/trade-path.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,20 @@ import { ETH } from '../tokens/eth';
66
export function getTradePath(
77
chainId: ChainId,
88
fromToken: Token,
9-
toToken: Token
9+
toToken: Token,
10+
customNetworkNativeWrappedTokenInfo: Token | undefined
1011
): TradePath {
11-
if (fromToken.contractAddress === ETH.info(chainId).contractAddress) {
12+
if (
13+
fromToken.contractAddress ===
14+
ETH.info(chainId, customNetworkNativeWrappedTokenInfo).contractAddress
15+
) {
1216
return TradePath.ethToErc20;
1317
}
1418

15-
if (toToken.contractAddress === ETH.info(chainId).contractAddress) {
19+
if (
20+
toToken.contractAddress ===
21+
ETH.info(chainId, customNetworkNativeWrappedTokenInfo).contractAddress
22+
) {
1623
return TradePath.erc20ToEth;
1724
}
1825

src/custom-multicall.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Provider } from '@ethersproject/providers';
2+
import { Multicall } from 'ethereum-multicall';
3+
4+
export class CustomMulticall extends Multicall {
5+
constructor(
6+
ethersProvider: Provider,
7+
multicallCustomContractAddress?: string | undefined
8+
) {
9+
super({
10+
ethersProvider,
11+
tryAggregate: true,
12+
multicallCustomContractAddress,
13+
});
14+
}
15+
}

src/ethers-provider.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ describe('EthersProvider', () => {
5151
});
5252
}).toThrowError(
5353
new UniswapError(
54-
'Can not find chain name for 10293. This lib only supports mainnet(1), ropsten(4), kovan(42), rinkeby(4), görli(5) and ropsten(3)',
54+
'Can not find chain name for 10293. This lib only supports mainnet(1), ropsten(4), kovan(42), rinkeby(4) and görli(5)',
5555
ErrorCodes.canNotFindChainId
5656
)
5757
);

src/ethers-provider.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@ import { Contract, ContractInterface, providers } from 'ethers';
33
import { ErrorCodes } from './common/errors/error-codes';
44
import { UniswapError } from './common/errors/uniswap-error';
55
import { ChainId, ChainNames } from './enums/chain-id';
6+
import { CustomNetwork } from './factories/pair/models/custom-network';
67

78
export interface ChainIdAndProvider {
89
chainId: ChainId;
910
providerUrl?: string | undefined;
11+
customNetwork?: CustomNetwork | undefined;
1012
}
1113

1214
export interface EthereumProvider {
1315
ethereumProvider: any;
16+
customNetwork?: CustomNetwork | undefined;
1417
}
1518

1619
export class EthersProvider {
@@ -22,14 +25,7 @@ export class EthersProvider {
2225
constructor(private _providerContext: ChainIdAndProvider | EthereumProvider) {
2326
const chainId = (<ChainIdAndProvider>this._providerContext).chainId;
2427
if (chainId) {
25-
const chainName = ChainNames.get(chainId);
26-
if (!chainName) {
27-
throw new UniswapError(
28-
`Can not find chain name for ${chainId}. This lib only supports mainnet(1), ropsten(4), kovan(42), rinkeby(4), görli(5) and ropsten(3)`,
29-
ErrorCodes.canNotFindChainId
30-
);
31-
}
32-
28+
const chainName = this.getChainName(chainId);
3329
const providerUrl = (<ChainIdAndProvider>this._providerContext)
3430
.providerUrl;
3531
if (providerUrl) {
@@ -64,6 +60,27 @@ export class EthersProvider {
6460
}
6561
}
6662

63+
/**
64+
* Get the chain name
65+
* @param chainId The chain id
66+
* @returns
67+
*/
68+
private getChainName(chainId: number): string {
69+
if (this._providerContext.customNetwork) {
70+
return this._providerContext.customNetwork.nameNetwork;
71+
}
72+
73+
const chainName = ChainNames.get(chainId);
74+
if (!chainName) {
75+
throw new UniswapError(
76+
`Can not find chain name for ${chainId}. This lib only supports mainnet(1), ropsten(4), kovan(42), rinkeby(4) and görli(5)`,
77+
ErrorCodes.canNotFindChainId
78+
);
79+
}
80+
81+
return chainName;
82+
}
83+
6784
/**
6885
* Creates a contract instance
6986
* @param abi The ABI
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Token } from '../../token/models/token';
2+
3+
export interface NativeCurrencyInfo {
4+
name: string;
5+
symbol: string;
6+
}
7+
8+
export interface CustomNetwork {
9+
nameNetwork: string;
10+
multicallContractAddress: string;
11+
nativeCurrency: NativeCurrencyInfo;
12+
nativeWrappedTokenInfo: Token;
13+
// defined your base tokens here!
14+
baseTokens?: {
15+
usdt?: Token | undefined;
16+
dai?: Token | undefined;
17+
comp?: Token | undefined;
18+
usdc?: Token | undefined;
19+
wbtc?: Token | undefined;
20+
};
21+
}

src/factories/pair/models/uniswap-pair-settings.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ErrorCodes } from '../../../common/errors/error-codes';
22
import { UniswapError } from '../../../common/errors/uniswap-error';
33
import { UniswapVersion } from '../../../enums/uniswap-version';
44
import { CloneUniswapContractDetails } from './clone-uniswap-contract-details';
5+
import { CustomNetwork } from './custom-network';
56
import { GasSettings } from './gas-settings';
67

78
export class UniswapPairSettings {
@@ -11,6 +12,7 @@ export class UniswapPairSettings {
1112
uniswapVersions: UniswapVersion[] = [UniswapVersion.v2, UniswapVersion.v3];
1213
gasSettings?: GasSettings = undefined;
1314
cloneUniswapContractDetails?: CloneUniswapContractDetails = undefined;
15+
customNetwork?: CustomNetwork = undefined;
1416

1517
constructor(settings?: {
1618
slippage?: number | undefined;
@@ -19,12 +21,14 @@ export class UniswapPairSettings {
1921
uniswapVersions?: UniswapVersion[] | undefined;
2022
gasSettings?: GasSettings | undefined;
2123
cloneUniswapContractDetails?: CloneUniswapContractDetails | undefined;
24+
customNetwork?: CustomNetwork | undefined;
2225
}) {
2326
this.slippage = settings?.slippage || 0.005;
2427
this.deadlineMinutes = settings?.deadlineMinutes || 20;
2528
this.disableMultihops = settings?.disableMultihops || false;
2629
this.gasSettings = settings?.gasSettings;
2730
this.cloneUniswapContractDetails = settings?.cloneUniswapContractDetails;
31+
this.customNetwork = settings?.customNetwork;
2832

2933
if (
3034
Array.isArray(settings?.uniswapVersions) &&

src/factories/pair/uniswap-pair.factory.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@ export class UniswapPairFactory {
3030
private _fromTokenFactory = new TokenFactory(
3131
this._uniswapPairFactoryContext.fromToken.contractAddress,
3232
this._uniswapPairFactoryContext.ethersProvider,
33+
this._uniswapPairFactoryContext.settings.customNetwork,
3334
this._uniswapPairFactoryContext.settings.cloneUniswapContractDetails
3435
);
3536

3637
private _toTokenFactory = new TokenFactory(
3738
this._uniswapPairFactoryContext.toToken.contractAddress,
38-
this._uniswapPairFactoryContext.ethersProvider
39+
this._uniswapPairFactoryContext.ethersProvider,
40+
this._uniswapPairFactoryContext.settings.customNetwork
3941
);
4042

4143
private _uniswapRouterFactory = new UniswapRouterFactory(
@@ -352,7 +354,10 @@ export class UniswapPairFactory {
352354
bestRouteQuote.uniswapVersion
353355
)
354356
: undefined,
355-
toToken: turnTokenIntoEthForResponse(this.toToken),
357+
toToken: turnTokenIntoEthForResponse(
358+
this.toToken,
359+
this._uniswapPairFactoryContext.settings?.customNetwork?.nativeCurrency
360+
),
356361
toBalance: new BigNumber(bestRouteQuotes.toBalance)
357362
.shiftedBy(this.toToken.decimals * -1)
358363
.toFixed(),
@@ -485,7 +490,10 @@ export class UniswapPairFactory {
485490
toBalance: new BigNumber(bestRouteQuotes.toBalance)
486491
.shiftedBy(this.toToken.decimals * -1)
487492
.toFixed(),
488-
fromToken: turnTokenIntoEthForResponse(this.fromToken),
493+
fromToken: turnTokenIntoEthForResponse(
494+
this.fromToken,
495+
this._uniswapPairFactoryContext.settings?.customNetwork?.nativeCurrency
496+
),
489497
fromBalance: {
490498
hasEnough: bestRouteQuotes.hasEnoughBalance,
491499
balance: bestRouteQuotes.fromBalance,
@@ -505,7 +513,13 @@ export class UniswapPairFactory {
505513
*/
506514
private tradePath(): TradePath {
507515
const network = this._uniswapPairFactoryContext.ethersProvider.network();
508-
return getTradePath(network.chainId, this.fromToken, this.toToken);
516+
return getTradePath(
517+
network.chainId,
518+
this.fromToken,
519+
this.toToken,
520+
this._uniswapPairFactoryContext.settings.customNetwork
521+
?.nativeWrappedTokenInfo
522+
);
509523
}
510524

511525
/**

0 commit comments

Comments
 (0)