Skip to content
This repository has been archived by the owner on Apr 20, 2022. It is now read-only.

Read set component quantities from on-chain #422

Merged
merged 18 commits into from
Nov 16, 2021

Conversation

rootulp
Copy link
Contributor

@rootulp rootulp commented Oct 12, 2021

Type of Change

  • Code Refactoring

Summary of Changes

The goal was to replace requests to the Set backend by reading product underlying tokens and their allocations from on-chain. See google doc for more context.

I observed sporadic 429s from CoinGecko during development. I think a paid tier CoinGecko API key would prevent indexcoop.com from observing similar throttles in production. I haven't observed these throttles since batching requests correctly so this may not strictly be necessary.

Test Data or Screenshots

Note: I removed the 24hr change column

Product Before After
DPI Screen Shot 2021-11-12 at 11 48 29 PM Screen Shot 2021-11-12 at 11 48 09 PM
MVI Screen Shot 2021-11-12 at 11 50 12 PM Screen Shot 2021-11-12 at 11 49 59 PM

@rootulp

This comment has been minimized.

@0xModene
Copy link
Contributor

  • Fetch all index components in one request

I'm looking into doing this for a lot of other things as well. I think we should combine the token calls by categories, not by token. Will require other sizeable changes, but will be cleaner and help get lessen the provider shelf in App.tsx

rootulp added a commit to rootulp/set.js that referenced this pull request Oct 12, 2021
While working on IndexCoop/index-ui#422 I
would've liked to see JSDoc comments for `Position`. I found docs on
https://docs.tokensets.com/contracts/core-contracts/set-token#getpositions
so I added them inline so devs can't see them while using types from
this package.

If reviewers approve of a change like this, I can add doc
comments to other types in this package. I would fetch docs from
https://docs.tokensets.com/contracts but open to other ideas.
@rootulp
Copy link
Contributor Author

rootulp commented Oct 12, 2021

I think we should combine the token calls by categories, not by token.

+1 to consolidating providers

@controtie
Copy link
Contributor

We should continue moving towards deprecating the Tokensets API. Check my understanding below, but I don't think we need to keep the IndexComponents provider if we make some compromises.

  1. Remove the 24hr price charge from the token allocations table for now. We can figure out another way to source this data later (maybe through a batch fetch to coingecko?)
  2. For token image, symbol etc... we should be able to get this data through a token list. I think at Set we use the 1inch token list for coin images. Accessing the token metadata should be easy since it's indexed by the token address.
  3. As part of larger website redesign efforts, we are moving most presentational/browsing elements to a separate website https://beta.indexcoop.com/ for now.
    This bifurcation is going to be similar to what you see on https://uniswap.org/ vs. https://app.uniswap.org/#/swap. I think it's okay for us to have more messaging/expectation that a user must first have their wallet connected to use the core application (this repo)

For reference, I'm basically driving us towards having comparable functionality displayed here:
https://www.tokensets.com/v2/set/ethereum/0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b

@rootulp
Copy link
Contributor Author

rootulp commented Oct 25, 2021

Based on that super helpful context, I'll take a stab at ripping out the IndexComponents provider

@rootulp
Copy link
Contributor Author

rootulp commented Oct 26, 2021

Like @controtie said, it seems possible to entirely remove IndexComponents providers. We can populate some of the position data from tokenLists which we can either copy + paste the JSON output of into a file or fetch client side (took the latter approach in this draft PR).

We'll need an alternative source for these fields: c964c8c#diff-75a9444fe9507131dc7438eb5eb9dc75dc9dfd712c8dc86c905142471030043eR45-R48

@0xModene
Copy link
Contributor

Like @controtie said, it seems possible to entirely remove IndexComponents providers.

good, kill as many of those avalanching providers as we can

@rootulp
Copy link
Contributor Author

rootulp commented Nov 5, 2021

@0xModene or @controtie do ya'll have insight on how percentOfSet is calculated on Set's server side? My naive idea on how to do this is get the CoinGecko price of each position and multiply by quantity of each position.

If we got this route, I think we want to make a batch CoinGecko request to make this performant.

@0xModene
Copy link
Contributor

0xModene commented Nov 5, 2021

@0xModene or @controtie do ya'll have insight on how percentOfSet is calculated on Set's server side? My naive idea on how to do this is get the CoinGecko price of each position and multiply by quantity of each position.

If we got this route, I think we want to make a batch CoinGecko request to make this performant.

This data is all on chain, and I believe it's just simple math

@ncitron may have a better idea

@ncitron
Copy link
Contributor

ncitron commented Nov 5, 2021

Fetching the amount of an underlying component per set token is relatively easy. This can be done through a call to setToken.getDefaultPositionRealUnits(underlyingComponentAddress). From there you can just fetch the prices from coingecko. One thing to be careful about though is the FLI tokens. For FLI, each token has two components: an equity amount of compound deposit tokens (cTokens), and a negative debt amount of USDC.

