Add chain fees to price carousel (#658)
* add chain fees to price carousel * restore check for valid carousel values * add nym to contributors --------- Co-authored-by: stargut <stargut@starguts-MacBook-Pro.local> Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
This commit is contained in:
parent
056be01f15
commit
e9a5d22a6e
|
@ -0,0 +1,37 @@
|
||||||
|
import lndService from 'ln-service'
|
||||||
|
import lnd from '../lnd'
|
||||||
|
|
||||||
|
const cache = new Map()
|
||||||
|
const expiresIn = 1000 * 30 // 30 seconds in milliseconds
|
||||||
|
|
||||||
|
async function fetchChainFeeRate () {
|
||||||
|
let chainFee = 0
|
||||||
|
try {
|
||||||
|
const fee = await lndService.getChainFeeRate({ lnd })
|
||||||
|
chainFee = fee.tokens_per_vbyte
|
||||||
|
} catch (err) {
|
||||||
|
console.error('fetchChainFee', err)
|
||||||
|
}
|
||||||
|
cache.set('fee', { fee: chainFee, createdAt: Date.now() })
|
||||||
|
return chainFee
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getChainFeeRate () {
|
||||||
|
if (cache.has('fee')) {
|
||||||
|
const { fee, createdAt } = cache.get('fee')
|
||||||
|
const expired = createdAt + expiresIn < Date.now()
|
||||||
|
if (expired) fetchChainFeeRate().catch(console.error) // update cache
|
||||||
|
return fee
|
||||||
|
} else {
|
||||||
|
fetchChainFeeRate().catch(console.error)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Query: {
|
||||||
|
chainFee: async (parent, opts, ctx) => {
|
||||||
|
return await getChainFeeRate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import price from './price'
|
||||||
import { GraphQLJSONObject as JSONObject } from 'graphql-type-json'
|
import { GraphQLJSONObject as JSONObject } from 'graphql-type-json'
|
||||||
import admin from './admin'
|
import admin from './admin'
|
||||||
import blockHeight from './blockHeight'
|
import blockHeight from './blockHeight'
|
||||||
|
import chainFee from './chainFee'
|
||||||
import image from './image'
|
import image from './image'
|
||||||
import { GraphQLScalarType, Kind } from 'graphql'
|
import { GraphQLScalarType, Kind } from 'graphql'
|
||||||
import { createIntScalar } from 'graphql-scalar'
|
import { createIntScalar } from 'graphql-scalar'
|
||||||
|
@ -53,4 +54,4 @@ const limit = createIntScalar({
|
||||||
})
|
})
|
||||||
|
|
||||||
export default [user, item, message, wallet, lnurl, notifications, invite, sub,
|
export default [user, item, message, wallet, lnurl, notifications, invite, sub,
|
||||||
upload, search, growth, rewards, referrals, price, admin, blockHeight, image, { JSONObject }, { Date: date }, { Limit: limit }]
|
upload, search, growth, rewards, referrals, price, admin, blockHeight, chainFee, image, { JSONObject }, { Date: date }, { Limit: limit }]
|
||||||
|
|
|
@ -10,6 +10,7 @@ import search from './search'
|
||||||
import { ME } from '../fragments/users'
|
import { ME } from '../fragments/users'
|
||||||
import { PRICE } from '../fragments/price'
|
import { PRICE } from '../fragments/price'
|
||||||
import { BLOCK_HEIGHT } from '../fragments/blockHeight'
|
import { BLOCK_HEIGHT } from '../fragments/blockHeight'
|
||||||
|
import { CHAIN_FEE } from '../fragments/chainFee'
|
||||||
import { getServerSession } from 'next-auth/next'
|
import { getServerSession } from 'next-auth/next'
|
||||||
import { getAuthOptions } from '../pages/api/auth/[...nextauth]'
|
import { getAuthOptions } from '../pages/api/auth/[...nextauth]'
|
||||||
|
|
||||||
|
@ -94,6 +95,10 @@ export function getGetServerSideProps (
|
||||||
query: BLOCK_HEIGHT, variables: {}
|
query: BLOCK_HEIGHT, variables: {}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { data: { chainFee } } = await client.query({
|
||||||
|
query: CHAIN_FEE, variables: {}
|
||||||
|
})
|
||||||
|
|
||||||
let error = null; let data = null; let props = {}
|
let error = null; let data = null; let props = {}
|
||||||
if (query) {
|
if (query) {
|
||||||
try {
|
try {
|
||||||
|
@ -125,6 +130,7 @@ export function getGetServerSideProps (
|
||||||
me,
|
me,
|
||||||
price,
|
price,
|
||||||
blockHeight,
|
blockHeight,
|
||||||
|
chainFee,
|
||||||
ssrData: data
|
ssrData: data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { gql } from 'graphql-tag'
|
||||||
|
|
||||||
|
export default gql`
|
||||||
|
extend type Query {
|
||||||
|
chainFee: Int!
|
||||||
|
}
|
||||||
|
`
|
|
@ -16,6 +16,7 @@ import referrals from './referrals'
|
||||||
import price from './price'
|
import price from './price'
|
||||||
import admin from './admin'
|
import admin from './admin'
|
||||||
import blockHeight from './blockHeight'
|
import blockHeight from './blockHeight'
|
||||||
|
import chainFee from './chainFee'
|
||||||
import image from './image'
|
import image from './image'
|
||||||
|
|
||||||
const common = gql`
|
const common = gql`
|
||||||
|
@ -37,4 +38,4 @@ const common = gql`
|
||||||
`
|
`
|
||||||
|
|
||||||
export default [common, user, item, itemForward, message, wallet, lnurl, notifications, invite,
|
export default [common, user, item, itemForward, message, wallet, lnurl, notifications, invite,
|
||||||
sub, upload, growth, rewards, referrals, price, admin, blockHeight, image]
|
sub, upload, growth, rewards, referrals, price, admin, blockHeight, chainFee, image]
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { createContext, useContext, useMemo } from 'react'
|
||||||
|
import { useQuery } from '@apollo/client'
|
||||||
|
import { SSR } from '../lib/constants'
|
||||||
|
import { CHAIN_FEE } from '../fragments/chainFee'
|
||||||
|
|
||||||
|
export const ChainFeeContext = createContext({
|
||||||
|
fee: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
export const useChainFee = () => useContext(ChainFeeContext)
|
||||||
|
|
||||||
|
export const ChainFeeProvider = ({ chainFee, children }) => {
|
||||||
|
const { data } = useQuery(CHAIN_FEE, {
|
||||||
|
...(SSR
|
||||||
|
? {}
|
||||||
|
: {
|
||||||
|
pollInterval: 30000,
|
||||||
|
nextFetchPolicy: 'cache-and-network'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const value = useMemo(() => ({
|
||||||
|
fee: data?.chainFee ?? chainFee ?? 0
|
||||||
|
}), [data, chainFee])
|
||||||
|
return (
|
||||||
|
<ChainFeeContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</ChainFeeContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import { PRICE } from '../fragments/price'
|
||||||
import { CURRENCY_SYMBOLS } from '../lib/currency'
|
import { CURRENCY_SYMBOLS } from '../lib/currency'
|
||||||
import { SSR } from '../lib/constants'
|
import { SSR } from '../lib/constants'
|
||||||
import { useBlockHeight } from './block-height'
|
import { useBlockHeight } from './block-height'
|
||||||
|
import { useChainFee } from './chain-fee'
|
||||||
|
|
||||||
export const PriceContext = React.createContext({
|
export const PriceContext = React.createContext({
|
||||||
price: null,
|
price: null,
|
||||||
|
@ -47,13 +48,15 @@ export default function Price ({ className }) {
|
||||||
const satSelection = window.localStorage.getItem('asSats')
|
const satSelection = window.localStorage.getItem('asSats')
|
||||||
setAsSats(satSelection ?? 'fiat')
|
setAsSats(satSelection ?? 'fiat')
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const { price, fiatSymbol } = usePrice()
|
const { price, fiatSymbol } = usePrice()
|
||||||
const { height: blockHeight } = useBlockHeight()
|
const { height: blockHeight } = useBlockHeight()
|
||||||
|
const { fee: chainFee } = useChainFee()
|
||||||
|
|
||||||
if (!price || price < 0 || blockHeight <= 0) return null
|
if (!price || price < 0 || blockHeight <= 0 || chainFee <= 0) return null
|
||||||
|
|
||||||
// Options: yep, 1btc, blockHeight, undefined
|
// Options: yep, 1btc, blockHeight, undefined
|
||||||
// yep -> 1btc -> blockHeight -> undefined -> yep
|
// yep -> 1btc -> blockHeight -> chainFee -> undefined -> yep
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
if (asSats === 'yep') {
|
if (asSats === 'yep') {
|
||||||
window.localStorage.setItem('asSats', '1btc')
|
window.localStorage.setItem('asSats', '1btc')
|
||||||
|
@ -62,6 +65,9 @@ export default function Price ({ className }) {
|
||||||
window.localStorage.setItem('asSats', 'blockHeight')
|
window.localStorage.setItem('asSats', 'blockHeight')
|
||||||
setAsSats('blockHeight')
|
setAsSats('blockHeight')
|
||||||
} else if (asSats === 'blockHeight') {
|
} else if (asSats === 'blockHeight') {
|
||||||
|
window.localStorage.setItem('asSats', 'chainFee')
|
||||||
|
setAsSats('chainFee')
|
||||||
|
} else if (asSats === 'chainFee') {
|
||||||
window.localStorage.removeItem('asSats')
|
window.localStorage.removeItem('asSats')
|
||||||
setAsSats('fiat')
|
setAsSats('fiat')
|
||||||
} else {
|
} else {
|
||||||
|
@ -96,6 +102,14 @@ export default function Price ({ className }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (asSats === 'chainFee') {
|
||||||
|
return (
|
||||||
|
<div className={compClassName} onClick={handleClick} variant='link'>
|
||||||
|
{chainFee} sat/vB
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (asSats === 'fiat') {
|
if (asSats === 'fiat') {
|
||||||
return (
|
return (
|
||||||
<div className={compClassName} onClick={handleClick} variant='link'>
|
<div className={compClassName} onClick={handleClick} variant='link'>
|
||||||
|
|
|
@ -5,3 +5,4 @@ WeAreAllSatoshi
|
||||||
rleed
|
rleed
|
||||||
bitcoinplebdev
|
bitcoinplebdev
|
||||||
benthecarman
|
benthecarman
|
||||||
|
stargut
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { gql } from '@apollo/client'
|
||||||
|
|
||||||
|
export const CHAIN_FEE = gql`{ chainFee }`
|
|
@ -17,6 +17,7 @@ import { SSR } from '../lib/constants'
|
||||||
import NProgress from 'nprogress'
|
import NProgress from 'nprogress'
|
||||||
import 'nprogress/nprogress.css'
|
import 'nprogress/nprogress.css'
|
||||||
import { LoggerProvider } from '../components/logger'
|
import { LoggerProvider } from '../components/logger'
|
||||||
|
import { ChainFeeProvider } from '../components/chain-fee.js'
|
||||||
|
|
||||||
NProgress.configure({
|
NProgress.configure({
|
||||||
showSpinner: false
|
showSpinner: false
|
||||||
|
@ -75,7 +76,7 @@ export default function MyApp ({ Component, pageProps: { ...props } }) {
|
||||||
If we are on the client, we populate the apollo cache with the
|
If we are on the client, we populate the apollo cache with the
|
||||||
ssr data
|
ssr data
|
||||||
*/
|
*/
|
||||||
const { apollo, ssrData, me, price, blockHeight, ...otherProps } = props
|
const { apollo, ssrData, me, price, blockHeight, chainFee, ...otherProps } = props
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
writeQuery(client, apollo, ssrData)
|
writeQuery(client, apollo, ssrData)
|
||||||
}, [client, apollo, ssrData])
|
}, [client, apollo, ssrData])
|
||||||
|
@ -96,9 +97,11 @@ export default function MyApp ({ Component, pageProps: { ...props } }) {
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
<ShowModalProvider>
|
<ShowModalProvider>
|
||||||
<BlockHeightProvider blockHeight={blockHeight}>
|
<BlockHeightProvider blockHeight={blockHeight}>
|
||||||
|
<ChainFeeProvider chainFee={chainFee}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Component ssrData={ssrData} {...otherProps} />
|
<Component ssrData={ssrData} {...otherProps} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
</ChainFeeProvider>
|
||||||
</BlockHeightProvider>
|
</BlockHeightProvider>
|
||||||
</ShowModalProvider>
|
</ShowModalProvider>
|
||||||
</ToastProvider>
|
</ToastProvider>
|
||||||
|
|
Loading…
Reference in New Issue