Skip to content

Commit

Permalink
Support for Uniswap subgraph and all exchanges (#363)
Browse files Browse the repository at this point in the history
* Fetch missing asset prices from Uniswap subgraph

* Support historical pricing info from Uniswap

* Use only uniswap pairs info for swap receive

* Add query for getting all Uniswap exchanges

* Section list support

* Add sectionlist to react-native-gesture-handler

* Set default data in currency selection list to headerless section

* Fix swipe conflicting gestures and cleanup

* Scroll to top when clearing input

* Added 250ms debounce

* Fix header section logic

Co-authored-by: Bruno Barbieri <brunobar79@gmail.com>
  • Loading branch information
jinchung and brunobar79 authored Feb 14, 2020
1 parent 49a6410 commit 4b0bc0f
Show file tree
Hide file tree
Showing 21 changed files with 757 additions and 408 deletions.
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
]
},
"dependencies": {
"@apollo/client": "^3.0.0-beta.34",
"@bankify/react-native-animate-number": "^0.2.1",
"@hocs/omit-props": "^0.4.0",
"@hocs/safe-timers": "^0.4.0",
Expand Down Expand Up @@ -57,7 +58,7 @@
"console-browserify": "^1.1.0",
"constants-browserify": "^1.0.0",
"cubic-spline": "3.0.3",
"date-fns": "^1.30.1",
"date-fns": "^2.9.0",
"delay": "^4.3.0",
"dns.js": "^1.0.1",
"domain-browser": "^1.2.0",
Expand All @@ -68,6 +69,7 @@
"fbjs": "^1.0.0",
"global": "^4.3.2",
"grapheme-splitter": "^1.0.4",
"graphql-tag": "^2.10.3",
"https-browserify": "0.0.1",
"husky": "^2.4.0",
"i18n-js": "^3.0.11",
Expand Down Expand Up @@ -125,8 +127,8 @@
"react-native-storage": "^1.0.1",
"react-native-store-review": "^0.1.5",
"react-native-svg": "11.0.0",
"react-native-tab-view-viewpager-adapter": "^1.0.7",
"react-native-tab-view": "^2.12.0",
"react-native-tab-view-viewpager-adapter": "^1.0.7",
"react-native-tcp": "^3.3.2",
"react-native-text-input-mask": "^2.0.0",
"react-native-tooltip": "marcosrdz/react-native-tooltip#master",
Expand Down Expand Up @@ -172,7 +174,7 @@
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",
"babel-plugin-date-fns": "^0.2.1",
"babel-plugin-date-fns": "^2.0.0",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-rewire": "^1.2.0",
"babel-plugin-styled-components": "^1.10.6",
Expand Down
22 changes: 22 additions & 0 deletions patches/react-native-gesture-handler+1.5.6.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
diff --git a/node_modules/react-native-gesture-handler/GestureComponents.js b/node_modules/react-native-gesture-handler/GestureComponents.js
index b7f2207..26c9fda 100644
--- a/node_modules/react-native-gesture-handler/GestureComponents.js
+++ b/node_modules/react-native-gesture-handler/GestureComponents.js
@@ -55,4 +55,17 @@ module.exports = {
}
return MEMOIZED.FlatList;
},
+ get SectionList() {
+ if (!MEMOIZED.SectionList) {
+ const ScrollView = this.ScrollView;
+ MEMOIZED.SectionList = React.forwardRef((props, ref) => (
+ <ReactNative.SectionList
+ ref={ref}
+ {...props}
+ renderScrollComponent={scrollProps => <ScrollView {...scrollProps} />}
+ />
+ ));
+ }
+ return MEMOIZED.SectionList;
+ },
};
16 changes: 16 additions & 0 deletions src/apollo/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';

export const client = new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
uri: 'https://api.thegraph.com/subgraphs/name/graphprotocol/compound-v2',
}),
uri: 'https://api.thegraph.com/subgraphs/name/graphprotocol/compound-v2',
});