This is a V0 of reading DPI index components from on-chain using set.js
There is room for a refactor here. Instead of plumbing two types of
components (`IndexComponents` and `SetComponents`) into child React
components, we can collapse these two objects at the provider / data
fetching layer. This will enable us to unit test the data merging logic
which is the sketchiest part about this PR.

We need to fallback to the existing `IndexComponents` fetched via Set
API because it contains other component metadata (e.g. image, symbol,
dailyPercentChange, etc.) which is used in the table.

Additionally we want to fallback to the existing `IndexComponents`
because the set.js request will only work after a user has connected
their wallet. We want the DPI page to still contain index component
information if a user hasn't connected their wallet yet.
The allocation table looks broken with this change
Fetched component price from CoinGecko
Switch to CoinGecko token list because 1inch list doesn't contain
WHALE or WAXE (two recent additions to MVI).
I'm getting HTTP 429's from CoinGecko so this is tough to manually test
@rootulp

This comment has been minimized.

@rootulp

This comment has been minimized.

CoinGecko API is difficult to use. It doesn't appear possible to fetch
all components of DPI in the same request b/c token contract address is
a query string parameter and fetching 18+ tokens in query string
overflows the max length of a URL.
eth2xfli and btc2xfli are still broken
.catch(e => console.error(e))
}

// HACKHACK is there a safer way to fetch decimal places for these components?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do reviewers know of a safer way to determine this? I naively assumed all component quantities would have 18 significant figures but I had to divide by 4, 6, 8, or 18 to get the component quantities for products like Eth2xFli to match up with what is visible on https://www.tokensets.com/explore

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's actually a read only function on the contract called decimals() with no params, returns a BN.

This will need to be checked for sure. MVI has a couple of tokens with different significant digits (WHALE comes to mind)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep WHALE and WAXE are definitely oddities so I special cased them in this function. Thanks for sharing the decimals() function. I manually verified the tokens that I special cased in the function and they all line up.

Symbol decimals contract
WHALE 4 https://etherscan.io/address/0x9355372396e3F6daF13359B7b607a3374cc638e0#readContract
USDC 6 https://etherscan.io/address/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48#readProxyContract
WBTC 8 https://etherscan.io/address/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599#readContract
WAXE 8 https://etherscan.io/address/0x7a2Bc711E19ba6aff6cE8246C546E8c4B4944DFD#readProxyContract
CETH 8 https://etherscan.io/address/0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5#readContract
CWBTC 8 https://etherscan.io/token/0xC11b1268C1A384e55C48c2391d8d480264A3A7F4#readContract

Considering this PR diff is huge, I'm inclined to merge this as-is and tackle reading decimals() from on-chain for all token contracts in a fast-follow PR if that's okay with reviewers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #499

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This data should be available in the tokenList response you're receiving.
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes this way easier than I expected. Will fix in this PR

@rootulp rootulp marked this pull request as ready for review November 13, 2021 04:56
@rootulp
Copy link
Contributor Author

rootulp commented Nov 13, 2021

@0xModene @controtie ready for review. Sorry for my delay on this PR.

@0xModene
Copy link
Contributor

0xModene commented Nov 13, 2021

@rootulp you mention batching requests correctly, and theres a TON of that we could do across the app. We may need to look more into that in another story

@rootulp
Copy link
Contributor Author

rootulp commented Nov 13, 2021

Sounds good. During development, I noticed that we were performing two network requests per product to fetch market chart data. This seems potentially wasteful. E.g.

  1. https://api.coingecko.com/api/v3/coins/data-economy-index/market_chart?vs_currency=usd&days=90
  2. https://api.coingecko.com/api/v3/coins/bankless-bed-index/market_chart?vs_currency=usd&days=max&interval=daily

Upon further inspection, it looks like the days=max query returns data at a lower granularity than days=90. From CoinGecko api docs

Minutely data will be used for duration within 1 day, Hourly data will be used for duration between 1 day and 90 days, Daily data will be used for duration above 90 days.

We likely want the hourly data if we continue supporting the one day and one week options on the market chart.

Screen Shot 2021-11-13 at 12 52 13 PM

@0xModene are there other non-batched CoinGecko requests you are interested in? Down to tackle in a separate GH issue

@0xModene
Copy link
Contributor

@rootulp actually those are coming for different reasons. hourly data can be had up to 90d, but not beyond through CG. The second call is got the 12m+ data charts

Copy link
Contributor

@controtie controtie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey this is a really great first draft @rootulp !
Left some comments re: pulling some data but overall it's really easy to read for something so complicated.

One more ask would be putting in a placeholder text when the user's wallet isn't connected under asset allocation.

Maybe something like Connect Your Metamask under the allocations section?

@@ -22,6 +21,7 @@ import {
ProductToken,
} from 'constants/productTokens'
import BigNumber from 'utils/bignumber'
import { SetComponent } from "../../contexts/SetComponents/SetComponent"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer absolute import

src/contexts/SetComponents/SetComponent.ts Show resolved Hide resolved
src/contexts/SetComponents/SetComponentsProvider.tsx Outdated Show resolved Hide resolved
src/contexts/SetComponents/SetComponentsProvider.tsx Outdated Show resolved Hide resolved
.catch(e => console.error(e))
}

