diff --git a/jest.config.js b/jest.config.js index 5c7ce4a695..33e5f31305 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,6 +13,7 @@ module.exports = { 'src/chains/binance-smart-chain/binance-smart-chain.ts', 'src/chains/ethereum/ethereum.ts', 'src/chains/avalanche/avalanche.ts', + 'src/chains/celo/celo.ts', 'src/chains/avalanche/pangolin/pangolin.ts', 'src/chains/cosmos/cosmos.ts', 'src/chains/near/near.ts', diff --git a/src/chains/avalanche/avalanche.ts b/src/chains/avalanche/avalanche.ts index f6fedf3ca3..08198832a8 100644 --- a/src/chains/avalanche/avalanche.ts +++ b/src/chains/avalanche/avalanche.ts @@ -8,6 +8,7 @@ import { TraderjoeConfig } from '../../connectors/traderjoe/traderjoe.config'; import { PangolinConfig } from '../../connectors/pangolin/pangolin.config'; import { OpenoceanConfig } from '../../connectors/openocean/openocean.config'; import { Ethereumish } from '../../services/common-interfaces'; +import { UniswapConfig } from '../../connectors/uniswap/uniswap.config'; import { SushiswapConfig } from '../../connectors/sushiswap/sushiswap.config'; import { ConfigManagerV2 } from '../../services/config-manager-v2'; import { EVMController } from '../ethereum/evm.controllers'; @@ -84,7 +85,14 @@ export class Avalanche extends EthereumBase implements Ethereumish { getSpender(reqSpender: string): string { let spender: string; - if (reqSpender === 'pangolin') { + if (reqSpender === 'uniswap') { + spender = UniswapConfig.config.uniswapV3SmartOrderRouterAddress( + 'avalanche', + this._chain, + ); + } else if (reqSpender === 'uniswapLP') { + spender = UniswapConfig.config.uniswapV3NftManagerAddress('avalanche', this._chain); + } else if (reqSpender === 'pangolin') { spender = PangolinConfig.config.routerAddress(this._chain); } else if (reqSpender === 'openocean') { spender = OpenoceanConfig.config.routerAddress('avalanche', this._chain); diff --git a/src/chains/avalanche/avalanche.validators.ts b/src/chains/avalanche/avalanche.validators.ts index c771a2d43a..03c0c8c8a5 100644 --- a/src/chains/avalanche/avalanche.validators.ts +++ b/src/chains/avalanche/avalanche.validators.ts @@ -23,7 +23,9 @@ export const validateSpender: Validator = mkValidator( (val) => typeof val === 'string' && - (val === 'pangolin' || + (val === 'uniswap' || + val === 'uniswapLP' || + val === 'pangolin' || val === 'traderjoe' || val === 'openocean' || val === 'sushiswap' || diff --git a/src/chains/binance-smart-chain/binance-smart-chain.ts b/src/chains/binance-smart-chain/binance-smart-chain.ts index 2cbe94c2c1..85981bb7b8 100644 --- a/src/chains/binance-smart-chain/binance-smart-chain.ts +++ b/src/chains/binance-smart-chain/binance-smart-chain.ts @@ -10,6 +10,7 @@ import { SushiswapConfig } from '../../connectors/sushiswap/sushiswap.config'; import { ConfigManagerV2 } from '../../services/config-manager-v2'; import { OpenoceanConfig } from '../../connectors/openocean/openocean.config'; import { EVMController } from '../ethereum/evm.controllers'; +import {UniswapConfig} from "../../connectors/uniswap/uniswap.config"; export class BinanceSmartChain extends EthereumBase implements Ethereumish { private static _instances: { [name: string]: BinanceSmartChain }; @@ -113,6 +114,13 @@ export class BinanceSmartChain extends EthereumBase implements Ethereumish { 'binance-smart-chain', this._chain ); + } else if (reqSpender === 'uniswap') { + spender = UniswapConfig.config.uniswapV3SmartOrderRouterAddress( + 'binance-smart-chain', + this._chain + ); + } else if (reqSpender === 'uniswapLP') { + spender = UniswapConfig.config.uniswapV3NftManagerAddress('binance-smart-chain', this._chain); } else { spender = reqSpender; } diff --git a/src/chains/celo/celo.ts b/src/chains/celo/celo.ts new file mode 100644 index 0000000000..83688e15cc --- /dev/null +++ b/src/chains/celo/celo.ts @@ -0,0 +1,130 @@ +import abi from '../ethereum/ethereum.abi.json'; +import { logger } from '../../services/logger'; +import { Contract, Transaction, Wallet } from 'ethers'; +import { EthereumBase } from '../ethereum/ethereum-base'; +import { getEthereumConfig as getCeloConfig } from '../ethereum/ethereum.config'; +import { Provider } from '@ethersproject/abstract-provider'; +import { Ethereumish } from '../../services/common-interfaces'; +import { ConfigManagerV2 } from '../../services/config-manager-v2'; +import { EVMController } from '../ethereum/evm.controllers'; +import { UniswapConfig } from '../../connectors/uniswap/uniswap.config'; + +export class Celo extends EthereumBase implements Ethereumish { + private static _instances: { [name: string]: Celo }; + private _gasPrice: number; + private _gasPriceRefreshInterval: number | null; + private _nativeTokenSymbol: string; + private _chain: string; + public controller; + + private constructor(network: string) { + const config = getCeloConfig('celo', network); + super( + 'celo', + config.network.chainID, + config.network.nodeURL, + config.network.tokenListSource, + config.network.tokenListType, + config.manualGasPrice, + config.gasLimitTransaction, + ConfigManagerV2.getInstance().get('server.nonceDbPath'), + ConfigManagerV2.getInstance().get('server.transactionDbPath') + ); + this._chain = config.network.name; + this._nativeTokenSymbol = config.nativeCurrencySymbol; + + this._gasPrice = config.manualGasPrice; + + this._gasPriceRefreshInterval = + config.network.gasPriceRefreshInterval !== undefined + ? config.network.gasPriceRefreshInterval + : null; + + this.updateGasPrice(); + this.controller = EVMController; + } + + public static getInstance(network: string): Celo { + if (Celo._instances === undefined) { + Celo._instances = {}; + } + if (!(network in Celo._instances)) { + Celo._instances[network] = new Celo(network); + } + + return Celo._instances[network]; + } + + public static getConnectedInstances(): { [name: string]: Celo } { + return Celo._instances; + } + + // getters + + public get gasPrice(): number { + return this._gasPrice; + } + + public get nativeTokenSymbol(): string { + return this._nativeTokenSymbol; + } + + public get chain(): string { + return this._chain; + } + + getContract(tokenAddress: string, signerOrProvider?: Wallet | Provider) { + return new Contract(tokenAddress, abi.ERC20Abi, signerOrProvider); + } + + getSpender(reqSpender: string): string { + let spender: string; + if (reqSpender === 'uniswap') { + spender = UniswapConfig.config.uniswapV3SmartOrderRouterAddress( + 'celo', + this._chain, + ); + } else if (reqSpender === 'uniswapLP') { + spender = UniswapConfig.config.uniswapV3NftManagerAddress('celo', this._chain); + } else { + spender = reqSpender; + } + return spender; + } + + // cancel transaction + async cancelTx(wallet: Wallet, nonce: number): Promise { + logger.info( + 'Canceling any existing transaction(s) with nonce number ' + nonce + '.' + ); + return super.cancelTxWithGasPrice(wallet, nonce, this._gasPrice * 2); + } + + /** + * Automatically update the prevailing gas price on the network. + */ + async updateGasPrice(): Promise { + if (this._gasPriceRefreshInterval === null) { + return; + } + + const gasPrice = await this.getGasPrice(); + if (gasPrice !== null) { + this._gasPrice = gasPrice; + } else { + logger.info('gasPrice is unexpectedly null.'); + } + + setTimeout( + this.updateGasPrice.bind(this), + this._gasPriceRefreshInterval * 1000 + ); + } + + async close() { + await super.close(); + if (this._chain in Celo._instances) { + delete Celo._instances[this._chain]; + } + } +} diff --git a/src/chains/celo/celo.validators.ts b/src/chains/celo/celo.validators.ts new file mode 100644 index 0000000000..a017aef071 --- /dev/null +++ b/src/chains/celo/celo.validators.ts @@ -0,0 +1,41 @@ +import { + mkRequestValidator, + mkValidator, + RequestValidator, + Validator, + validateAmount, + validateToken, + validateTokenSymbols, + } from '../../services/validators'; + import { + isAddress, + validateNonce, + validateAddress, + } from '../ethereum/ethereum.validators'; + + export const invalidSpenderError: string = + 'The spender param is not a valid Celo address (0x followed by 40 hexidecimal characters).'; + + // given a request, look for a key called spender that is 'uniswap' or an Ethereum address + export const validateSpender: Validator = mkValidator( + 'spender', + invalidSpenderError, + + (val) => + typeof val === 'string' && + (val === 'uniswap' || + val === 'uniswapLP' || + isAddress(val)) + ); + + export const validateCeloApproveRequest: RequestValidator = + mkRequestValidator([ + validateAddress, + validateSpender, + validateToken, + validateAmount, + validateNonce, + ]); + + export const validateCeloAllowancesRequest: RequestValidator = + mkRequestValidator([validateAddress, validateSpender, validateTokenSymbols]); diff --git a/src/chains/ethereum/ethereum.ts b/src/chains/ethereum/ethereum.ts index 6375c53e3f..ca4b1ab5c7 100644 --- a/src/chains/ethereum/ethereum.ts +++ b/src/chains/ethereum/ethereum.ts @@ -182,6 +182,7 @@ export class Ethereum extends EthereumBase implements Ethereumish { let spender: string; if (reqSpender === 'uniswap') { spender = UniswapConfig.config.uniswapV3SmartOrderRouterAddress( + this.chainName, this._chain, ); } else if (reqSpender === 'pancakeswap') { @@ -196,7 +197,9 @@ export class Ethereum extends EthereumBase implements Ethereumish { this._chain, ); } else if (reqSpender === 'uniswapLP') { - spender = UniswapConfig.config.uniswapV3NftManagerAddress(this._chain); + spender = UniswapConfig.config.uniswapV3NftManagerAddress( + this.chainName, + this._chain); } else if (reqSpender === 'carbonamm') { spender = CarbonConfig.config.carbonContractsConfig( 'ethereum', diff --git a/src/chains/polygon/polygon.ts b/src/chains/polygon/polygon.ts index 8439824d72..f85117ea70 100644 --- a/src/chains/polygon/polygon.ts +++ b/src/chains/polygon/polygon.ts @@ -75,10 +75,11 @@ export class Polygon extends EthereumBase implements Ethereumish { let spender: string; if (reqSpender === 'uniswap') { spender = UniswapConfig.config.uniswapV3SmartOrderRouterAddress( + 'polygon', this._chain ); } else if (reqSpender === 'uniswapLP') { - spender = UniswapConfig.config.uniswapV3NftManagerAddress(this._chain); + spender = UniswapConfig.config.uniswapV3NftManagerAddress('polygon', this._chain); } else if (reqSpender === 'quickswap') { spender = QuickswapConfig.config.routerAddress(this._chain); } else if (reqSpender === 'sushiswap') { diff --git a/src/connectors/uniswap/uniswap.config.ts b/src/connectors/uniswap/uniswap.config.ts index b098972037..cbd1648f0d 100644 --- a/src/connectors/uniswap/uniswap.config.ts +++ b/src/connectors/uniswap/uniswap.config.ts @@ -6,15 +6,15 @@ export namespace UniswapConfig { gasLimitEstimate: number; ttl: number; maximumHops: number; - uniswapV3SmartOrderRouterAddress: (network: string) => string; - uniswapV3NftManagerAddress: (network: string) => string; - uniswapV3FactoryAddress: (network: string) => string; + uniswapV3SmartOrderRouterAddress: (chain: string, network: string) => string; + uniswapV3NftManagerAddress: (chain: string, network: string) => string; + uniswapV3FactoryAddress: (chain: string, network: string) => string; + quoterContractAddress: (chain: string, network: string) => string; tradingTypes: (type: string) => Array; chainType: string; availableNetworks: Array; useRouter?: boolean; feeTier?: string; - quoterContractAddress: (network: string) => string; } export const config: NetworkConfig = { @@ -26,17 +26,37 @@ export namespace UniswapConfig { ), ttl: ConfigManagerV2.getInstance().get(`uniswap.ttl`), maximumHops: ConfigManagerV2.getInstance().get(`uniswap.maximumHops`), - uniswapV3SmartOrderRouterAddress: (network: string) => + uniswapV3SmartOrderRouterAddress: (chain: string, network: string) => ConfigManagerV2.getInstance().get( - `uniswap.contractAddresses.${network}.uniswapV3SmartOrderRouterAddress` + 'uniswap.contractAddresses.' + + chain + + '.' + + network + + '.uniswapV3SmartOrderRouterAddress' ), - uniswapV3NftManagerAddress: (network: string) => + uniswapV3NftManagerAddress: (chain: string, network: string) => ConfigManagerV2.getInstance().get( - `uniswap.contractAddresses.${network}.uniswapV3NftManagerAddress` + 'uniswap.contractAddresses.' + + chain + + '.' + + network + + '.uniswapV3NftManagerAddress' ), - uniswapV3FactoryAddress: (network: string) => + uniswapV3FactoryAddress: (chain: string, network: string) => ConfigManagerV2.getInstance().get( - `uniswap.contractAddresses.${network}.uniswapV3FactoryAddress` + 'uniswap.contractAddresses.' + + chain + + '.' + + network + + '.uniswapV3FactoryAddress' + ), + quoterContractAddress: (chain: string, network: string) => + ConfigManagerV2.getInstance().get( + 'uniswap.contractAddresses.' + + chain + + '.' + + network + + '.uniswapV3QuoterV2ContractAddress' ), tradingTypes: (type: string) => { return type === 'swap' ? ['AMM'] : ['AMM_LP']; @@ -46,29 +66,51 @@ export namespace UniswapConfig { { chain: 'ethereum', networks: Object.keys( - ConfigManagerV2.getInstance().get('uniswap.contractAddresses') + ConfigManagerV2.getInstance().get('uniswap.contractAddresses.ethereum') ).filter((network) => Object.keys( ConfigManagerV2.getInstance().get('ethereum.networks') ).includes(network) ), }, - { - chain: 'polygon', + { chain: 'polygon', networks: Object.keys( - ConfigManagerV2.getInstance().get('uniswap.contractAddresses') + ConfigManagerV2.getInstance().get('uniswap.contractAddresses.polygon') ).filter((network) => Object.keys( ConfigManagerV2.getInstance().get('polygon.networks') ).includes(network) ), }, + { chain: 'binance-smart-chain', + networks: Object.keys( + ConfigManagerV2.getInstance().get('uniswap.contractAddresses.binance-smart-chain') + ).filter((network) => + Object.keys( + ConfigManagerV2.getInstance().get('binance-smart-chain.networks') + ).includes(network) + ), + }, + { chain: 'avalanche', + networks: Object.keys( + ConfigManagerV2.getInstance().get('uniswap.contractAddresses.avalanche') + ).filter((network) => + Object.keys( + ConfigManagerV2.getInstance().get('avalanche.networks') + ).includes(network) + ), + }, + { chain: 'celo', + networks: Object.keys( + ConfigManagerV2.getInstance().get('uniswap.contractAddresses.celo') + ).filter((network) => + Object.keys( + ConfigManagerV2.getInstance().get('celo.networks') + ).includes(network) + ), + }, ], useRouter: ConfigManagerV2.getInstance().get(`uniswap.useRouter`), feeTier: ConfigManagerV2.getInstance().get(`uniswap.feeTier`), - quoterContractAddress: (network: string) => - ConfigManagerV2.getInstance().get( - `uniswap.contractAddresses.${network}.uniswapV3QuoterV2ContractAddress` - ), }; } diff --git a/src/connectors/uniswap/uniswap.lp.helper.ts b/src/connectors/uniswap/uniswap.lp.helper.ts index b59dd78b0f..e7ae786eca 100644 --- a/src/connectors/uniswap/uniswap.lp.helper.ts +++ b/src/connectors/uniswap/uniswap.lp.helper.ts @@ -11,6 +11,10 @@ import { AlphaRouter } from '@uniswap/smart-order-router'; import { providers, Wallet, Signer, utils } from 'ethers'; import { percentRegexp } from '../../services/config-manager-v2'; import { Ethereum } from '../../chains/ethereum/ethereum'; +import { Avalanche } from '../../chains/avalanche/avalanche'; +import { Polygon } from '../../chains/polygon/polygon'; +import { BinanceSmartChain } from "../../chains/binance-smart-chain/binance-smart-chain"; + import { PoolState, RawPosition, @@ -19,33 +23,58 @@ import { } from './uniswap.lp.interfaces'; import * as math from 'mathjs'; import { getAddress } from 'ethers/lib/utils'; +import { Celo } from '../../chains/celo/celo'; export class UniswapLPHelper { - protected ethereum: Ethereum; + protected chain: Ethereum | Polygon | BinanceSmartChain | Avalanche | Celo; protected chainId; + private _factory: string; private _router: string; private _nftManager: string; private _ttl: number; private _routerAbi: ContractInterface; private _nftAbi: ContractInterface; private _poolAbi: ContractInterface; - private _alphaRouter: AlphaRouter; + private _alphaRouter: AlphaRouter | null; private tokenList: Record = {}; - private _chain: string; private _ready: boolean = false; public abiDecoder: any; constructor(chain: string, network: string) { - this.ethereum = Ethereum.getInstance(network); - this._chain = chain; - this.chainId = this.ethereum.chainId; - this._alphaRouter = new AlphaRouter({ - chainId: this.chainId, - provider: this.ethereum.provider, - }); + if (chain === 'ethereum') { + this.chain = Ethereum.getInstance(network); + } else if (chain === 'polygon') { + this.chain = Polygon.getInstance(network); + } else if (chain === 'binance-smart-chain') { + this.chain = BinanceSmartChain.getInstance(network); + } else if (chain === 'avalanche') { + this.chain = Avalanche.getInstance(network); + } else if (chain === 'celo') { + this.chain = Celo.getInstance(network); + } else { + throw new Error('Unsupported chain'); + } + this.chainId = this.chain.chainId; + + this._alphaRouter = null; + const excluded_chainIds = [ + 11155111, // sepolia + 8453, // base + 56, // binance-smart-chain + 42220, // celo + 43114, // avalanche + ]; + if (this.chainId in excluded_chainIds) { + this._alphaRouter = new AlphaRouter({ + chainId: this.chainId, + provider: this.chain.provider, + }); + } + + this._factory = UniswapConfig.config.uniswapV3FactoryAddress(chain, network); this._router = - UniswapConfig.config.uniswapV3SmartOrderRouterAddress(network); - this._nftManager = UniswapConfig.config.uniswapV3NftManagerAddress(network); + UniswapConfig.config.uniswapV3SmartOrderRouterAddress(chain, network); + this._nftManager = UniswapConfig.config.uniswapV3NftManagerAddress(chain, network); this._ttl = UniswapConfig.config.ttl; this._routerAbi = require('@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json').abi; @@ -63,6 +92,9 @@ export class UniswapLPHelper { } public get alphaRouter(): AlphaRouter { + if (this._alphaRouter === null) { + throw new Error('AlphaRouter is not initialized'); + } return this._alphaRouter; } @@ -101,12 +133,13 @@ export class UniswapLPHelper { } public async init() { - if (this._chain == 'ethereum' && !this.ethereum.ready()) + const chainName = this.chain.chainName.toString(); + if (!this.chain.ready()) throw new InitializationError( - SERVICE_UNITIALIZED_ERROR_MESSAGE('ETH'), + SERVICE_UNITIALIZED_ERROR_MESSAGE(chainName), SERVICE_UNITIALIZED_ERROR_CODE ); - for (const token of this.ethereum.storedTokenList) { + for (const token of this.chain.storedTokenList) { this.tokenList[token.address] = new Token( this.chainId, token.address, @@ -156,7 +189,7 @@ export class UniswapLPHelper { ): Promise { const poolContract = this.getPoolContract( poolAddress, - this.ethereum.provider + this.chain.provider ); const minTick = uniV3.nearestUsableTick( uniV3.TickMath.MIN_TICK, @@ -221,9 +254,9 @@ export class UniswapLPHelper { const prices = []; const fee = uniV3.FeeAmount[tier as keyof typeof uniV3.FeeAmount]; const poolContract = new Contract( - uniV3.Pool.getAddress(token0, token1, fee), + uniV3.Pool.getAddress(token0, token1, fee, undefined, this._factory), this.poolAbi, - this.ethereum.provider + this.chain.provider ); for ( let x = Math.ceil(period / interval) * interval; @@ -311,7 +344,7 @@ export class UniswapLPHelper { const lowerPriceInFraction = math.fraction(lowerPrice) as math.Fraction; const upperPriceInFraction = math.fraction(upperPrice) as math.Fraction; const poolData = await this.getPoolState( - uniV3.Pool.getAddress(token0, token1, fee), + uniV3.Pool.getAddress(token0, token1, fee, undefined, this._factory), fee ); const pool = new uniV3.Pool( @@ -392,13 +425,14 @@ export class UniswapLPHelper { const positionData = await this.getRawPosition(wallet, tokenId); const token0 = this.getTokenByAddress(positionData.token0); const token1 = this.getTokenByAddress(positionData.token1); + const factoryAddress = this._factory const fee = positionData.fee; if (!token0 || !token1) { throw new Error( `One of the tokens in this position isn't recognized. $token0: ${token0}, $token1: ${token1}` ); } - const poolAddress = uniV3.Pool.getAddress(token0, token1, fee); + const poolAddress = uniV3.Pool.getAddress(token0, token1, fee, undefined, factoryAddress); const poolData = await this.getPoolState(poolAddress, fee); const position = new uniV3.Position({ pool: new uniV3.Pool( diff --git a/src/connectors/uniswap/uniswap.lp.ts b/src/connectors/uniswap/uniswap.lp.ts index 274591e6bd..b32924f85b 100644 --- a/src/connectors/uniswap/uniswap.lp.ts +++ b/src/connectors/uniswap/uniswap.lp.ts @@ -53,10 +53,10 @@ export class UniswapLP extends UniswapLPHelper implements UniswapLPish { } async getPosition(tokenId: number): Promise { - const contract = this.getContract('nft', this.ethereum.provider); + const contract = this.getContract('nft', this.chain.provider); const requests = [ contract.positions(tokenId), - this.collectFees(this.ethereum.provider, tokenId), // static call to calculate earned fees + this.collectFees(this.chain.provider, tokenId), // static call to calculate earned fees ]; const positionInfoReq = await Promise.allSettled(requests); const rejected = positionInfoReq.filter( @@ -141,7 +141,7 @@ export class UniswapLP extends UniswapLPHelper implements UniswapLPish { ); if (nonce === undefined) { - nonce = await this.ethereum.nonceManager.getNextNonce(wallet.address); + nonce = await this.chain.nonceManager.getNextNonce(wallet.address); } const tx = await wallet.sendTransaction({ @@ -179,7 +179,7 @@ export class UniswapLP extends UniswapLPHelper implements UniswapLPish { ); if (nonce === undefined) { - nonce = await this.ethereum.nonceManager.getNextNonce(wallet.address); + nonce = await this.chain.nonceManager.getNextNonce(wallet.address); } const tx = await contract.multicall( @@ -219,7 +219,7 @@ export class UniswapLP extends UniswapLPHelper implements UniswapLPish { } else { collectData.recipient = wallet.address; if (nonce === undefined) { - nonce = await this.ethereum.nonceManager.getNextNonce(wallet.address); + nonce = await this.chain.nonceManager.getNextNonce(wallet.address); } return await contract.collect( collectData, diff --git a/src/connectors/uniswap/uniswap.ts b/src/connectors/uniswap/uniswap.ts index eb1b99b90f..23c17f56ac 100644 --- a/src/connectors/uniswap/uniswap.ts +++ b/src/connectors/uniswap/uniswap.ts @@ -36,13 +36,16 @@ import { import { logger } from '../../services/logger'; import { percentRegexp } from '../../services/config-manager-v2'; import { Ethereum } from '../../chains/ethereum/ethereum'; +import { Avalanche } from '../../chains/avalanche/avalanche'; import { Polygon } from '../../chains/polygon/polygon'; +import { BinanceSmartChain } from "../../chains/binance-smart-chain/binance-smart-chain"; import { ExpectedTrade, Uniswapish } from '../../services/common-interfaces'; import { getAddress } from 'ethers/lib/utils'; +import { Celo } from '../../chains/celo/celo'; export class Uniswap implements Uniswapish { private static _instances: { [name: string]: Uniswap }; - private chain: Ethereum | Polygon; + private chain: Ethereum | Polygon | BinanceSmartChain | Avalanche | Celo; private _alphaRouter: AlphaRouter | null; private _router: string; private _routerAbi: ContractInterface; @@ -61,15 +64,31 @@ export class Uniswap implements Uniswapish { const config = UniswapConfig.config; if (chain === 'ethereum') { this.chain = Ethereum.getInstance(network); - } else { + } else if (chain === 'polygon') { this.chain = Polygon.getInstance(network); + } else if (chain === 'binance-smart-chain') { + this.chain = BinanceSmartChain.getInstance(network); + } else if (chain === 'avalanche') { + this.chain = Avalanche.getInstance(network); + } else if (chain === 'celo') { + this.chain = Celo.getInstance(network); + } else { + throw new Error('Unsupported chain'); } + this.chainId = this.chain.chainId; this._ttl = UniswapConfig.config.ttl; this._maximumHops = UniswapConfig.config.maximumHops; this._alphaRouter = null; - if (this.chainId !== 11155111 && this.chainId !== 8453) { + const excluded_chainIds = [ + 11155111, // sepolia + 8453, // base + 56, // binance-smart-chain + 42220, // celo + 43114, // avalanche + ]; + if (this.chainId in excluded_chainIds) { this._alphaRouter = new AlphaRouter({ chainId: this.chainId, provider: this.chain.provider, @@ -77,7 +96,7 @@ export class Uniswap implements Uniswapish { } this._routerAbi = routerAbi.abi; this._gasLimitEstimate = UniswapConfig.config.gasLimitEstimate; - this._router = config.uniswapV3SmartOrderRouterAddress(network); + this._router = config.uniswapV3SmartOrderRouterAddress(chain, network); if (config.useRouter === false && config.feeTier == null) { throw new Error('Must specify fee tier if not using router'); @@ -91,8 +110,8 @@ export class Uniswap implements Uniswapish { this._feeTier = config.feeTier ? FeeAmount[config.feeTier as keyof typeof FeeAmount] : FeeAmount.MEDIUM; - this._quoterContractAddress = config.quoterContractAddress(network); - this._factoryAddress = config.uniswapV3FactoryAddress(network); + this._quoterContractAddress = config.quoterContractAddress(chain, network); + this._factoryAddress = config.uniswapV3FactoryAddress(chain, network); } public static getInstance(chain: string, network: string): Uniswap { diff --git a/src/network/network.controllers.ts b/src/network/network.controllers.ts index fb16e1a43b..52af731679 100644 --- a/src/network/network.controllers.ts +++ b/src/network/network.controllers.ts @@ -4,6 +4,7 @@ import { BinanceSmartChain } from '../chains/binance-smart-chain/binance-smart-c import { Ethereum } from '../chains/ethereum/ethereum'; import { Harmony } from '../chains/harmony/harmony'; import { Polygon } from '../chains/polygon/polygon'; +import { Celo } from '../chains/celo/celo'; import { Xdc } from '../chains/xdc/xdc'; import { Tezos } from '../chains/tezos/tezos'; import { Kujira } from '../chains/kujira/kujira'; @@ -86,6 +87,11 @@ export async function getStatus( cronosConnections ? Object.values(cronosConnections) : [], ); + const celoConnections = Celo.getConnectedInstances(); + connections = connections.concat( + celoConnections ? Object.values(celoConnections) : [] + ); + const nearConnections = Near.getConnectedInstances(); connections = connections.concat( nearConnections ? Object.values(nearConnections) : [], diff --git a/src/services/connection-manager.ts b/src/services/connection-manager.ts index d9a4c8df95..f247b534cc 100644 --- a/src/services/connection-manager.ts +++ b/src/services/connection-manager.ts @@ -1,4 +1,5 @@ import { Avalanche } from '../chains/avalanche/avalanche'; +import { Celo } from '../chains/celo/celo'; import { Cronos } from '../chains/cronos/cronos'; import { Ethereum } from '../chains/ethereum/ethereum'; import { BinanceSmartChain } from '../chains/binance-smart-chain/binance-smart-chain'; @@ -127,6 +128,8 @@ export async function getChainInstance( connection = Cronos.getInstance(network); } else if (chain === 'cosmos') { connection = Cosmos.getInstance(network); + } else if (chain === 'celo') { + connection = Celo.getInstance(network); } else if (chain === 'osmosis') { connection = Osmosis.getInstance(network); } else if (chain === 'near') { @@ -190,68 +193,50 @@ export async function getConnector( ): Promise> { let connectorInstance: ConnectorUnion; - if ( - (chain === 'ethereum' || chain === 'polygon') && - connector === 'uniswap' - ) { + if (connector === 'uniswap') { connectorInstance = Uniswap.getInstance(chain, network); - } else if (chain === 'polygon' && connector === 'quickswap') { - connectorInstance = Quickswap.getInstance(chain, network); - } else if ( - (chain === 'ethereum' || chain === 'polygon') && - connector === 'uniswapLP' - ) { + } else if (connector === 'uniswapLP') { connectorInstance = UniswapLP.getInstance(chain, network); - } else if (chain === 'ethereum' && connector === 'perp') { + } else if (connector === 'quickswap') { + connectorInstance = Quickswap.getInstance(chain, network); + } else if (connector === 'perp') { connectorInstance = Perp.getInstance(chain, network, address); - } else if (chain === 'avalanche' && connector === 'pangolin') { + } else if (connector === 'pangolin') { connectorInstance = Pangolin.getInstance(chain, network); } else if (connector === 'openocean') { connectorInstance = Openocean.getInstance(chain, network); - } else if (chain === 'avalanche' && connector === 'traderjoe') { + } else if (connector === 'traderjoe') { connectorInstance = Traderjoe.getInstance(chain, network); - } else if (chain === 'cronos' && connector === 'mad_meerkat') { + } else if (connector === 'mad_meerkat') { connectorInstance = MadMeerkat.getInstance(chain, network); - } else if (chain === 'cronos' && connector === 'vvs') { + } else if (connector === 'vvs') { connectorInstance = VVSConnector.getInstance(chain, network); - } else if (chain === 'near' && connector === 'ref') { + } else if (connector === 'ref') { connectorInstance = Ref.getInstance(chain, network); - } else if ( - (chain === 'binance-smart-chain' || chain === 'ethereum') && - connector === 'pancakeswap' - ) { + } else if (connector === 'pancakeswap') { connectorInstance = PancakeSwap.getInstance(chain, network); - } else if ( - (chain === 'binance-smart-chain' || chain === 'ethereum') && - connector === 'pancakeswapLP' - ) { + } else if (connector === 'pancakeswapLP') { connectorInstance = PancakeswapLP.getInstance(chain, network); } else if (connector === 'sushiswap') { connectorInstance = Sushiswap.getInstance(chain, network); - } else if (chain === 'xdc' && connector === 'xsswap') { + } else if (connector === 'xsswap') { connectorInstance = Xsswap.getInstance(chain, network); - } else if (chain === 'avalanche' && connector === 'dexalot') { - connectorInstance = DexalotCLOB.getInstance(network); - } else if (chain == 'algorand' && connector == 'tinyman') { - connectorInstance = Tinyman.getInstance(network); - } else if (chain === 'tezos' && connector === 'plenty') { - connectorInstance = Plenty.getInstance(network); - } else if (chain === 'xrpl' && connector === 'xrpl') { + } else if (connector === 'xrpl') { connectorInstance = XRPLCLOB.getInstance(chain, network); - } else if (chain === 'kujira' && connector === 'kujira') { + } else if (connector === 'kujira') { connectorInstance = KujiraCLOB.getInstance(chain, network); - } else if ( - (chain === 'ethereum' || chain === 'polygon') && - connector === 'curve' - ) { + } else if (connector === 'curve') { connectorInstance = Curve.getInstance(chain, network); - } else if ( - (chain === 'ethereum' || chain === 'polygon' || chain === 'avalanche') && - connector === 'balancer' - ) { + } else if (connector === 'balancer') { connectorInstance = Balancer.getInstance(chain, network); - } else if (chain === 'ethereum' && connector === 'carbonamm') { + } else if (connector === 'carbonamm') { connectorInstance = Carbonamm.getInstance(chain, network); + } else if (connector === 'dexalot') { + connectorInstance = DexalotCLOB.getInstance(network); + } else if (connector == 'tinyman') { + connectorInstance = Tinyman.getInstance(network); + } else if (connector === 'plenty') { + connectorInstance = Plenty.getInstance(network); } else { throw new Error('unsupported chain or connector'); } diff --git a/src/services/schema/uniswap-schema.json b/src/services/schema/uniswap-schema.json index 89a9c23ef9..a9c05a1ce1 100644 --- a/src/services/schema/uniswap-schema.json +++ b/src/services/schema/uniswap-schema.json @@ -13,20 +13,26 @@ "contractAddresses": { "type": "object", "patternProperties": { - "^\\w+$": { + "[\\w-]+$": { "type": "object", - "properties": { - "uniswapV3SmartOrderRouterAddress": { "type": "string" }, - "uniswapV3NftManagerAddress": { "type": "string" }, - "uniswapV3QuoterV2ContractAddress": { "type": "string" }, - "uniswapV3FactoryAddress": { "type": "string" } + "patternProperties": { + "^\\w+$": { + "type": "object", + "properties": { + "uniswapV3SmartOrderRouterAddress": { "type": "string" }, + "uniswapV3NftManagerAddress": { "type": "string" }, + "uniswapV3QuoterV2ContractAddress": { "type": "string" }, + "uniswapV3FactoryAddress": { "type": "string" } + }, + "required": [ + "uniswapV3SmartOrderRouterAddress", + "uniswapV3NftManagerAddress", + "uniswapV3QuoterV2ContractAddress", + "uniswapV3FactoryAddress" + ], + "additionalProperties": false + } }, - "required": [ - "uniswapV3SmartOrderRouterAddress", - "uniswapV3NftManagerAddress", - "uniswapV3QuoterV2ContractAddress", - "uniswapV3FactoryAddress" - ], "additionalProperties": false } }, diff --git a/src/services/wallet/wallet.validators.ts b/src/services/wallet/wallet.validators.ts index faf67a5f58..ac53a621c4 100644 --- a/src/services/wallet/wallet.validators.ts +++ b/src/services/wallet/wallet.validators.ts @@ -108,6 +108,11 @@ export const validatePrivateKey: Validator = mkSelectingValidator( invalidCosmosPrivateKeyError, (val) => typeof val === 'string' && isCosmosPrivateKey(val), ), + celo: mkValidator( + 'privateKey', + invalidEthPrivateKeyError, + (val) => typeof val === 'string' && isEthPrivateKey(val) + ), osmosis: mkValidator( 'privateKey', invalidCosmosPrivateKeyError, @@ -173,6 +178,7 @@ export const validateChain: Validator = mkValidator( val === 'ethereum' || val === 'avalanche' || val === 'polygon' || + val === 'celo' || val === 'xdc' || val === 'near' || val === 'harmony' || diff --git a/src/templates/celo.yml b/src/templates/celo.yml new file mode 100644 index 0000000000..b4ab9616f6 --- /dev/null +++ b/src/templates/celo.yml @@ -0,0 +1,20 @@ +# list the Celo networks available to gateway +networks: + celo: + chainID: 42220 + nodeURL: https://rpc.ankr.com/celo + tokenListType: 'FILE' + tokenListSource: '/home/gateway/conf/lists/celo_token.json' + nativeCurrencySymbol: 'CELO' + gasPriceRefreshInterval: 60 + celo_alfajores: + chainID: 44787 + nodeURL: https://celo-alfajores.infura.io/v3 + tokenListType: 'FILE' + tokenListSource: '/home/gateway/conf/lists/celo_token.json' + nativeCurrencySymbol: 'CELO' + gasPriceRefreshInterval: 60 + +# if you use the gas assumptions below, your wallet needs >0.01 AVAX balance for gas +gasLimitTransaction: 3000000 +manualGasPrice: 33 diff --git a/src/templates/lists/celo_token.json b/src/templates/lists/celo_token.json new file mode 100644 index 0000000000..b907770f67 --- /dev/null +++ b/src/templates/lists/celo_token.json @@ -0,0 +1,549 @@ +{ + "name": "Celo Token List", + "version": { + "major": 2, + "minor": 5, + "patch": 0 + }, + "logoURI": "https://celo-org.github.io/celo-token-list/assets/celo_logo.svg", + "keywords": ["celo", "tokens", "refi"], + "timestamp": "2022-05-25T20:37:00.000+00:00", + "tokens": [ + { + "name": "Green CELO", + "address": "0x8a1639098644a229d08f441ea45a63ae050ee018", + "symbol": "gCELO", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/spiralsprotocol/spirals-brand/main/gCELO.svg" + }, + { + "name": "Green cUSD", + "address": "0xFB42E2e90fc79CfA6A6B4EBa4877d5Faf4e29287", + "symbol": "gcUSD", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/spiralsprotocol/spirals-brand/main/gcUSD.svg" + }, + { + "name": "cRecy", + "address": "0x34C11A932853Ae24E845Ad4B633E3cEf91afE583", + "symbol": "cRecy", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://user-images.githubusercontent.com/101748448/187026740-27f51d9d-e60d-48e9-b378-416c1eda0cb1.svg" + }, + { + "name": "Staked Celo", + "address": "0xC668583dcbDc9ae6FA3CE46462758188adfdfC24", + "symbol": "stCelo", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://celo-org.github.io/celo-token-list/assets/token-stcelo.svg" + }, + { + "name": "Nature Carbon Tonne", + "address": "0x02de4766c272abc10bc88c220d214a26960a7e92", + "symbol": "NCT", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://toucan.earth/img/icons/nct.svg" + }, + { + "name": "Biochar", + "address": "0x50E85c754929840B58614F48e29C64BC78C58345", + "symbol": "CHAR", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://app.toucan.earth/svg/pools/char.svg" + }, + { + "name": "USDC (Portal from Ethereum)", + "address": "0x37f750B7cC259A2f741AF45294f6a16572CF5cAd", + "symbol": "USDCet", + "decimals": 6, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/certusone/wormhole-token-list/main/assets/USDCet_wh_small.png" + }, + { + "name": "DAI Stablecoin (Portal)", + "address": "0x97926a82930bb7B33178E3c2f4ED1BFDc91A9FBF", + "symbol": "DAI", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/certusone/wormhole-token-list/main/assets/DAI_wh_small.png" + }, + { + "name": "Portal WETH", + "address": "0x66803FB87aBd4aaC3cbB3fAd7C3aa01f6F3FB207", + "symbol": "WETH", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_ETH.png" + }, + { + "name": "wrapped.com ETH", + "address": "0x2DEf4285787d58a2f811AF24755A8150622f4361", + "symbol": "cETH", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cETH.svg" + }, + { + "name": "Ubeswap", + "address": "0x00Be915B9dCf56a3CBE739D9B9c202ca692409EC", + "symbol": "UBE", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_UBE.png" + }, + { + "name": "Celo Moss Carbon Credit", + "address": "0x32A9FE697a32135BFd313a6Ac28792DaE4D9979d", + "symbol": "cMCO2", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cMCO2.png" + }, + { + "name": "Celo", + "address": "0x471EcE3750Da237f93B8E339c536989b8978a438", + "symbol": "CELO", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://celo-org.github.io/celo-token-list/assets/celo_logo.svg" + }, + { + "name": "Celo Dollar", + "address": "0x765DE816845861e75A25fCA122bb6898B8B1282a", + "symbol": "cUSD", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cUSD.png" + }, + { + "name": "Duniapay West African CFA franc", + "address": "0x832F03bCeE999a577cb592948983E35C048B5Aa4", + "symbol": "cXOF", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cXOF.png" + }, + { + "name": "wrapped.com Bitcoin", + "address": "0xD629eb00dEced2a080B7EC630eF6aC117e614f1b", + "symbol": "cBTC", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cBTC.png" + }, + { + "name": "Celo Euro", + "address": "0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73", + "symbol": "cEUR", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cEUR.png" + }, + { + "name": "Beefy Finance", + "address": "0x639A647fbe20b6c8ac19E48E2de44ea792c62c5C", + "decimals": 18, + "symbol": "BIFI", + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/sushiswap/assets/master/blockchains/celo/assets/0x639A647fbe20b6c8ac19E48E2de44ea792c62c5C/logo.png" + }, + { + "name": "Optics v2 WMATIC via Polygon", + "address": "0x2E3487F967DF2Ebc2f236E16f8fCAeac7091324D", + "symbol": "WMATIC", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_WMATIC.png" + }, + { + "name": "Optics v2 SUSHI", + "address": "0x29dFce9c22003A4999930382Fd00f9Fd6133Acd1", + "symbol": "SUSHI", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_SUSHI.png" + }, + { + "name": "Optics v2 USDT", + "address": "0x88eeC49252c8cbc039DCdB394c0c2BA2f1637EA0", + "decimals": 6, + "symbol": "USDT", + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_USDT.png" + }, + { + "name": "Mobius DAO Token", + "address": "0x73a210637f6F6B7005512677Ba6B3C96bb4AA44B", + "symbol": "MOBI", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_MOBI.png" + }, + { + "name": "impactMarket", + "address": "0x46c9757C5497c5B1f2eb73aE79b6B67D119B0B58", + "symbol": "PACT", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_PACT.png" + }, + { + "name": "Source", + "address": "0x74c0C58B99b68cF16A717279AC2d056A34ba2bFe", + "symbol": "SOURCE", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_SOURCE.png" + }, + { + "name": "Poof", + "address": "0x00400FcbF0816bebB94654259de7273f4A05c762", + "symbol": "POOF", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_POOF.png" + }, + { + "name": "Stabilite USD", + "address": "0x0a60c25Ef6021fC3B479914E6bcA7C03c18A97f1", + "symbol": "stabilUSD", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_stabilUSD.png" + }, + { + "name": "Allbridge SOL", + "address": "0x173234922eB27d5138c5e481be9dF5261fAeD450", + "symbol": "SOL", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_SOL.png" + }, + { + "name": "Ariswap", + "address": "0x20677d4f3d0F08e735aB512393524A3CfCEb250C", + "symbol": "ARI", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_ARI.png" + }, + { + "name": "Anyswap FTM", + "address": "0x218c3c3D49d0E7B37aff0D8bB079de36Ae61A4c0", + "symbol": "FTM", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_FTM.png" + }, + { + "name": "Poof CELO", + "address": "0x301a61D01A63c8D670c2B8a43f37d12eF181F997", + "symbol": "pCELO", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pCELO.png" + }, + { + "name": "CeloStarter", + "address": "0x452EF5a4bD00796e62E5e5758548e0dA6e8CCDF3", + "symbol": "cStar", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cStar.png" + }, + { + "name": "Allbridge SBR", + "address": "0x47264aE1Fc0c8e6418ebe78630718E11a07346A8", + "symbol": "SBR", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_SBR.png" + }, + { + "name": "Allbridge", + "address": "0x6e512BFC33be36F2666754E996ff103AD1680Cc9", + "symbol": "ABR", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_ABR.png" + }, + { + "name": "Staked Allbridge", + "address": "0x788BA01f8E2b87c08B142DB46F82094e0bdCad4F", + "symbol": "xABR", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_xABR.png" + }, + { + "name": "Moola CELO", + "address": "0x7D00cd74FF385c955EA3d79e47BF06bD7386387D", + "symbol": "mCELO", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_mCELO.png" + }, + { + "name": "Symmetric", + "address": "0x8427bD503dd3169cCC9aFF7326c15258Bc305478", + "symbol": "SYMM", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_SYMM.png" + }, + { + "name": "Allbridge AVAX", + "address": "0x8E3670FD7B0935d3FE832711deBFE13BB689b690", + "symbol": "AVAX", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_AVAX.png" + }, + { + "name": "Moola cUSD", + "address": "0x918146359264C492BD6934071c6Bd31C854EDBc3", + "symbol": "mcUSD", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_mcUSD.png" + }, + { + "name": "Premio", + "address": "0x94140c2eA9D208D8476cA4E3045254169791C59e", + "symbol": "PREMIO", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_PREMIO.png" + }, + { + "name": "Moola cREAL", + "address": "0x9802d866fdE4563d088a6619F7CeF82C0B991A55", + "symbol": "mcREAL", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_mcREAL.png" + }, + { + "name": "Anyswap BNB", + "address": "0xA649325Aa7C5093d12D6F98EB4378deAe68CE23F", + "symbol": "BNB", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_BNB.png" + }, + { + "name": "KnoxEdge", + "address": "0xa81D9a2d29373777E4082d588958678a6Df5645c", + "symbol": "KNX", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_KNX.png" + }, + { + "name": "TrueFeedBack New", + "address": "0xbDd31EFfb9E9f7509fEaAc5B4091b31645A47e4b", + "symbol": "TFBX", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_TFBX.png" + }, + { + "name": "Moola cEUR", + "address": "0xE273Ad7ee11dCfAA87383aD5977EE1504aC07568", + "symbol": "mcEUR", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_mcEUR.png" + }, + { + "name": "Immortal", + "address": "0xE685d21b7B0FC7A248a6A8E03b8Db22d013Aa2eE", + "decimals": 9, + "symbol": "IMMO", + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_IMMO.png" + }, + { + "name": "Celo Real", + "address": "0xe8537a3d056DA446677B9E9d6c5dB704EaAb4787", + "symbol": "cREAL", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_cREAL.png" + }, + { + "name": "Poof USD", + "address": "0xEadf4A7168A82D30Ba0619e64d5BCf5B30B45226", + "symbol": "pUSD", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pUSD.png" + }, + { + "name": "Poof v1 EUR", + "address": "0x56072D4832642dB29225dA12d6Fd1290E4744682", + "symbol": "pEURxV1", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pEUR.png" + }, + { + "name": "Marzipan Finance", + "address": "0x9Ee153D4Fdf0E3222eFD092c442ebB21DFd346AC", + "symbol": "MZPN", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_MZPN.png" + }, + { + "name": "Poof v1 USD", + "address": "0xB4aa2986622249B1F45eb93F28Cfca2b2606d809", + "symbol": "pUSDxV1", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pUSD.png" + }, + { + "name": "Duino-Coin on Celo", + "address": "0xDB452CC669D3Ae454226AbF232Fe211bAfF2a1F9", + "symbol": "celoDUCO", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_celoDUCO.png" + }, + { + "name": "Poof v1 CELO", + "address": "0xE74AbF23E1Fdf7ACbec2F3a30a772eF77f1601E1", + "symbol": "pCELOxV1", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_pCELO.png" + }, + { + "name": "NetM Token", + "address": "0x123ED050805E0998EBEf43671327139224218e50", + "symbol": "NTMX", + "decimals": 18, + "chainId": 44787, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_NTMX.png" + }, + { + "name": "Moola", + "address": "0x17700282592D6917F6A73D0bF8AcCf4D578c131e", + "symbol": "MOO", + "decimals": 18, + "chainId": 44787, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_MOO.png" + }, + { + "name": "AtlasX Carbon Credits", + "address": "0xc3377Ea71F1dc8e55Ba360724eff2d7aD62a8670", + "symbol": "ATLASX", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://myterrablobs.blob.core.windows.net/public/token-icon.png" + }, + { + "name": "PLASTIK Token", + "address": "0x27cd006548dF7C8c8e9fdc4A67fa05C2E3CA5CF9", + "symbol": "PLASTIK", + "decimals": 9, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/ubeswap/default-token-list/master/assets/asset_PLASTIK.png" + }, + { + "name": "Axelar Wrapped Bitcoin", + "address": "0x1a35EE4640b0A3B87705B0A4B45D227Ba60Ca2ad", + "symbol": "axlWBTC", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://celo-org.github.io/celo-token-list/assets/axelar_wbtc.svg" + }, + { + "name": "Wormhole Wrapped Bitcoin", + "address": "0xd71Ffd0940c920786eC4DbB5A12306669b5b81EF", + "symbol": "WBTC", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://celo-org.github.io/celo-token-list/assets/wormhole_wbtc.png" + }, + { + "name": "Good Dollar", + "address": "0x62B8B11039FcfE5aB0C56E502b1C372A3d2a9c7A", + "symbol": "G$", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/GoodDollar/GoodDAPP/master/src/assets/Splash/logo.svg" + }, + { + "name": "Axelar WETH", + "address": "0xb829b68f57cc546da7e5806a929e53be32a4625d", + "symbol": "axlEth", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://celo-org.github.io/celo-token-list/assets/axelar_eth.png" + }, + { + "name": "JumpToken", + "address": "0x1d18d0386f51ab03e7e84e71bda1681eba865f1f", + "symbol": "JMPT", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://celo-org.github.io/celo-token-list/assets/jumpToken.png" + }, + { + "name": "Glo Dollar", + "address": "0x4f604735c1cf31399c6e711d5962b2b3e0225ad3", + "symbol": "USDGLO", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://app.glodollar.org/glo-logo.svg" + }, + { + "name": "Curve DAO Token", + "address": "0x173fd7434B8B50dF08e3298f173487ebDB35FD14", + "symbol": "CRV", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/curvefi/curve-assets/main/branding/logo.svg" + }, + { + "name": "agEUR", + "address": "0xC16B81Af351BA9e64C1a069E3Ab18c244A1E3049", + "symbol": "agEUR", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/AngleProtocol/angle-assets/main/0_tokens/agEUR/cross-chain/agEUR-celo.svg" + }, + { + "name": "EURC (Wormhole)", + "address": "0xBddC3554269053544bE0d6d027a73271225E9859", + "symbol": "EURC", + "decimals": 6, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/wormhole-foundation/wormhole-token-list/main/assets/EURC_wh.png" + }, + { + "name": "USD Coin", + "address": "0xcebA9300f2b948710d2653dD7B07f33A8B32118C", + "symbol": "USDC", + "decimals": 6, + "chainId": 42220, + "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/asset_USDC.svg" + }, + { + "name": "Celo Kenyan Shilling", + "address": "0x456a3D042C0DbD3db53D5489e98dFb038553B0d0", + "symbol": "cKES", + "decimals": 18, + "chainId": 42220, + "logoURI": "https://github.com/mento-protocol/mento-web/blob/main/src/images/tokens/cKES.svg" + } + ] +} \ No newline at end of file diff --git a/src/templates/root.yml b/src/templates/root.yml index 274a465a1b..f62e5c2790 100644 --- a/src/templates/root.yml +++ b/src/templates/root.yml @@ -28,6 +28,10 @@ configurations: configurationPath: polygon.yml schemaPath: ethereum-schema.json + $namespace celo: + configurationPath: celo.yml + schemaPath: ethereum-schema.json + $namespace near: configurationPath: near.yml schemaPath: ethereum-schema.json diff --git a/src/templates/uniswap.yml b/src/templates/uniswap.yml index 0d910d0a7b..27f6ef9dbd 100644 --- a/src/templates/uniswap.yml +++ b/src/templates/uniswap.yml @@ -9,7 +9,7 @@ gasLimitEstimate: 150688 # perform the trade, but the gas will still be sent. ttl: 600 -# For each swap, the maximum number of hops to consider. +# For each swap, the maximum number of hops to consider. # Note: More hops will increase latency of the algorithm. maximumHops: 4 @@ -24,33 +24,63 @@ useRouter: false feeTier: 'MEDIUM' contractAddresses: - mainnet: - uniswapV3SmartOrderRouterAddress: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45' - uniswapV3NftManagerAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' - uniswapV3QuoterV2ContractAddress: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' - uniswapV3FactoryAddress: '0x1F98431c8aD98523631AE4a59f267346ea31F984' - goerli: - uniswapV3SmartOrderRouterAddress: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45' - uniswapV3NftManagerAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' - uniswapV3QuoterV2ContractAddress: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' - uniswapV3FactoryAddress: '0x1F98431c8aD98523631AE4a59f267346ea31F984' - arbitrum: - uniswapV3SmartOrderRouterAddress: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45' - uniswapV3NftManagerAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' - uniswapV3QuoterV2ContractAddress: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' - uniswapV3FactoryAddress: '0x1F98431c8aD98523631AE4a59f267346ea31F984' - optimism: - uniswapV3SmartOrderRouterAddress: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45' - uniswapV3NftManagerAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' - uniswapV3QuoterV2ContractAddress: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' - uniswapV3FactoryAddress: '0x1F98431c8aD98523631AE4a59f267346ea31F984' - base: - uniswapV3SmartOrderRouterAddress: '0x2626664c2603336E57B271c5C0b26F421741e481' - uniswapV3NftManagerAddress: '0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1' - uniswapV3QuoterV2ContractAddress: '0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a' - uniswapV3FactoryAddress: '0x33128a8fC17869897dcE68Ed026d694621f6FDfD' - sepolia: - uniswapV3SmartOrderRouterAddress: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45' - uniswapV3NftManagerAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' - uniswapV3QuoterV2ContractAddress: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' - uniswapV3FactoryAddress: '0x1F98431c8aD98523631AE4a59f267346ea31F984' + ethereum: + mainnet: + uniswapV3SmartOrderRouterAddress: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45' + uniswapV3NftManagerAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' + uniswapV3QuoterV2ContractAddress: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' + uniswapV3FactoryAddress: '0x1F98431c8aD98523631AE4a59f267346ea31F984' + goerli: + uniswapV3SmartOrderRouterAddress: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45' + uniswapV3NftManagerAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' + uniswapV3QuoterV2ContractAddress: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' + uniswapV3FactoryAddress: '0x1F98431c8aD98523631AE4a59f267346ea31F984' + arbitrum: + uniswapV3SmartOrderRouterAddress: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45' + uniswapV3NftManagerAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' + uniswapV3QuoterV2ContractAddress: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' + uniswapV3FactoryAddress: '0x1F98431c8aD98523631AE4a59f267346ea31F984' + optimism: + uniswapV3SmartOrderRouterAddress: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45' + uniswapV3NftManagerAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' + uniswapV3QuoterV2ContractAddress: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' + uniswapV3FactoryAddress: '0x1F98431c8aD98523631AE4a59f267346ea31F984' + base: + uniswapV3SmartOrderRouterAddress: '0x2626664c2603336E57B271c5C0b26F421741e481' + uniswapV3NftManagerAddress: '0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1' + uniswapV3QuoterV2ContractAddress: '0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a' + uniswapV3FactoryAddress: '0x33128a8fC17869897dcE68Ed026d694621f6FDfD' + sepolia: + uniswapV3SmartOrderRouterAddress: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45' + uniswapV3NftManagerAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' + uniswapV3QuoterV2ContractAddress: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' + uniswapV3FactoryAddress: '0x1F98431c8aD98523631AE4a59f267346ea31F984' + polygon: + mainnet: + uniswapV3SmartOrderRouterAddress: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45' + uniswapV3NftManagerAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' + uniswapV3QuoterV2ContractAddress: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' + uniswapV3FactoryAddress: '0x1F98431c8aD98523631AE4a59f267346ea31F984' + mumbai: + uniswapV3SmartOrderRouterAddress: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45' + uniswapV3NftManagerAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' + uniswapV3QuoterV2ContractAddress: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' + uniswapV3FactoryAddress: '0x1F98431c8aD98523631AE4a59f267346ea31F984' + binance-smart-chain: + mainnet: + uniswapV3SmartOrderRouterAddress: '0xB971eF87ede563556b2ED4b1C0b0019111Dd85d2' + uniswapV3NftManagerAddress: '0x7b8A01B39D58278b5DE7e48c8449c9f4F5170613' + uniswapV3QuoterV2ContractAddress: '0x78D78E420Da98ad378D7799bE8f4AF69033EB077' + uniswapV3FactoryAddress: '0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7' + avalanche: + avalanche: + uniswapV3FactoryAddress: '0x740b1c1de25031C31FF4fC9A62f554A55cdC1baD' + uniswapV3SmartOrderRouterAddress: '0xbb00FF08d01D300023C629E8fFfFcb65A5a578cE' + uniswapV3NftManagerAddress: '0x655C406EBFa14EE2006250925e54ec43AD184f8B' + uniswapV3QuoterV2ContractAddress: '0xbe0F5544EC67e9B3b2D979aaA43f18Fd87E6257F' + celo: + celo: + uniswapV3FactoryAddress: '0xAfE208a311B21f13EF87E33A90049fC17A7acDEc' + uniswapV3SmartOrderRouterAddress: '0x5615CDAb10dc425a742d643d949a7F474C01abc4' + uniswapV3NftManagerAddress: '0x3d79EdAaBC0EaB6F08ED885C05Fc0B014290D95A' + uniswapV3QuoterV2ContractAddress: '0x82825d0554fA07f7FC52Ab63c961F330fdEFa8E8' diff --git a/test/connectors/uniswap/uniswap.routes.test.ts b/test/connectors/uniswap/uniswap.routes.test.ts index 642aeaf514..6be897a18f 100644 --- a/test/connectors/uniswap/uniswap.routes.test.ts +++ b/test/connectors/uniswap/uniswap.routes.test.ts @@ -670,7 +670,7 @@ describe('POST /amm/estimateGas', () => { .send({ chain: 'ethereum', network: 'goerli', - connector: 'pangolin', + connector: 'plenty', }) .set('Accept', 'application/json') .expect(500); diff --git a/test/connectors/uniswap/uniswap.test.ts b/test/connectors/uniswap/uniswap.test.ts index 9b3b02a510..37da4e28d0 100644 --- a/test/connectors/uniswap/uniswap.test.ts +++ b/test/connectors/uniswap/uniswap.test.ts @@ -123,12 +123,12 @@ const patchMockProvider = () => { mockProvider.stub(FACTORY_ADDRESS, 'getPool', DAI_WETH_POOL_ADDRESS); mockProvider.setMockContract( - UniswapConfig.config.quoterContractAddress('goerli'), + UniswapConfig.config.quoterContractAddress('ethereum', 'goerli'), require('@uniswap/swap-router-contracts/artifacts/contracts/lens/QuoterV2.sol/QuoterV2.json') .abi ); mockProvider.stub( - UniswapConfig.config.quoterContractAddress('goerli'), + UniswapConfig.config.quoterContractAddress('ethereum', 'goerli'), 'quoteExactInputSingle', /* amountOut */ 1, /* sqrtPriceX96After */ 0, @@ -136,7 +136,7 @@ const patchMockProvider = () => { /* gasEstimate */ 0 ); mockProvider.stub( - UniswapConfig.config.quoterContractAddress('goerli'), + UniswapConfig.config.quoterContractAddress('ethereum', 'goerli'), 'quoteExactOutputSingle', /* amountIn */ 1, /* sqrtPriceX96After */ 0, @@ -161,6 +161,7 @@ const patchMockProvider = () => { /* unlocked */ true ); mockProvider.stub(DAI_WETH_POOL_ADDRESS, 'liquidity', 0); + mockProvider.stub(DAI_WETH_POOL_ADDRESS, 'fee', FeeAmount.LOW); patch(ethereum, 'provider', () => { return mockProvider; }); diff --git a/test/services/data/config-manager-v2/test1/root.yml b/test/services/data/config-manager-v2/test1/root.yml index b15c278c49..229791ae5c 100644 --- a/test/services/data/config-manager-v2/test1/root.yml +++ b/test/services/data/config-manager-v2/test1/root.yml @@ -19,7 +19,11 @@ configurations: $namespace polygon: configurationPath: polygon.yml schemaPath: ethereum-schema.json - + + $namespace celo: + configurationPath: celo.yml + schemaPath: ethereum-schema.json + $namespace pangolin: configurationPath: pangolin.yml schemaPath: pangolin-schema.json diff --git a/test/services/wallet/wallet.validators.test.ts b/test/services/wallet/wallet.validators.test.ts index 2d4a71c3d6..db06fe86f4 100644 --- a/test/services/wallet/wallet.validators.test.ts +++ b/test/services/wallet/wallet.validators.test.ts @@ -124,6 +124,16 @@ describe('validatePrivateKey', () => { ).toEqual([]); }); + it('valid when req.privateKey is a celo key', () => { + expect( + validatePrivateKey({ + chain: 'celo', + privateKey: + 'da857cbda0ba96757fed842617a40693d06d00001e55aa972955039ae747bac4', // noqa: mock + }) + ).toEqual([]); + }); + it('valid when req.privateKey is a avalanche key', () => { expect( validatePrivateKey({