export const uniswapClient = new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
uri: 'https://api.thegraph.com/subgraphs/name/ianlapham/uniswapbackup',
}),
});
110 changes: 110 additions & 0 deletions src/apollo/queries.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import gql from 'graphql-tag';

export const COMPOUND_MARKET_QUERY = gql`
query markets {
markets(first: 7) {
blockTimestamp
id
exchangeRate
interestRateModelAddress
name
supplyRate
underlyingAddress
underlyingDecimals
underlyingPriceUSD
underlyingPrice
}
}
`;

export const COMPOUND_DAI_ACCOUNT_TOKEN_QUERY = gql`
query accountCToken($addr: String!) {
accountCToken(where: { id: $addr }) {
cTokenBalance
id
lifetimeSupplyInterestAccrued
market {
blockTimestamp
id
exchangeRate
interestRateModelAddress
name
supplyRate
underlyingAddress
underlyingDecimals
underlyingPriceUSD
underlyingPrice
}
}
}
`;

export const COMPOUND_USDC_ACCOUNT_TOKEN_QUERY = gql`
query accountCToken {
accountCToken(
id: "0x39aa39c021dfbae8fac545936693ac917d5e7563-0xf0f21ab2012731542731df194cff6c77d29cb31a"
) {
cTokenBalance
id
lifetimeSupplyInterestAccrued
market {
blockTimestamp
id
exchangeRate
interestRateModelAddress
name
supplyRate
underlyingAddress
underlyingDecimals
underlyingPriceUSD
underlyingPrice
}
}
}
`;

export const UNISWAP_PRICES_QUERY = gql`
query exchanges($addresses: [String]!) {
exchanges(where: { tokenAddress_in: $addresses, price_gt: 0 }) {
id
tokenAddress
tokenSymbol
price
}
}
`;

export const UNISWAP_24HOUR_PRICE_QUERY = gql`
query exchangeHistoricalDatas($timestamp: Int!, $exchangeAddress: String!) {
exchangeHistoricalDatas(
where: { exchangeAddress: $exchangeAddress, timestamp_lt: $timestamp }
first: 1
orderBy: tradeVolumeEth
orderDirection: desc
) {
id
timestamp
exchangeAddress
price
}
}
`;

export const DIRECTORY_QUERY = gql`
query exchanges($excluded: [String]!, $first: Int!, $skip: Int!) {
exchanges(
first: $first
skip: $skip
orderBy: combinedBalanceInUSD
orderDirection: desc
where: { tokenAddress_not_in: $excluded }
) {
id
tokenSymbol
tokenName
tokenDecimals
tokenAddress
ethBalance
}
}
`;
14 changes: 10 additions & 4 deletions src/components/exchange/CurrencySelectionList.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const skeletonTransition = (
</Transition.Sequence>
);