// HACKHACK is there a safer way to fetch decimal places for these components?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This data should be available in the tokenList response you're receiving.
image

Copy link
Contributor

@controtie controtie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping me if you have any questions!

@0xModene
Copy link
Contributor

@controtie I'll look today for a way to lint external/internal imports

@rootulp

This comment has been minimized.

src/App.tsx Outdated
render={() => (window.location.href = discordLink)}
/>
<Route exact path='/discord'
<Route
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI I don't think Prettier actually runs on this codebase b/c this diff was applied when I used VSCode CMD + P > Format Document With... > Prettier

I've seen a handful of instances where double quotes are used when single quotes are specified:

https://github.com/SetProtocol/index-ui/blob/master/.prettierrc.json#L3

We should auto-apply Prettier code-base wide. I don't think the
pre-commit hooks are working as expected
Copy link
Contributor

@0xModene 0xModene left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@@ -3,12 +3,25 @@ import { createContext } from 'react'
interface PricesContextValues {
indexPrice?: string
ethereumPrice?: string
dpiPrice: number
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noting here, but unrelated to PR. Let's make another story that turns these all to numbers to match your new values

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #508

@rootulp
Copy link
Contributor Author

rootulp commented Nov 16, 2021

The last commit changes copy from "Unlock" to "Connect" to match the button in the top right

Screen Shot 2021-11-16 at 11 32 46 AM

@controtie
Copy link
Contributor

I'm having trouble getting the set allocations to show up when connecting my ledger.
The error I'm receiving is An operation that changes the device state is in progress.

I'm not an expert on our ledger integration. This shows up in my google search:
https://github.com/crosswalk-project/chromium-crosswalk/blob/master/third_party/WebKit/Source/modules/webusb/USBDevice.cpp

Since this change sort of coincides with the v2 deployment of the website (strongly encouraging users to login with the majority of non-logged in info displayed in the wrapper marketing application) I'd like to point this to the v2-website-master branch and merge it now while we investigate this issue in parallel

@controtie controtie changed the base branch from master to website-v2-master November 16, 2021 19:17
@controtie controtie merged commit 46e42c0 into IndexCoop:website-v2-master Nov 16, 2021
@rootulp rootulp deleted the rp/index-components branch November 19, 2021 14:45
controtie added a commit that referenced this pull request Nov 23, 2021
* Read set component quantities from on-chain (#422)

* Read set component quantities from on-chain

This is a V0 of reading DPI index components from on-chain using set.js
There is room for a refactor here. Instead of plumbing two types of
components (`IndexComponents` and `SetComponents`) into child React
components, we can collapse these two objects at the provider / data
fetching layer. This will enable us to unit test the data merging logic
which is the sketchiest part about this PR.

We need to fallback to the existing `IndexComponents` fetched via Set
API because it contains other component metadata (e.g. image, symbol,
dailyPercentChange, etc.) which is used in the table.

Additionally we want to fallback to the existing `IndexComponents`
because the set.js request will only work after a user has connected
their wallet. We want the DPI page to still contain index component
information if a user hasn't connected their wallet yet.

* Fetch tokenList data and remove IndexComponents

The allocation table looks broken with this change

* Display allocation per component

Fetched component price from CoinGecko

* Remove percent daily change

* Fetch MVI components

Switch to CoinGecko token list because 1inch list doesn't contain
WHALE or WAXE (two recent additions to MVI).

* [WIP] FLI and Data products

I'm getting HTTP 429's from CoinGecko so this is tough to manually test

* Sort positions based on percent of set

* Remove old component providers and hooks

* WIP: Attempt to batch requests to CoinGecko

CoinGecko API is difficult to use. It doesn't appear possible to fetch
all components of DPI in the same request b/c token contract address is
a query string parameter and fetching 18+ tokens in query string
overflows the max length of a URL.

* WIP batch fetch token prices from CoinGecko

* Fetch MVI component prices

* Fetch all product component prices

eth2xfli and btc2xfli are still broken

* Code cleanup from reviewing draft PR

* Fix NaNs!

* Address @controtie comments

* Format document with Prettier

We should auto-apply Prettier code-base wide. I don't think the
pre-commit hooks are working as expected

* Unlock -> Connect

Label on the button got changed in
#505

* deprecate unused navbar links

* remove home from navbar

* separate product dropdowns

* remove footer

* remove home page

* add index as home placeholder

* remove unused pages in mobile navbar

* remove unused views

* remove unused view imports from home page

* remove news api

* remove unused api + hooks

* remove unused tests

* remove footer tests

* replace homepage with index to dpi

* [WIP] V2 Launch Polish (#512)

* remove product descriptions

* remove index token description

* remove how to buy link

* remove scrollbars on navbar dropdown

* remove airdrop functionality

* remove unused airdrop test

Co-authored-by: Rootul Patel <rootulp@gmail.com>
Co-authored-by: 0xModene <7647623+0xModene@users.noreply.github.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants