From 38e413060df68bd131a46e0ceb4cc4b507081282 Mon Sep 17 00:00:00 2001 From: Eamon-McNamee Date: Tue, 26 Mar 2019 14:27:47 +0700 Subject: [PATCH 1/2] [UPD] get exchange from factory contract --- abi/external/uniswapFactory.json | 137 +++++++++++++++++++++++++++++++ examples/two.js | 4 +- src/constants.ts | 16 ++-- src/core/exchanges/uniswap.ts | 22 +++-- src/helpers/contracts.ts | 1 + src/types.ts | 1 + 6 files changed, 160 insertions(+), 21 deletions(-) create mode 100644 abi/external/uniswapFactory.json diff --git a/abi/external/uniswapFactory.json b/abi/external/uniswapFactory.json new file mode 100644 index 0000000..393e907 --- /dev/null +++ b/abi/external/uniswapFactory.json @@ -0,0 +1,137 @@ +[ + { + "name": "NewExchange", + "inputs": [ + { + "type": "address", + "name": "token", + "indexed": true + }, + { + "type": "address", + "name": "exchange", + "indexed": true + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "initializeFactory", + "outputs": [], + "inputs": [ + { + "type": "address", + "name": "template" + } + ], + "constant": false, + "payable": false, + "type": "function", + "gas": 35725 + }, + { + "name": "createExchange", + "outputs": [ + { + "type": "address", + "name": "out" + } + ], + "inputs": [ + { + "type": "address", + "name": "token" + } + ], + "constant": false, + "payable": false, + "type": "function", + "gas": 187911 + }, + { + "name": "getExchange", + "outputs": [ + { + "type": "address", + "name": "out" + } + ], + "inputs": [ + { + "type": "address", + "name": "token" + } + ], + "constant": true, + "payable": false, + "type": "function", + "gas": 715 + }, + { + "name": "getToken", + "outputs": [ + { + "type": "address", + "name": "out" + } + ], + "inputs": [ + { + "type": "address", + "name": "exchange" + } + ], + "constant": true, + "payable": false, + "type": "function", + "gas": 745 + }, + { + "name": "getTokenWithId", + "outputs": [ + { + "type": "address", + "name": "out" + } + ], + "inputs": [ + { + "type": "uint256", + "name": "token_id" + } + ], + "constant": true, + "payable": false, + "type": "function", + "gas": 736 + }, + { + "name": "exchangeTemplate", + "outputs": [ + { + "type": "address", + "name": "out" + } + ], + "inputs": [], + "constant": true, + "payable": false, + "type": "function", + "gas": 633 + }, + { + "name": "tokenCount", + "outputs": [ + { + "type": "uint256", + "name": "out" + } + ], + "inputs": [], + "constant": true, + "payable": false, + "type": "function", + "gas": 663 + } +] \ No newline at end of file diff --git a/examples/two.js b/examples/two.js index 350e27e..553a0be 100644 --- a/examples/two.js +++ b/examples/two.js @@ -14,11 +14,10 @@ const detherJs = new DetherJS(false); const destAddress = '0xB06c40B9c72231502949B33bC8b2543701C863Ef'; const sellToken = 'ETH' const buyToken = 'DAI' -const value = '1.32111'; +const value = '0.32111'; let buyAmount; -let wallet; let encryptedWallet; let user; @@ -48,7 +47,6 @@ const testUniswap = async () => { const wallet = new ethers.Wallet(USER_PRIV_KEY, rpcURL) const encryptedWallet = await wallet.encrypt(USER_PASSWORD); await detherJs.loadUser(encryptedWallet) - // const result = await detherJs.execExchange(USER_PASSWORD, sellToken, buyToken, ethers.utils.parseEther(value).toString(), buyAmount) // const result = await sendDelayedExchangeRawTx(buyAmount) const result = await execExchange(buyAmount) console.log('exec exchange result: ', result) diff --git a/src/constants.ts b/src/constants.ts index 5e58759..a932a68 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -80,6 +80,7 @@ export const CONTRACT_ADDRESSES: any = { // Mkr/Oasis is not on ropsten // AirSwap is not on ropsten kyberNetworkProxy: '0x818E6FECD516Ecc3849DAf6845e3EC868087B755', + uniswapExchange: '', }, kovan: { // dether @@ -96,10 +97,8 @@ export const CONTRACT_ADDRESSES: any = { // Mkr/Oasis is not on ropsten // AirSwap is not on ropsten kyberNetworkProxy: '0x7e6b8b9510D71BF8EF0f893902EbB9C865eEF4Df', - uniswapExchange: { - DAI: '0x8779C708e2C3b1067de9Cd63698E4334866c691C', - MKR: '0xc64F00B099649D578Bf289894d3A51ee7d0b04e5', - } + uniswapFactory: '0xD3E51Ef092B2845f10401a0159B2B96e8B6c3D30', + uniswapExchange: '', }, rinkeby: { // dether @@ -115,6 +114,7 @@ export const CONTRACT_ADDRESSES: any = { // external // Mkr/Oasis is not on rinkeby airswapExchange: '0x07fc7c43d8168a2730344e5cf958aaecc3b42b41', + uniswapExchange: '', }, homestead: { // dether @@ -129,12 +129,7 @@ export const CONTRACT_ADDRESSES: any = { Shops: '', // external kyberNetworkProxy: '0x818E6FECD516Ecc3849DAf6845e3EC868087B755', - uniswapExchange: { - DAI: '0x09cabEC1eAd1c0Ba254B09efb3EE13841712bE14', - ZRX: '0xaE76c84C9262Cdb9abc0C2c8888e62Db8E22A0bF', - KNC: '0x49c4f9bc14884f6210F28342ceD592A633801a8b', - MKR: '0x2C4Bd064b998838076fa341A83d007FC2FA50957', - } + uniswapExchange: '', }, custom: { // dether @@ -149,6 +144,7 @@ export const CONTRACT_ADDRESSES: any = { Shops: '', // external kyberNetworkProxy: '', + uniswapExchange: '', }, }; diff --git a/src/core/exchanges/uniswap.ts b/src/core/exchanges/uniswap.ts index f0e62b1..36810c1 100644 --- a/src/core/exchanges/uniswap.ts +++ b/src/core/exchanges/uniswap.ts @@ -1,7 +1,7 @@ import ExchangeBase from './base'; import { ethers } from 'ethers' import * as contract from '../../helpers/contracts'; -import { CONTRACT_ADDRESSES } from '../../constants'; +import { CONTRACT_ADDRESSES, TICKER } from '../../constants'; import { Token, Exchange, ExternalContract, ITxOptions @@ -15,9 +15,9 @@ export default class ExchangeUniswap extends ExchangeBase { async estimate(sellAmount: string, provider: ethers.providers.Provider): Promise { const erc20Token = this.sellToken === 'ETH' ? this.buyToken : this.sellToken - let { name: networkName } = await provider.getNetwork(); - const exchangeAddress = CONTRACT_ADDRESSES[networkName][ExternalContract.uniswapExchange][erc20Token] + const exchangeAddress = await this.getUniswapExchangeAddress(provider, erc20Token) const uniswapContract = await contract.get(provider, ExternalContract.uniswapExchange, exchangeAddress); + const network = await provider.getNetwork(); const buyAmountWei = this.sellToken === 'ETH' ? (await uniswapContract.getEthToTokenInputPrice(sellAmount)) @@ -32,8 +32,7 @@ export default class ExchangeUniswap extends ExchangeBase { async trade(sellAmount: string, buyAmount: string, wallet: ethers.Wallet, txOptions: ITxOptions): Promise { const erc20Token = this.sellToken === 'ETH' ? this.buyToken : this.sellToken - let { name: networkName } = await wallet.provider.getNetwork(); - const exchangeAddress = CONTRACT_ADDRESSES[networkName][ExternalContract.uniswapExchange][erc20Token] + const exchangeAddress = await this.getUniswapExchangeAddress(wallet.provider, erc20Token) const uniswapContract = await contract.get(wallet.provider, ExternalContract.uniswapExchange, exchangeAddress); if (this.sellToken === Token.ETH) { @@ -53,16 +52,23 @@ export default class ExchangeUniswap extends ExchangeBase { throw new Error('erc20 token to erc20 token not yet implemented'); } + async getUniswapExchangeAddress(provider: ethers.providers.Provider, token: string): Promise { + const contractName = ExternalContract.uniswapFactory + let { name: networkName } = await provider.getNetwork(); + const tokenAddress = TICKER[networkName][token]; + const uniswapFactoryInstance = await contract.get(provider, contractName); + + return uniswapFactoryInstance.getExchange(tokenAddress) + } + async trade_delayed(sellAmount: string, buyAmount: string, wallet: ethers.Wallet, nonce: number, txOptions: ITxOptions): Promise { // https://docs.ethers.io/ethers.js/html/api-advanced.html?highlight=encode const uniswapInterfaceAbi = await contract.getAbi(ExternalContract.uniswapExchange) const iUniswap = new ethers.utils.Interface(uniswapInterfaceAbi); - let { name: networkName } = await wallet.provider.getNetwork(); const erc20Token = this.sellToken === 'ETH' ? this.buyToken : this.sellToken - const exchangeAddress = CONTRACT_ADDRESSES[networkName][ExternalContract.uniswapExchange][erc20Token] - + const exchangeAddress = await this.getUniswapExchangeAddress(wallet.provider, erc20Token) const uniswapContract = await contract.get(wallet.provider, ExternalContract.uniswapExchange, exchangeAddress); const deadline = Math.floor(Date.now() / 1000) + 600 // 600 seconds from now diff --git a/src/helpers/contracts.ts b/src/helpers/contracts.ts index d23e161..856780f 100644 --- a/src/helpers/contracts.ts +++ b/src/helpers/contracts.ts @@ -17,6 +17,7 @@ const ABI: any = { [ExternalContract.erc20]: require('../../abi/external/erc20.json'), [ExternalContract.kyberNetworkProxy]: require('../../abi/external/kyberNetworkProxy.json'), [ExternalContract.uniswapExchange]: require('../../abi/external/uniswapExchange.json'), + [ExternalContract.uniswapFactory]: require('../../abi/external/uniswapFactory.json'), }; export const getAbi = async (contractName: DetherContract | ExternalContract): Promise => { diff --git a/src/types.ts b/src/types.ts index 7237574..fc333f4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -93,6 +93,7 @@ export enum ExternalContract { airswapExchange = 'airswapExchange', kyberNetworkProxy = 'kyberNetworkProxy', uniswapExchange = 'uniswapExchange', + uniswapFactory = 'uniswapFactory', appealableArbitrator = 'appealableArbitrator', } From f4e52a32336c3de793fa3c4f4ffcbb6441aa287a Mon Sep 17 00:00:00 2001 From: Eamon-McNamee Date: Tue, 26 Mar 2019 16:24:53 +0700 Subject: [PATCH 2/2] [UPD] use getExchange() address from contract helpers --- examples/two.js | 8 ++++---- src/core/exchanges/uniswap.ts | 17 ++++------------- src/core/wallet.ts | 6 ++---- src/helpers/contracts.ts | 9 +++++++++ 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/examples/two.js b/examples/two.js index 553a0be..e78c0d3 100644 --- a/examples/two.js +++ b/examples/two.js @@ -12,9 +12,9 @@ const rpcURL = 'https://kovan.infura.io'; const detherJs = new DetherJS(false); const destAddress = '0xB06c40B9c72231502949B33bC8b2543701C863Ef'; -const sellToken = 'ETH' -const buyToken = 'DAI' -const value = '0.32111'; +const sellToken = 'DAI' +const buyToken = 'ETH' +const value = '3.32111'; let buyAmount; @@ -26,7 +26,7 @@ const checkApproved = () => detherJs.hasApproval(address, buyToken, '44444444'); const sendDelayedExchangeRawTx = async buyAmount => { const txCount = await detherJs.provider.getTransactionCount(address) - const nonce = txCount + 1 + const nonce = txCount const rawTx = await detherJs.execExchange_delayed(USER_PASSWORD, sellToken, buyToken, ethers.utils.parseEther(value).toString(), buyAmount, nonce) console.log('Raw tx: ', rawTx) const txResponse = await detherJs.provider.sendTransaction(rawTx) diff --git a/src/core/exchanges/uniswap.ts b/src/core/exchanges/uniswap.ts index 36810c1..320f140 100644 --- a/src/core/exchanges/uniswap.ts +++ b/src/core/exchanges/uniswap.ts @@ -1,7 +1,6 @@ import ExchangeBase from './base'; import { ethers } from 'ethers' import * as contract from '../../helpers/contracts'; -import { CONTRACT_ADDRESSES, TICKER } from '../../constants'; import { Token, Exchange, ExternalContract, ITxOptions @@ -15,7 +14,7 @@ export default class ExchangeUniswap extends ExchangeBase { async estimate(sellAmount: string, provider: ethers.providers.Provider): Promise { const erc20Token = this.sellToken === 'ETH' ? this.buyToken : this.sellToken - const exchangeAddress = await this.getUniswapExchangeAddress(provider, erc20Token) + const exchangeAddress = await contract.getUniswapExchangeAddress(provider, erc20Token) const uniswapContract = await contract.get(provider, ExternalContract.uniswapExchange, exchangeAddress); const network = await provider.getNetwork(); @@ -31,8 +30,9 @@ export default class ExchangeUniswap extends ExchangeBase { } async trade(sellAmount: string, buyAmount: string, wallet: ethers.Wallet, txOptions: ITxOptions): Promise { + const erc20Token = this.sellToken === 'ETH' ? this.buyToken : this.sellToken - const exchangeAddress = await this.getUniswapExchangeAddress(wallet.provider, erc20Token) + const exchangeAddress = await contract.getUniswapExchangeAddress(wallet.provider, erc20Token) const uniswapContract = await contract.get(wallet.provider, ExternalContract.uniswapExchange, exchangeAddress); if (this.sellToken === Token.ETH) { @@ -52,15 +52,6 @@ export default class ExchangeUniswap extends ExchangeBase { throw new Error('erc20 token to erc20 token not yet implemented'); } - async getUniswapExchangeAddress(provider: ethers.providers.Provider, token: string): Promise { - const contractName = ExternalContract.uniswapFactory - let { name: networkName } = await provider.getNetwork(); - const tokenAddress = TICKER[networkName][token]; - const uniswapFactoryInstance = await contract.get(provider, contractName); - - return uniswapFactoryInstance.getExchange(tokenAddress) - } - async trade_delayed(sellAmount: string, buyAmount: string, wallet: ethers.Wallet, nonce: number, txOptions: ITxOptions): Promise { // https://docs.ethers.io/ethers.js/html/api-advanced.html?highlight=encode @@ -68,7 +59,7 @@ export default class ExchangeUniswap extends ExchangeBase { const iUniswap = new ethers.utils.Interface(uniswapInterfaceAbi); const erc20Token = this.sellToken === 'ETH' ? this.buyToken : this.sellToken - const exchangeAddress = await this.getUniswapExchangeAddress(wallet.provider, erc20Token) + const exchangeAddress = await contract.getUniswapExchangeAddress(wallet.provider, erc20Token) const uniswapContract = await contract.get(wallet.provider, ExternalContract.uniswapExchange, exchangeAddress); const deadline = Math.floor(Date.now() / 1000) + 600 // 600 seconds from now diff --git a/src/core/wallet.ts b/src/core/wallet.ts index 2f3db78..c1e2511 100644 --- a/src/core/wallet.ts +++ b/src/core/wallet.ts @@ -60,8 +60,7 @@ export const getAvailableToken = async (provider: ethers.providers.Provider, for export const hasApproval = async (owner: string, sellToken: Token, amount: string, provider: ethers.providers.Provider): Promise => { const erc20instance = await contract.getErc20(provider, sellToken); - let { name: networkName } = await provider.getNetwork(); - const exchangeAddress = constants.CONTRACT_ADDRESSES[networkName][ExternalContract.uniswapExchange][sellToken] + const exchangeAddress = await contract.getUniswapExchangeAddress(provider, sellToken) const approve = await erc20instance.allowance(owner, exchangeAddress); if (Number(approve) > Number(amount)) { @@ -106,8 +105,7 @@ export const sendCrypto = async (amount: string, toAddress: string, token: Token }; export const approveToken = async (token: Token, wallet: ethers.Wallet, txOptions: ITxOptions): Promise => { - const network = await wallet.provider.getNetwork(); - const exchangeAddress = constants.CONTRACT_ADDRESSES[network.name][ExternalContract.uniswapExchange][token] + const exchangeAddress = await contract.getUniswapExchangeAddress(wallet.provider, token) const erc20instance = await contract.getErc20(wallet.provider, token); return erc20instance.connect(wallet).approve(exchangeAddress, ethers.utils.bigNumberify(2).pow(256).sub(1), txOptions); }; diff --git a/src/helpers/contracts.ts b/src/helpers/contracts.ts index 856780f..9e4e27a 100644 --- a/src/helpers/contracts.ts +++ b/src/helpers/contracts.ts @@ -28,6 +28,15 @@ export const getContractAddress = async (contractName: DetherContract | External return CONTRACT_ADDRESSES[networkName][contractName]; } +export const getUniswapExchangeAddress = async (provider: ethers.providers.Provider, token: string): Promise => { + const contractName = ExternalContract.uniswapFactory + let { name: networkName } = await provider.getNetwork(); + const tokenAddress = TICKER[networkName][token]; + const uniswapFactoryInstance = await get(provider, contractName); + + return uniswapFactoryInstance.getExchange(tokenAddress) +} + export const get = async (provider: ethers.providers.Provider, contractName: DetherContract | ExternalContract, address?: string, overwriteAbi?: any): Promise => { if (!provider) throw new Error('missing provider arg'); if (!contractName) throw new Error('missing contract name');