const CurrencySelectionList = ({ itemProps, listItems, showList }) => {
const CurrencySelectionList = ({ itemProps, listItems, showList, query }) => {
const skeletonTransitionRef = useRef();
const [showSkeleton, setShowSkeleton] = useState(true);

Expand Down Expand Up @@ -71,6 +71,7 @@ const CurrencySelectionList = ({ itemProps, listItems, showList }) => {
itemProps={itemProps}
items={listItems}
onLayout={onListLayout}
query={query}
/>
)}
</Centered>
Expand All @@ -89,11 +90,16 @@ const CurrencySelectionList = ({ itemProps, listItems, showList }) => {
CurrencySelectionList.propTypes = {
itemProps: PropTypes.object,
listItems: PropTypes.array,
query: PropTypes.string,
showList: PropTypes.bool,
};

const propsAreEqual = (prev, next) =>
prev.listItems.length === next.listItems.length &&
prev.showList === next.showList;
const propsAreEqual = (prev, next) => {
return (
prev.listItems.length === next.listItems.length &&
prev.query === next.query &&
prev.showList === next.showList
);
};

export default memo(CurrencySelectionList, propsAreEqual);
63 changes: 56 additions & 7 deletions src/components/exchange/ExchangeAssetList.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
import PropTypes from 'prop-types';
import React, { useCallback } from 'react';
import { FlatList } from 'react-native-gesture-handler';
import React, { useCallback, useEffect, useRef } from 'react';
import { StyleSheet, View } from 'react-native';
import { SectionList } from 'react-native-gesture-handler';
import { exchangeModalBorderRadius } from '../../screens/ExchangeModal';
import { CoinRow, ExchangeCoinRow } from '../coin-row';
import { Text } from '../text';
import { colors } from '../../styles';

const styles = StyleSheet.create({
headerStyle: {
backgroundColor: colors.white,
flex: 1,
paddingBottom: 6,
paddingHorizontal: 19,
paddingTop: 12,
},
headerStyleText: {
color: colors.blueGreyDark,
fontSize: 12,
opacity: 0.4,
},
});

const getItemLayout = (_, index) => ({
index,
Expand All @@ -20,18 +38,46 @@ const scrollIndicatorInsets = {
bottom: exchangeModalBorderRadius,
};

const ExchangeAssetList = ({ itemProps, items, onLayout }) => {
const ExchangeAssetList = ({ itemProps, items, onLayout, query }) => {
// Scroll to top once the query is cleared
const scrollView = useRef();
const prevQueryRef = useRef();
useEffect(() => {
prevQueryRef.current = query;
});
const prevQuery = prevQueryRef.current;
if (prevQuery && prevQuery.length && !query.length) {
scrollView.current.scrollToLocation({
animated: true,
itemIndex: 0,
sectionIndex: 0,
viewOffset: 0,
viewPosition: 0,
});
}

const renderItemCallback = useCallback(
({ item }) => <ExchangeCoinRow {...itemProps} item={item} />,
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);

const renderSectionHeaderCallback = useCallback(
({ section }) =>
section.title ? (
<View style={styles.headerStyle}>
<Text style={styles.headerStyleText}>{section.title}</Text>
</View>
) : null,
[]
);

return (
<FlatList
<SectionList
ref={scrollView}
alwaysBounceVertical
contentContainerStyle={contentContainerStyle}
data={items}
sections={items}
directionalLockEnabled
getItemLayout={getItemLayout}
height="100%"
Expand All @@ -40,8 +86,8 @@ const ExchangeAssetList = ({ itemProps, items, onLayout }) => {
keyboardShouldPersistTaps="always"
keyExtractor={keyExtractor}
onLayout={onLayout}
removeClippedSubviews
renderItem={renderItemCallback}
renderSectionHeader={renderSectionHeaderCallback}
scrollEventThrottle={32}
scrollIndicatorInsets={scrollIndicatorInsets}
windowSize={11}
Expand All @@ -53,8 +99,11 @@ ExchangeAssetList.propTypes = {
itemProps: PropTypes.object,
items: PropTypes.array.isRequired,
onLayout: PropTypes.func,
query: PropTypes.string,
};

const propsAreEqual = (prev, next) => prev.items.length === next.items.length;
const propsAreEqual = (prev, next) => {
return prev.items.length === next.items.length && prev.query === next.query;
};

export default React.memo(ExchangeAssetList, propsAreEqual);
8 changes: 3 additions & 5 deletions src/components/send/SendContactList.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,9 @@ class SendContactList extends Component {
}

static getDerivedStateFromProps(props, state) {
const newAssets = filterList(
props.allAssets,
props.currentInput,
'nickname'
);
const newAssets = filterList(props.allAssets, props.currentInput, [
'nickname',
]);
if (newAssets !== state.contacts) {
return { ...state, contacts: newAssets };
}
Expand Down
Loading

0 comments on commit 4b0bc0f

Please sign in to comment.