Commit c465d2d0 authored by Chi Kei Chan's avatar Chi Kei Chan Committed by GitHub

Add all smart trade methods to swap (#45)

* wip

* ETH to ERC20 swap input

* ERC20 to ETH swap input

* Add ERC20 to ETH swapInput

* Add ETH to ERC20 swapOutput

* Add ERC20 to ETH swapOutput

* Add ERC20 to ERC20 swapInput

* Add ERC20 to ERC20 swapOutput

* Remove console.log
parent b6ad0dbb
[{"name": "TokenPurchase", "inputs": [{"type": "address", "name": "buyer", "indexed": true}, {"type": "uint256", "name": "eth_sold", "indexed": true}, {"type": "uint256", "name": "tokens_bought", "indexed": true}], "anonymous": false, "type": "event"}, {"name": "EthPurchase", "inputs": [{"type": "address", "name": "buyer", "indexed": true}, {"type": "uint256", "name": "tokens_sold", "indexed": true}, {"type": "uint256", "name": "eth_bought", "indexed": true}], "anonymous": false, "type": "event"}, {"name": "AddLiquidity", "inputs": [{"type": "address", "name": "provider", "indexed": true}, {"type": "uint256", "name": "eth_amount", "indexed": true}, {"type": "uint256", "name": "token_amount", "indexed": true}], "anonymous": false, "type": "event"}, {"name": "RemoveLiquidity", "inputs": [{"type": "address", "name": "provider", "indexed": true}, {"type": "uint256", "name": "eth_amount", "indexed": true}, {"type": "uint256", "name": "token_amount", "indexed": true}], "anonymous": false, "type": "event"}, {"name": "Transfer", "inputs": [{"type": "address", "name": "_from", "indexed": true}, {"type": "address", "name": "_to", "indexed": true}, {"type": "uint256", "name": "_value", "indexed": false}], "anonymous": false, "type": "event"}, {"name": "Approval", "inputs": [{"type": "address", "name": "_owner", "indexed": true}, {"type": "address", "name": "_spender", "indexed": true}, {"type": "uint256", "name": "_value", "indexed": false}], "anonymous": false, "type": "event"}, {"name": "setup", "outputs": [], "inputs": [{"type": "address", "name": "token_addr"}], "constant": false, "payable": false, "type": "function", "gas": 175875}, {"name": "addLiquidity", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "min_liquidity"}, {"type": "uint256", "name": "max_tokens"}, {"type": "uint256", "name": "deadline"}], "constant": false, "payable": true, "type": "function", "gas": 82605}, {"name": "removeLiquidity", "outputs": [{"type": "uint256", "name": "out"}, {"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "amount"}, {"type": "uint256", "name": "min_eth"}, {"type": "uint256", "name": "min_tokens"}, {"type": "uint256", "name": "deadline"}], "constant": false, "payable": false, "type": "function", "gas": 116814}, {"name": "__default__", "outputs": [], "inputs": [], "constant": false, "payable": true, "type": "function"}, {"name": "ethToTokenSwapInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "min_tokens"}, {"type": "uint256", "name": "deadline"}], "constant": false, "payable": true, "type": "function", "gas": 12757}, {"name": "ethToTokenTransferInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "min_tokens"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}], "constant": false, "payable": true, "type": "function", "gas": 12965}, {"name": "ethToTokenSwapOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}, {"type": "uint256", "name": "deadline"}], "constant": false, "payable": true, "type": "function", "gas": 50463}, {"name": "ethToTokenTransferOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}], "constant": false, "payable": true, "type": "function", "gas": 50671}, {"name": "tokenToEthSwapInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}, {"type": "uint256", "name": "min_eth"}, {"type": "uint256", "name": "deadline"}], "constant": false, "payable": false, "type": "function", "gas": 47503}, {"name": "tokenToEthTransferInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}, {"type": "uint256", "name": "min_eth"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}], "constant": false, "payable": false, "type": "function", "gas": 47712}, {"name": "tokenToEthSwapOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "eth_bought"}, {"type": "uint256", "name": "max_tokens"}, {"type": "uint256", "name": "deadline"}], "constant": false, "payable": false, "type": "function", "gas": 50175}, {"name": "tokenToEthTransferOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "eth_bought"}, {"type": "uint256", "name": "max_tokens"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}], "constant": false, "payable": false, "type": "function", "gas": 50384}, {"name": "tokenToTokenSwapInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}, {"type": "uint256", "name": "min_tokens_bought"}, {"type": "uint256", "name": "min_eth_bought"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "token_addr"}], "constant": false, "payable": false, "type": "function", "gas": 51007}, {"name": "tokenToTokenTransferInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}, {"type": "uint256", "name": "min_tokens_bought"}, {"type": "uint256", "name": "min_eth_bought"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}, {"type": "address", "name": "token_addr"}], "constant": false, "payable": false, "type": "function", "gas": 51098}, {"name": "tokenToTokenSwapOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}, {"type": "uint256", "name": "max_tokens_sold"}, {"type": "uint256", "name": "max_eth_sold"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "token_addr"}], "constant": false, "payable": false, "type": "function", "gas": 54928}, {"name": "tokenToTokenTransferOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}, {"type": "uint256", "name": "max_tokens_sold"}, {"type": "uint256", "name": "max_eth_sold"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}, {"type": "address", "name": "token_addr"}], "constant": false, "payable": false, "type": "function", "gas": 55019}, {"name": "tokenToExchangeSwapInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}, {"type": "uint256", "name": "min_tokens_bought"}, {"type": "uint256", "name": "min_eth_bought"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "exchange_addr"}], "constant": false, "payable": false, "type": "function", "gas": 49342}, {"name": "tokenToExchangeTransferInput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}, {"type": "uint256", "name": "min_tokens_bought"}, {"type": "uint256", "name": "min_eth_bought"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}, {"type": "address", "name": "exchange_addr"}], "constant": false, "payable": false, "type": "function", "gas": 49532}, {"name": "tokenToExchangeSwapOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}, {"type": "uint256", "name": "max_tokens_sold"}, {"type": "uint256", "name": "max_eth_sold"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "exchange_addr"}], "constant": false, "payable": false, "type": "function", "gas": 53233}, {"name": "tokenToExchangeTransferOutput", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}, {"type": "uint256", "name": "max_tokens_sold"}, {"type": "uint256", "name": "max_eth_sold"}, {"type": "uint256", "name": "deadline"}, {"type": "address", "name": "recipient"}, {"type": "address", "name": "exchange_addr"}], "constant": false, "payable": false, "type": "function", "gas": 53423}, {"name": "getEthToTokenInputPrice", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "eth_sold"}], "constant": true, "payable": false, "type": "function", "gas": 5542}, {"name": "getEthToTokenOutputPrice", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_bought"}], "constant": true, "payable": false, "type": "function", "gas": 6872}, {"name": "getTokenToEthInputPrice", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "tokens_sold"}], "constant": true, "payable": false, "type": "function", "gas": 5637}, {"name": "getTokenToEthOutputPrice", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "uint256", "name": "eth_bought"}], "constant": true, "payable": false, "type": "function", "gas": 6897}, {"name": "tokenAddress", "outputs": [{"type": "address", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1413}, {"name": "factoryAddress", "outputs": [{"type": "address", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1443}, {"name": "balanceOf", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "address", "name": "_owner"}], "constant": true, "payable": false, "type": "function", "gas": 1645}, {"name": "transfer", "outputs": [{"type": "bool", "name": "out"}], "inputs": [{"type": "address", "name": "_to"}, {"type": "uint256", "name": "_value"}], "constant": false, "payable": false, "type": "function", "gas": 75034}, {"name": "transferFrom", "outputs": [{"type": "bool", "name": "out"}], "inputs": [{"type": "address", "name": "_from"}, {"type": "address", "name": "_to"}, {"type": "uint256", "name": "_value"}], "constant": false, "payable": false, "type": "function", "gas": 110907}, {"name": "approve", "outputs": [{"type": "bool", "name": "out"}], "inputs": [{"type": "address", "name": "_spender"}, {"type": "uint256", "name": "_value"}], "constant": false, "payable": false, "type": "function", "gas": 38769}, {"name": "allowance", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "address", "name": "_owner"}, {"type": "address", "name": "_spender"}], "constant": true, "payable": false, "type": "function", "gas": 1925}, {"name": "name", "outputs": [{"type": "bytes32", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1623}, {"name": "symbol", "outputs": [{"type": "bytes32", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1653}, {"name": "decimals", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1683}, {"name": "totalSupply", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1713}]
......@@ -103,7 +103,7 @@ class CurrencyInputPanel extends Component {
}
if (selectedTokenAddress === 'ETH') {
return `Balance: ${web3.utils.fromWei(balance, 'ether')}`;
return `Balance: ${BN(web3.utils.fromWei(balance, 'ether')).toFixed(2)}`;
}
const tokenData = this.getTokenData(selectedTokenAddress);
......@@ -145,32 +145,36 @@ class CurrencyInputPanel extends Component {
this.props.onCurrencySelected(address);
if (address && address !== 'ETH') {
// Add Token Contract
const { drizzle } = this.context;
const { fromToken } = this.props.exchangeAddresses;
const { web3 } = drizzle;
const tokenConfig = {
contractName: address,
web3Contract: new web3.eth.Contract(ERC20_ABI, address),
};
const tokenEvents = ['Approval', 'Transfer'];
this.context.drizzle.addContract(tokenConfig, tokenEvents, { from: this.props.account });
// Add Token Contract
if (!this.props.contracts[address]) {
// console.log(`Adding Token Contract - ${address}`);
const tokenConfig = {
contractName: address,
web3Contract: new web3.eth.Contract(ERC20_ABI, address),
};
const tokenEvents = ['Approval', 'Transfer'];
this.context.drizzle.addContract(tokenConfig, tokenEvents, { from: this.props.account });
}
// Add Exchange Contract
const exchangeAddress = fromToken[address];
if (!exchangeAddress) {
return;
}
const exchangeConfig = {
contractName: exchangeAddress,
web3Contract: new web3.eth.Contract(EXCHANGE_ABI, exchangeAddress),
};
const exchangeEvents = ['Approval', 'Transfer', 'TokenPurchase', 'EthPurchase', 'AddLiquidity', 'RemoveLiquidity'];
this.context.drizzle.addContract(exchangeConfig, exchangeEvents , { from: this.props.account });
if (!this.props.contracts[exchangeAddress]) {
// console.log(`Adding Exchange Contract - ${exchangeAddress}`);
const exchangeConfig = {
contractName: exchangeAddress,
web3Contract: new web3.eth.Contract(EXCHANGE_ABI, exchangeAddress),
};
const exchangeEvents = ['Approval', 'Transfer', 'TokenPurchase', 'EthPurchase', 'AddLiquidity', 'RemoveLiquidity'];
this.context.drizzle.addContract(exchangeConfig, exchangeEvents , { from: this.props.account });
}
}
};
......
import {BigNumber as BN} from "bignumber.js";
import promisify from "./web3-promisfy";
export const calculateExchangeRateFromInput = async opts => {
const { inputCurrency, outputCurrency } = opts;
......@@ -47,6 +48,53 @@ export const calculateExchangeRateFromOutput = async opts => {
return ERC20_TO_ERC20.calculateInput(opts);
};
export const swapInput = async opts => {
const { inputCurrency, outputCurrency } = opts;
if (!inputCurrency || !outputCurrency) {
return;
}
if (inputCurrency === outputCurrency) {
console.error(`Input and Output currency cannot be the same`);
return;
}
if (inputCurrency === 'ETH' && outputCurrency !== 'ETH') {
return ETH_TO_ERC20.swapInput(opts);
}
if (outputCurrency === 'ETH' && inputCurrency !== 'ETH') {
return ERC20_TO_ETH.swapInput(opts);
}
return ERC20_TO_ERC20.swapInput(opts);
};
export const swapOutput = async opts => {
const { inputCurrency, outputCurrency } = opts;
if (!inputCurrency || !outputCurrency) {
return;
}
if (inputCurrency === outputCurrency) {
console.error(`Input and Output currency cannot be the same`);
return;
}
if (inputCurrency === 'ETH' && outputCurrency !== 'ETH') {
return ETH_TO_ERC20.swapOutput(opts);
}
if (outputCurrency === 'ETH' && inputCurrency !== 'ETH') {
return ERC20_TO_ETH.swapOutput(opts);
}
return ERC20_TO_ERC20.swapOutput(opts);
};
const ETH_TO_ERC20 = {
calculateOutput: async ({drizzleCtx, contractStore, input, inputCurrency, outputCurrency, exchangeAddresses }) => {
if (inputCurrency !== 'ETH') {
......@@ -137,6 +185,73 @@ const ETH_TO_ERC20 = {
return exchangeRate;
},
swapInput: async ({drizzleCtx, contractStore, input, output, account, inputCurrency, outputCurrency, exchangeAddresses }) => {
if (inputCurrency !== 'ETH') {
console.error('Input Currency should be ETH');
return;
}
if (!outputCurrency || outputCurrency === 'ETH') {
console.error('Output Currency should be ERC20');
return;
}
const exchangeAddress = exchangeAddresses.fromToken[outputCurrency];
const exchange = drizzleCtx.contracts[exchangeAddress];
if (!exchangeAddress || !exchange) {
console.error(`Cannot find Exchange Address for ${outputCurrency}`);
return;
}
const { web3 } = drizzleCtx;
const blockNumber = await promisify(web3, 'getBlockNumber');
const block = await promisify(web3, 'getBlock', blockNumber);
const deadline = block.timestamp + 300;
const ALLOWED_SLIPPAGE = BN(0.025);
const outputDecimals = await getDecimals({ address: outputCurrency, contractStore, drizzleCtx });
const minOutput = BN(output).multipliedBy(10 ** outputDecimals).multipliedBy(BN(1).minus(ALLOWED_SLIPPAGE));
exchange.methods.ethToTokenSwapInput.cacheSend(minOutput.toFixed(0), deadline, {
from: account,
value: BN(input).multipliedBy(10 ** 18).toFixed(0),
});
},
swapOutput: async ({drizzleCtx, contractStore, input, output, account, inputCurrency, outputCurrency, exchangeAddresses }) => {
if (inputCurrency !== 'ETH') {
console.error('Input Currency should be ETH');
return;
}
if (!outputCurrency || outputCurrency === 'ETH') {
console.error('Output Currency should be ERC20');
return;
}
const exchangeAddress = exchangeAddresses.fromToken[outputCurrency];
const exchange = drizzleCtx.contracts[exchangeAddress];
if (!exchangeAddress || !exchange) {
console.error(`Cannot find Exchange Address for ${outputCurrency}`);
return;
}
const { web3 } = drizzleCtx;
const blockNumber = await promisify(web3, 'getBlockNumber');
const block = await promisify(web3, 'getBlock', blockNumber);
const deadline = block.timestamp + 300;
const ALLOWED_SLIPPAGE = BN(0.025);
const outputDecimals = await getDecimals({ address: outputCurrency, contractStore, drizzleCtx });
const outputAmount = BN(output).multipliedBy(BN(10 ** outputDecimals));
const maxInput = BN(input).multipliedBy(10 ** 18).multipliedBy(BN(1).plus(ALLOWED_SLIPPAGE));
exchange.methods.ethToTokenSwapOutput.cacheSend(outputAmount.toFixed(0), deadline, {
from: account,
value: maxInput.toFixed(0),
});
},
};
const ERC20_TO_ETH = {
......@@ -192,7 +307,7 @@ const ERC20_TO_ETH = {
}
if (!inputCurrency || inputCurrency === 'ETH') {
console.error('Output Currency should be ERC20');
console.error('Input Currency should be ERC20');
return;
}
......@@ -229,6 +344,78 @@ const ERC20_TO_ETH = {
return exchangeRate;
},
swapInput: async ({drizzleCtx, contractStore, input, output, account, inputCurrency, outputCurrency, exchangeAddresses }) => {
if (outputCurrency !== 'ETH') {
console.error('Output Currency should be ETH');
return;
}
if (!inputCurrency || inputCurrency === 'ETH') {
console.error('Input Currency should be ERC20');
return;
}
const exchangeAddress = exchangeAddresses.fromToken[inputCurrency];
const exchange = drizzleCtx.contracts[exchangeAddress];
if (!exchangeAddress || !exchange) {
console.error(`Cannot find Exchange Address for ${inputCurrency}`);
return;
}
const { web3 } = drizzleCtx;
const blockNumber = await promisify(web3, 'getBlockNumber');
const block = await promisify(web3, 'getBlock', blockNumber);
const deadline = block.timestamp + 300;
const ALLOWED_SLIPPAGE = BN(0.025);
const inputDecimals = await getDecimals({ address: inputCurrency, contractStore, drizzleCtx });
const minOutput = BN(output).multipliedBy(10 ** 18).multipliedBy(BN(1).minus(ALLOWED_SLIPPAGE));
const inputAmount = BN(input).multipliedBy(10 ** inputDecimals);
exchange.methods.tokenToEthSwapInput.cacheSend(
inputAmount.toFixed(0),
minOutput.toFixed(0),
deadline,
{ from: account, value: '0x0' },
);
},
swapOutput: async ({drizzleCtx, contractStore, input, output, account, inputCurrency, outputCurrency, exchangeAddresses }) => {
if (outputCurrency !== 'ETH') {
console.error('Output Currency should be ETH');
return;
}
if (!inputCurrency || inputCurrency === 'ETH') {
console.error('Output Currency should be ERC20');
return;
}
const exchangeAddress = exchangeAddresses.fromToken[inputCurrency];
const exchange = drizzleCtx.contracts[exchangeAddress];
if (!exchangeAddress || !exchange) {
console.error(`Cannot find Exchange Address for ${inputCurrency}`);
return;
}
const { web3 } = drizzleCtx;
const blockNumber = await promisify(web3, 'getBlockNumber');
const block = await promisify(web3, 'getBlock', blockNumber);
const deadline = block.timestamp + 300;
const ALLOWED_SLIPPAGE = BN(0.025);
const inputDecimals = await getDecimals({ address: inputCurrency, contractStore, drizzleCtx });
const maxInput = BN(input).multipliedBy(10 ** inputDecimals).multipliedBy(BN(1).plus(ALLOWED_SLIPPAGE));
const outputAmount = BN(output).multipliedBy(10 ** 18);
exchange.methods.tokenToEthSwapOutput.cacheSend(
outputAmount.toFixed(0),
maxInput.toFixed(0),
deadline,
{ from: account },
);
},
};
const ERC20_TO_ERC20 = {
......@@ -278,9 +465,111 @@ const ERC20_TO_ERC20 = {
return exchangeRateA.multipliedBy(exchangeRateB);
},
};
swapInput: async ({drizzleCtx, contractStore, input, output, account, inputCurrency, outputCurrency, exchangeAddresses }) => {
if (!outputCurrency || outputCurrency === 'ETH') {
console.error('Output Currency should be ERC20');
return;
}
if (!inputCurrency || inputCurrency === 'ETH') {
console.error('Input Currency should be ERC20');
return;
}
const exchangeAddress = exchangeAddresses.fromToken[inputCurrency];
const exchange = drizzleCtx.contracts[exchangeAddress];
if (!exchangeAddress || !exchange) {
console.error(`Cannot find Exchange Address for ${inputCurrency}`);
return;
}
const { web3 } = drizzleCtx;
const blockNumber = await promisify(web3, 'getBlockNumber');
const block = await promisify(web3, 'getBlock', blockNumber);
const deadline = block.timestamp + 300;
const ALLOWED_SLIPPAGE = BN(0.04);
const inputDecimals = await getDecimals({ address: inputCurrency, contractStore, drizzleCtx });
const outputDecimals = await getDecimals({ address: outputCurrency, contractStore, drizzleCtx });
const inputAmount = BN(input).multipliedBy(BN(10 ** inputDecimals));
const outputAmount = BN(input).multipliedBy(BN(10 ** outputDecimals));
const tokenAddress = outputCurrency;
const tokensSold = inputAmount.toFixed(0);
const minTokensBought = outputAmount.multipliedBy(BN(1).plus(ALLOWED_SLIPPAGE)).toFixed(0);
const minEthBought = 1;
exchange.methods.tokenToTokenSwapInput.cacheSend(
tokensSold,
minTokensBought,
minEthBought,
deadline,
tokenAddress,
{ from: account },
);
},
swapOutput: async opts => {
const {
drizzleCtx,
contractStore,
input,
output,
account,
inputCurrency,
outputCurrency,
exchangeAddresses
} = opts;
const exchangeRateA = await ETH_TO_ERC20.calculateInput({ ...opts, inputCurrency: 'ETH' });
if (!exchangeRateA) {
return;
}
if (!outputCurrency || outputCurrency === 'ETH') {
console.error('Output Currency should be ERC20');
return;
}
if (!inputCurrency || inputCurrency === 'ETH') {
console.error('Input Currency should be ERC20');
return;
}
const exchangeAddress = exchangeAddresses.fromToken[inputCurrency];
const exchange = drizzleCtx.contracts[exchangeAddress];
if (!exchangeAddress || !exchange) {
console.error(`Cannot find Exchange Address for ${inputCurrency}`);
return;
}
const { web3 } = drizzleCtx;
const blockNumber = await promisify(web3, 'getBlockNumber');
const block = await promisify(web3, 'getBlock', blockNumber);
const deadline = block.timestamp + 300;
const ALLOWED_SLIPPAGE = BN(0.04);
const inputDecimals = await getDecimals({ address: inputCurrency, contractStore, drizzleCtx });
const outputDecimals = await getDecimals({ address: outputCurrency, contractStore, drizzleCtx });
const inputAmount = BN(input).multipliedBy(BN(10 ** inputDecimals));
const outputAmount = BN(output).multipliedBy(BN(10 ** outputDecimals));
const inputAmountB = BN(output).dividedBy(exchangeRateA).multipliedBy(BN(10 ** 18));
const tokenAddress = outputCurrency;
const tokensBought = outputAmount.toFixed(0);
const maxTokensSold = inputAmount.multipliedBy(BN(1).plus(ALLOWED_SLIPPAGE)).toFixed(0);
const maxEthSold = inputAmountB.multipliedBy(1.2).toFixed(0);
exchange.methods.tokenToTokenSwapOutput.cacheSend(
tokensBought,
maxTokensSold,
maxEthSold,
deadline,
tokenAddress,
{ from: account },
);
},
};
function getDecimals({ address, drizzleCtx, contractStore }) {
return new Promise(async (resolve, reject) => {
......@@ -295,6 +584,8 @@ function getDecimals({ address, drizzleCtx, contractStore }) {
});
}
const BALANCE_KEY = {};
function getBalance({ currency, address, drizzleCtx, contractStore }) {
return new Promise(async (resolve, reject) => {
if (currency === 'ETH') {
......@@ -310,7 +601,14 @@ function getBalance({ currency, address, drizzleCtx, contractStore }) {
if (!token) {
return;
}
const balanceKey = token.methods.balanceOf.cacheCall(address);
let balanceKey = BALANCE_KEY[address];
if (!balanceKey) {
balanceKey = token.methods.balanceOf.cacheCall(address);
BALANCE_KEY[address] = balanceKey;
}
const tokenStore = contractStore[currency];
if (!tokenStore) {
......
export default function promisify(web3, methodName, ...args) {
return new Promise((resolve, reject) => {
if (!web3) {
reject(new Error('No Web3 object'));
return;
}
const method = web3.eth[methodName];
if (!method) {
reject(new Error(`Cannot find web3.eth.${methodName}`));
return;
}
method(...args, (error, data) => {
if (error) {
reject(error);
return;
}
resolve(data);
})
});
}
......@@ -6,9 +6,6 @@ import store from './store';
import './index.scss';
import registerServiceWorker from './registerServiceWorker';
window.addEventListener('load', function() {
ReactDOM.render(
<DrizzleProvider options={{
......@@ -20,7 +17,5 @@ window.addEventListener('load', function() {
</DrizzleProvider>
, document.getElementById('root')
);
registerServiceWorker();
});
......@@ -9,7 +9,13 @@ import Header from '../../components/Header';
import CurrencyInputPanel from '../../components/CurrencyInputPanel';
import OversizedPanel from '../../components/OversizedPanel';
import ArrowDown from '../../assets/images/arrow-down-blue.svg';
import { calculateExchangeRateFromInput, calculateExchangeRateFromOutput } from '../../helpers/exchange-utils';
import {
calculateExchangeRateFromInput,
calculateExchangeRateFromOutput,
swapInput,
swapOutput,
} from '../../helpers/exchange-utils';
import promisify from '../../helpers/web3-promisfy';
import "./swap.scss";
......@@ -36,6 +42,30 @@ class Swap extends Component {
exchangeRate: BN(0),
};
componentWillReceiveProps(nextProps) {
this.getExchangeRate(nextProps)
.then(exchangeRate => {
this.setState({ exchangeRate });
if (!exchangeRate) {
return;
}
if (nextProps.lastEditedField === 'input') {
this.props.updateField('output', `${BN(nextProps.input).multipliedBy(exchangeRate).toFixed(7)}`);
} else if (nextProps.lastEditedField === 'output') {
this.props.updateField('input', `${BN(nextProps.output).multipliedBy(BN(1).dividedBy(exchangeRate)).toFixed(7)}`);
}
});
}
componentWillUnmount() {
this.props.updateField('output', '');
this.props.updateField('input', '');
this.props.updateField('outputCurrency', '');
this.props.updateField('inputCurrency', '');
this.props.updateField('lastEditedField', '');
}
getTokenLabel(address) {
if (address === 'ETH') {
return 'ETH';
......@@ -113,41 +143,60 @@ class Swap extends Component {
}) ;
}
componentWillReceiveProps(nextProps) {
this.getExchangeRate(nextProps)
.then(exchangeRate => {
this.setState({ exchangeRate });
if (!exchangeRate) {
return;
}
onSwap = async () => {
const {
input,
output,
inputCurrency,
outputCurrency,
exchangeAddresses,
lastEditedField,
account,
contracts,
} = this.props;
if (nextProps.lastEditedField === 'input') {
this.props.updateField('output', `${BN(nextProps.input).multipliedBy(exchangeRate).toFixed(7)}`);
} else if (nextProps.lastEditedField === 'output') {
this.props.updateField('input', `${BN(nextProps.output).multipliedBy(BN(1).dividedBy(exchangeRate)).toFixed(7)}`);
}
});
}
const { drizzle } = this.context;
componentWillUnmount() {
this.props.updateField('output', '');
this.props.updateField('input', '');
this.props.updateField('outputCurrency', '');
this.props.updateField('inputCurrency', '');
this.props.updateField('lastEditedField', '');
}
if (lastEditedField === 'input') {
swapInput({
drizzleCtx: drizzle,
contractStore: contracts,
input,
output,
inputCurrency,
outputCurrency,
exchangeAddresses,
account,
});
}
onCurrencySelected(field, data) {
this.props.updateField(field, data);
// this.props
}
if (lastEditedField === 'output') {
swapOutput({
drizzleCtx: drizzle,
contractStore: contracts,
input,
output,
inputCurrency,
outputCurrency,
exchangeAddresses,
account,
});
}
// this.context.drizzle.web3.eth.getBlockNumber((_, d) => this.context.drizzle.web3.eth.getBlock(d, (_,d) => {
// const deadline = d.timestamp + 300;
// const id = exchange.methods.ethToTokenSwapInput.cacheSend(`${output * 10 ** 18}`, deadline, {
// from: "0xCf1dE0b4d1e492080336909f70413a5F4E7eEc62",
// value: `${input * 10 ** 18}`,
// }, );
// }));
};
render() {
const { lastEditedField, inputCurrency, outputCurrency, input, output } = this.props;
const { exchangeRate } = this.state;
const inputLabel = this.getTokenLabel(inputCurrency);
const outputLabel = this.getTokenLabel(outputCurrency);
const estimatedText = '(estimated)'
const estimatedText = '(estimated)';
return (
<div className="swap">
......@@ -202,6 +251,7 @@ class Swap extends Component {
className={classnames('swap__cta-btn', {
'swap--inactive': !this.props.isConnected,
})}
onClick={this.onSwap}
>
Swap
</button>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment