Commit 6b5621fe authored by Chi Kei Chan's avatar Chi Kei Chan

Remove Drizzle; Header MediaQuery

parent a8b3fda0
import React, { Component } from 'react';
import { connect } from 'react-redux';
import AboutMessage from './AboutMessage';
class About extends Component {
render () {
return(
<div>
<section className="about" ref={(section) => { this.props.location.About = section; }}>
<a onClick={() => {this.props.toggleAbout()}} className="link border pa2 f-a">
<p className="underline">About Uniswap.</p>
<p></p>
</a>
</section>
<AboutMessage toggled={this.props.web3Store.aboutToggle} />
</div>
)
}
}
const mapStateToProps = state => ({
web3Store: state.web3Store
});
export default connect (mapStateToProps)(About)
import React from 'react'
function AboutMessage ({ toggled }) {
if (toggled === true) {
return (
<section className="expand grey-bg border pa2">
<p>Read the: <a href="https://hackmd.io/C-DvwDSfSxuh-Gd4WKE_ig#Uniswap-Whitepaper-%F0%9F%A6%84">Uniswap Whitepaper</a></p>
<p>Uniswap is an automated market maker for exchanging ERC20 tokens. Anyone can become a liquidity provider, and invest in the liquidity pool of an ERC20 token. This allows other users to trade that token for other tokens at an exchange rate based on their relative availibility. When a token trade is executed, a small fee is paid to the liquidity providers that enabled the transaction.</p>
<p>Please reach out if you would like to get involved or support the project.</p>
<p><span role="img" aria-label="GitHub">⟪⟫</span> <a href="https://github.com/uniswap">github.com/uniswap</a></p>
<p><span role="img" aria-label="Email">📧 </span><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;
import React, { Component } from 'react';
import { drizzleConnect } from 'drizzle-react';
import PropTypes from 'prop-types';
import c from 'classnames';
......@@ -59,4 +58,4 @@ class AddressInputPanel extends Component {
}
}
export default drizzleConnect(AddressInputPanel);
export default AddressInputPanel;
import React, { Component } from 'react';
import * as d3 from 'd3';
import axios from 'axios';
class Candlesticks extends Component {
constructor (props) {
super(props);
this.state = {
data: null
}
this.visualizeData.bind(this)
}
// note, this url is being used for development
// the actual url will be wherever the API is hosted
componentDidMount() {
let query = `{
Event(input:"UNI") {
ethValueOfToken
createdAt
}
}`;
axios.get('http://ec2-18-233-168-186.compute-1.amazonaws.com:3000/graphql', { params: { query: query } })
.then(data => this.setState({data: data.data.data.Event}))
.then(()=> this.visualizeData())
.catch(err => console.error(err));
}
visualizeData() {
let svg = d3.select('svg');
let coeff = 1000 * 60 * 15
let nested_date = d3.nest()
.key((d) => new Date (Math.round(new Date(d.createdAt).getTime() / coeff) * coeff))
.sortValues()
.entries(this.state.data)
console.log(nested_date, 'something better happen here')
// var enter = svg.selectAll('rect')
// .data(this.state.data)
// .enter()
// .append('rect')
}
render() {
return (
<svg>
</svg>
)
}
}
export default Candlesticks;
\ No newline at end of file
import React from 'react';
import { connect } from 'react-redux';
function ConnectionHelper(props) {
if (!props.metamask) {
return (
<div className="grey-bg connection border pa2">
<p>Welcome! Uniswap is a decentralized exhange for ERC20 Tokens. <a onClick={() => {props.toggleAbout()}} className="f-a">How it works </a><br /><br />
<b>To get started, please install <a href="https://metamask.io/">Metamask</a>.</b></p>
</div>
)
} else if (props.web3Store.connected && props.web3Store.interaction === 'disconnected') {
return (
<div className="grey-bg connection border pa2">
<p>
Welcome! Uniswap is a decentralized exchange for ERC20 Tokens. <a onClick={() => {props.toggleAbout()}} className="f-a" >How it works </a><br /><br />
It looks like you aren't connected. <b>Please switch to the Rinkeby testnet.</b>
</p>
</div>
)
} else if (props.web3Store.metamaskLocked) {
return (
<div className="grey-bg connection border pa2">
<p>Welcome! Uniswap is a decentralized exhange platform for ERC20 Tokens. <a onClick={() => {props.toggleAbout()}} className="f-a" >How it works ↘</a><br /><br />
It looks like you aren't connected. <b>Please unlock Metamask to continue.</b></p>
</div>
)
} else if (props.web3Store.exchangeType === "Token to itself" || props.web3Store.exchangeType === "ETH to ETH") {
return (
<div className="grey-bg connection border pa2">
<p>You can't swap a token for itself! <span role="img" aria-label="Crying">😂</span></p>
</div>
)
} else if (props.web3Store.interaction === "submitted") {
return (
<div className="grey-bg connection border pa2">
<p>{"Transaction submitted!"}</p>
</div>
)
} else if (props.exchange.inputValue > props.exchange.inputBalance/10**18 && props.exchange.inputToken.value === 'ETH') {
return (
<div className="grey-bg red connection border pa2">
<p>You don't have enough ETH to make this transaction! Get more at the <a target="_blank" rel="noopener noreferrer" href="https://faucet.rinkeby.io/">Rinkeby Faucet.</a></p>
</div>
)
} else if (!props.exchange.allowanceApproved && props.web3Store.exchangeType === "Token to Token") {
return (
<div className="grey-bg red connection border pa2">
<p>Uniswap has to be approved by your address to trade {props.exchange.inputToken.value}.</p>
<p></p>
<a className="f-a" onClick={() => props.approveAllowance()}>Approve </a>
</div>
)
} else if (!props.exchange.allowanceApproved && props.web3Store.exchangeType === "Token to ETH") {
return (
<div className="grey-bg red connection border pa2">
<p>Uniswap has to be approved by your address to trade {props.exchange.inputToken.value}.</p>
<p></p>
<a className="f-a" onClick={() => props.approveAllowance()}>Approve </a>
</div>
)
} else if (props.exchange.inputValue > props.exchange.inputBalance/10**18) {
return (
<div className="grey-bg red connection border pa2">
<p>{"You don't have enough " + props.exchange.inputToken.value + " to make this transaction. 😢 "}</p>
</div>
)
} else {
return (
<div className="grey-bg connection border pa2">
<p>Welcome! Uniswap is a decentralized exhange for ERC20 Tokens. <a onClick={() => {props.toggleAbout()}} className="f-a">How it works </a><br /><br />
You're connected. Enter a value below to get started.</p>
<p>↓</p>
</div>
)
}
}
const mapStateToProps = state => ({
web3Store: state.web3Store,
exchange: state.exchange
});
export default connect(mapStateToProps)(ConnectionHelper);
import React, { Component } from 'react';
import { drizzleConnect } from 'drizzle-react'
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { CSSTransitionGroup } from "react-transition-group";
import classnames from 'classnames';
......@@ -66,10 +66,6 @@ class CurrencyInputPanel extends Component {
selectedTokenAddress: '',
};
static contextTypes = {
drizzle: PropTypes.object,
};
state = {
isShowingModal: false,
searchQuery: '',
......@@ -334,8 +330,7 @@ class CurrencyInputPanel extends Component {
}
}
export default drizzleConnect(
CurrencyInputPanel,
export default connect(
state => ({
factoryAddress: state.addresses.factoryAddress,
exchangeAddresses: state.addresses.exchangeAddresses,
......@@ -349,4 +344,4 @@ export default drizzleConnect(
selectors: () => dispatch(selectors()),
addExchange: opts => dispatch(addExchange(opts)),
}),
);
)(CurrencyInputPanel);
import React, { Component }from 'react';
import React, { Component }from 'react';
import SelectToken from './SelectToken';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { setInteractionState, setExchangeType } from '../ducks/addresses';
import { setExchangeInputValue, setExchangeOutputValue, setExchangeRate, setExchangeFee, setInputToken, setOutputToken, setInputBalance, setOutputBalance, setAllowanceApprovalState } from '../ducks/exchange';
class Exchange extends Component {
onInputChange = async (event) => {
var inputValue = event.target.value;
await this.props.setExchangeInputValue(inputValue);
this.setExchangeOutput();
}
onSelectToken = async (selected, type) => {
this.props.setExchangeInputValue(0);
this.props.setExchangeOutputValue(0);
this.props.setExchangeRate(0);
this.props.setExchangeFee(0);
this.props.setInteractionState('connected');
if (type === 'input') {
await this.props.setInputToken(selected);
} else if (type === 'output'){
await this.props.setOutputToken(selected);
}
await this.getMarketType();
// eventually pull these out into HOC
this.props.getAccountInfo();
this.props.getMarketInfo();
}
setExchangeOutput = () => {
var inputValue = this.props.exchange.inputValue;
if (this.props.web3Store.exchangeType === 'ETH to ETH' || this.props.web3Store.exchangeType === 'Token to itself'){
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.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 = '';
var inputSymbol = this.props.exchange.inputToken.value;
var outputSymbol = this.props.exchange.outputToken.value;
if (inputSymbol === 'ETH' && outputSymbol === 'ETH') {
marketType = 'ETH to ETH';
this.props.setInteractionState('error1');
} else if (inputSymbol === outputSymbol){
marketType = 'Token to itself';
} else if (inputSymbol === 'ETH'){
marketType = 'ETH to Token';
} else if (outputSymbol === 'ETH'){
marketType = 'Token to ETH';
} else{
marketType = 'Token to Token';
}
this.props.setExchangeType(marketType);
console.log('type: ', marketType, 'input: ', inputSymbol, 'output: ', outputSymbol);
}
getExchangeRate = (input) => {
if (this.props.web3Store.exchangeType === 'ETH to Token') {
// console.log('Getting Rate: ETH to ' + this.props.exchange.outputToken.value);
this.ethToTokenRate(input);
} else if (this.props.web3Store.exchangeType === 'Token to ETH') {
// console.log('Getting Rate: ' + this.props.exchange.inputToken.value + ' to ETH');
this.tokenToEthRate(input);
} else if (this.props.web3Store.exchangeType === 'Token to Token') {
// console.log('Getting Rate: ' + this.props.exchange.inputToken.value + ' to ' + this.props.exchange.outputToken.value);
this.tokenToTokenRate(input);
}
}
ethToTokenRate = (ethInput) => {
var ethInMarket = +this.props.exchange.ethPool2;
var tokensInMarket = +this.props.exchange.tokenPool2;
var invar = ethInMarket*tokensInMarket
var ethIn = ethInput*10**18;
var exchangeFee = ethIn/500;
var ethSold = ethIn - exchangeFee;
var newEthInMarket = ethInMarket + ethSold;
var newTokensInMarket = invar/newEthInMarket;
var tokensOut = tokensInMarket - newTokensInMarket;
var buyRate = tokensOut/ethIn;
this.props.setExchangeRate(buyRate);
this.props.setExchangeFee(exchangeFee);
this.props.setExchangeOutputValue(tokensOut);
console.log('ethToTokenRate', buyRate);
}
tokenToEthRate = (tokenInput) => {
var ethInMarket = +this.props.exchange.ethPool1;
var tokensInMarket = +this.props.exchange.tokenPool1;
var invar = ethInMarket*tokensInMarket;
var tokensIn = tokenInput*10**18;
var exchangeFee = tokensIn/500;
var tokensSold = tokensIn - exchangeFee;
var newTokensInMarket = tokensInMarket + tokensSold;
var newEthInMarket = invar/newTokensInMarket;
var ethOut = ethInMarket - newEthInMarket;
var buyRate = ethOut/tokensIn;
this.props.setExchangeRate(buyRate);
this.props.setExchangeFee(exchangeFee);
this.props.setExchangeOutputValue(ethOut);
}
tokenToTokenRate = (tokenInput) => {
// Token to ETH on Exchange 1
var ethInMarket1 = +this.props.exchange.ethPool1;
var tokensInMarket1 = +this.props.exchange.tokenPool1;
var invar1 = ethInMarket1*tokensInMarket1;
var tokensIn = tokenInput*10**18;
var exchangeFee1 = tokensIn/500;
var tokensSold = tokensIn - exchangeFee1;
var newTokensInMarket1 = tokensInMarket1 + tokensSold;
var newEthInMarket1 = invar1/newTokensInMarket1;
var ethToExchange2 = ethInMarket1 - newEthInMarket1;
// ETH to Token on Exchange 2
var ethInMarket2 = +this.props.exchange.ethPool2;
var tokensInMarket2 = +this.props.exchange.tokenPool2;
var invar2 = ethInMarket2*tokensInMarket2;
var exchangeFee2 = ethToExchange2/500;
var ethSold = ethToExchange2 - exchangeFee2;
var newEthInMarket2 = ethInMarket2 + ethSold;
var newTokensInMarket2 = invar2/newEthInMarket2;
var tokensOut = tokensInMarket2 - newTokensInMarket2;
var buyRate = tokensOut/tokensIn;
this.props.setExchangeRate(buyRate);
this.props.setExchangeFee(exchangeFee1);
this.props.setExchangeOutputValue(tokensOut);
}
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(4)} 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)(Exchange);
......@@ -69,11 +69,10 @@
}
@media only screen and (min-device-width : 768px) {
position: fixed;
//position: fixed;
top: 0px;
left: 0px;
width: 100vw;
width: 100vw;
flex: 0 0 auto;
&__top {
border: none;
......
import React from 'react';
import PropTypes from 'prop-types';
import { drizzleConnect } from 'drizzle-react'
import { connect } from 'react-redux';
import classnames from 'classnames';
import UAParser from 'ua-parser-js';
import Logo from '../Logo';
......@@ -11,7 +11,6 @@ import MetamaskLogo from '../../assets/images/metamask-logo.png';
import Web3Status from '../Web3Status';
import "./header.scss";
import NavigationTabs from "../NavigationTabs";
const links = {
coinbaseWallet: {
......@@ -130,11 +129,10 @@ Header.propTypes = {
isConnected: PropTypes.bool.isRequired,
};
export default drizzleConnect(
Header,
export default connect(
state => ({
currentAddress: state.web3connect.account,
initialized: state.web3connect.initialized,
isConnected: !!state.web3connect.web3 && !!state.web3connect.account,
}),
);
)(Header);
import React from 'react';
function HelperMessages(props) {
let message = ''
switch (props.interaction) {
case 'connected':
message = <p>{"Nice! You're connected. Enter an input value below to get started."}</p>
break;
case 'locked':
message = <p>{"Your metamask is locked! Please unlock to continue."}</p>
break;
case 'input':
message = <p>{"You're swapping " + props.inputToken.value + " for " + props.outputToken.value + ". Want to know more about how the prices are determined?"}</p>
// message = "Oops, looks like this address doesn't have a enough " + props.inputToken.value + " to make this trade. Add more funds to make this swap."
break;
case 'error1':
message = <p>{"You can't swap a token for itself! 😂"}</p>
// message = "Oops, looks like this address doesn't have a enough " + props.inputToken.value + " to make this trade. Add more funds to make this swap."
break;
default:
message = <p>{"Hi there! This site helps you swap ERC20 tokens. Looks like you aren't connected. Need help?"}</p>
}
return (
<section className="info border pa2">
{message}
<p></p>
</section>
)
}
export default HelperMessages;
import React, { Component } from 'react'
import SelectToken from './SelectToken';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { setInvestToken,
setInvestEthPool,
setInvestTokenPool,
setInvestShares,
setInvestTokenBalance,
setInvestEthBalance,
setInvestTokenAllowance,
setInvestSharesInput,
setUserShares,
setInvestEthRequired,
setInvestTokensRequired,
setInvestChecked} from '../ducks/exchange';
class Invest extends Component {
onSelectToken = async (selected, type) => {
if(selected.value !== 'ETH') {
await this.props.setInvestToken(selected);
this.getInvestExchangeState();
this.getInvestBalance();
} else {
await this.props.setInvestToken(selected);
this.props.setInvestTokenPool(0);
this.props.setInvestEthPool(0);
this.props.setInvestTokenBalance(0);
this.props.setInvestEthBalance(0);
this.props.setInvestTokenAllowance(0);
this.props.setInvestShares(0);
this.props.setUserShares(0);
}
}
onInputChange = async (event) => {
if (this.props.exchange.investToken.value != "ETH") {
await this.props.setInvestSharesInput(event.target.value);
this.getInvestOutput();
console.log('investToken', this.props.exchange.investToken.value)
}
}
toggleCheck = () => {
this.props.setInvestChecked(!this.props.exchange.investChecked);
}
getInvestExchangeState = () => {
var exchange = this.props.symbolToExchangeContract(this.props.exchange.investToken.value);
exchange.methods.ethPool().call().then((result, error) => {
this.props.setInvestEthPool(result);
});
exchange.methods.tokenPool().call().then((result, error) => {
this.props.setInvestTokenPool(result);
});
exchange.methods.totalShares().call().then((result, error) => {
this.props.setInvestShares(result);
});
}
getInvestBalance = () => {
var symbol = this.props.exchange.investToken.value;
var investor = this.props.web3Store.currentMaskAddress;
var token = this.props.symbolToTokenContract(symbol);
var exchange = this.props.symbolToExchangeContract(symbol);
var exchangeAddr = this.props.symbolToExchangeAddress(symbol);
this.props.web3Store.web3.eth.getBalance(investor, (error, balance) => {
this.props.setInvestEthBalance(balance);
});
token.methods.balanceOf(investor).call((error, balance) => {
this.props.setInvestTokenBalance(balance);
});
token.methods.allowance(investor, exchangeAddr).call((error, balance) => {
this.props.setInvestTokenAllowance(balance);
console.log('invest allowance: ', balance)
});
exchange.methods.getShares(investor).call().then((result, error) => {
this.props.setUserShares(result);
});
}
getInvestOutput = () => {
var inputValue = this.props.exchange.investSharesInput;
if(inputValue && inputValue !== 0 && inputValue !== '0'){
var ethRequired = ((this.props.exchange.investEthPool)/this.props.exchange.investShares)*inputValue;
var tokensRequired = ((this.props.exchange.investTokenPool)/this.props.exchange.investShares)*inputValue;
this.props.setInvestEthRequired(ethRequired);
this.props.setInvestTokensRequired(tokensRequired);
// console.log("requires ", ethRequired, " ETH ", tokensRequired, this.props.exchange.investToken.value);
} else {
this.props.setInvestEthRequired(0);
this.props.setInvestTokensRequired(0);
}
}
approveInvestAllowance = () => {
var symbol = this.props.exchange.investToken.value;
var token = this.props.symbolToTokenContract(symbol);
var exchangeAddress = this.props.symbolToExchangeAddress(symbol);
var amount = this.props.web3Store.web3.utils.toWei('100000');
var gasCost = this.props.web3Store.web3.utils.toWei('25', 'gwei')
token.methods.approve(exchangeAddress, amount).send({from: this.props.web3Store.currentMaskAddress, gasPrice: gasCost})
.on('transactionHash', console.log('Transaction Hash created'))
.on('receipt', (receipt) => {
console.log(receipt)
this.props.setAllowanceApprovalState(true);
}) //Transaction Submitted to blockchain
.on('confirmation', (confirmationNumber, receipt) => {console.log("Block Confirmations: " + confirmationNumber)}) //Transaction Mined
.on('error', console.error);
}
render () {
if (this.props.web3Store.investToggle === true) {
return (
<section className="grey-bg border pa2">
<div onChange={this.toggleCheck.bind(this)}>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Invest <input type="radio" checked={this.props.exchange.investChecked} onChange={()=>{}} /> &nbsp;&nbsp;&nbsp;&nbsp;
Divest <input type="radio" checked={!this.props.exchange.investChecked} onChange={()=>{}} />
</div>
<div className="invest investValue border pa2">
<p> Select token and input number of shares: &nbsp;&nbsp;&nbsp;</p>
<div className="investSelectToken">
<SelectToken token={this.props.exchange.investToken} onSelectToken={this.onSelectToken} />
<p className="investDropdown">{'<'}</p>
</div>
<input type="number" className="investValueInput" value={this.props.exchange.inputShares} placeholder="0" onChange={this.onInputChange} />
</div>
<div className="investValue border pa2">
<p> Total Shares: {this.props.exchange.investShares} </p>
<p> Your shares: {this.props.exchange.userShares} </p>
<p> Your fees {((this.props.exchange.userShares*100)/this.props.exchange.investShares).toFixed(2)}% </p>
</div>
<div className="investValue border pa2">
<p> Total Liquidity </p>
<p> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{(this.props.exchange.investEthPool/10**18).toFixed(5)} ETH </p>
<p> {(this.props.exchange.investTokenPool/10**18).toFixed(5)} {this.props.exchange.investToken.value} </p>
</div>
<div className="investValue border pa2">
<p> &nbsp;Each share is worth: </p>
<p> {((this.props.exchange.investEthPool/10**18)/this.props.exchange.investShares).toFixed(5)} ETH &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p> {((this.props.exchange.investTokenPool/10**18)/this.props.exchange.investShares).toFixed(5)} {this.props.exchange.investToken.value} </p>
</div>
<div className="investValue border pa2">
<p> Account Balance: </p>
<p> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{(this.props.exchange.investEthBalance/10**18).toFixed(5)} ETH </p>
<p> {(this.props.exchange.investTokenBalance/10**18).toFixed(5)} {this.props.exchange.investToken.value} </p>
</div>
</section>
)
} else {
return (<section className="expand grey-bg border pa2 hidden"></section>)
}
}
}
const mapStateToProps = state => ({
web3Store: state.web3Store,
exchange: state.exchange
});
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
setInvestToken,
setInvestEthPool,
setInvestTokenPool,
setInvestShares,
setInvestTokenBalance,
setInvestEthBalance,
setInvestTokenAllowance,
setInvestSharesInput,
setInvestEthRequired,
setInvestTokensRequired,
setUserShares,
setInvestChecked
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Invest);
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Invest from './Invest';
class Links extends Component {
render () {
return(
<div>
<section className="links" ref={(section) => { this.props.location.Links = section; }} >
<a onClick={() => {this.props.toggleInvest()}} className="link border pa2 f-a">
<p className="underline">Invest liquidity to collect fees</p>
<p></p>
</a>
</section>
<Invest
token={this.props.exchange.investToken}
symbolToTokenContract={this.props.symbolToTokenContract}
symbolToExchangeContract={this.props.symbolToExchangeContract}
symbolToExchangeAddress={this.props.symbolToExchangeAddress}
/>
</div>
)
}
}
const mapStateToProps = state => ({
web3Store: state.web3Store,
exchange: state.exchange
});
export default connect (mapStateToProps) (Links);
import React, { Component }from 'react';
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { subscribe } from 'redux-subscriber';
import { setWeb3ConnectionStatus, setInteractionState, setNetworkMessage, metamaskLocked } from '../ducks/addresses';
class NetworkStatus extends Component {
componentDidMount(){
// eslint-disable-next-line no-unused-vars
const interactionStateSubscriber = subscribe('web3Store.currentMaskAddress', state => {
if (state.web3Store.currentMaskAddress !== undefined) {
// console.log('METAMASK UNLOCKED FROM NETWORK STATUS')
this.checkNetwork();
} else { console.log('METAMASK LOCKED') }
})
}
checkNetwork = () => {
this.props.web3Store.web3.eth.net.getNetworkType((err, networkId) => {
console.log("Connected to " + networkId)
let interactionState = networkId === 'rinkeby' ? 'connected' : 'disconnected';
let connectionStatus = networkId === 'rinkeby' ? true : false;
this.props.setNetworkMessage(networkId);
this.props.setWeb3ConnectionStatus(connectionStatus);
this.props.setInteractionState(interactionState);
})
}
render () {
if (this.props.web3Store.connected && this.props.web3Store.interaction !== 'disconnected'){
return (
<div className="connection border pa2 green">
<p className="userBalance">{this.props.exchange.inputToken.value + ": " + (this.props.exchange.inputBalance/10**18).toFixed(2)}</p>
<p className="userBalance">{this.props.exchange.outputToken.value + ": " + (this.props.exchange.outputBalance/10**18).toFixed(2)}</p>
<a target="_blank" rel="noopener noreferrer" href={'https://rinkeby.etherscan.io/search?q=' + this.props.web3Store.currentMaskAddress}>{this.props.web3Store.currentMaskAddress}</a>
<p></p>
</div>
)
} else if (!this.props.metamask) {
return (
<div className="connection red border pa2">
<p>{"Waiting for connection to the blockchain..."}</p>
<p></p>
</div>
)
} else if (this.props.web3Store.metamaskLocked && !this.props.web3Store.connected) {
return (
<div className="connection yellow border pa2">
<p>{"Waiting for Metamask to unlock..."}</p>
<p></p>
</div>
)
} else {
return (
<div className="connection yellow border pa2">
<p>{'MetaMask connected to ' + this.props.web3Store.networkMessage + '. Please switch to Rinkeby!'}</p>
<p></p>
</div>
)
}
}
}
const mapStateToProps = state => ({
global: state.global,
web3Store: state.web3Store,
exchange: state.exchange
});
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
setWeb3ConnectionStatus,
setInteractionState,
setNetworkMessage,
metamaskLocked
}, dispatch)
}
export default connect (mapStateToProps, mapDispatchToProps)(NetworkStatus);
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);
import React, { Component } from 'react';
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux';
import { setBlockTimestamp, setInteractionState } from '../ducks/addresses';
import { setExchangeInputValue, setExchangeOutputValue } from '../ducks/exchange';
class Purchase extends Component {
purchaseTokens = async () => {
await this.props.setBlockTimestamp(this.props.web3Store.web3);
if (this.props.web3Store.exchangeType === 'ETH to Token') {
this.ethToTokenPurchase();
} else if (this.props.web3Store.exchangeType === 'Token to ETH') {
this.tokenToEthPurchase();
} else if (this.props.web3Store.exchangeType === 'Token to Token') {
this.tokenToTokenPurchase();
}
}
ethToTokenPurchase = () => {
// console.log('symbol: ', this.props.exchange.outputToken.value);
var exchange = this.props.symbolToExchangeContract(this.props.exchange.outputToken.value);
// console.log('exchange: ', exchange);
var estimatedTokens = this.props.exchange.outputValue
var minTokensInt = parseInt((estimatedTokens*0.9), 10).toString();
var ethSold = this.props.exchange.inputValue;
var weiSold = this.props.web3Store.web3.utils.toWei(ethSold);
var timeout = this.props.web3Store.blockTimestamp + 300; //current block time + 5mins
// console.log('minTokensInt: ', minTokensInt);
// console.log('weiSold: ', weiSold);
// console.log('timeout: ', timeout);
exchange.methods.ethToTokenSwap(minTokensInt, timeout).send({from: this.props.web3Store.currentMaskAddress, value: weiSold})
.on('transactionHash', (result) => {
// console.log('Transaction Hash created'
// let transactions = this.state.transactions
// transactions.push(result);
// transactions is cookie stuff, we'll keep that in state
// this.setState({ transactions: transactions })
// any particular reason why there are initialized as 0, but get turned to empty strings after the transaction is over?
// this.props.setExchangeInputValue(0);
// this.props.setExchangeOutputValue(0);
this.props.setInteractionState('submitted');
// cookie.save('transactions', transactions, { path: '/' })
})
.on('receipt', (receipt) => {
console.log(receipt)
}) //Transaction Submitted to blockchain
.on('confirmation', (confirmationNumber, receipt) => {
console.log("Block Confirmations: " + confirmationNumber);
// if(confirmationNumber === 1) {
// this.getAccountInfo();
// }
}) //Transaction Mined
.on('error', console.error);
}
tokenToEthPurchase = () => {
var exchange = this.props.symbolToExchangeContract(this.props.exchange.inputToken.value);
var estimatedEth = this.props.exchange.outputValue
var minEthInt = parseInt((estimatedEth*0.9), 10).toString();
var tokensSold = this.props.exchange.inputValue;
// toWei needs to be replaced with *decimals
var tokensSoldInt = this.props.web3Store.web3.utils.toWei(tokensSold);
var timeout = this.props.web3Store.blockTimestamp + 300; //current block time + 5mins
exchange.methods.tokenToEthSwap(tokensSoldInt, minEthInt, timeout).send({from: this.props.web3Store.currentMaskAddress})
.on('transactionHash', (result) => {
// console.log('Transaction Hash created')
// let transactions = this.state.transactions
// transactions.push(result)
// this.setState({ transactions: transactions });
// this.props.setExchangeInputValue(0);
// this.props.setExchangeOutputValue(0);
this.props.setInteractionState('submitted');
// cookie.save('transactions', transactions, { path: '/' })
})
.on('receipt', (receipt) => {console.log(receipt)}) //Transaction Submitted to blockchain
.on('confirmation', (confirmationNumber, receipt) => {console.log("Block Confirmations: " + confirmationNumber)}) //Transaction Mined
.on('error', console.error);
}
tokenToTokenPurchase = () => {
var exchange = this.props.symbolToExchangeContract(this.props.exchange.inputToken.value);
var tokenOutAddress = this.props.symbolToTokenAddress(this.props.exchange.outputToken.value);
var estimatedTokens = this.props.exchange.outputValue;
var minTokensInt = parseInt((estimatedTokens*0.9), 10).toString();
var tokensSold = this.props.exchange.inputValue;
var tokensSoldInt = this.props.web3Store.web3.utils.toWei(tokensSold);
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})
.on('transactionHash', (result) => {
// console.log('Transaction Hash created')
// let transactions = this.state.transactions
// transactions.push(result)
// this.setState({ transactions: transactions });
// this.props.setExchangeInputValue(0);
// this.props.setExchangeOutputValue(0);
this.props.setInteractionState('submitted');
// cookie.save('transactions', transactions, { path: '/' })
})
.on('receipt', (receipt) => {console.log(receipt)}) //Transaction Submitted to blockchain
.on('confirmation', (confirmationNumber, receipt) => {console.log("Block Confirmations: " + confirmationNumber)}) //Transaction Mined
.on('error', console.error);
}
render() {
if (this.props.web3Store.interaction === 'input') {
return (
<a className="swap border pa2 blue-bg" 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).toFixed(4) + " " + this.props.exchange.outputToken.value}</b>
</a>
)
} else {
// eslint-disable-next-line
return (<a className="swap grey-bg hidden border pa2"></a>)
}
}
}
const mapStateToProps = state => ({
global: state.global,
web3Store: state.web3Store,
exchange: state.exchange
})
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
setBlockTimestamp,
setExchangeInputValue,
setExchangeOutputValue,
setInteractionState
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Purchase);
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(4)} {outputTokenValue + "/" + inputTokenValue}</p>
</span>
<span className="rate-info">
<p>Fee</p>
<p>{(exchangeFee/10**18).toFixed(4)} {inputTokenValue}</p>
</span>
</section>
)
}
export default RateAndFee;
/**
* React Select
* ============
* Created by Jed Watson and Joss Mackison for KeystoneJS, http://www.keystonejs.com/
* https://twitter.com/jedwatson https://twitter.com/jossmackison https://twitter.com/keystonejs
* MIT License: https://github.com/JedWatson/react-select
*/
.Select-input {
overflow: hidden;
max-width: calc(100% - 20px);
}
.Select {
position: relative;
/* width: 100%; */
}
.Select-control{
width: 5rem;
}
.Select-clear-zone{
display: none;
}
.Select-value{
position: absolute;
width: 5rem;
margin-top: 5px;
}
.Select-input{
width: 5rem;
margin-top: 5px;
}
.Select-menu-outer {
background-color: #fff;
border: 1px solid #ccc;
border-top-color: #e6e6e6;
box-sizing: border-box;
margin-top: -1px;
max-height: 200px;
position: absolute;
right: 0;
top: 100%;
width: 100%;
width: 200px;
z-index: 1;
-webkit-overflow-scrolling: touch;
}
.Select-menu {
max-height: 198px;
overflow-y: auto;
}
.Select-option {
box-sizing: border-box;
background-color: #fff;
color: #666666;
cursor: pointer;
display: block;
padding: 8px 10px;
}
.Select .Select-aria-only {
position: absolute;
display: inline-block;
height: 1px;
width: 1px;
clip: rect(0, 0, 0, 0);
overflow: hidden;
float: left;
}
.Select-option {
box-sizing: border-box;
background-color: #fff;
color: #666666;
cursor: pointer;
display: block;
padding: 8px 10px;
}
.Select-option.is-selected {
background-color: #f5faff;
background-color: rgba(0, 126, 255, 0.04);
color: #333;
}
.Select-option.is-focused {
background-color: #ebf5ff;
background-color: rgba(0, 126, 255, 0.08);
color: #333;
}
.Select-option.is-disabled {
color: #cccccc;
cursor: default;
}
.Select-noresults {
box-sizing: border-box;
color: #999999;
cursor: default;
display: block;
padding: 8px 10px;
}
.Select--multi .Select-input {
vertical-align: middle;
margin-left: 10px;
padding: 0;
}
.Select--multi.Select--rtl .Select-input {
margin-left: 0;
margin-right: 10px;
}
.Select--multi.has-value .Select-input {
margin-left: 5px;
}
.Select--multi .Select-value {
background-color: #ebf5ff;
background-color: rgba(0, 126, 255, 0.08);
/* border: 1px solid #c2e0ff;
border: 1px solid rgba(0, 126, 255, 0.24); */
color: #007eff;
display: inline-block;
font-size: 0.9em;
line-height: 1.4;
margin-left: 5px;
margin-top: 5px;
vertical-align: top;
}
.Select--multi .Select-value-icon,
.Select--multi .Select-value-label {
display: inline-block;
vertical-align: middle;
}
.Select--multi .Select-value-label {
border-bottom-right-radius: 2px;
border-top-right-radius: 2px;
cursor: default;
padding: 2px 5px;
}
.Select--multi a.Select-value-label {
color: #007eff;
cursor: pointer;
text-decoration: none;
}
.Select--multi a.Select-value-label:hover {
text-decoration: underline;
}
.Select--multi .Select-value-icon {
cursor: pointer;
border-bottom-left-radius: 2px;
border-top-left-radius: 2px;
border-right: 1px solid #c2e0ff;
border-right: 1px solid rgba(0, 126, 255, 0.24);
padding: 1px 5px 3px;
}
.Select--multi .Select-value-icon:hover,
.Select--multi .Select-value-icon:focus {
background-color: #d8eafd;
background-color: rgba(0, 113, 230, 0.08);
color: #0071e6;
}
.Select--multi .Select-value-icon:active {
background-color: #c2e0ff;
background-color: rgba(0, 126, 255, 0.24);
}
.Select--multi.Select--rtl .Select-value {
margin-left: 0;
margin-right: 5px;
}
.Select--multi.Select--rtl .Select-value-icon {
border-right: none;
border-left: 1px solid #c2e0ff;
border-left: 1px solid rgba(0, 126, 255, 0.24);
}
.Select--multi.is-disabled .Select-value {
background-color: #fcfcfc;
border: 1px solid #e3e3e3;
color: #333;
}
.Select--multi.is-disabled .Select-value-icon {
cursor: not-allowed;
border-right: 1px solid #e3e3e3;
}
.Select--multi.is-disabled .Select-value-icon:hover,
.Select--multi.is-disabled .Select-value-icon:focus,
.Select--multi.is-disabled .Select-value-icon:active {
background-color: #fcfcfc;
}
.Select input::-webkit-contacts-auto-fill-button,
.Select input::-webkit-credentials-auto-fill-button {
display: none !important;
}
.Select input::-ms-clear {
display: none !important;
}
.Select input::-ms-reveal {
display: none !important;
}
.Select,
.Select div,
.Select input,
.Select span {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.Select input:focus{
/* outline: none; */
}
.Select.is-searchable.is-open > .Select-control {
cursor: text;
}
.Select.is-searchable.is-focused:not(.is-open) > .Select-control {
cursor: text;
}
.Select.Select--rtl {
direction: rtl;
text-align: right;
}
.Select.is-open .Select-input {
padding: 0 !important;
}
.Select.is-open .Select-input,
.Select.is-open .Select-input input {
width: 100% !important;
}
.Select.is-open .Select-input input {
text-indent: 1px;
}
import React, { Component } from 'react';
import Select from 'react-select';
import './SelectToken.css';
import { connect } from 'react-redux';
class SelectToken extends Component {
constructor (props) {
super(props)
this.state = {
selectedOption: this.props.token,
tokenList: []
}
}
componentDidMount() {
let tokenList = this.createTokenList();
this.setState({ tokenList })
}
handleChange = (selectedOption) => {
this.setState({ selectedOption });
this.props.onSelectToken(selectedOption, this.props.type)
// console.log(`Selected: ${selectedOption.label}`)
}
createTokenList = () => {
let tokens = this.props.web3Store.tokenAddresses.addresses;
let tokenList = [ { value: 'ETH', label: 'ETH', clearableValue: false } ];
for (let i = 0; i < tokens.length; i++) {
let entry = { value: '', label: '', clearableValue: false }
entry.value = tokens[i][0];
entry.label = tokens[i][0];
tokenList.push(entry);
}
return tokenList;
}
render () {
const { selectedOption } = this.state
const value = selectedOption && selectedOption.value
return (
<Select
name="form-field-name"
value={value}
onChange={this.handleChange}
className="select"
options={this.state.tokenList}
/>
)
}
}
const mapStateToProps = state => ({
web3Store: state.web3Store
})
export default connect(mapStateToProps)(SelectToken);
import React, { Component } from 'react';
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux';
import { setBlockTimestamp, setInteractionState } from '../ducks/addresses';
import { setExchangeInputValue, setExchangeOutputValue } from '../ducks/exchange';
class Purchase extends Component {
buyOrSellShares = async () => {
await this.props.setBlockTimestamp(this.props.web3Store.web3);
if(this.props.exchange.investChecked) {
this.buyShares();
} else {
this.sellShares();
}
}
buyShares = () => {
var exchange = this.props.symbolToExchangeContract(this.props.exchange.investToken.value);
var minShares = 1;
var ethRequiredInt = parseInt(this.props.exchange.investEthRequired, 10).toString();
// var timeout = this.props.web3Store.blockTimestamp + 300; //current block time + 5mins
exchange.methods.investLiquidity(minShares).send({from: this.props.web3Store.currentMaskAddress, value: ethRequiredInt})
.on('transactionHash', (result) => {
// console.log('Transaction Hash created')
// let transactions = this.state.transactions
// transactions.push(result)
// this.setState({ transactions: transactions });
// this.props.setExchangeInputValue(0);
// this.props.setExchangeOutputValue(0);
// this.props.setInteractionState('submitted');
console.log(result);
// cookie.save('transactions', transactions, { path: '/' })
})
.on('receipt', (receipt) => {console.log(receipt)}) //Transaction Submitted to blockchain
.on('confirmation', (confirmationNumber, receipt) => {console.log("Block Confirmations: " + confirmationNumber)}) //Transaction Mined
.on('error', console.error);
}
sellShares = () => {
var exchange = this.props.symbolToExchangeContract(this.props.exchange.investToken.value);
var minEth = 1;
var minTokens = 1;
var sharesInt = parseInt(this.props.exchange.investSharesInput, 10).toString();
// var timeout = this.props.web3Store.blockTimestamp + 300; //current block time + 5mins
exchange.methods.divestLiquidity(sharesInt, minEth, minTokens).send({from: this.props.web3Store.currentMaskAddress})
.on('transactionHash', (result) => {
// console.log('Transaction Hash created')
// let transactions = this.state.transactions
// transactions.push(result)
// this.setState({ transactions: transactions });
// this.props.setExchangeInputValue(0);
// this.props.setExchangeOutputValue(0);
// this.props.setInteractionState('submitted');
console.log(result);
})
.on('receipt', (receipt) => {console.log(receipt)}) //Transaction Submitted to blockchain
.on('confirmation', (confirmationNumber, receipt) => {console.log("Block Confirmations: " + confirmationNumber)}) //Transaction Mined
.on('error', console.error);
}
approveInvestAllowance = () => {
var symbol = this.props.exchange.investToken.value;
var token = this.props.symbolToTokenContract(symbol);
var exchangeAddress = this.props.symbolToExchangeAddress(symbol);
var amount = this.props.web3Store.web3.utils.toWei('100000');
var gasCost = this.props.web3Store.web3.utils.toWei('25', 'gwei')
token.methods.approve(exchangeAddress, amount).send({from: this.props.web3Store.currentMaskAddress, gasPrice: gasCost})
.on('transactionHash', console.log('Transaction Hash created'))
.on('receipt', (receipt) => {
console.log(receipt)
this.props.setAllowanceApprovalState(true);
}) //Transaction Submitted to blockchain
.on('confirmation', (confirmationNumber, receipt) => {console.log("Block Confirmations: " + confirmationNumber)}) //Transaction Mined
.on('error', console.error);
}
render() {
if(this.props.web3Store.investToggle == true && this.props.exchange.investSharesInput > 0) {
// Doesn't work
if (this.props.exchange.investToken.value == "ETH") {
return (
<div className="swap border pa2 red-bg">
<b><p>Please select a token other than ETH.</p></b>
</div>
)
} else if(this.props.exchange.investTokenAllowance == 0) {
return (
<div className="swap border pa2 blue-bg" role="button" onClick={() => {this.approveInvestAllowance()}}>
<b><p>Click to approve {this.props.exchange.investToken.value} spending</p></b>
</div>
)
} else if(this.props.exchange.investChecked) {
if(this.props.exchange.investEthRequired > this.props.exchange.investEthBalance) {
return (
<div className="swap border pa2 red-bg">
<b><p>😭 You can't afford to invest {(this.props.exchange.investEthRequired/10**18).toFixed(4)} ETH and {(this.props.exchange.investTokensRequired/10**18).toFixed(4)} {this.props.exchange.investToken.value} for {this.props.exchange.investSharesInput} shares 😭</p></b>
</div>
)
} else {
return (
<a className="swap border pa2 blue-bg" role="button" onClick={() => {this.buyOrSellShares()}}>
<b>I want to invest {(this.props.exchange.investEthRequired/10**18).toFixed(4)} ETH and {(this.props.exchange.investTokensRequired/10**18).toFixed(4)} {this.props.exchange.investToken.value} for {this.props.exchange.investSharesInput} shares</b>
</a>
)
}
} else {
if(this.props.exchange.investSharesInput > this.props.exchange.userShares) {
return (
<a className="swap border pa2 red-bg">
<b>You do not have {this.props.exchange.investSharesInput} shares.</b>
</a>
)
} else {
return (
<a className="swap border pa2 blue-bg" role="button" onClick={() => {this.buyOrSellShares()}}>
<b>{this.props.exchange.investSharesInput > this.props.exchange.userShares} I want to divest {(this.props.exchange.investEthRequired/10**18).toFixed(4)} ETH and {(this.props.exchange.investTokensRequired/10**18).toFixed(4)} {this.props.exchange.investToken.value} for {this.props.exchange.investSharesInput} shares</b>
</a>
)
}
}
} else {
return (<a className="swap grey-bg hidden border pa2"></a>)
}
}
}
const mapStateToProps = state => ({
global: state.global,
web3Store: state.web3Store,
exchange: state.exchange
})
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
setBlockTimestamp,
setExchangeInputValue,
setExchangeOutputValue,
setInteractionState
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Purchase);
import React from 'react';
function Transactions(props) {
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;
import React from 'react';
import { Helmet } from "react-helmet";
import unicorn from '../assets/images/logo.png'
function UniHead(props) {
return (
<Helmet>
<meta charSet="utf-8" />
<link rel="icon" href={unicorn} sizes="32x32" type="image/png" />
<title>Uniswap - ERC20 Market Maker</title>
</Helmet>
);
}
export default UniHead;
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import { subscribe } from 'redux-subscriber'
import * as d3 from 'd3';
import axios from 'axios';
class Visualization extends Component {
constructor(props) {
super(props);
this.state = {
data: null
}
}
// TODO: find a way to get this thing to listen for changes in the output token
componentDidMount() {
this.d3Graph = d3.select(ReactDOM.findDOMNode(this.refs.graph));
//let inputToken = this.props.exchange.inputToken.value;
let outputToken = this.props.exchange.outputToken.value;
//console.log(outputToken, 'output token');
let query = `{
Event(input:"${outputToken}"){
ethValueOfToken
createdAt
}
}`;
//console.log(query, 'query')
axios.get('http://ec2-34-197-169-170.compute-1.amazonaws.com:3000/graphql', { params: {query: query } })
.then(data => this.setState({data: data.data.data.Event }))
.then(() => this.createLineGraph())
.catch(err => console.error(err));
this.outputTokenSubscriber();
}
outputTokenSubscriber() {
const outputTokenSubscriber = subscribe('exchange.outputToken', state => {
let outputToken = state.exchange.outputToken.value;
console.log('outputToken change deteced', outputToken)
let query = `{
Event(input:"${outputToken}"){
ethValueOfToken
createdAt
}
}`;
axios.get('http://ec2-34-197-169-170.compute-1.amazonaws.com:3000/graphql', { params: { query: query }})
.then(data => this.setState({data: data.data.data.Event}))
.then(() => {
this.createNewLineGraph()
})
.catch(err => console.error(err));
})
}
createLineGraph() {
// TODO: as you change whether you want to see days or years, change the extent
// scales are wicked important
let margin = {top: 20, bottom:20, left:20, right:20}
let width = 1039 - margin.left - margin.right;
let height = 325 - margin.top - margin.bottom;
let svg = this.d3Graph
// first we want to see the min and max of our token prices
let ethValueOfTokenExtent = d3.extent(this.state.data, element => element.ethValueOfToken);
// console.log('initial data visualized', this.state.data, 'extent of data', ethValueOfTokenExtent)
// create a y scale, for the eth value of the token
let yScale = d3.scaleLinear()
.domain([ethValueOfTokenExtent[1], ethValueOfTokenExtent[0]])
.range([margin.bottom, height - margin.top]);
// now that we have the scale, we create the actual axis
let yAxis = d3.axisLeft()
.scale(yScale)
.ticks(8)
// time to put this y axis on the page
svg.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(100)')
.call(yAxis);
// sanitize the data for the x-axis
this.state.data.map(e => e.createdAt = new Date(e.createdAt));
// similarly, check the min and max of our times
let timeExtent = d3.extent(this.state.data, element => element.createdAt)
// console.log('previous time extent', timeExtent)
// with this extent, create a scale for the x axis
// BIG NOTE: for timeScales, you need to create new Date objects from the date string
// also, domain needs to take in an array
let xScale = d3.scaleTime()
.domain(timeExtent)
.range([margin.left, width - margin.right]);
// we have a scale, lets create the axis
let xAxis = d3.axisBottom()
.scale(xScale);
// append the axis to the DOM, make sure it's positioned correctly
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(80, 265)')
.call(xAxis);
// this right here is the x-axis label
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 20 )
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.attr('font-size', '15px')
.style("text-anchor", "middle")
.text("Price (ETH)");
let line = d3.line()
.x(element => xScale(element.createdAt))
.y(element => yScale(element.ethValueOfToken))
svg.append('path')
.datum(this.state.data)
.attr('d', line)
.attr('class', 'line')
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2.5)
.attr('transform', 'translate(80)')
}
createNewLineGraph() {
let margin = {top: 20, bottom:20, left:20, right:20}
let width = 1039 - margin.left - margin.right;
let height = 325 - margin.top - margin.bottom;
this.state.data.map(e => e.createdAt = new Date(e.createdAt));
console.log('data is being set correctly', this.state.data)
// we set the range of the data again
let yExtent = d3.extent(this.state.data, e => e.ethValueOfToken);
let xExtent = d3.extent(this.state.data, e => e.createdAt);
console.log('new yExtent', yExtent)
console.log('new xExtent', xExtent)
// we also redefine the scales for the new data
let yScale = d3.scaleLinear()
.domain([yExtent[1], yExtent[0]])
.range([margin.bottom, height - margin.top]);
let xScale = d3.scaleTime()
.domain(xExtent)
.range([margin.left, width - margin.right]);
// redefine the axes
let xAxis = d3.axisBottom()
.scale(xScale)
let yAxis = d3.axisLeft()
.scale(yScale)
.ticks(8)
let svg = this.d3Graph.transition()
let line = d3.line()
.x(element => xScale(element.createdAt))
.y(element => yScale(element.ethValueOfToken))
svg.select('.line')
.duration(750)
.attr('d', line(this.state.data))
svg.select('.x.axis')
.duration(750)
.call(xAxis)
svg.select('.y.axis')
.duration(750)
.call(yAxis)
}
render () {
const width = 1039;
const height = 375;
return (
<div align="center">
<svg width={width} height={height}>
<g ref="graph"/>
</svg>
</div>
)
}
}
const mapStateToProps = state => ({
exchange: state.exchange
})
export default connect (mapStateToProps)(Visualization);
// input and output tokens will have to be considered for this to work correctly
// we'll start by pulling the output token
// potential problem that these data points are not being written at the exact same time
// we will deal with that when we get there
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { drizzleConnect } from 'drizzle-react'
import { connect } from 'react-redux';
import classnames from 'classnames';
import Web3 from 'web3';
import Jazzicon from 'jazzicon';
......@@ -142,15 +142,14 @@ Web3Status.defaultProps = {
address: 'Disconnected',
};
export default drizzleConnect(
Web3Status,
export default connect(
state => {
const pendingTransactions = [];
Object.keys(state.transactions).forEach((transaction) => {
if (state.transactions[transaction] && state.transactions[transaction].status === 'pending') {
pendingTransactions.push(transaction);
}
});
// Object.keys(state.transactions).forEach((transaction) => {
// if (state.transactions[transaction] && state.transactions[transaction].status === 'pending') {
// pendingTransactions.push(transaction);
// }
// });
return {
address: state.web3connect.account,
......@@ -159,4 +158,4 @@ export default drizzleConnect(
hasPendingTransactions: pendingTransactions.length > 0,
};
}
);
)(Web3Status);
import { combineReducers } from 'redux';
import { drizzleReducers } from 'drizzle'
import addresses from './addresses';
import send from './send';
import web3connect from './web3connect';
export default combineReducers({
addresses,
// exchangeContracts,
// tokenContracts,
// exchange,
send,
// swap,
web3connect,
...drizzleReducers,
});
import React, { Component } from 'react';
import PropTypes from 'prop-types';
// import { connect } from 'react-redux';
import { drizzleConnect } from 'drizzle-react';
import { connect } from 'react-redux';
import {BigNumber as BN} from 'bignumber.js';
import Web3 from 'web3';
import ERC20_ABI from "../abi/erc20";
......@@ -108,7 +107,7 @@ const Balance = (value, label = '', decimals = 18) => ({
export const initialize = () => (dispatch, getState) => {
const { web3connect } = getState();
return new Promise(async resolve => {
return new Promise(async (resolve, reject) => {
if (web3connect.web3) {
resolve(web3connect.web3);
return;
......@@ -126,6 +125,8 @@ export const initialize = () => (dispatch, getState) => {
return;
} catch (error) {
console.error('User denied access.');
dispatch({ type: INITIALIZE });
reject();
return;
}
}
......@@ -138,6 +139,9 @@ export const initialize = () => (dispatch, getState) => {
});
resolve(web3);
}
dispatch({ type: INITIALIZE });
reject();
})
};
......@@ -442,8 +446,7 @@ export class _Web3Connect extends Component {
}
}
export const Web3Connect = drizzleConnect(
_Web3Connect,
export const Web3Connect = connect(
({ web3connect }) => ({
web3: web3connect.web3,
}),
......@@ -451,4 +454,4 @@ export const Web3Connect = drizzleConnect(
initialize: () => dispatch(initialize()),
startWatching: () => dispatch(startWatching()),
}),
);
)(_Web3Connect);
import {BigNumber as BN} from "bignumber.js";
import { getDecimals } from './contract-utils';
export const isExchangeUnapproved = opts => {
const {
value,
currency,
drizzleCtx,
account,
contractStore,
exchangeAddresses,
} = opts;
if (!currency || currency === 'ETH') {
return false;
}
const inputExchange = exchangeAddresses.fromToken[currency];
if (!inputExchange) {
return false;
}
const allowanceKey = drizzleCtx.contracts[currency].methods.allowance.cacheCall(account, inputExchange);
if (!contractStore[currency]) {
return false;
}
const allowance = contractStore[currency].allowance[allowanceKey];
if (!allowance) {
return false;
}
return BN(value).isGreaterThan(BN(allowance.value));
};
export const approveExchange = async opts => {
const {
currency,
contractStore,
drizzleCtx,
account,
exchangeAddresses,
} = opts;
const { web3 } = drizzleCtx;
const inputExchange = exchangeAddresses.fromToken[currency];
if (!inputExchange) {
return;
}
const decimals = await getDecimals({ address: currency, drizzleCtx, contractStore });
const approvals = BN(10 ** decimals).multipliedBy(BN(10 ** 8)).toFixed(0);
return drizzleCtx.contracts[currency].methods
.approve(inputExchange, web3.utils.toHex(approvals))
.send({ from: account })
// .then((e, d) => console.log(e, d));
};
export function getDecimals({ address, drizzleCtx, contractStore }) {
return new Promise(async (resolve, reject) => {
if (address === 'ETH') {
resolve('18');
return;
}
const decimalsKey = drizzleCtx.contracts[address].methods.decimals.cacheCall();
const decimals = contractStore[address].decimals[decimalsKey];
resolve(decimals && decimals.value);
});
}
const BALANCE_KEY = {};
export function getBalance({ currency, address, drizzleCtx, contractStore }) {
return new Promise(async (resolve, reject) => {
if (currency === 'ETH') {
drizzleCtx.web3.eth.getBalance(address, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
} else {
const token = drizzleCtx.contracts[currency];
if (!token) {
return;
}
let balanceKey = BALANCE_KEY[address];
if (!balanceKey) {
balanceKey = token.methods.balanceOf.cacheCall(address);
BALANCE_KEY[address] = balanceKey;
}
const tokenStore = contractStore[currency];
if (!tokenStore) {
reject(new Error(`Cannot find ${currency} in contract store`));
return;
}
let balance = tokenStore.balanceOf[balanceKey];
resolve(balance && balance.value);
}
});
}
export const getTxStatus = opts => {
const {
drizzleCtx,
txId
} = opts;
const st = drizzleCtx.store.getState();
const tx = st.transactionStack[txId];
const status = st.transactions[tx] && st.transactions[tx].status;
return status;
};
This diff is collapsed.
module.exports.exchangeABI =
[{"anonymous":false,"inputs":[{"indexed":true,"name":"liquidityProvider","type":"address"},{"indexed":true,"name":"sharesBurned","type":"uint256"}],"name":"Divestment","type":"event"},{"constant":false,"inputs":[],"name":"addFeesToMarketPublic","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_sharesBurned","type":"uint256"},{"name":"_minEth","type":"uint256"},{"name":"_minTokens","type":"uint256"}],"name":"divestLiquidity","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_minTokens","type":"uint256"},{"name":"_timeout","type":"uint256"},{"name":"_recipient","type":"address"}],"name":"ethToTokenPayment","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_minTokens","type":"uint256"},{"name":"_timeout","type":"uint256"}],"name":"ethToTokenSwap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_tokenAmount","type":"uint256"}],"name":"initializeExchange","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_minShares","type":"uint256"}],"name":"investLiquidity","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":true,"name":"ethIn","type":"uint256"},{"indexed":true,"name":"tokensOut","type":"uint256"}],"name":"EthToTokenPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"liquidityProvider","type":"address"},{"indexed":true,"name":"sharesPurchased","type":"uint256"}],"name":"Investment","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":true,"name":"tokensIn","type":"uint256"},{"indexed":true,"name":"ethOut","type":"uint256"}],"name":"TokenToEthPurchase","type":"event"},{"constant":false,"inputs":[{"name":"_tokenAmount","type":"uint256"},{"name":"_minEth","type":"uint256"},{"name":"_timeout","type":"uint256"},{"name":"_recipient","type":"address"}],"name":"tokenToEthPayment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_tokenAmount","type":"uint256"},{"name":"_minEth","type":"uint256"},{"name":"_timeout","type":"uint256"}],"name":"tokenToEthSwap","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_recipient","type":"address"},{"name":"_minTokens","type":"uint256"}],"name":"tokenToTokenIn","outputs":[{"name":"","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_tokenPurchased","type":"address"},{"name":"_recipient","type":"address"},{"name":"_tokensSold","type":"uint256"},{"name":"_minTokensReceived","type":"uint256"},{"name":"_timeout","type":"uint256"}],"name":"tokenToTokenPayment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_tokenPurchased","type":"address"},{"name":"_tokensSold","type":"uint256"},{"name":"_minTokensReceived","type":"uint256"},{"name":"_timeout","type":"uint256"}],"name":"tokenToTokenSwap","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_tokenAddress","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"ethFees","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ethPool","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"factoryAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FEE_RATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_provider","type":"address"}],"name":"getShares","outputs":[{"name":"_shares","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"invariant","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenFees","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenPool","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalShares","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]
\ No newline at end of file
module.exports.factoryABI =
[
{
"constant": true,
"inputs": [],
"name": "getExchangeCount",
"outputs": [
{
"name": "exchangeCount",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_token",
"type": "address"
}
],
"name": "launchExchange",
"outputs": [
{
"name": "exchange",
"type": "address"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint256"
}
],
"name": "tokenList",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_token",
"type": "address"
}
],
"name": "tokenToExchangeLookup",
"outputs": [
{
"name": "exchange",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_exchange",
"type": "address"
}
],
"name": "exchangeToTokenLookup",
"outputs": [
{
"name": "token",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "exchange",
"type": "address"
},
{
"indexed": true,
"name": "token",
"type": "address"
}
],
"name": "ExchangeLaunch",
"type": "event"
}
]
module.exports.tokenABI =
[
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "spender",
"type": "address"
},
{
"name": "value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "from",
"type": "address"
},
{
"name": "to",
"type": "address"
},
{
"name": "value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [
{
"name": "",
"type": "uint8"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "who",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "to",
"type": "address"
},
{
"name": "value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "owner",
"type": "address"
},
{
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "owner",
"type": "address"
},
{
"indexed": true,
"name": "spender",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "from",
"type": "address"
},
{
"indexed": true,
"name": "to",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
}
]
import React from 'react';
import ReactDOM from 'react-dom';
import { DrizzleProvider } from 'drizzle-react';
import { Provider } from 'react-redux';
import App from './pages/App';
import store from './store';
......@@ -8,13 +8,9 @@ import './index.scss';
window.addEventListener('load', function() {
ReactDOM.render(
<DrizzleProvider options={{
contracts: [],
events: [],
polls: { accounts: 60000, blocks: 60000 },
}} store={store}>
<Provider store={store}>
<App />
</DrizzleProvider>
</Provider>
, document.getElementById('root')
);
});
......
import React, { Component } from 'react';
import { drizzleConnect } from 'drizzle-react'
import { BrowserRouter, Switch, Redirect, Route } from 'react-router-dom';
import { connect } from 'react-redux';
import { BrowserRouter, Redirect, Route } from 'react-router-dom';
import MediaQuery from 'react-responsive';
import { AnimatedSwitch } from 'react-router-transition';
import { Web3Connect, startWatching, initialize } from '../ducks/web3connect';
import { setAddresses } from '../ducks/addresses';
import Header from '../components/Header';
import Swap from './Swap';
import Send from './Send';
import Pool from './Pool';
......@@ -11,9 +13,6 @@ import Pool from './Pool';
import './App.scss';
class App extends Component {
shouldComponentUpdate() {
return true;
}
componentWillMount() {
const { initialize, startWatching} = this.props;
initialize().then(startWatching);
......@@ -41,6 +40,9 @@ class App extends Component {
return (
<div id="app-container">
<MediaQuery query="(min-device-width: 768px)">
<Header />
</MediaQuery>
<Web3Connect />
<BrowserRouter>
<AnimatedSwitch
......@@ -60,11 +62,10 @@ class App extends Component {
}
}
export default drizzleConnect(
App,
export default connect(
state => ({
account: state.web3connect.account,
initialized: !!state.web3connect.web3,
initialized: state.web3connect.initialized,
web3: state.web3connect.web3,
}),
dispatch => ({
......@@ -72,4 +73,4 @@ export default drizzleConnect(
initialize: () => dispatch(initialize()),
startWatching: () => dispatch(startWatching()),
}),
);
)(App);
......@@ -6,7 +6,8 @@
position: relative;
@media only screen and (min-device-width : 768px) {
margin-top: 4rem;
margin: auto;
width: 560px;
}
& > div {
......@@ -25,6 +26,6 @@
@extend %col-nowrap;
@media only screen and (min-device-width : 768px) {
max-width: 560px;
//max-width: 560px;
}
}
import React, { Component } from 'react';
import { drizzleConnect } from 'drizzle-react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import classnames from "classnames";
import { CSSTransitionGroup } from "react-transition-group";
......@@ -551,8 +551,7 @@ class AddLiquidity extends Component {
}
}
export default drizzleConnect(
AddLiquidity,
export default connect(
state => ({
isConnected: Boolean(state.web3connect.account),
account: state.web3connect.account,
......@@ -563,7 +562,7 @@ export default drizzleConnect(
dispatch => ({
selectors: () => dispatch(selectors()),
})
)
)(AddLiquidity);
function b(text) {
return <span className="swap__highlight-text">{text}</span>
......
import React, { Component } from 'react';
import { drizzleConnect } from 'drizzle-react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {selectors} from "../../ducks/web3connect";
import classnames from "classnames";
......@@ -205,8 +205,7 @@ class CreateExchange extends Component {
}
}
export default drizzleConnect(
CreateExchange,
export default connect(
state => ({
isConnected: Boolean(state.web3connect.account),
account: state.web3connect.account,
......@@ -219,4 +218,4 @@ export default drizzleConnect(
selectors: () => dispatch(selectors()),
addExchange: opts => dispatch(addExchange(opts)),
})
);
\ No newline at end of file
)(CreateExchange);
\ No newline at end of file
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { drizzleConnect } from 'drizzle-react';
import OversizedPanel from "../../components/OversizedPanel";
import Dropdown from "../../assets/images/dropdown.svg";
import Modal from "../../components/Modal";
......
......@@ -4,23 +4,16 @@ import AddLiquidity from './AddLiquidity';
import CreateExchange from './CreateExchange';
import { Switch, Redirect, Route } from 'react-router-dom';
import "./pool.scss";
import Swap from "../Swap";
import Send from "../Send";
import {AnimatedSwitch} from "react-router-transition";
import MediaQuery from "react-responsive";
const ADD_LIQUIDITY = 'Add Liquidity';
const REMOVE_LIQUIDITY = 'Remove Liquidity';
const CREATE_EXCHANGE = 'Create Exchange';
class Pool extends Component {
state = {
selectedMode: ADD_LIQUIDITY,
};
render() {
return (
<div className="pool">
<Header />
<MediaQuery query="(max-device-width: 768px)">
<Header />
</MediaQuery>
<Switch>
<Route exact path="/pool/add" component={AddLiquidity} />
{/*<Route exact path="/remove" component={Send} />*/}
......
import React, { Component } from 'react';
import { drizzleConnect } from 'drizzle-react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import {BigNumber as BN} from "bignumber.js";
......@@ -18,6 +18,7 @@ import EXCHANGE_ABI from '../../abi/exchange';
import "./send.scss";
import promisify from "../../helpers/web3-promisfy";
import MediaQuery from "react-responsive";
const INPUT = 0;
const OUTPUT = 1;
......@@ -697,7 +698,9 @@ class Send extends Component {
return (
<div className="send">
<Header />
<MediaQuery query="(max-device-width: 767px)">
<Header />
</MediaQuery>
<div
className={classnames('swap__content', {
'swap--inactive': !this.props.isConnected,
......@@ -770,8 +773,7 @@ class Send extends Component {
}
}
export default drizzleConnect(
Send,
export default connect(
state => ({
balances: state.web3connect.balances,
isConnected: !!state.web3connect.account,
......@@ -782,7 +784,7 @@ export default drizzleConnect(
dispatch => ({
selectors: () => dispatch(selectors()),
}),
);
)(Send);
const b = text => <span className="swap__highlight-text">{text}</span>;
......
import React, { Component } from 'react';
import { drizzleConnect } from 'drizzle-react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import {BigNumber as BN} from "bignumber.js";
import { selectors } from '../../ducks/web3connect';
import { CSSTransitionGroup } from "react-transition-group";
import MediaQuery from 'react-responsive';
import Header from '../../components/Header';
import NavigationTabs from '../../components/NavigationTabs';
import Modal from '../../components/Modal';
......@@ -25,7 +26,6 @@ class Swap extends Component {
static propTypes = {
account: PropTypes.string,
isConnected: PropTypes.bool.isRequired,
isValid: PropTypes.bool.isRequired,
selectors: PropTypes.func.isRequired,
web3: PropTypes.object.isRequired,
};
......@@ -689,7 +689,9 @@ class Swap extends Component {
return (
<div className="swap">
<Header />
<MediaQuery query="(max-device-width: 767px)">
<Header />
</MediaQuery>
<div
className={classnames('swap__content', {
'swap--inactive': !this.props.isConnected,
......@@ -753,8 +755,7 @@ class Swap extends Component {
}
}
export default drizzleConnect(
Swap,
export default connect(
state => ({
balances: state.web3connect.balances,
isConnected: !!state.web3connect.account,
......@@ -765,7 +766,7 @@ export default drizzleConnect(
dispatch => ({
selectors: () => dispatch(selectors()),
}),
);
)(Swap);
const b = text => <span className="swap__highlight-text">{text}</span>;
......
import { generateContractsInitialState } from 'drizzle'
export default {
contracts: generateContractsInitialState({ contracts: [], events: [], polls: [] }),
}
export default {};
......@@ -2,19 +2,8 @@ import { applyMiddleware, compose, createStore } from 'redux';
import thunk from 'redux-thunk'
import initialState from './initial-state';
import reducer from '../ducks';
import createSagaMiddleware from 'redux-saga';
import { all, fork } from 'redux-saga/effects'
import { drizzleSagas } from 'drizzle'
function* root() {
yield all(
drizzleSagas.map(saga => fork(saga))
)
}
const sagaMiddleware = createSagaMiddleware();
const middleware = [thunk, sagaMiddleware];
const middleware = [thunk];
const enhancers = [];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
......@@ -27,6 +16,4 @@ const store = createStore(
)
);
sagaMiddleware.run(root);
export default store;
\ No newline at end of file
This diff is collapsed.
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