Commit 22845601 authored by Uciel's avatar Uciel

major render refactor, minor logout error

parent 9f2d5ee9
......@@ -7867,6 +7867,11 @@
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
"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": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
......@@ -9998,6 +10003,14 @@
"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": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.2.0.tgz",
......
This diff is collapsed.
......@@ -9,7 +9,7 @@ import {
SET_NETWORK_MESSAGE,
SET_BLOCK_TIMESTAMP,
SET_EXCHANGE_TYPE,
PUT_WEB3_IN_STORE
INITIALIZE_GLOBAL_WEB3
} from '../constants';
// this actions folder is actually full of action creators
......@@ -55,8 +55,9 @@ export const setNetworkMessage = (networkMessage) => ({
networkMessage
});
export const setBlockTimestamp = (globalWeb3) => {
export const setBlockTimestamp = () => {
return async (dispatch, getState) => {
const { globalWeb3 } = getState().web3Store
await globalWeb3.eth.getBlock('latest', (error, blockInfo) => {
console.log(blockInfo.timestamp, 'BLOCKTIMESTAMP');
dispatch({
......@@ -72,7 +73,7 @@ export const setExchangeType = (exchangeType) => ({
exchangeType
});
export const putWeb3InStore = (globalWeb3) => ({
type: PUT_WEB3_IN_STORE,
export const initializeGlobalWeb3 = (globalWeb3) => ({
type: INITIALIZE_GLOBAL_WEB3,
globalWeb3
})
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>
)
}
}
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;
\ No newline at end of file
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;
\ No newline at end of file
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);
\ No newline at end of file
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;
\ No newline at end of file
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;
// 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;
\ No newline at end of file
import React from 'react';
function Transactions(props) {
return (
<div className="connection">
<ol>
{props.transactions.map((t) =>
<li key={t.toString()}><p><a target="_blank" rel="noopener noreferrer" href={'https://rinkeby.etherscan.io/search?q=' + t}>{t}</a></p></li>
)}
</ol>
</div>
)
if (props.transactions.length > 0 && props.interaction !== 'disconnected') {
return (
<section className="transaction border pa2">
<p className="underline">Past Transactions:</p>
<div className="connection">
<ol>
{props.transactions.map((t) =>
<li key={t.toString()}><p><a target="_blank" rel="noopener noreferrer" href={'https://rinkeby.etherscan.io/search?q=' + t}>{t}</a></p></li>
)}
</ol>
</div>
</section>
)
} else {
return (<section className="hidden border pa2"></section>)
}
}
export default Transactions;
......@@ -44,4 +44,4 @@ export const SET_EXCHANGE_RATE = 'SET_EXCHANGE_RATE';
export const SET_EXCHANGE_FEE = 'SET_EXCHANGE_FEE';
// test setInteractionState
export const PUT_WEB3_IN_STORE = 'PUT_WEB3_IN_STORE';
export const INITIALIZE_GLOBAL_WEB3 = 'INITIALIZE_GLOBAL_WEB3';
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_BLOCK_TIMESTAMP,
SET_EXCHANGE_TYPE,
PUT_WEB3_IN_STORE
INITIALIZE_GLOBAL_WEB3
} from '../constants';
export default (state = {}, action) => {
......@@ -36,7 +36,7 @@ export default (state = {}, action) => {
return Object.assign({}, state, { blockTimestamp: timestamp });
case SET_EXCHANGE_TYPE:
return Object.assign({}, state, { exchangeType: exchangeType });
case PUT_WEB3_IN_STORE:
case INITIALIZE_GLOBAL_WEB3:
return Object.assign({}, state, { globalWeb3: globalWeb3 });
default: return state;
}
......
......@@ -13,6 +13,7 @@ export default {
// we're going to need to include a seperate nest for exchange actions
web3Store: {
connected: false,
globalWeb3: {},
currentMaskAddress: '',
metamaskLocked: true,
interaction: '',
......
import { applyMiddleware, compose, createStore } from 'redux';
import reducer from '../reducers';
import thunk from 'redux-thunk'
import initSubscriber from 'redux-subscriber';
import initialState from './initial-state';
const middleware = [thunk];
......@@ -11,6 +12,9 @@ const store = createStore(
reducer,
initialState,
composeEnhancers(applyMiddleware(...middleware), ...enhancers)
)
);
// redux-subscribe solution attempt
// eslint-disable-next-line no-unused-vars
const subscribe = initSubscriber(store);
export default store;
\ No newline at end of file
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