Merge pull request #3 from uvilchis/dev
major render refactor, minor logout error
This commit is contained in:
commit
2c7829bbc0
13
package-lock.json
generated
13
package-lock.json
generated
@ -7867,6 +7867,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
|
||||||
"integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0="
|
"integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0="
|
||||||
},
|
},
|
||||||
|
"object-path": {
|
||||||
|
"version": "0.11.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz",
|
||||||
|
"integrity": "sha1-NwrnUvvzfePqcKhhwju6iRVpGUk="
|
||||||
|
},
|
||||||
"object-visit": {
|
"object-visit": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
|
||||||
@ -9998,6 +10003,14 @@
|
|||||||
"symbol-observable": "1.2.0"
|
"symbol-observable": "1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"redux-subscriber": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux-subscriber/-/redux-subscriber-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-PKwopnTOwHtungFcp6q7vawVVUM=",
|
||||||
|
"requires": {
|
||||||
|
"object-path": "0.11.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"redux-thunk": {
|
"redux-thunk": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.2.0.tgz",
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
"react-scroll-to-component": "^1.0.2",
|
"react-scroll-to-component": "^1.0.2",
|
||||||
"react-select": "^1.2.1",
|
"react-select": "^1.2.1",
|
||||||
"redux": "^3.7.2",
|
"redux": "^3.7.2",
|
||||||
|
"redux-subscriber": "^1.1.0",
|
||||||
"redux-thunk": "^2.2.0",
|
"redux-thunk": "^2.2.0",
|
||||||
"web3": "1.0.0-beta.22"
|
"web3": "1.0.0-beta.22"
|
||||||
},
|
},
|
||||||
|
308
src/App.js
308
src/App.js
@ -1,15 +1,22 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Web3 from 'web3';
|
import Web3 from 'web3';
|
||||||
import scrollToComponent from 'react-scroll-to-component';
|
|
||||||
import cookie from 'react-cookies'
|
import cookie from 'react-cookies'
|
||||||
|
|
||||||
import Head from './components/Head'
|
import Head from './components/Head'
|
||||||
import NetworkStatus from './components/NetworkStatus'
|
import NetworkStatus from './components/NetworkStatus'
|
||||||
import ConnectionHelper from './components/ConnectionHelper'
|
import ConnectionHelper from './components/ConnectionHelper'
|
||||||
import Transactions from './components/Transactions'
|
import Transactions from './components/Transactions'
|
||||||
import SelectToken from './components/SelectToken'
|
// import SelectToken from './components/SelectToken'
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
|
// enter the react refactor
|
||||||
|
import About from './components/About';
|
||||||
|
import Links from './components/Links';
|
||||||
|
import Swap from './components/Swap';
|
||||||
|
import Order from './components/Order';
|
||||||
|
import OrderContainer from './containers/OrderContainer';
|
||||||
|
import RateAndFee from './components/RateAndFee';
|
||||||
|
|
||||||
import { exchangeABI } from './helpers/exchangeABI.js'
|
import { exchangeABI } from './helpers/exchangeABI.js'
|
||||||
import { tokenABI } from './helpers/tokenABI.js'
|
import { tokenABI } from './helpers/tokenABI.js'
|
||||||
import { factoryABI } from './helpers/factoryABI.js'
|
import { factoryABI } from './helpers/factoryABI.js'
|
||||||
@ -28,7 +35,7 @@ import {
|
|||||||
setNetworkMessage,
|
setNetworkMessage,
|
||||||
setBlockTimestamp,
|
setBlockTimestamp,
|
||||||
setExchangeType,
|
setExchangeType,
|
||||||
putWeb3InStore
|
initializeGlobalWeb3
|
||||||
} from './actions/web3-actions';
|
} from './actions/web3-actions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -59,6 +66,9 @@ import {
|
|||||||
setExchangeFee
|
setExchangeFee
|
||||||
} from './actions/exchange-actions';
|
} from './actions/exchange-actions';
|
||||||
|
|
||||||
|
// redux-subscribe
|
||||||
|
import { subscribe } from 'redux-subscriber';
|
||||||
|
|
||||||
// enter d3
|
// enter d3
|
||||||
// import Candlesticks from './components/Candlesticks'
|
// import Candlesticks from './components/Candlesticks'
|
||||||
|
|
||||||
@ -78,14 +88,12 @@ class App extends Component {
|
|||||||
swapAdded: false, // cookie stuff
|
swapAdded: false, // cookie stuff
|
||||||
firstRun: true, // cookie stuff
|
firstRun: true, // cookie stuff
|
||||||
transactions: [], // cookie stuff
|
transactions: [], // cookie stuff
|
||||||
about: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO: get rid of redundant localweb3 === 'undefined' checks in componentWill/DidMount
|
||||||
// lets start with what's in the componentDidMount
|
// STATUS: kind of done
|
||||||
componentWillMount(){
|
componentWillMount() {
|
||||||
this.props.putWeb3InStore(localweb3);
|
console.log('props', this.props);
|
||||||
console.log('props', this.props)
|
|
||||||
if(localweb3 === 'undefined') {
|
if(localweb3 === 'undefined') {
|
||||||
this.props.web3ConnectionUnsuccessful();
|
this.props.web3ConnectionUnsuccessful();
|
||||||
} else {
|
} else {
|
||||||
@ -95,63 +103,52 @@ class App extends Component {
|
|||||||
uniAdded: cookie.load('uniAdded') || false,
|
uniAdded: cookie.load('uniAdded') || false,
|
||||||
transactions: cookie.load('transactions') || [],
|
transactions: cookie.load('transactions') || [],
|
||||||
})
|
})
|
||||||
|
// we're working with asynchronous redux
|
||||||
|
this.props.initializeGlobalWeb3(localweb3)
|
||||||
this.getInfoFirstTime();
|
this.getInfoFirstTime();
|
||||||
this.checkNetwork();
|
this.checkNetwork();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount (){
|
componentDidMount (){
|
||||||
if(localweb3 === 'undefined') {
|
// basic format to wrap functions you want firing upon a certain state change
|
||||||
this.props.web3ConnectionUnsuccessful();
|
// eslint-disable-next-line no-unused-vars
|
||||||
this.props.putWeb3InStore(localweb3);
|
const web3Subscriber = subscribe('web3Store.connected', state => {
|
||||||
} else if(this.props.web3Store.connected === true) {
|
if(state.web3Store.connected === true && !state.web3Store.metamaskLocked) {
|
||||||
console.log('successfully connected to metamask');
|
console.log('successfully connected to metamask', state.web3Store.currentMaskAddress);
|
||||||
setInterval(this.getMarketInfo, 15000);
|
setInterval(this.getMarketInfo, 15000);
|
||||||
setInterval(this.getAccountInfo, 15000);
|
setInterval(this.getAccountInfo, 15000);
|
||||||
setInterval(this.getUserAddress, 10000);
|
setInterval(this.getUserAddress, 10000);
|
||||||
} else {
|
} else {
|
||||||
|
console.log('web3 not connected, getting user address')
|
||||||
setInterval(this.getUserAddress, 500);
|
setInterval(this.getUserAddress, 500);
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
// console.log('nextProps', nextProps)
|
//console.log('nextProps', nextProps)
|
||||||
}
|
}
|
||||||
|
// TODO: getInfoFirstTime and getUserAddress are WET af
|
||||||
// getInfoFirstTime = async () => {
|
// lets do something about it
|
||||||
// await this.getUserAddress();
|
|
||||||
// if(this.props.web3Store.currentMaskAddress !== '') {
|
|
||||||
// await this.getContracts();
|
|
||||||
// this.getMarketInfo();
|
|
||||||
// this.getAccountInfo();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
getInfoFirstTime = () => {
|
getInfoFirstTime = () => {
|
||||||
localweb3.eth.getAccounts((error, result) => {
|
localweb3.eth.getAccounts((error, result) => {
|
||||||
console.log('getInfoFirstTime result', result)
|
|
||||||
if(result.length > 0){
|
if(result.length > 0){
|
||||||
// REEEEDUUUUUUXXX
|
|
||||||
this.props.setCurrentMaskAddress(result[0]);
|
this.props.setCurrentMaskAddress(result[0]);
|
||||||
this.props.metamaskUnlocked();
|
this.props.metamaskUnlocked();
|
||||||
this.props.web3ConnectionSuccessful();
|
this.props.web3ConnectionSuccessful();
|
||||||
this.getContracts();
|
this.getContracts();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.props.metamaskLocked();
|
this.props.metamaskLocked();
|
||||||
this.props.web3ConnectionUnsuccessful();
|
this.props.web3ConnectionUnsuccessful();
|
||||||
this.props.setInteractionState('locked');
|
this.props.setInteractionState('locked');
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// fun fact, localweb3.eth.getAccounts will return something even without anything inside
|
||||||
getUserAddress = async () => {
|
getUserAddress = () => {
|
||||||
// kind of redundant
|
localweb3.eth.getAccounts((error, result) => {
|
||||||
// only difference is getInfoFirstTime fires getContracts too
|
if (result.length > 0) {
|
||||||
// THIS FIRES EVERY TEN SECONDS, NEEDS A REFACTOR
|
|
||||||
await localweb3.eth.getAccounts(async (error, result) => {
|
|
||||||
if(result.length > 0) {
|
|
||||||
this.props.setCurrentMaskAddress(result[0]);
|
this.props.setCurrentMaskAddress(result[0]);
|
||||||
this.props.metamaskUnlocked();
|
this.props.metamaskUnlocked();
|
||||||
this.props.web3ConnectionSuccessful();
|
this.props.web3ConnectionSuccessful();
|
||||||
@ -164,32 +161,7 @@ class App extends Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getContracts = async () => {
|
checkNetwork = () => {
|
||||||
const uniExchangeAddress = this.props.web3Store.exchangeAddresses.UNI;
|
|
||||||
const uniExchangeContract = new localweb3.eth.Contract(exchangeABI, uniExchangeAddress);
|
|
||||||
this.props.uniExchangeContractReady(uniExchangeContract);
|
|
||||||
|
|
||||||
const swapExchangeAddress = this.props.web3Store.exchangeAddresses.SWT;
|
|
||||||
const swapExchangeContract = new localweb3.eth.Contract(exchangeABI, swapExchangeAddress);
|
|
||||||
this.props.swtExchangeContractReady(swapExchangeContract);
|
|
||||||
|
|
||||||
const uniTokenAddress = this.props.web3Store.tokenAddresses.UNI;
|
|
||||||
const uniTokenContract = new localweb3.eth.Contract(tokenABI, uniTokenAddress);
|
|
||||||
this.props.uniTokenContractReady(uniTokenContract);
|
|
||||||
|
|
||||||
const swapTokenAddress = this.props.web3Store.tokenAddresses.SWT;
|
|
||||||
const swapTokenContract = new localweb3.eth.Contract(tokenABI, swapTokenAddress);
|
|
||||||
this.props.swtTokenContractReady(swapTokenContract);
|
|
||||||
|
|
||||||
const factoryAddress = this.props.web3Store.factoryAddress;
|
|
||||||
const factoryContract = new localweb3.eth.Contract(factoryABI, factoryAddress);
|
|
||||||
this.props.factoryContractReady(factoryContract);
|
|
||||||
|
|
||||||
this.getAccountInfo();
|
|
||||||
this.getMarketInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
checkNetwork() {
|
|
||||||
localweb3.eth.net.getNetworkType((err, networkId) => {
|
localweb3.eth.net.getNetworkType((err, networkId) => {
|
||||||
console.log("Connected to " + networkId)
|
console.log("Connected to " + networkId)
|
||||||
switch (networkId) {
|
switch (networkId) {
|
||||||
@ -226,11 +198,42 @@ class App extends Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// will require rewriting the connectionStatus reducer to take in true or falsa
|
||||||
|
// connectionSetter = (networkMessage, connectionStatus, interactionState) => {
|
||||||
|
// this.props.setNetworkMessage(networkMessage)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// could possibly use refactoring
|
||||||
|
getContracts = () => {
|
||||||
|
const uniExchangeAddress = this.props.web3Store.exchangeAddresses.UNI;
|
||||||
|
const uniExchangeContract = new localweb3.eth.Contract(exchangeABI, uniExchangeAddress);
|
||||||
|
this.props.uniExchangeContractReady(uniExchangeContract);
|
||||||
|
|
||||||
|
const swapExchangeAddress = this.props.web3Store.exchangeAddresses.SWT;
|
||||||
|
const swapExchangeContract = new localweb3.eth.Contract(exchangeABI, swapExchangeAddress);
|
||||||
|
this.props.swtExchangeContractReady(swapExchangeContract);
|
||||||
|
|
||||||
|
const uniTokenAddress = this.props.web3Store.tokenAddresses.UNI;
|
||||||
|
const uniTokenContract = new localweb3.eth.Contract(tokenABI, uniTokenAddress);
|
||||||
|
this.props.uniTokenContractReady(uniTokenContract);
|
||||||
|
|
||||||
|
const swapTokenAddress = this.props.web3Store.tokenAddresses.SWT;
|
||||||
|
const swapTokenContract = new localweb3.eth.Contract(tokenABI, swapTokenAddress);
|
||||||
|
this.props.swtTokenContractReady(swapTokenContract);
|
||||||
|
|
||||||
|
const factoryAddress = this.props.web3Store.factoryAddress;
|
||||||
|
const factoryContract = new localweb3.eth.Contract(factoryABI, factoryAddress);
|
||||||
|
this.props.factoryContractReady(factoryContract);
|
||||||
|
|
||||||
|
this.getAccountInfo();
|
||||||
|
this.getMarketInfo();
|
||||||
|
}
|
||||||
|
|
||||||
symbolToTokenAddress = (symbol) => {
|
symbolToTokenAddress = (symbol) => {
|
||||||
if(symbol === 'UNI') {
|
if(symbol === 'UNI') {
|
||||||
return this.props.web3Store.exchangeAddresses.UNI;
|
return this.props.web3Store.tokenAddresses.UNI;
|
||||||
} else if (symbol === 'SWAP') {
|
} else if (symbol === 'SWAP') {
|
||||||
return this.props.web3Store.exchangeAddresses.SWT;
|
return this.props.web3Store.tokenAddresses.SWT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +276,9 @@ class App extends Component {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// this quadruplet of functions will end up being shared amongst multiple components
|
||||||
|
// will need to bring this out into a higher order component (we'll put that to the side for now)
|
||||||
|
// TODO: multiple components currently need this function, we will pass it to them via props
|
||||||
getAccountInfo = () => {
|
getAccountInfo = () => {
|
||||||
switch (this.props.web3Store.exchangeType) {
|
switch (this.props.web3Store.exchangeType) {
|
||||||
case 'ETH to Token':
|
case 'ETH to Token':
|
||||||
@ -398,73 +403,13 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: stuff
|
|
||||||
tokenToExchangeFactoryLookup = (tokenAddress) => {
|
tokenToExchangeFactoryLookup = (tokenAddress) => {
|
||||||
this.props.web3Store.factoryContract.methods.tokenToExchangeLookup(tokenAddress).call((error, exchangeAddress) => {
|
this.props.web3Store.factoryContract.methods.tokenToExchangeLookup(tokenAddress).call((error, exchangeAddress) => {
|
||||||
console.log(exchangeAddress)
|
console.log(exchangeAddress)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectToken = async (selected, type) => {
|
|
||||||
this.props.setExchangeInputValue(0);
|
|
||||||
this.props.setExchangeOutputValue(0);
|
|
||||||
this.props.setExchangeRate(0);
|
|
||||||
this.props.setExchangeFee(0);
|
|
||||||
this.props.setInteractionState('connected');
|
|
||||||
this.setState({ firstRun: true })
|
|
||||||
|
|
||||||
if (type === 'input') {
|
|
||||||
await this.props.setInputToken(selected);
|
|
||||||
} else if (type === 'output'){
|
|
||||||
await this.props.setOutputToken(selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.getMarketType();
|
|
||||||
this.getAccountInfo();
|
|
||||||
this.getMarketInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
getMarketType = () => {
|
|
||||||
var marketType = '';
|
|
||||||
|
|
||||||
if (this.props.exchange.inputToken.value === this.props.exchange.outputToken.value) {
|
|
||||||
marketType = 'Invalid';
|
|
||||||
this.props.setInteractionState('error1');
|
|
||||||
} else if (this.props.exchange.inputToken.value === 'ETH'){
|
|
||||||
marketType = 'ETH to Token';
|
|
||||||
} else if (this.props.exchange.outputToken.value === 'ETH'){
|
|
||||||
marketType = 'Token to ETH';
|
|
||||||
} else{
|
|
||||||
marketType = 'Token to Token';
|
|
||||||
}
|
|
||||||
this.props.setExchangeType(marketType);
|
|
||||||
console.log('type: ', marketType);
|
|
||||||
console.log('input: ', this.props.exchange.inputToken.value);
|
|
||||||
console.log('output: ', this.props.exchange.outputToken.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
onInputChange = async (event) => {
|
|
||||||
var inputValue = event.target.value;
|
|
||||||
await this.props.setExchangeInputValue(inputValue);
|
|
||||||
this.setExchangeOutput();
|
|
||||||
}
|
|
||||||
|
|
||||||
setExchangeOutput = () => {
|
|
||||||
var inputValue = this.props.exchange.inputValue;
|
|
||||||
if (this.props.web3Store.exchangeType === 'Invalid'){
|
|
||||||
this.props.setExchangeOutputValue(0);
|
|
||||||
this.props.setInteractionState('error1');
|
|
||||||
} else if(inputValue && inputValue !== 0 && inputValue !== '0'){
|
|
||||||
this.props.setInteractionState('input');
|
|
||||||
this.getExchangeRate(inputValue);
|
|
||||||
} else {
|
|
||||||
this.props.setExchangeOutputValue(0);
|
|
||||||
this.props.setInteractionState('connected');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getExchangeRate = (input) => {
|
getExchangeRate = (input) => {
|
||||||
console.log(this.props.web3Store.globalWeb3, 'this better be defined')
|
|
||||||
if (this.props.web3Store.exchangeType === 'ETH to Token') {
|
if (this.props.web3Store.exchangeType === 'ETH to Token') {
|
||||||
console.log('Getting Rate: ETH to ' + this.props.exchange.outputToken.value);
|
console.log('Getting Rate: ETH to ' + this.props.exchange.outputToken.value);
|
||||||
this.ethToTokenRate(input);
|
this.ethToTokenRate(input);
|
||||||
@ -618,6 +563,10 @@ class App extends Component {
|
|||||||
var tokensSold = this.props.exchange.inputValue;
|
var tokensSold = this.props.exchange.inputValue;
|
||||||
var tokensSoldInt = localweb3.utils.toWei(tokensSold);
|
var tokensSoldInt = localweb3.utils.toWei(tokensSold);
|
||||||
var timeout = this.props.web3Store.blockTimestamp + 300; //current block time + 5mins
|
var timeout = this.props.web3Store.blockTimestamp + 300; //current block time + 5mins
|
||||||
|
console.log('tokenOutAddress', tokenOutAddress);
|
||||||
|
console.log('minTokensInt', minTokensInt);
|
||||||
|
console.log('tokensSoldInt', tokensSoldInt);
|
||||||
|
console.log('timeout', timeout);
|
||||||
|
|
||||||
exchange.methods.tokenToTokenSwap(tokenOutAddress, tokensSoldInt, minTokensInt, timeout).send({from: this.props.web3Store.currentMaskAddress})
|
exchange.methods.tokenToTokenSwap(tokenOutAddress, tokensSoldInt, minTokensInt, timeout).send({from: this.props.web3Store.currentMaskAddress})
|
||||||
.on('transactionHash', (result) => {
|
.on('transactionHash', (result) => {
|
||||||
@ -648,16 +597,6 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleAbout = () => {
|
|
||||||
|
|
||||||
this.setState({about: !this.state.about})
|
|
||||||
setTimeout(this.scrollToAbout, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollToAbout = () => {
|
|
||||||
scrollToComponent(this.About, { offset: 0, align: 'top', duration: 500})
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className={this.props.web3Store.connected && !this.props.web3Store.metamaskLocked && this.props.web3Store.interaction !== 'disconnected' ? "App" : "App dim"}>
|
<div className={this.props.web3Store.connected && !this.props.web3Store.metamaskLocked && this.props.web3Store.interaction !== 'disconnected' ? "App" : "App dim"}>
|
||||||
@ -673,7 +612,8 @@ class App extends Component {
|
|||||||
interaction={this.props.web3Store.interaction}
|
interaction={this.props.web3Store.interaction}
|
||||||
address={this.props.web3Store.currentMaskAddress}
|
address={this.props.web3Store.currentMaskAddress}
|
||||||
locked={this.props.web3Store.metamaskLocked}
|
locked={this.props.web3Store.metamaskLocked}
|
||||||
balance={this.props.exchange.inputBalance}/>
|
balance={this.props.exchange.inputBalance}
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
<ConnectionHelper
|
<ConnectionHelper
|
||||||
network={this.props.web3Store.networkMessage}
|
network={this.props.web3Store.networkMessage}
|
||||||
@ -697,74 +637,30 @@ class App extends Component {
|
|||||||
outputToken={this.props.exchange.outputToken}
|
outputToken={this.props.exchange.outputToken}
|
||||||
about={this.state.about}
|
about={this.state.about}
|
||||||
/>
|
/>
|
||||||
<section className="order">
|
<Order
|
||||||
<div className="value border pa2">
|
getAccountInfo={this.getAccountInfo}
|
||||||
<input type="number" value={this.props.exchange.inputValue} placeholder="0" onChange={this.onInputChange} />
|
getMarketInfo={this.getMarketInfo}
|
||||||
<SelectToken token={this.props.exchange.inputToken} onSelectToken={this.onSelectToken} type="input" />
|
getExchangeRate={this.getExchangeRate}
|
||||||
<p className="dropdown">{'<'}</p>
|
symbolToTokenContract={this.symbolToTokenContract}
|
||||||
</div>
|
symbolToExchangeAddress={this.symbolToExchangeAddress}
|
||||||
<div className="arrow border pa2">
|
/>
|
||||||
<p>→</p>
|
<RateAndFee
|
||||||
</div>
|
exchangeRate={this.props.exchange.rate}
|
||||||
<div className="value border pa2">
|
outputTokenValue={this.props.exchange.outputToken.value}
|
||||||
<input type="number" readOnly={true} value={(this.props.exchange.outputValue/10**18).toFixed(5)} placeholder="0"/>
|
inputTokenValue={this.props.exchange.inputToken.value}
|
||||||
<SelectToken token={this.props.exchange.outputToken} onSelectToken={this.onSelectToken} type="output"/>
|
exchangeFee={this.props.exchange.fee}
|
||||||
<p className="dropdown">{'<'}</p>
|
/>
|
||||||
</div>
|
<Swap
|
||||||
</section>
|
interaction={this.props.web3Store.interaction}
|
||||||
<section className="rate border pa2">
|
inputValue={this.props.exchange.inputValue }
|
||||||
<span className="rate-info">
|
inputTokenValue={this.props.exchange.inputToken.value}
|
||||||
<p>Rate</p>
|
outputValue={this.props.exchange.outputValue}
|
||||||
<p>{(this.props.exchange.rate).toFixed(5)} {this.props.exchange.outputToken.value + "/" + this.props.exchange.inputToken.value}</p>
|
outputTokenValue={this.props.exchange.outputToken.value}
|
||||||
</span>
|
purchaseTokens={this.purchaseTokens}
|
||||||
<span className="rate-info">
|
/>
|
||||||
<p>Fee</p>
|
<About />
|
||||||
<p>{(this.props.exchange.fee/10**18).toFixed(5)} {this.props.exchange.inputToken.value}</p>
|
<Links />
|
||||||
</span>
|
<Transactions transactions={this.state.transactions} interaction={this.props.web3Store.interaction} />
|
||||||
</section>
|
|
||||||
|
|
||||||
{this.props.web3Store.interaction === 'input' ?
|
|
||||||
<a className="swap border pa2" role="button" onClick={() => {this.purchaseTokens()}}>
|
|
||||||
|
|
||||||
<b>{"I want to swap " + this.props.exchange.inputValue + " " + this.props.exchange.inputToken.value + " for " + this.props.exchange.outputValue/10**18 + " " + this.props.exchange.outputToken.value}</b>
|
|
||||||
</a>
|
|
||||||
: <a className="swap grey-bg hidden border pa2"></a>}
|
|
||||||
|
|
||||||
<section className="About" ref={(section) => { this.About = section; }}>
|
|
||||||
<a onClick={() => {this.toggleAbout()}} className="link border pa2 f-a">
|
|
||||||
<p className="underline">About Uniswap.</p>
|
|
||||||
<p>↘</p>
|
|
||||||
</a>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{this.state.about ?
|
|
||||||
<section className="expand grey-bg border pa2">
|
|
||||||
<p>Uniswap is a trustless, decentralized exchange for Ether and ERC20 tokens. It uses a market maker mechanism, where liquidity providers invest a reserve of ETH and a single ERC20 token within an Ethereum smart contract. An exchange rate is set between the tokens and ETH based on the relative availibility of each token. A small transaction fee is payed to the liquidity providers proportional to their investment.</p>
|
|
||||||
<p>There is a separate contract for each ETH-ERC20 pair. These contracts can "tunnel" between each other for direct ERC20-ERC20 trades. Only one exchange can exist per token, and anyone can contribute liquidity to any exchange. A factory/registry contract provides a public interface for creating new Uniswap exchanges, and looking up the exchange associated a given token address.</p>
|
|
||||||
<p>A full writeup will be available soon. Until then, here is some more info on Market Makers:</p>
|
|
||||||
<p>Please reach out if you would like to get involved or support the project.</p>
|
|
||||||
<p>Email: <a href="mailto:hayden@uniswap.io">hayden@uniswap.io</a></p>
|
|
||||||
</section>
|
|
||||||
: <section className="expand grey-bg border pa2 hidden"> </section>
|
|
||||||
}
|
|
||||||
|
|
||||||
<section className="links">
|
|
||||||
<a href="" className="link border pa2 liq">
|
|
||||||
<p className="underline">Provide liquidity to collect fees</p>
|
|
||||||
<p>+</p>
|
|
||||||
</a>
|
|
||||||
<a href="" className="link border pa2 ex">
|
|
||||||
<p className="underline">Launch a new exchange</p>
|
|
||||||
<p>+</p>
|
|
||||||
</a>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{this.state.transactions.length > 0 && this.props.web3Store.interaction !== 'disconnected' ?
|
|
||||||
<section className="transaction border pa2">
|
|
||||||
<p className="underline">Past Transactions:</p>
|
|
||||||
<Transactions transactions={this.state.transactions}/>
|
|
||||||
</section>
|
|
||||||
: <section className="hidden border pa2"></section>}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -808,7 +704,7 @@ const mapDispatchToProps = (dispatch) => {
|
|||||||
setExchangeOutputValue,
|
setExchangeOutputValue,
|
||||||
setExchangeRate,
|
setExchangeRate,
|
||||||
setExchangeFee,
|
setExchangeFee,
|
||||||
putWeb3InStore
|
initializeGlobalWeb3
|
||||||
}, dispatch)
|
}, dispatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
SET_NETWORK_MESSAGE,
|
SET_NETWORK_MESSAGE,
|
||||||
SET_BLOCK_TIMESTAMP,
|
SET_BLOCK_TIMESTAMP,
|
||||||
SET_EXCHANGE_TYPE,
|
SET_EXCHANGE_TYPE,
|
||||||
PUT_WEB3_IN_STORE
|
INITIALIZE_GLOBAL_WEB3
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
|
|
||||||
// this actions folder is actually full of action creators
|
// this actions folder is actually full of action creators
|
||||||
@ -55,8 +55,9 @@ export const setNetworkMessage = (networkMessage) => ({
|
|||||||
networkMessage
|
networkMessage
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setBlockTimestamp = (globalWeb3) => {
|
export const setBlockTimestamp = () => {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
|
const { globalWeb3 } = getState().web3Store
|
||||||
await globalWeb3.eth.getBlock('latest', (error, blockInfo) => {
|
await globalWeb3.eth.getBlock('latest', (error, blockInfo) => {
|
||||||
console.log(blockInfo.timestamp, 'BLOCKTIMESTAMP');
|
console.log(blockInfo.timestamp, 'BLOCKTIMESTAMP');
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -72,7 +73,7 @@ export const setExchangeType = (exchangeType) => ({
|
|||||||
exchangeType
|
exchangeType
|
||||||
});
|
});
|
||||||
|
|
||||||
export const putWeb3InStore = (globalWeb3) => ({
|
export const initializeGlobalWeb3 = (globalWeb3) => ({
|
||||||
type: PUT_WEB3_IN_STORE,
|
type: INITIALIZE_GLOBAL_WEB3,
|
||||||
globalWeb3
|
globalWeb3
|
||||||
})
|
})
|
||||||
|
34
src/components/About.js
Normal file
34
src/components/About.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import AboutMessage from './AboutMessage';
|
||||||
|
import scrollToComponent from 'react-scroll-to-component';
|
||||||
|
|
||||||
|
export default class About extends Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
this.state = { toggled: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAbout = () => {
|
||||||
|
this.setState({toggled: !this.state.toggled})
|
||||||
|
setTimeout(this.scrollToAbout, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToAbout = () => {
|
||||||
|
scrollToComponent(this.About, { offset: 0, align: 'top', duration: 500})
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { toggled } = this.state;
|
||||||
|
return(
|
||||||
|
<div>
|
||||||
|
<section className="About" ref={(section) => { this.About = section; }}>
|
||||||
|
<a onClick={() => {this.toggleAbout()}} className="link border pa2 f-a">
|
||||||
|
<p className="underline">About Uniswap.</p>
|
||||||
|
<p>↘</p>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
<AboutMessage toggled={toggled} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
19
src/components/AboutMessage.js
Normal file
19
src/components/AboutMessage.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
function AboutMessage ({ toggled }) {
|
||||||
|
if (toggled === true) {
|
||||||
|
return (
|
||||||
|
<section className="expand grey-bg border pa2">
|
||||||
|
<p>Uniswap is a trustless, decentralized exchange for Ether and ERC20 tokens. It uses a market maker mechanism, where liquidity providers invest a reserve of ETH and a single ERC20 token within an Ethereum smart contract. An exchange rate is set between the tokens and ETH based on the relative availibility of each token. A small transaction fee is payed to the liquidity providers proportional to their investment.</p>
|
||||||
|
<p>There is a separate contract for each ETH-ERC20 pair. These contracts can "tunnel" between each other for direct ERC20-ERC20 trades. Only one exchange can exist per token, and anyone can contribute liquidity to any exchange. A factory/registry contract provides a public interface for creating new Uniswap exchanges, and looking up the exchange associated a given token address.</p>
|
||||||
|
<p>A full writeup will be available soon. Until then, here is some more info on Market Makers:</p>
|
||||||
|
<p>Please reach out if you would like to get involved or support the project.</p>
|
||||||
|
<p>Email: <a href="mailto:hayden@uniswap.io">hayden@uniswap.io</a></p>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (<section className="expand grey-bg border pa2 hidden"></section>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AboutMessage;
|
18
src/components/Links.js
Normal file
18
src/components/Links.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function Links () {
|
||||||
|
return (
|
||||||
|
<section className="links">
|
||||||
|
<a href="" className="link border pa2 liq">
|
||||||
|
<p className="underline">Provide liquidity to collect fees</p>
|
||||||
|
<p>+</p>
|
||||||
|
</a>
|
||||||
|
<a href="" className="link border pa2 ex">
|
||||||
|
<p className="underline">Launch a new exchange</p>
|
||||||
|
<p>+</p>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Links;
|
205
src/components/Order.js
Normal file
205
src/components/Order.js
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
import React, { Component }from 'react';
|
||||||
|
import SelectToken from './SelectToken';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { subscribe } from 'redux-subscriber';
|
||||||
|
import { setInteractionState, setExchangeType } from '../actions/web3-actions';
|
||||||
|
import {
|
||||||
|
setExchangeInputValue,
|
||||||
|
setExchangeOutputValue,
|
||||||
|
setExchangeRate,
|
||||||
|
setExchangeFee,
|
||||||
|
setInputToken,
|
||||||
|
setOutputToken,
|
||||||
|
setInputBalance,
|
||||||
|
setOutputBalance,
|
||||||
|
setAllowanceApprovalState
|
||||||
|
} from '../actions/exchange-actions';
|
||||||
|
|
||||||
|
class Order extends Component {
|
||||||
|
constructor (props){
|
||||||
|
super(props)
|
||||||
|
}
|
||||||
|
// props and functions ready
|
||||||
|
onInputChange = async (event) => {
|
||||||
|
var inputValue = event.target.value;
|
||||||
|
await this.props.setExchangeInputValue(inputValue);
|
||||||
|
this.setExchangeOutput();
|
||||||
|
}
|
||||||
|
// props ready,
|
||||||
|
onSelectToken = async (selected, type) => {
|
||||||
|
this.props.setExchangeInputValue(0);
|
||||||
|
this.props.setExchangeOutputValue(0);
|
||||||
|
this.props.setExchangeRate(0);
|
||||||
|
this.props.setExchangeFee(0);
|
||||||
|
this.props.setInteractionState('connected');
|
||||||
|
// what the flip does this do
|
||||||
|
// this.setState({ firstRun: true })
|
||||||
|
|
||||||
|
if (type === 'input') {
|
||||||
|
await this.props.setInputToken(selected);
|
||||||
|
} else if (type === 'output'){
|
||||||
|
await this.props.setOutputToken(selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.getMarketType();
|
||||||
|
// these two functions are actually being passed from the parent component, because they're used in multiple places
|
||||||
|
// eventually pull these out into HOC
|
||||||
|
this.props.getAccountInfo();
|
||||||
|
this.props.getMarketInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
setExchangeOutput = () => {
|
||||||
|
var inputValue = this.props.exchange.inputValue;
|
||||||
|
if (this.props.web3Store.exchangeType === 'Invalid'){
|
||||||
|
this.props.setExchangeOutputValue(0);
|
||||||
|
this.props.setInteractionState('error1');
|
||||||
|
} else if(inputValue && inputValue !== 0 && inputValue !== '0'){
|
||||||
|
this.props.setInteractionState('input');
|
||||||
|
// another function to be pulled out into HOC
|
||||||
|
this.props.getExchangeRate(inputValue);
|
||||||
|
} else {
|
||||||
|
this.props.setExchangeOutputValue(0);
|
||||||
|
this.props.setInteractionState('connected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// props ready
|
||||||
|
// TODO: change this to use the redux-subscribe pattern
|
||||||
|
getMarketType = () => {
|
||||||
|
var marketType = '';
|
||||||
|
if (this.props.exchange.inputToken.value === this.props.exchange.outputToken.value) {
|
||||||
|
marketType = 'Invalid';
|
||||||
|
this.props.setInteractionState('error1');
|
||||||
|
} else if (this.props.exchange.inputToken.value === 'ETH'){
|
||||||
|
marketType = 'ETH to Token';
|
||||||
|
} else if (this.props.exchange.outputToken.value === 'ETH'){
|
||||||
|
marketType = 'Token to ETH';
|
||||||
|
} else{
|
||||||
|
marketType = 'Token to Token';
|
||||||
|
}
|
||||||
|
this.props.setExchangeType(marketType);
|
||||||
|
console.log('type: ', marketType);
|
||||||
|
console.log('input: ', this.props.exchange.inputToken.value);
|
||||||
|
console.log('output: ', this.props.exchange.outputToken.value);
|
||||||
|
}
|
||||||
|
// we are here
|
||||||
|
// TODO: change this to use the redux-subscribe pattern
|
||||||
|
getAccountInfo = () => {
|
||||||
|
switch (this.props.web3Store.exchangeType) {
|
||||||
|
case 'ETH to Token':
|
||||||
|
this.getEthBalance('input');
|
||||||
|
this.getTokenBalance('output');
|
||||||
|
break;
|
||||||
|
case 'Token to ETH':
|
||||||
|
this.getEthBalance('output');
|
||||||
|
this.getTokenBalance('input');
|
||||||
|
this.getAllowance();
|
||||||
|
break;
|
||||||
|
case 'Token to Token':
|
||||||
|
this.getTokenBalance('input');
|
||||||
|
this.getTokenBalance('output');
|
||||||
|
this.getAllowance();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
console.log("Getting account info");
|
||||||
|
}
|
||||||
|
|
||||||
|
// props ready
|
||||||
|
// TODO: TODO: TODO: TURN THIS INTO A REDUX-SUBSCRIBE LISTENER NOW!!!
|
||||||
|
getEthBalance = (type) => {
|
||||||
|
// this.props.web3Store.globalWeb3
|
||||||
|
if (type === 'input') {
|
||||||
|
this.props.web3Store.globalWeb3.eth.getBalance(this.props.web3Store.currentMaskAddress, (error, balance) => {
|
||||||
|
this.props.setInputBalance(balance);
|
||||||
|
// console.log('ETH Balance: ' + balance);
|
||||||
|
});
|
||||||
|
} else if (type === 'output') {
|
||||||
|
this.props.web3Store.globalWeb3.eth.getBalance(this.props.web3Store.currentMaskAddress, (error, balance) => {
|
||||||
|
this.props.setOutputBalance(balance);
|
||||||
|
// console.log('ETH Balance: ' + balance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// props ready
|
||||||
|
// TODO: this might also be able to change to the redux-subscribe method
|
||||||
|
getTokenBalance = (type) => {
|
||||||
|
var token;
|
||||||
|
if (type === 'input') {
|
||||||
|
token = this.symbolToTokenContract(this.props.exchange.inputToken.value);
|
||||||
|
token.methods.balanceOf(this.props.web3Store.currentMaskAddress).call((error, balance) => {
|
||||||
|
this.props.setInputBalance(balance);
|
||||||
|
// console.log(this.props.exchange.inputToken.value + ' Balance: ' + balance);
|
||||||
|
});
|
||||||
|
} else if (type === 'output') {
|
||||||
|
token = this.symbolToTokenContract(this.props.exchange.outputToken.value);
|
||||||
|
token.methods.balanceOf(this.props.web3Store.currentMaskAddress).call((error, balance) => {
|
||||||
|
this.props.setOutputBalance(balance);
|
||||||
|
// console.log(this.props.exchange.outputToken.value + ' Balance: ' + balance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: refactor to redux-subscribe
|
||||||
|
// props ready
|
||||||
|
getAllowance = () => {
|
||||||
|
var type = this.props.web3Store.exchangeType;
|
||||||
|
if(type === 'Token to ETH' || type === 'Token to Token') {
|
||||||
|
// another pair of functions to be exported to a HOC
|
||||||
|
var token = this.props.symbolToTokenContract(this.props.exchange.inputToken.value);
|
||||||
|
var exchangeAddress = this.props.symbolToExchangeAddress(this.props.exchange.inputToken.value);
|
||||||
|
|
||||||
|
token.methods.allowance(this.props.web3Store.currentMaskAddress, exchangeAddress).call().then((result, error) => {
|
||||||
|
console.log(this.props.exchange.inputToken.value + ' allowance: ' + result);
|
||||||
|
if(result === '0'){
|
||||||
|
this.props.setAllowanceApprovalState(false)
|
||||||
|
console.log(this.props.exchange.allowanceApproved)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<section className="order">
|
||||||
|
<div className="value border pa2">
|
||||||
|
<input type="number" value={this.props.exchange.inputValue} placeholder="0" onChange={this.onInputChange} />
|
||||||
|
<SelectToken token={this.props.exchange.inputToken} onSelectToken={this.onSelectToken} type="input" />
|
||||||
|
<p className="dropdown">{'<'}</p>
|
||||||
|
</div>
|
||||||
|
<div className="arrow border pa2">
|
||||||
|
<p>→</p>
|
||||||
|
</div>
|
||||||
|
<div className="value border pa2">
|
||||||
|
<input type="number" readOnly={true} value={(this.props.exchange.outputValue/10**18).toFixed(5)} placeholder="0"/>
|
||||||
|
<SelectToken token={this.props.exchange.outputToken} onSelectToken={this.onSelectToken} type="output"/>
|
||||||
|
<p className="dropdown">{'<'}</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
web3Store: state.web3Store,
|
||||||
|
exchange: state.exchange
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => {
|
||||||
|
return bindActionCreators({
|
||||||
|
setExchangeInputValue,
|
||||||
|
setExchangeOutputValue,
|
||||||
|
setExchangeRate,
|
||||||
|
setExchangeFee,
|
||||||
|
setInteractionState,
|
||||||
|
setInputToken,
|
||||||
|
setOutputToken,
|
||||||
|
setExchangeType,
|
||||||
|
setInputBalance,
|
||||||
|
setOutputBalance,
|
||||||
|
setAllowanceApprovalState
|
||||||
|
}, dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(Order);
|
18
src/components/RateAndFee.js
Normal file
18
src/components/RateAndFee.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function RateAndFee ({ exchangeRate, outputTokenValue, inputTokenValue, exchangeFee }) {
|
||||||
|
return(
|
||||||
|
<section className="rate border pa2">
|
||||||
|
<span className="rate-info">
|
||||||
|
<p>Rate</p>
|
||||||
|
<p>{(exchangeRate).toFixed(5)} {outputTokenValue + "/" + inputTokenValue}</p>
|
||||||
|
</span>
|
||||||
|
<span className="rate-info">
|
||||||
|
<p>Fee</p>
|
||||||
|
<p>{(exchangeFee/10**18).toFixed(5)} {inputTokenValue}</p>
|
||||||
|
</span>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RateAndFee;
|
16
src/components/Swap.js
Normal file
16
src/components/Swap.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function Swap ({ interaction, inputValue, inputTokenValue, outputValue, outputTokenValue, purchaseTokens }) {
|
||||||
|
if (interaction === 'input') {
|
||||||
|
return (
|
||||||
|
<a className="swap border pa2" role="button" onClick={() => {purchaseTokens()}}>
|
||||||
|
<b>{"I want to swap " + inputValue + " " + inputTokenValue + " for " + outputValue/10**18 + " " + outputTokenValue}</b>
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (<a className="swap grey-bg hidden border pa2"></a>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Swap;
|
||||||
|
|
23
src/components/Title.js
Normal file
23
src/components/Title.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// import React from 'react';
|
||||||
|
// import NetworkStatus from '../components/NetworkStatus';
|
||||||
|
|
||||||
|
// function Title (){
|
||||||
|
// return (
|
||||||
|
// <section className="title">
|
||||||
|
// <div className="logo border pa2">
|
||||||
|
// <span role="img" aria-label="Unicorn">🦄</span>
|
||||||
|
// </div>
|
||||||
|
// <NetworkStatus
|
||||||
|
// network={this.props.web3Store.networkMessage}
|
||||||
|
// connected={this.props.web3Store.connected}
|
||||||
|
// metamask={this.props.metamask}
|
||||||
|
// interaction={this.props.web3Store.interaction}
|
||||||
|
// address={this.props.web3Store.currentMaskAddress}
|
||||||
|
// locked={this.props.web3Store.metamaskLocked}
|
||||||
|
// balance={this.props.exchange.inputBalance}
|
||||||
|
// />
|
||||||
|
// </section>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export default Title;
|
@ -1,7 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
function Transactions(props) {
|
function Transactions(props) {
|
||||||
|
if (props.transactions.length > 0 && props.interaction !== 'disconnected') {
|
||||||
return (
|
return (
|
||||||
|
<section className="transaction border pa2">
|
||||||
|
<p className="underline">Past Transactions:</p>
|
||||||
<div className="connection">
|
<div className="connection">
|
||||||
<ol>
|
<ol>
|
||||||
{props.transactions.map((t) =>
|
{props.transactions.map((t) =>
|
||||||
@ -9,7 +12,11 @@ function Transactions(props) {
|
|||||||
)}
|
)}
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
return (<section className="hidden border pa2"></section>)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Transactions;
|
export default Transactions;
|
||||||
|
@ -44,4 +44,4 @@ export const SET_EXCHANGE_RATE = 'SET_EXCHANGE_RATE';
|
|||||||
export const SET_EXCHANGE_FEE = 'SET_EXCHANGE_FEE';
|
export const SET_EXCHANGE_FEE = 'SET_EXCHANGE_FEE';
|
||||||
|
|
||||||
// test setInteractionState
|
// test setInteractionState
|
||||||
export const PUT_WEB3_IN_STORE = 'PUT_WEB3_IN_STORE';
|
export const INITIALIZE_GLOBAL_WEB3 = 'INITIALIZE_GLOBAL_WEB3';
|
||||||
|
213
src/containers/OrderContainer.js
Normal file
213
src/containers/OrderContainer.js
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import Order from '../components/Order';
|
||||||
|
import { setInteractionState, setExchangeType } from '../actions/web3-actions';
|
||||||
|
import {
|
||||||
|
setExchangeInputValue,
|
||||||
|
setExchangeOutputValue,
|
||||||
|
setExchangeRate,
|
||||||
|
setExchangeFee,
|
||||||
|
setInputToken,
|
||||||
|
setOutputToken,
|
||||||
|
setInputBalance,
|
||||||
|
setOutputBalance,
|
||||||
|
setAllowanceApprovalState
|
||||||
|
} from '../actions/exchange-actions';
|
||||||
|
|
||||||
|
function withOrderFunctionality (WrappedComponent) {
|
||||||
|
return class extends Component {
|
||||||
|
constructor (props){
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
// props and functions ready
|
||||||
|
onInputChange = async (event) => {
|
||||||
|
var inputValue = event.target.value;
|
||||||
|
await this.props.setExchangeInputValue(inputValue);
|
||||||
|
this.setExchangeOutput();
|
||||||
|
}
|
||||||
|
// props ready,
|
||||||
|
onSelectToken = async (selected, type) => {
|
||||||
|
this.props.setExchangeInputValue(0);
|
||||||
|
this.props.setExchangeOutputValue(0);
|
||||||
|
this.props.setExchangeRate(0);
|
||||||
|
this.props.setExchangeFee(0);
|
||||||
|
this.props.setInteractionState('connected');
|
||||||
|
// what the flip does this do
|
||||||
|
//this.setState({ firstRun: true })
|
||||||
|
|
||||||
|
if (type === 'input') {
|
||||||
|
await this.props.setInputToken(selected);
|
||||||
|
} else if (type === 'output'){
|
||||||
|
await this.props.setOutputToken(selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.getMarketType();
|
||||||
|
this.getAccountInfo();
|
||||||
|
this.getMarketInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
setExchangeOutput = () => {
|
||||||
|
var inputValue = this.props.exchange.inputValue;
|
||||||
|
if (this.props.web3Store.exchangeType === 'Invalid'){
|
||||||
|
this.props.setExchangeOutputValue(0);
|
||||||
|
this.props.setInteractionState('error1');
|
||||||
|
} else if(inputValue && inputValue !== 0 && inputValue !== '0'){
|
||||||
|
this.props.setInteractionState('input');
|
||||||
|
this.getExchangeRate(inputValue);
|
||||||
|
} else {
|
||||||
|
this.props.setExchangeOutputValue(0);
|
||||||
|
this.props.setInteractionState('connected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// props ready
|
||||||
|
// TODO: change this to use the redux-subscribe pattern
|
||||||
|
getMarketType = () => {
|
||||||
|
var marketType = '';
|
||||||
|
if (this.props.exchange.inputToken.value === this.props.exchange.outputToken.value) {
|
||||||
|
marketType = 'Invalid';
|
||||||
|
this.props.setInteractionState('error1');
|
||||||
|
} else if (this.props.exchange.inputToken.value === 'ETH'){
|
||||||
|
marketType = 'ETH to Token';
|
||||||
|
} else if (this.props.exchange.outputToken.value === 'ETH'){
|
||||||
|
marketType = 'Token to ETH';
|
||||||
|
} else{
|
||||||
|
marketType = 'Token to Token';
|
||||||
|
}
|
||||||
|
this.props.setExchangeType(marketType);
|
||||||
|
console.log('type: ', marketType);
|
||||||
|
console.log('input: ', this.props.exchange.inputToken.value);
|
||||||
|
console.log('output: ', this.props.exchange.outputToken.value);
|
||||||
|
}
|
||||||
|
// we are here
|
||||||
|
// TODO: change this to use the redux-subscribe pattern
|
||||||
|
getAccountInfo = () => {
|
||||||
|
switch (this.props.web3Store.exchangeType) {
|
||||||
|
case 'ETH to Token':
|
||||||
|
this.getEthBalance('input');
|
||||||
|
this.getTokenBalance('output');
|
||||||
|
break;
|
||||||
|
case 'Token to ETH':
|
||||||
|
this.getEthBalance('output');
|
||||||
|
this.getTokenBalance('input');
|
||||||
|
this.getAllowance();
|
||||||
|
break;
|
||||||
|
case 'Token to Token':
|
||||||
|
this.getTokenBalance('input');
|
||||||
|
this.getTokenBalance('output');
|
||||||
|
this.getAllowance();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
console.log("Getting account info");
|
||||||
|
}
|
||||||
|
|
||||||
|
// props ready
|
||||||
|
// TODO: TODO: TODO: TURN THIS INTO A REDUX-SUBSCRIBE LISTENER NOW!!!
|
||||||
|
getEthBalance = (type) => {
|
||||||
|
// this.props.web3Store.globalWeb3
|
||||||
|
if (type === 'input') {
|
||||||
|
this.props.web3Store.globalWeb3.eth.getBalance(this.props.web3Store.currentMaskAddress, (error, balance) => {
|
||||||
|
this.props.setInputBalance(balance);
|
||||||
|
// console.log('ETH Balance: ' + balance);
|
||||||
|
});
|
||||||
|
} else if (type === 'output') {
|
||||||
|
this.props.web3Store.globalWeb3.eth.getBalance(this.props.web3Store.currentMaskAddress, (error, balance) => {
|
||||||
|
this.props.setOutputBalance(balance);
|
||||||
|
// console.log('ETH Balance: ' + balance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// props ready
|
||||||
|
// TODO: this might also be able to change to the redux-subscribe method
|
||||||
|
getTokenBalance = (type) => {
|
||||||
|
var token;
|
||||||
|
if (type === 'input') {
|
||||||
|
token = this.symbolToTokenContract(this.props.exchange.inputToken.value);
|
||||||
|
token.methods.balanceOf(this.props.web3Store.currentMaskAddress).call((error, balance) => {
|
||||||
|
this.props.setInputBalance(balance);
|
||||||
|
// console.log(this.props.exchange.inputToken.value + ' Balance: ' + balance);
|
||||||
|
});
|
||||||
|
} else if (type === 'output') {
|
||||||
|
token = this.symbolToTokenContract(this.props.exchange.outputToken.value);
|
||||||
|
token.methods.balanceOf(this.props.web3Store.currentMaskAddress).call((error, balance) => {
|
||||||
|
this.props.setOutputBalance(balance);
|
||||||
|
// console.log(this.props.exchange.outputToken.value + ' Balance: ' + balance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: refactor to redux-subscribe
|
||||||
|
// props ready
|
||||||
|
getAllowance = () => {
|
||||||
|
var type = this.props.web3Store.exchangeType;
|
||||||
|
if(type === 'Token to ETH' || type === 'Token to Token') {
|
||||||
|
var token = this.symbolToTokenContract(this.props.exchange.inputToken.value);
|
||||||
|
var exchangeAddress = this.symbolToExchangeAddress(this.props.exchange.inputToken.value);
|
||||||
|
|
||||||
|
token.methods.allowance(this.props.web3Store.currentMaskAddress, exchangeAddress).call().then((result, error) => {
|
||||||
|
console.log(this.props.exchange.inputToken.value + ' allowance: ' + result);
|
||||||
|
if(result === '0'){
|
||||||
|
this.props.setAllowanceApprovalState(false)
|
||||||
|
console.log(this.props.exchange.allowanceApproved)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ready, we'll think of improvements later
|
||||||
|
symbolToTokenContract = (symbol) => {
|
||||||
|
if(symbol === 'UNI') {
|
||||||
|
return this.props.tokenContracts.UNI;
|
||||||
|
} else if(symbol === 'SWAP') {
|
||||||
|
return this.props.tokenContracts.SWT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
symbolToExchangeAddress = (symbol) => {
|
||||||
|
if(symbol === 'UNI') {
|
||||||
|
return this.props.web3Store.exchangeAddresses.UNI;
|
||||||
|
} else if(symbol === 'SWAP') {
|
||||||
|
return this.props.web3Store.exchangeAddresses.SWT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<WrappedComponent
|
||||||
|
onInputChange={this.onInputChange}
|
||||||
|
onSelectToken={this.onSelectToken}
|
||||||
|
setExchangeOutput={this.setExchangeOutput}
|
||||||
|
getMarketType={this.getMarketType}
|
||||||
|
getAccountInfo={this.getAccountInfo}
|
||||||
|
getEthBalance={this.getEthBalance}
|
||||||
|
getTokenBalance={this.getTokenBalance}
|
||||||
|
getAllowance={this.getAllowance}
|
||||||
|
symbolToTokenContract={this.symbolToTokenContract}
|
||||||
|
symbolToExchangeAddress={this.symbolToExchangeAddress}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
web3Store: state.web3Store,
|
||||||
|
exchange: state.exchange
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => {
|
||||||
|
return {
|
||||||
|
setExchangeInputValue,
|
||||||
|
setExchangeOutputValue,
|
||||||
|
setExchangeRate,
|
||||||
|
setExchangeFee,
|
||||||
|
setInteractionState,
|
||||||
|
setInputToken,
|
||||||
|
setOutputToken,
|
||||||
|
setExchangeType,
|
||||||
|
setInputBalance,
|
||||||
|
setOutputBalance,
|
||||||
|
setAllowanceApprovalState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(withOrderFunctionality);
|
@ -10,7 +10,7 @@ import {
|
|||||||
SET_NETWORK_MESSAGE,
|
SET_NETWORK_MESSAGE,
|
||||||
SET_BLOCK_TIMESTAMP,
|
SET_BLOCK_TIMESTAMP,
|
||||||
SET_EXCHANGE_TYPE,
|
SET_EXCHANGE_TYPE,
|
||||||
PUT_WEB3_IN_STORE
|
INITIALIZE_GLOBAL_WEB3
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
|
|
||||||
export default (state = {}, action) => {
|
export default (state = {}, action) => {
|
||||||
@ -36,7 +36,7 @@ export default (state = {}, action) => {
|
|||||||
return Object.assign({}, state, { blockTimestamp: timestamp });
|
return Object.assign({}, state, { blockTimestamp: timestamp });
|
||||||
case SET_EXCHANGE_TYPE:
|
case SET_EXCHANGE_TYPE:
|
||||||
return Object.assign({}, state, { exchangeType: exchangeType });
|
return Object.assign({}, state, { exchangeType: exchangeType });
|
||||||
case PUT_WEB3_IN_STORE:
|
case INITIALIZE_GLOBAL_WEB3:
|
||||||
return Object.assign({}, state, { globalWeb3: globalWeb3 });
|
return Object.assign({}, state, { globalWeb3: globalWeb3 });
|
||||||
default: return state;
|
default: return state;
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ export default {
|
|||||||
// we're going to need to include a seperate nest for exchange actions
|
// we're going to need to include a seperate nest for exchange actions
|
||||||
web3Store: {
|
web3Store: {
|
||||||
connected: false,
|
connected: false,
|
||||||
|
globalWeb3: {},
|
||||||
currentMaskAddress: '',
|
currentMaskAddress: '',
|
||||||
metamaskLocked: true,
|
metamaskLocked: true,
|
||||||
interaction: '',
|
interaction: '',
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { applyMiddleware, compose, createStore } from 'redux';
|
import { applyMiddleware, compose, createStore } from 'redux';
|
||||||
import reducer from '../reducers';
|
import reducer from '../reducers';
|
||||||
import thunk from 'redux-thunk'
|
import thunk from 'redux-thunk'
|
||||||
|
import initSubscriber from 'redux-subscriber';
|
||||||
import initialState from './initial-state';
|
import initialState from './initial-state';
|
||||||
|
|
||||||
const middleware = [thunk];
|
const middleware = [thunk];
|
||||||
@ -11,6 +12,9 @@ const store = createStore(
|
|||||||
reducer,
|
reducer,
|
||||||
initialState,
|
initialState,
|
||||||
composeEnhancers(applyMiddleware(...middleware), ...enhancers)
|
composeEnhancers(applyMiddleware(...middleware), ...enhancers)
|
||||||
)
|
);
|
||||||
|
// redux-subscribe solution attempt
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const subscribe = initSubscriber(store);
|
||||||
|
|
||||||
export default store;
|
export default store;
|
Loading…
Reference in New Issue
Block a user