Commit 1a331fea authored by tom's avatar tom

Merge branch 'pw-setup-pt2' into pw-tests

parents 4f37e0b9 59af04fd
...@@ -66,7 +66,7 @@ The app instance could be customized by passing following variables to NodeJS en ...@@ -66,7 +66,7 @@ The app instance could be customized by passing following variables to NodeJS en
| NEXT_PUBLIC_FOOTER_STAKING_LINK | `string` *(optional)* | Link to staking dashboard in the footer | `https://duneanalytics.com/maxaleks/xdai-staking` | | NEXT_PUBLIC_FOOTER_STAKING_LINK | `string` *(optional)* | Link to staking dashboard in the footer | `https://duneanalytics.com/maxaleks/xdai-staking` |
| NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER | `boolean` *(optional)* | Set to false if network doesn't have gas tracker | `true` | | NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER | `boolean` *(optional)* | Set to false if network doesn't have gas tracker | `true` |
| NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` *(optional)* | Set to false if average block time is useless for the network | `true` | | NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` *(optional)* | Set to false if average block time is useless for the network | `true` |
| NEXT_PUBLIC_MARKETPLACE_APP_LIST | `Array<MarketplaceApp>` where `MarketplaceApp` can have following [properties](#marketplace-app-configuration-properties) | List of apps that will be shown on the marketplace page | `[{'author': 'Bob', 'id': 'app', 'title': 'The App', 'logo': 'https://foo.app/icon.png', 'categories': ['security'], 'shortDescription': 'Awesome app', 'site': 'https://foo.app', 'description': 'The best app', 'url': 'https://foo.app/launch'}]` | | NEXT_PUBLIC_MARKETPLACE_APP_LIST | `Array<MarketplaceApp>` where `MarketplaceApp` can have following [properties](#marketplace-app-configuration-properties) | List of apps that will be shown on the marketplace page | `[{'author': 'Bob', 'id': 'app', 'external': true, 'title': 'The App', 'logo': 'https://foo.app/icon.png', 'categories': ['security'], 'shortDescription': 'Awesome app', 'site': 'https://foo.app', 'description': 'The best app', 'url': 'https://foo.app/launch'}]` |
| NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM | `string` | Link to form where authors can submit their dapps to the marketplace | `https://airtable.com/shrqUAcjgGJ4jU88C` | | NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM | `string` | Link to form where authors can submit their dapps to the marketplace | `https://airtable.com/shrqUAcjgGJ4jU88C` |
| NEXT_PUBLIC_NETWORK_EXPLORERS | `Array<NetworkExplorer>` where `NetworkExplorer` can have following [properties](#network-explorer-configuration-properties) | Used to build up links to transactions, blocks, addresses in other chain explorers. | `[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/tx'}}]` | | NEXT_PUBLIC_NETWORK_EXPLORERS | `Array<NetworkExplorer>` where `NetworkExplorer` can have following [properties](#network-explorer-configuration-properties) | Used to build up links to transactions, blocks, addresses in other chain explorers. | `[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/tx'}}]` |
| NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE | `validation` or `mining` *(optional)* | Verification type in the network | `mining` | | NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE | `validation` or `mining` *(optional)* | Verification type in the network | `mining` |
...@@ -125,6 +125,7 @@ The app instance could be customized by passing following variables to NodeJS en ...@@ -125,6 +125,7 @@ The app instance could be customized by passing following variables to NodeJS en
| Property | Type | Description | Example value | Property | Type | Description | Example value
| --- | --- | --- | --- | | --- | --- | --- | --- |
| id | `string` | Used as slug for the app. Must be unique in the app list. | `'app'` | | id | `string` | Used as slug for the app. Must be unique in the app list. | `'app'` |
| external | `boolean` | If true means that the application opens in a new window, but not in an iframe. | `true` |
| title | `string` | Displayed title of the app. | `'The App'` | | title | `string` | Displayed title of the app. | `'The App'` |
| logo | `string` | URL to logo file. Should be at least 144x144. | `'https://foo.app/icon.png'` | | logo | `string` | URL to logo file. Should be at least 144x144. | `'https://foo.app/icon.png'` |
| shortDescription | `string` | Displayed only in the app list. | `'Awesome app'` | | shortDescription | `string` | Displayed only in the app list. | `'Awesome app'` |
......
...@@ -15,7 +15,7 @@ NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 ...@@ -15,7 +15,7 @@ NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_MARKETPLACE_APP_LIST=[{'author': 'Blockscout', 'id': 'token-approval-tracker', 'title': 'Token Approval Tracker', 'logo': 'https://approval-tracker.vercel.app/icon-192.png', 'categories': ['security', 'tools'], 'shortDescription': 'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.', 'site': 'https://docs.blockscout.com/for-users/blockscout-apps/token-approval-tracker', 'description': 'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.', 'url': 'https://approval-tracker.vercel.app/'},{'author': 'Revoke', 'id': 'revoke.cash', 'title':'Revoke.cash', 'logo': 'https://revoke.cash/assets/images/revoke.svg', 'categories': ['security', 'tools'], 'shortDescription': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.', 'site': 'https://revoke.cash/about', 'description': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.', 'url': 'https://revoke.cash/'},{'author': 'Hop', 'id': 'hop-exchange', 'title': 'Hop', 'logo': 'https://goerli.hop.exchange/static/media/hop-logo-black.36655970.svg', 'categories': ['tools'], 'shortDescription': 'Hop is a scalable rollup-to-rollup general token bridge. It allows users to send tokens from one rollup or sidechain to another almost immediately without having to wait for the networks challenge period.', 'site': 'https://help.hop.exchange/hc/en-us/articles/4405172445197-What-is-Hop-Protocol-', 'description': 'Hop is a scalable rollup-to-rollup general token bridge. It allows users to send tokens from one rollup or sidechain to another almost immediately without having to wait for the networks challenge period.', 'url': 'https://goerli.hop.exchange'}, {'author': 'Aave', 'id': 'aave', 'title': 'Aave', 'logo': 'https://staging.aave.com/aaveLogo.svg', 'categories': ['tools'], 'shortDescription': 'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.', 'site': 'https://docs.aave.com/faq/', 'description': 'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.', 'url': 'https://staging.aave.com/'}] NEXT_PUBLIC_MARKETPLACE_APP_LIST=[{'author': 'Blockscout','id':'token-approval-tracker','title':'Token Approval Tracker','logo':'https://approval-tracker.vercel.app/icon-192.png','categories':['security','tools'],'shortDescription':'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.','site':'https://docs.blockscout.com/for-users/blockscout-apps/token-approval-tracker','description':'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.','url':'https://approval-tracker.vercel.app/'},{'author': 'Revoke','id':'revoke.cash','title':'Revoke.cash','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FVBMGyUFnd6CScjfK7CYQ%252Frevoke_sing.png%3Falt%3Dmedia%26token%3D9ab94986-7ab1-41c8-bf7e-d9ce11d23182','categories':['security','tools'],'shortDescription': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.','site': 'https://revoke.cash/about','description': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.','url':'https://revoke.cash/'},{'author':'Aave','id': 'aave','title': 'Aave','logo': 'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FrZkUTIUCG7Zx8BW6Em34%252FAave.png%3Falt%3Dmedia%26token%3D249797a4-4c1e-4372-9cd2-3e48e05e5f30','categories':['tools'],'shortDescription':'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.','site': 'https://docs.aave.com/faq/','description': 'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.','url': 'https://staging.aave.com/'},{'author':'LooksRare','id':'looksrare','external':true,'title':'LooksRare','logo': 'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FeAI4gy3qPMt68mZOZHAx%252FLooksRare.png%3Falt%3Dmedia%26token%3D44c01439-ae09-40aa-b904-3a9ce5b2e002','categories':['tools'],'shortDescription': 'LooksRare is the web3 NFT Marketplace where traders and collectors have earned over $1.3 Billion in rewards.','site':'https://docs.looksrare.org/about/welcome-to-looksrare','description':'LooksRare is the web3 NFT Marketplace where traders and collectors have earned over $1.3 Billion in rewards.','url': 'https://goerli.looksrare.org/'},{'author':'zkSync Bridge','id':'zksync-bridge','external':true,'title':'zkSync Bridge','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FrtQsaAz9BjGBc35tVAnq%252FzkSync.png%3Falt%3Dmedia%26token%3D5c18171c-8ccf-4a88-8f44-680cbf238115','categories':['security','tools'],'shortDescription':'zkSync 2.0 Goerli Bridge','site':'https://v2-docs.zksync.io/dev/','description':'zkSync 2.0 Goerli Bridge','url':'https://portal.zksync.io/bridge'},{'author':'dYdX','id':'dydx','external':true,'title':'dYdX','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FCrOglR72wpi0UhEscwe4%252Fdxdy.png%3Falt%3Dmedia%26token%3D8811909e-93e3-487c-9614-dffce37223e9','categories': ['security','tools'],'shortDescription':'dYdX is a leading decentralized exchange that currently supports perpetual trading. dYdX runs on smart contracts on the Ethereum blockchain, and allows users to trade with no intermediaries.','site':'https://help.dydx.exchange/en/articles/3047379-introduction-and-overview','description':'dYdX is a leading decentralized exchange that currently supports perpetual trading. dYdX runs on smart contracts on the Ethereum blockchain, and allows users to trade with no intermediaries.','url':'https://trade.stage.dydx.exchange/portfolio/overview'},{'author':'MetalSwap','id':'metalswap','title':'MetalSwap','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252F8xqldTvxb6avrwVVc3rS%252FMetalSwap.png%3Falt%3Dmedia%26token%3D92d2db99-853a-487d-8d8c-8cdeaeaaf014','categories':['security','tools'],'shortDescription':'MetalSwap is a decentralised platform that enables hedging swaps in financial markets with the aim of providing a hedge for commodity traders and an investment opportunity for those who contribute to the shared liquidity of the project.','site':'https://docs.metalswap.finance/','description':'MetalSwap is a decentralised platform that enables hedging swaps in financial markets with the aim of providing a hedge for commodity traders and an investment opportunity for those who contribute to the shared liquidity of the project.','url':'https://demo.metalswap.finance/'},{'author':'FaucetDao','id':'faucetdao','title':'FaucetDao','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252Ffnnt3ZNZhzRwqMM5YYjD%252FPlaceholder.png%3Falt%3Dmedia%26token%3D507571bb-d76f-4d96-a35e-2b278608f7ca','categories':['tools'],'shortDescription':'FaucetDao is a decentralised community fund providing liquidity and support to early-stage well vetted blockchain projects.','site':'https://linktr.ee/faucet_dao','description':'FaucetDao is a decentralised community fund providing liquidity and support to early-stage well vetted blockchain projects.','url':'https://www.faucetdao.shop/swap?chain=goerli'},{'author':'Uniswap','id':'uniswap','title':'Uniswap','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FJc0QAyeaBmFIL97tSmGv%252FUniswap.png%3Falt%3Dmedia%26token%3D5d25d796-c273-4e22-92fa-ff85206bec76','categories':['tools'],'shortDescription':'Uniswap is a cryptocurrency exchange which uses a decentralized network protocol.','site':'https://docs.uniswap.org/','description':'Uniswap is a cryptocurrency exchange which uses a decentralized network protocol.','url':'https://app.uniswap.org/swap'}]
# api config # api config
NEXT_PUBLIC_API_BASE_PATH=/eth/goerli NEXT_PUBLIC_API_BASE_PATH=/eth/goerli
...@@ -18,7 +18,7 @@ NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=0x029a799563238d0e75e20be2f4bda0ea68d00172 ...@@ -18,7 +18,7 @@ NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=0x029a799563238d0e75e20be2f4bda0ea68d00172
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_MARKETPLACE_APP_LIST=[{'author': 'Blockscout', 'id': 'token-approval-tracker', 'title': 'Token Approval Tracker', 'logo': 'https://approval-tracker.vercel.app/icon-192.png', 'categories': ['security', 'tools'], 'shortDescription': 'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.', 'site': 'https://docs.blockscout.com/for-users/blockscout-apps/token-approval-tracker', 'description': 'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.', 'url': 'https://approval-tracker.vercel.app/'},{'author': 'Revoke', 'id': 'revoke.cash', 'title':'Revoke.cash', 'logo': 'https://revoke.cash/assets/images/revoke.svg', 'categories': ['security', 'tools'], 'shortDescription': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.', 'site': 'https://revoke.cash/about', 'description': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.', 'url': 'https://revoke.cash/'},{'author': 'Hop', 'id': 'hop-exchange', 'title': 'Hop', 'logo': 'https://goerli.hop.exchange/static/media/hop-logo-black.36655970.svg', 'categories': ['tools'], 'shortDescription': 'Hop is a scalable rollup-to-rollup general token bridge. It allows users to send tokens from one rollup or sidechain to another almost immediately without having to wait for the networks challenge period.', 'site': 'https://help.hop.exchange/hc/en-us/articles/4405172445197-What-is-Hop-Protocol-', 'description': 'Hop is a scalable rollup-to-rollup general token bridge. It allows users to send tokens from one rollup or sidechain to another almost immediately without having to wait for the networks challenge period.', 'url': 'https://goerli.hop.exchange'}, {'author': 'Aave', 'id': 'aave', 'title': 'Aave', 'logo': 'https://staging.aave.com/aaveLogo.svg', 'categories': ['tools'], 'shortDescription': 'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.', 'site': 'https://docs.aave.com/faq/', 'description': 'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.', 'url': 'https://staging.aave.com/'}] NEXT_PUBLIC_MARKETPLACE_APP_LIST=[{'author': 'Blockscout','id':'token-approval-tracker','title':'Token Approval Tracker','logo':'https://approval-tracker.vercel.app/icon-192.png','categories':['security','tools'],'shortDescription':'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.','site':'https://docs.blockscout.com/for-users/blockscout-apps/token-approval-tracker','description':'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.','url':'https://approval-tracker.vercel.app/'},{'author': 'Revoke','id':'revoke.cash','title':'Revoke.cash','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FVBMGyUFnd6CScjfK7CYQ%252Frevoke_sing.png%3Falt%3Dmedia%26token%3D9ab94986-7ab1-41c8-bf7e-d9ce11d23182','categories':['security','tools'],'shortDescription': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.','site': 'https://revoke.cash/about','description': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.','url':'https://revoke.cash/'},{'author':'Aave','id': 'aave','title': 'Aave','logo': 'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FrZkUTIUCG7Zx8BW6Em34%252FAave.png%3Falt%3Dmedia%26token%3D249797a4-4c1e-4372-9cd2-3e48e05e5f30','categories':['tools'],'shortDescription':'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.','site': 'https://docs.aave.com/faq/','description': 'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.','url': 'https://staging.aave.com/'},{'author':'LooksRare','id':'looksrare','external':true,'title':'LooksRare','logo': 'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FeAI4gy3qPMt68mZOZHAx%252FLooksRare.png%3Falt%3Dmedia%26token%3D44c01439-ae09-40aa-b904-3a9ce5b2e002','categories':['tools'],'shortDescription': 'LooksRare is the web3 NFT Marketplace where traders and collectors have earned over $1.3 Billion in rewards.','site':'https://docs.looksrare.org/about/welcome-to-looksrare','description':'LooksRare is the web3 NFT Marketplace where traders and collectors have earned over $1.3 Billion in rewards.','url': 'https://goerli.looksrare.org/'},{'author':'zkSync Bridge','id':'zksync-bridge','external':true,'title':'zkSync Bridge','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FrtQsaAz9BjGBc35tVAnq%252FzkSync.png%3Falt%3Dmedia%26token%3D5c18171c-8ccf-4a88-8f44-680cbf238115','categories':['security','tools'],'shortDescription':'zkSync 2.0 Goerli Bridge','site':'https://v2-docs.zksync.io/dev/','description':'zkSync 2.0 Goerli Bridge','url':'https://portal.zksync.io/bridge'},{'author':'dYdX','id':'dydx','external':true,'title':'dYdX','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FCrOglR72wpi0UhEscwe4%252Fdxdy.png%3Falt%3Dmedia%26token%3D8811909e-93e3-487c-9614-dffce37223e9','categories': ['security','tools'],'shortDescription':'dYdX is a leading decentralized exchange that currently supports perpetual trading. dYdX runs on smart contracts on the Ethereum blockchain, and allows users to trade with no intermediaries.','site':'https://help.dydx.exchange/en/articles/3047379-introduction-and-overview','description':'dYdX is a leading decentralized exchange that currently supports perpetual trading. dYdX runs on smart contracts on the Ethereum blockchain, and allows users to trade with no intermediaries.','url':'https://trade.stage.dydx.exchange/portfolio/overview'},{'author':'MetalSwap','id':'metalswap','title':'MetalSwap','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252F8xqldTvxb6avrwVVc3rS%252FMetalSwap.png%3Falt%3Dmedia%26token%3D92d2db99-853a-487d-8d8c-8cdeaeaaf014','categories':['security','tools'],'shortDescription':'MetalSwap is a decentralised platform that enables hedging swaps in financial markets with the aim of providing a hedge for commodity traders and an investment opportunity for those who contribute to the shared liquidity of the project.','site':'https://docs.metalswap.finance/','description':'MetalSwap is a decentralised platform that enables hedging swaps in financial markets with the aim of providing a hedge for commodity traders and an investment opportunity for those who contribute to the shared liquidity of the project.','url':'https://demo.metalswap.finance/'},{'author':'FaucetDao','id':'faucetdao','title':'FaucetDao','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252Ffnnt3ZNZhzRwqMM5YYjD%252FPlaceholder.png%3Falt%3Dmedia%26token%3D507571bb-d76f-4d96-a35e-2b278608f7ca','categories':['tools'],'shortDescription':'FaucetDao is a decentralised community fund providing liquidity and support to early-stage well vetted blockchain projects.','site':'https://linktr.ee/faucet_dao','description':'FaucetDao is a decentralised community fund providing liquidity and support to early-stage well vetted blockchain projects.','url':'https://www.faucetdao.shop/swap?chain=goerli'},{'author':'Uniswap','id':'uniswap','title':'Uniswap','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FJc0QAyeaBmFIL97tSmGv%252FUniswap.png%3Falt%3Dmedia%26token%3D5d25d796-c273-4e22-92fa-ff85206bec76','categories':['tools'],'shortDescription':'Uniswap is a cryptocurrency exchange which uses a decentralized network protocol.','site':'https://docs.uniswap.org/','description':'Uniswap is a cryptocurrency exchange which uses a decentralized network protocol.','url':'https://app.uniswap.org/swap'}]
# api config # api config
NEXT_PUBLIC_API_BASE_PATH=/poa/core NEXT_PUBLIC_API_BASE_PATH=/poa/core
...@@ -155,17 +155,19 @@ postgres: ...@@ -155,17 +155,19 @@ postgres:
strategy: Recreate strategy: Recreate
persistence: true
resources: resources:
limits: limits:
memory: memory:
_default: "2Gi" _default: "4Gi"
cpu: cpu:
_default: "2" _default: "3"
requests: requests:
memory: memory:
_default: "2Gi" _default: "4Gi"
cpu: cpu:
_default: "2" _default: "3"
environment: environment:
POSTGRES_USER: POSTGRES_USER:
...@@ -498,7 +500,7 @@ frontend: ...@@ -498,7 +500,7 @@ frontend:
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM:
_default: https://airtable.com/shrqUAcjgGJ4jU88C _default: https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_MARKETPLACE_APP_LIST: NEXT_PUBLIC_MARKETPLACE_APP_LIST:
_default: "[{'author': 'Blockscout', 'id': 'token-approval-tracker', 'title': 'Token Approval Tracker', 'logo': 'https://approval-tracker.vercel.app/icon-192.png', 'categories': ['security', 'tools'], 'shortDescription': 'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.', 'site': 'https://docs.blockscout.com/for-users/blockscout-apps/token-approval-tracker', 'description': 'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.', 'url': 'https://approval-tracker.vercel.app/'},{'author': 'Revoke', 'id': 'revoke.cash', 'title':'Revoke.cash', 'logo': 'https://revoke.cash/assets/images/revoke.svg', 'categories': ['security', 'tools'], 'shortDescription': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.', 'site': 'https://revoke.cash/about', 'description': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.', 'url': 'https://revoke.cash/'},{'author': 'Hop', 'id': 'hop-exchange', 'title': 'Hop', 'logo': 'https://goerli.hop.exchange/static/media/hop-logo-black.36655970.svg', 'categories': ['tools'], 'shortDescription': 'Hop is a scalable rollup-to-rollup general token bridge. It allows users to send tokens from one rollup or sidechain to another almost immediately without having to wait for the networks challenge period.', 'site': 'https://help.hop.exchange/hc/en-us/articles/4405172445197-What-is-Hop-Protocol-', 'description': 'Hop is a scalable rollup-to-rollup general token bridge. It allows users to send tokens from one rollup or sidechain to another almost immediately without having to wait for the networks challenge period.', 'url': 'https://goerli.hop.exchange'}, {'author': 'Aave', 'id': 'aave', 'title': 'Aave', 'logo': 'https://staging.aave.com/aaveLogo.svg', 'categories': ['tools'], 'shortDescription': 'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.', 'site': 'https://docs.aave.com/faq/', 'description': 'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.', 'url': 'https://staging.aave.com/'}]" _default: "[{'author': 'Blockscout','id':'token-approval-tracker','title':'Token Approval Tracker','logo':'https://approval-tracker.vercel.app/icon-192.png','categories':['security','tools'],'shortDescription':'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.','site':'https://docs.blockscout.com/for-users/blockscout-apps/token-approval-tracker','description':'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.','url':'https://approval-tracker.vercel.app/'},{'author': 'Revoke','id':'revoke.cash','title':'Revoke.cash','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FVBMGyUFnd6CScjfK7CYQ%252Frevoke_sing.png%3Falt%3Dmedia%26token%3D9ab94986-7ab1-41c8-bf7e-d9ce11d23182','categories':['security','tools'],'shortDescription': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.','site': 'https://revoke.cash/about','description': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.','url':'https://revoke.cash/'},{'author':'Aave','id': 'aave','title': 'Aave','logo': 'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FrZkUTIUCG7Zx8BW6Em34%252FAave.png%3Falt%3Dmedia%26token%3D249797a4-4c1e-4372-9cd2-3e48e05e5f30','categories':['tools'],'shortDescription':'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.','site': 'https://docs.aave.com/faq/','description': 'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.','url': 'https://staging.aave.com/'},{'author':'LooksRare','id':'looksrare','external':true,'title':'LooksRare','logo': 'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FeAI4gy3qPMt68mZOZHAx%252FLooksRare.png%3Falt%3Dmedia%26token%3D44c01439-ae09-40aa-b904-3a9ce5b2e002','categories':['tools'],'shortDescription': 'LooksRare is the web3 NFT Marketplace where traders and collectors have earned over $1.3 Billion in rewards.','site':'https://docs.looksrare.org/about/welcome-to-looksrare','description':'LooksRare is the web3 NFT Marketplace where traders and collectors have earned over $1.3 Billion in rewards.','url': 'https://goerli.looksrare.org/'},{'author':'zkSync Bridge','id':'zksync-bridge','external':true,'title':'zkSync Bridge','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FrtQsaAz9BjGBc35tVAnq%252FzkSync.png%3Falt%3Dmedia%26token%3D5c18171c-8ccf-4a88-8f44-680cbf238115','categories':['security','tools'],'shortDescription':'zkSync 2.0 Goerli Bridge','site':'https://v2-docs.zksync.io/dev/','description':'zkSync 2.0 Goerli Bridge','url':'https://portal.zksync.io/bridge'},{'author':'dYdX','id':'dydx','external':true,'title':'dYdX','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FCrOglR72wpi0UhEscwe4%252Fdxdy.png%3Falt%3Dmedia%26token%3D8811909e-93e3-487c-9614-dffce37223e9','categories': ['security','tools'],'shortDescription':'dYdX is a leading decentralized exchange that currently supports perpetual trading. dYdX runs on smart contracts on the Ethereum blockchain, and allows users to trade with no intermediaries.','site':'https://help.dydx.exchange/en/articles/3047379-introduction-and-overview','description':'dYdX is a leading decentralized exchange that currently supports perpetual trading. dYdX runs on smart contracts on the Ethereum blockchain, and allows users to trade with no intermediaries.','url':'https://trade.stage.dydx.exchange/portfolio/overview'},{'author':'MetalSwap','id':'metalswap','title':'MetalSwap','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252F8xqldTvxb6avrwVVc3rS%252FMetalSwap.png%3Falt%3Dmedia%26token%3D92d2db99-853a-487d-8d8c-8cdeaeaaf014','categories':['security','tools'],'shortDescription':'MetalSwap is a decentralised platform that enables hedging swaps in financial markets with the aim of providing a hedge for commodity traders and an investment opportunity for those who contribute to the shared liquidity of the project.','site':'https://docs.metalswap.finance/','description':'MetalSwap is a decentralised platform that enables hedging swaps in financial markets with the aim of providing a hedge for commodity traders and an investment opportunity for those who contribute to the shared liquidity of the project.','url':'https://demo.metalswap.finance/'},{'author':'FaucetDao','id':'faucetdao','title':'FaucetDao','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252Ffnnt3ZNZhzRwqMM5YYjD%252FPlaceholder.png%3Falt%3Dmedia%26token%3D507571bb-d76f-4d96-a35e-2b278608f7ca','categories':['tools'],'shortDescription':'FaucetDao is a decentralised community fund providing liquidity and support to early-stage well vetted blockchain projects.','site':'https://linktr.ee/faucet_dao','description':'FaucetDao is a decentralised community fund providing liquidity and support to early-stage well vetted blockchain projects.','url':'https://www.faucetdao.shop/swap?chain=goerli'},{'author':'Uniswap','id':'uniswap','title':'Uniswap','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FJc0QAyeaBmFIL97tSmGv%252FUniswap.png%3Falt%3Dmedia%26token%3D5d25d796-c273-4e22-92fa-ff85206bec76','categories':['tools'],'shortDescription':'Uniswap is a cryptocurrency exchange which uses a decentralized network protocol.','site':'https://docs.uniswap.org/','description':'Uniswap is a cryptocurrency exchange which uses a decentralized network protocol.','url':'https://app.uniswap.org/swap'}]"
# api config # api config
NEXT_PUBLIC_API_BASE_PATH: NEXT_PUBLIC_API_BASE_PATH:
_default: / _default: /
......
...@@ -124,19 +124,19 @@ postgres: ...@@ -124,19 +124,19 @@ postgres:
command: '["docker-entrypoint.sh", "-c"]' command: '["docker-entrypoint.sh", "-c"]'
args: '["max_connections=300"]' args: '["max_connections=300"]'
strategy: Recreate persistence: true
resources: resources:
limits: limits:
memory: memory:
_default: "1Gi" _default: "4Gi"
cpu: cpu:
_default: "1" _default: "3"
requests: requests:
memory: memory:
_default: "1Gi" _default: "4Gi"
cpu: cpu:
_default: "1" _default: "3"
environment: environment:
POSTGRES_USER: POSTGRES_USER:
...@@ -375,7 +375,7 @@ frontend: ...@@ -375,7 +375,7 @@ frontend:
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM:
_default: https://airtable.com/shrqUAcjgGJ4jU88C _default: https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_MARKETPLACE_APP_LIST: NEXT_PUBLIC_MARKETPLACE_APP_LIST:
_default: "[{'author': 'Blockscout', 'id': 'token-approval-tracker', 'title': 'Token Approval Tracker', 'logo': 'https://approval-tracker.vercel.app/icon-192.png', 'categories': ['security', 'tools'], 'shortDescription': 'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.', 'site': 'https://docs.blockscout.com/for-users/blockscout-apps/token-approval-tracker', 'description': 'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.', 'url': 'https://approval-tracker.vercel.app/'},{'author': 'Revoke', 'id': 'revoke.cash', 'title':'Revoke.cash', 'logo': 'https://revoke.cash/assets/images/revoke.svg', 'categories': ['security', 'tools'], 'shortDescription': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.', 'site': 'https://revoke.cash/about', 'description': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.', 'url': 'https://revoke.cash/'},{'author': 'Hop', 'id': 'hop-exchange', 'title': 'Hop', 'logo': 'https://goerli.hop.exchange/static/media/hop-logo-black.36655970.svg', 'categories': ['tools'], 'shortDescription': 'Hop is a scalable rollup-to-rollup general token bridge. It allows users to send tokens from one rollup or sidechain to another almost immediately without having to wait for the networks challenge period.', 'site': 'https://help.hop.exchange/hc/en-us/articles/4405172445197-What-is-Hop-Protocol-', 'description': 'Hop is a scalable rollup-to-rollup general token bridge. It allows users to send tokens from one rollup or sidechain to another almost immediately without having to wait for the networks challenge period.', 'url': 'https://goerli.hop.exchange'}, {'author': 'Aave', 'id': 'aave', 'title': 'Aave', 'logo': 'https://staging.aave.com/aaveLogo.svg', 'categories': ['tools'], 'shortDescription': 'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.', 'site': 'https://docs.aave.com/faq/', 'description': 'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.', 'url': 'https://staging.aave.com/'}]" _default: "[{'author': 'Blockscout','id':'token-approval-tracker','title':'Token Approval Tracker','logo':'https://approval-tracker.vercel.app/icon-192.png','categories':['security','tools'],'shortDescription':'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.','site':'https://docs.blockscout.com/for-users/blockscout-apps/token-approval-tracker','description':'Token Approval Tracker shows all approvals for any ERC20-compliant tokens and NFTs and lets to revoke them or adjust the approved amount.','url':'https://approval-tracker.vercel.app/'},{'author': 'Revoke','id':'revoke.cash','title':'Revoke.cash','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FVBMGyUFnd6CScjfK7CYQ%252Frevoke_sing.png%3Falt%3Dmedia%26token%3D9ab94986-7ab1-41c8-bf7e-d9ce11d23182','categories':['security','tools'],'shortDescription': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.','site': 'https://revoke.cash/about','description': 'Revoke.cash comes in as a preventative tool to manage your token allowances and practice proper wallet hygiene. By regularly revoking active allowances you reduce the chances of becoming the victim of allowance exploits.','url':'https://revoke.cash/'},{'author':'Aave','id': 'aave','title': 'Aave','logo': 'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FrZkUTIUCG7Zx8BW6Em34%252FAave.png%3Falt%3Dmedia%26token%3D249797a4-4c1e-4372-9cd2-3e48e05e5f30','categories':['tools'],'shortDescription':'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.','site': 'https://docs.aave.com/faq/','description': 'Aave is a decentralised non-custodial liquidity market protocol where users can participate as suppliers or borrowers. Suppliers provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralised (perpetually) or undercollateralised (one-block liquidity) fashion.','url': 'https://staging.aave.com/'},{'author':'LooksRare','id':'looksrare','external':true,'title':'LooksRare','logo': 'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FeAI4gy3qPMt68mZOZHAx%252FLooksRare.png%3Falt%3Dmedia%26token%3D44c01439-ae09-40aa-b904-3a9ce5b2e002','categories':['tools'],'shortDescription': 'LooksRare is the web3 NFT Marketplace where traders and collectors have earned over $1.3 Billion in rewards.','site':'https://docs.looksrare.org/about/welcome-to-looksrare','description':'LooksRare is the web3 NFT Marketplace where traders and collectors have earned over $1.3 Billion in rewards.','url': 'https://goerli.looksrare.org/'},{'author':'zkSync Bridge','id':'zksync-bridge','external':true,'title':'zkSync Bridge','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FrtQsaAz9BjGBc35tVAnq%252FzkSync.png%3Falt%3Dmedia%26token%3D5c18171c-8ccf-4a88-8f44-680cbf238115','categories':['security','tools'],'shortDescription':'zkSync 2.0 Goerli Bridge','site':'https://v2-docs.zksync.io/dev/','description':'zkSync 2.0 Goerli Bridge','url':'https://portal.zksync.io/bridge'},{'author':'dYdX','id':'dydx','external':true,'title':'dYdX','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FCrOglR72wpi0UhEscwe4%252Fdxdy.png%3Falt%3Dmedia%26token%3D8811909e-93e3-487c-9614-dffce37223e9','categories': ['security','tools'],'shortDescription':'dYdX is a leading decentralized exchange that currently supports perpetual trading. dYdX runs on smart contracts on the Ethereum blockchain, and allows users to trade with no intermediaries.','site':'https://help.dydx.exchange/en/articles/3047379-introduction-and-overview','description':'dYdX is a leading decentralized exchange that currently supports perpetual trading. dYdX runs on smart contracts on the Ethereum blockchain, and allows users to trade with no intermediaries.','url':'https://trade.stage.dydx.exchange/portfolio/overview'},{'author':'MetalSwap','id':'metalswap','title':'MetalSwap','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252F8xqldTvxb6avrwVVc3rS%252FMetalSwap.png%3Falt%3Dmedia%26token%3D92d2db99-853a-487d-8d8c-8cdeaeaaf014','categories':['security','tools'],'shortDescription':'MetalSwap is a decentralised platform that enables hedging swaps in financial markets with the aim of providing a hedge for commodity traders and an investment opportunity for those who contribute to the shared liquidity of the project.','site':'https://docs.metalswap.finance/','description':'MetalSwap is a decentralised platform that enables hedging swaps in financial markets with the aim of providing a hedge for commodity traders and an investment opportunity for those who contribute to the shared liquidity of the project.','url':'https://demo.metalswap.finance/'},{'author':'FaucetDao','id':'faucetdao','title':'FaucetDao','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252Ffnnt3ZNZhzRwqMM5YYjD%252FPlaceholder.png%3Falt%3Dmedia%26token%3D507571bb-d76f-4d96-a35e-2b278608f7ca','categories':['tools'],'shortDescription':'FaucetDao is a decentralised community fund providing liquidity and support to early-stage well vetted blockchain projects.','site':'https://linktr.ee/faucet_dao','description':'FaucetDao is a decentralised community fund providing liquidity and support to early-stage well vetted blockchain projects.','url':'https://www.faucetdao.shop/swap?chain=goerli'},{'author':'Uniswap','id':'uniswap','title':'Uniswap','logo':'https://www.gitbook.com/cdn-cgi/image/width=32,dpr=2.200000047683716,format=auto/https%3A%2F%2Ffiles.gitbook.com%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-Lq1XoWGmy8zggj_u2fM%252Fuploads%252FJc0QAyeaBmFIL97tSmGv%252FUniswap.png%3Falt%3Dmedia%26token%3D5d25d796-c273-4e22-92fa-ff85206bec76','categories':['tools'],'shortDescription':'Uniswap is a cryptocurrency exchange which uses a decentralized network protocol.','site':'https://docs.uniswap.org/','description':'Uniswap is a cryptocurrency exchange which uses a decentralized network protocol.','url':'https://app.uniswap.org/swap'}]"
NEXT_PUBLIC_LOGOUT_URL: NEXT_PUBLIC_LOGOUT_URL:
_default: https://blockscoutcom.us.auth0.com/v2/logout _default: https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_LOGOUT_RETURN_URL: NEXT_PUBLIC_LOGOUT_RETURN_URL:
......
...@@ -19,5 +19,6 @@ ...@@ -19,5 +19,6 @@
"app_index": "/apps/:id", "app_index": "/apps/:id",
"search_results": "/search-results", "search_results": "/search-results",
"other": "/search-results", "other": "/search-results",
"auth": "/auth/auth0" "auth": "/auth/auth0",
"stats": "/stats"
} }
...@@ -85,6 +85,10 @@ export const ROUTES = { ...@@ -85,6 +85,10 @@ export const ROUTES = {
pattern: PATHS.app_index, pattern: PATHS.app_index,
}, },
stats: {
pattern: PATHS.stats,
},
// SEARCH // SEARCH
search_results: { search_results: {
pattern: PATHS.search_results, pattern: PATHS.search_results,
......
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import Stats from '../ui/pages/Stats';
const StatsPage: NextPage = () => {
return (
<>
<Head><title>Ethereum Stats</title></Head>
<Stats/>
</>
);
};
export default StatsPage;
export { getServerSideProps } from 'lib/next/getServerSideProps';
...@@ -19,15 +19,16 @@ export type MarketplaceCategory = { id: MarketplaceCategoriesIds; name: string } ...@@ -19,15 +19,16 @@ export type MarketplaceCategory = { id: MarketplaceCategoriesIds; name: string }
export type AppItemPreview = { export type AppItemPreview = {
id: string; id: string;
external: boolean;
title: string; title: string;
logo: string; logo: string;
shortDescription: string; shortDescription: string;
categories: Array<MarketplaceCategoriesIds>; categories: Array<MarketplaceCategoriesIds>;
url: string;
} }
export type AppItemOverview = AppItemPreview & { export type AppItemOverview = AppItemPreview & {
author: string; author: string;
url: string;
description: string; description: string;
site?: string; site?: string;
twitter?: string; twitter?: string;
......
...@@ -16,4 +16,5 @@ export enum QueryKeys { ...@@ -16,4 +16,5 @@ export enum QueryKeys {
chartsMarket = 'charts-market', chartsMarket = 'charts-market',
indexBlocks='indexBlocks', indexBlocks='indexBlocks',
indexTxs='indexTxs', indexTxs='indexTxs',
jsonRpcUrl='json-rpc-url'
} }
export enum StatsSectionId {
'all',
'accounts',
'blocks',
'transactions',
'gas',
}
export type StatsSectionIds = keyof typeof StatsSectionId;
export type StatsSection = { id: StatsSectionIds; value: string }
export enum StatsIntervalId {
'all',
'oneMonth',
'threeMonths',
'sixMonths',
'oneYear',
}
export type StatsIntervalIds = keyof typeof StatsIntervalId;
export type StatsInterval = { id: StatsIntervalIds; value: string }
import { Box, Heading, Icon, IconButton, Image, Link, LinkBox, LinkOverlay, Text, useColorModeValue } from '@chakra-ui/react'; import { Box, Heading, Icon, IconButton, Image, Link, LinkBox, Text, useColorModeValue } from '@chakra-ui/react';
import NextLink from 'next/link';
import type { MouseEvent } from 'react'; import type { MouseEvent } from 'react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
...@@ -8,9 +7,9 @@ import type { AppItemPreview } from 'types/client/apps'; ...@@ -8,9 +7,9 @@ import type { AppItemPreview } from 'types/client/apps';
import northEastIcon from 'icons/arrows/north-east.svg'; import northEastIcon from 'icons/arrows/north-east.svg';
import starFilledIcon from 'icons/star_filled.svg'; import starFilledIcon from 'icons/star_filled.svg';
import starOutlineIcon from 'icons/star_outline.svg'; import starOutlineIcon from 'icons/star_outline.svg';
import link from 'lib/link/link';
import notEmpty from 'lib/notEmpty'; import notEmpty from 'lib/notEmpty';
import AppCardLink from './AppCardLink';
import { APP_CATEGORIES } from './constants'; import { APP_CATEGORIES } from './constants';
interface Props extends AppItemPreview { interface Props extends AppItemPreview {
...@@ -19,7 +18,10 @@ interface Props extends AppItemPreview { ...@@ -19,7 +18,10 @@ interface Props extends AppItemPreview {
onFavoriteClick: (id: string, isFavorite: boolean) => void; onFavoriteClick: (id: string, isFavorite: boolean) => void;
} }
const AppCard = ({ id, const AppCard = ({
id,
url,
external,
title, title,
logo, logo,
shortDescription, shortDescription,
...@@ -85,11 +87,12 @@ const AppCard = ({ id, ...@@ -85,11 +87,12 @@ const AppCard = ({ id,
size={{ base: 'xs', sm: 'sm' }} size={{ base: 'xs', sm: 'sm' }}
fontWeight="semibold" fontWeight="semibold"
> >
<NextLink href={ link('app_index', { id: id }) } passHref> <AppCardLink
<LinkOverlay> id={ id }
{ title } url={ url }
</LinkOverlay> external={ external }
</NextLink> title={ title }
/>
</Heading> </Heading>
<Text <Text
......
import { LinkOverlay } from '@chakra-ui/react';
import NextLink from 'next/link';
import React from 'react';
import link from 'lib/link/link';
type Props = {
id: string;
url: string;
external: boolean;
title: string;
}
const AppLink = ({ url, external, id, title }: Props) => {
return external ? (
<LinkOverlay href={ url } isExternal={ true }>
{ title }
</LinkOverlay>
) : (
<NextLink href={ link('app_index', { id: id }) } passHref>
<LinkOverlay>
{ title }
</LinkOverlay>
</NextLink>
);
};
export default AppLink;
...@@ -41,6 +41,8 @@ const AppList = ({ apps, onAppClick, displayedAppId, onModalClose, favoriteApps, ...@@ -41,6 +41,8 @@ const AppList = ({ apps, onAppClick, displayedAppId, onModalClose, favoriteApps,
<AppCard <AppCard
onInfoClick={ onAppClick } onInfoClick={ onAppClick }
id={ app.id } id={ app.id }
external={ app.external }
url={ app.url }
title={ app.title } title={ app.title }
logo={ app.logo } logo={ app.logo }
shortDescription={ app.shortDescription } shortDescription={ app.shortDescription }
......
import { import {
Box, Button, Flex, Heading, Icon, IconButton, Image, Link, List, Modal, ModalBody, Box, Flex, Heading, Icon, IconButton, Image, Link, List, Modal, ModalBody,
ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Tag, Text, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Tag, Text,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import NextLink from 'next/link';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { AppItemOverview, MarketplaceCategoriesIds } from 'types/client/apps'; import type { AppItemOverview, MarketplaceCategoriesIds } from 'types/client/apps';
...@@ -15,9 +14,9 @@ import twIcon from 'icons/social/tweet.svg'; ...@@ -15,9 +14,9 @@ import twIcon from 'icons/social/tweet.svg';
import starFilledIcon from 'icons/star_filled.svg'; import starFilledIcon from 'icons/star_filled.svg';
import starOutlineIcon from 'icons/star_outline.svg'; import starOutlineIcon from 'icons/star_outline.svg';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'lib/html-entities';
import link from 'lib/link/link';
import notEmpty from 'lib/notEmpty'; import notEmpty from 'lib/notEmpty';
import AppModalLink from './AppModalLink';
import { APP_CATEGORIES } from './constants'; import { APP_CATEGORIES } from './constants';
type Props = { type Props = {
...@@ -35,6 +34,8 @@ const AppModal = ({ ...@@ -35,6 +34,8 @@ const AppModal = ({
}: Props) => { }: Props) => {
const { const {
title, title,
url,
external,
author, author,
description, description,
site, site,
...@@ -119,16 +120,12 @@ const AppModal = ({ ...@@ -119,16 +120,12 @@ const AppModal = ({
marginTop={{ base: 6, sm: 0 }} marginTop={{ base: 6, sm: 0 }}
> >
<Box display="flex"> <Box display="flex">
<NextLink href={ link('app_index', { id: id }) } passHref> <AppModalLink
<Button id={ id }
as="a" url={ url }
size="sm" external={ external }
marginRight={ 2 } title={ title }
width={{ base: '100%', sm: 'auto' }} />
>
Launch app
</Button>
</NextLink>
<IconButton <IconButton
aria-label="Mark as favorite" aria-label="Mark as favorite"
......
import { Button } from '@chakra-ui/react';
import NextLink from 'next/link';
import React from 'react';
import link from 'lib/link/link';
type Props = {
id: string;
url: string;
external: boolean;
title: string;
}
const AppModalLink = ({ url, external, id }: Props) => {
const buttonProps = {
size: 'sm',
marginRight: 2,
width: { base: '100%', sm: 'auto' },
...(external ? {
target: '_blank',
rel: 'noopener noreferrer',
} : {}),
};
return external ? (
<Button
as="a"
href={ url }
{ ...buttonProps }
>Launch app</Button>
) : (
<NextLink href={ link('app_index', { id: id }) } passHref>
<Button
as="a"
{ ...buttonProps }
>Launch app</Button>
</NextLink>
);
};
export default AppModalLink;
import { useToken } from '@chakra-ui/react'; import { useToken } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ChainIndicatorChartData } from './types'; import type { TimeChartData } from 'ui/shared/chart/types';
import ChartArea from 'ui/shared/chart/ChartArea'; import ChartArea from 'ui/shared/chart/ChartArea';
import ChartLine from 'ui/shared/chart/ChartLine'; import ChartLine from 'ui/shared/chart/ChartLine';
...@@ -11,7 +11,7 @@ import useChartSize from 'ui/shared/chart/useChartSize'; ...@@ -11,7 +11,7 @@ import useChartSize from 'ui/shared/chart/useChartSize';
import useTimeChartController from 'ui/shared/chart/useTimeChartController'; import useTimeChartController from 'ui/shared/chart/useTimeChartController';
interface Props { interface Props {
data: ChainIndicatorChartData; data: TimeChartData;
caption?: string; caption?: string;
} }
......
import { Flex, Spinner } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { ChainIndicatorChartData } from './types'; import type { TimeChartData } from 'ui/shared/chart/types';
import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import ChainIndicatorChart from './ChainIndicatorChart'; import ChainIndicatorChart from './ChainIndicatorChart';
type Props = UseQueryResult<ChainIndicatorChartData>; type Props = UseQueryResult<TimeChartData>;
const ChainIndicatorChartContainer = ({ data, isError, isLoading }: Props) => { const ChainIndicatorChartContainer = ({ data, isError, isLoading }: Props) => {
const content = (() => { const content = (() => {
if (isLoading) { if (isLoading) {
return <Spinner size="md" m="auto"/>; return <ContentLoader mt="auto"/>;
} }
if (isError) { if (isError) {
......
...@@ -18,7 +18,7 @@ interface Props { ...@@ -18,7 +18,7 @@ interface Props {
} }
const ChainIndicatorItem = ({ id, title, value, icon, isSelected, onClick, stats }: Props) => { const ChainIndicatorItem = ({ id, title, value, icon, isSelected, onClick, stats }: Props) => {
const bgColor = useColorModeValue('white', 'gray.900'); const bgColor = useColorModeValue('white', 'black');
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const handleClick = React.useCallback(() => { const handleClick = React.useCallback(() => {
......
...@@ -40,8 +40,8 @@ const ChainIndicators = () => { ...@@ -40,8 +40,8 @@ const ChainIndicators = () => {
() => fetch('/node-api/stats'), () => fetch('/node-api/stats'),
); );
const bgColor = useColorModeValue('white', 'gray.900'); const bgColor = useColorModeValue('white', 'black');
const listBgColor = useColorModeValue('gray.50', 'black'); const listBgColor = useColorModeValue('gray.50', 'gray.900');
if (indicators.length === 0) { if (indicators.length === 0) {
return null; return null;
...@@ -91,7 +91,16 @@ const ChainIndicators = () => { ...@@ -91,7 +91,16 @@ const ChainIndicators = () => {
<ChainIndicatorChartContainer { ...queryResult }/> <ChainIndicatorChartContainer { ...queryResult }/>
</Flex> </Flex>
{ indicators.length > 1 && ( { indicators.length > 1 && (
<Flex flexShrink={ 0 } flexDir="column" as="ul" p={ 3 } borderRadius="lg" bgColor={ listBgColor } rowGap={ 3 } order={{ base: 1, lg: 2 }}> <Flex
flexShrink={ 0 }
flexDir="column"
as="ul"
p={ 3 }
borderRadius="lg"
bgColor={ listBgColor }
rowGap={ 3 }
order={{ base: 1, lg: 2 }}
>
{ indicators.map((indicator) => ( { indicators.map((indicator) => (
<ChainIndicatorItem <ChainIndicatorItem
key={ indicator.id } key={ indicator.id }
......
import type { ChartTransactionResponse, ChartMarketResponse } from 'types/api/charts'; import type { ChartMarketResponse, ChartTransactionResponse } from 'types/api/charts';
import type { Stats } from 'types/api/stats'; import type { Stats } from 'types/api/stats';
import type { QueryKeys } from 'types/client/queries'; import type { QueryKeys } from 'types/client/queries';
import type { TimeChartDataItem } from 'ui/shared/chart/types'; import type { TimeChartData } from 'ui/shared/chart/types';
export type ChartsQueryKeys = QueryKeys.chartsTxs | QueryKeys.chartsMarket; export type ChartsQueryKeys = QueryKeys.chartsTxs | QueryKeys.chartsMarket;
...@@ -16,7 +16,7 @@ export interface TChainIndicator<Q extends ChartsQueryKeys> { ...@@ -16,7 +16,7 @@ export interface TChainIndicator<Q extends ChartsQueryKeys> {
api: { api: {
queryName: Q; queryName: Q;
path: string; path: string;
dataFn: (response: ChartsResponse<Q>) => ChainIndicatorChartData; dataFn: (response: ChartsResponse<Q>) => TimeChartData;
}; };
} }
...@@ -24,5 +24,3 @@ export type ChartsResponse<Q extends ChartsQueryKeys> = ...@@ -24,5 +24,3 @@ export type ChartsResponse<Q extends ChartsQueryKeys> =
Q extends QueryKeys.chartsTxs ? ChartTransactionResponse : Q extends QueryKeys.chartsTxs ? ChartTransactionResponse :
Q extends QueryKeys.chartsMarket ? ChartMarketResponse : Q extends QueryKeys.chartsMarket ? ChartMarketResponse :
never; never;
export type ChainIndicatorChartData = Array<TimeChartDataItem>;
...@@ -2,13 +2,14 @@ import type { UseQueryResult } from '@tanstack/react-query'; ...@@ -2,13 +2,14 @@ import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { TChainIndicator, ChartsResponse, ChartsQueryKeys, ChainIndicatorChartData } from './types'; import type { TChainIndicator, ChartsResponse, ChartsQueryKeys } from './types';
import type { TimeChartData } from 'ui/shared/chart/types';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
type NotUndefined<T> = T extends undefined ? never : T; type NotUndefined<T> = T extends undefined ? never : T;
export default function useFetchCharData<Q extends ChartsQueryKeys>(indicator: TChainIndicator<Q> | undefined): UseQueryResult<ChainIndicatorChartData> { export default function useFetchChartData<Q extends ChartsQueryKeys>(indicator: TChainIndicator<Q> | undefined): UseQueryResult<TimeChartData> {
const fetch = useFetch(); const fetch = useFetch();
type ResponseType = ChartsResponse<NotUndefined<typeof indicator>['api']['queryName']>; type ResponseType = ChartsResponse<NotUndefined<typeof indicator>['api']['queryName']>;
...@@ -23,6 +24,6 @@ export default function useFetchCharData<Q extends ChartsQueryKeys>(indicator: T ...@@ -23,6 +24,6 @@ export default function useFetchCharData<Q extends ChartsQueryKeys>(indicator: T
return { return {
...queryResult, ...queryResult,
data: queryResult.data && indicator ? indicator.api.dataFn(queryResult.data) : queryResult.data, data: queryResult.data && indicator ? indicator.api.dataFn(queryResult.data) : queryResult.data,
} as UseQueryResult<ChainIndicatorChartData>; } as UseQueryResult<TimeChartData>;
}, [ indicator, queryResult ]); }, [ indicator, queryResult ]);
} }
...@@ -25,7 +25,7 @@ const dailyTxsIndicator: TChainIndicator<QueryKeys.chartsTxs> = { ...@@ -25,7 +25,7 @@ const dailyTxsIndicator: TChainIndicator<QueryKeys.chartsTxs> = {
.map((item) => ({ date: new Date(item.date), value: item.tx_count })) .map((item) => ({ date: new Date(item.date), value: item.tx_count }))
.sort(sortByDateDesc), .sort(sortByDateDesc),
name: 'Tx/day', name: 'Tx/day',
valueFormatter: (x) => shortenNumberWithLetter(x, undefined, { maximumFractionDigits: 2 }), valueFormatter: (x: number) => shortenNumberWithLetter(x, undefined, { maximumFractionDigits: 2 }),
} ]), } ]),
}, },
}; };
...@@ -44,7 +44,7 @@ const coinPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = { ...@@ -44,7 +44,7 @@ const coinPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = {
.map((item) => ({ date: new Date(item.date), value: Number(item.closing_price) })) .map((item) => ({ date: new Date(item.date), value: Number(item.closing_price) }))
.sort(sortByDateDesc), .sort(sortByDateDesc),
name: `${ appConfig.network.currency.symbol } price`, name: `${ appConfig.network.currency.symbol } price`,
valueFormatter: (x) => '$' + x.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }), valueFormatter: (x: number) => '$' + x.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }),
} ]), } ]),
}, },
}; };
...@@ -64,7 +64,7 @@ const marketPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = { ...@@ -64,7 +64,7 @@ const marketPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = {
.map((item) => ({ date: new Date(item.date), value: Number(item.closing_price) * Number(response.available_supply) })) .map((item) => ({ date: new Date(item.date), value: Number(item.closing_price) * Number(response.available_supply) }))
.sort(sortByDateDesc), .sort(sortByDateDesc),
name: 'Market cap', name: 'Market cap',
valueFormatter: (x) => '$' + shortenNumberWithLetter(x, undefined, { maximumFractionDigits: 0 }), valueFormatter: (x: number) => '$' + shortenNumberWithLetter(x, undefined, { maximumFractionDigits: 0 }),
} ]), } ]),
}, },
}; };
......
...@@ -14,13 +14,12 @@ import ApiKeyListItem from 'ui/apiKey/ApiKeyTable/ApiKeyListItem'; ...@@ -14,13 +14,12 @@ import ApiKeyListItem from 'ui/apiKey/ApiKeyTable/ApiKeyListItem';
import ApiKeyTable from 'ui/apiKey/ApiKeyTable/ApiKeyTable'; import ApiKeyTable from 'ui/apiKey/ApiKeyTable/ApiKeyTable';
import DeleteApiKeyModal from 'ui/apiKey/DeleteApiKeyModal'; import DeleteApiKeyModal from 'ui/apiKey/DeleteApiKeyModal';
import AccountPageDescription from 'ui/shared/AccountPageDescription'; import AccountPageDescription from 'ui/shared/AccountPageDescription';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile'; import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile';
import SkeletonTable from 'ui/shared/SkeletonTable'; import SkeletonTable from 'ui/shared/SkeletonTable';
import DataFetchAlert from '../shared/DataFetchAlert';
const DATA_LIMIT = 3; const DATA_LIMIT = 3;
const ApiKeysPage: React.FC = () => { const ApiKeysPage: React.FC = () => {
......
...@@ -13,13 +13,12 @@ import CustomAbiListItem from 'ui/customAbi/CustomAbiTable/CustomAbiListItem'; ...@@ -13,13 +13,12 @@ import CustomAbiListItem from 'ui/customAbi/CustomAbiTable/CustomAbiListItem';
import CustomAbiTable from 'ui/customAbi/CustomAbiTable/CustomAbiTable'; import CustomAbiTable from 'ui/customAbi/CustomAbiTable/CustomAbiTable';
import DeleteCustomAbiModal from 'ui/customAbi/DeleteCustomAbiModal'; import DeleteCustomAbiModal from 'ui/customAbi/DeleteCustomAbiModal';
import AccountPageDescription from 'ui/shared/AccountPageDescription'; import AccountPageDescription from 'ui/shared/AccountPageDescription';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile'; import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile';
import SkeletonTable from 'ui/shared/SkeletonTable'; import SkeletonTable from 'ui/shared/SkeletonTable';
import DataFetchAlert from '../shared/DataFetchAlert';
const CustomAbiPage: React.FC = () => { const CustomAbiPage: React.FC = () => {
const customAbiModalProps = useDisclosure(); const customAbiModalProps = useDisclosure();
const deleteModalProps = useDisclosure(); const deleteModalProps = useDisclosure();
......
...@@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; ...@@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
import type { JsonRpcUrlResponse } from 'types/api/json-rpc-url'; import type { JsonRpcUrlResponse } from 'types/api/json-rpc-url';
import type { AppItemOverview } from 'types/client/apps'; import type { AppItemOverview } from 'types/client/apps';
import { QueryKeys } from 'types/client/queries';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
...@@ -27,7 +28,7 @@ const MarketplaceApp = ({ app, isLoading }: Props) => { ...@@ -27,7 +28,7 @@ const MarketplaceApp = ({ app, isLoading }: Props) => {
}, []); }, []);
const { data: jsonRpcUrlResponse } = useQuery<unknown, unknown, JsonRpcUrlResponse>( const { data: jsonRpcUrlResponse } = useQuery<unknown, unknown, JsonRpcUrlResponse>(
[ 'json-rpc-url' ], [ QueryKeys.jsonRpcUrl ],
async() => await fetch(`/node-api/config/json-rpc-url`), async() => await fetch(`/node-api/config/json-rpc-url`),
{ refetchOnMount: false }, { refetchOnMount: false },
); );
......
import { Box } from '@chakra-ui/react';
import React from 'react';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import StatsFilters from '../stats/StatsFilters';
import WidgetsList from '../stats/WidgetsList';
const Stats = () => {
return (
<Page>
<PageTitle text="Ethereum Stats"/>
<Box mb={{ base: 6, sm: 8 }}>
<StatsFilters/>
</Box>
<WidgetsList/>
</Page>
);
};
export default Stats;
import { Box, Text } from '@chakra-ui/react'; import { Box, Text, chakra } from '@chakra-ui/react';
import { keyframes } from '@chakra-ui/system'; import { keyframes } from '@chakra-ui/system';
import React from 'react'; import React from 'react';
...@@ -7,9 +7,13 @@ const runnerAnimation = keyframes` ...@@ -7,9 +7,13 @@ const runnerAnimation = keyframes`
100% { left: '100%'; transform: translateX(-99%); } 100% { left: '100%'; transform: translateX(-99%); }
`; `;
const ContentLoader = () => { interface Props {
className?: string;
}
const ContentLoader = ({ className }: Props) => {
return ( return (
<Box display="inline-block"> <Box display="inline-block" className={ className }>
<Box <Box
width="100%" width="100%"
height="6px" height="6px"
...@@ -31,4 +35,4 @@ const ContentLoader = () => { ...@@ -31,4 +35,4 @@ const ContentLoader = () => {
); );
}; };
export default ContentLoader; export default chakra(ContentLoader);
...@@ -15,6 +15,7 @@ type Props = { ...@@ -15,6 +15,7 @@ type Props = {
const FilterInput = ({ onChange, className, size = 'sm', placeholder }: Props) => { const FilterInput = ({ onChange, className, size = 'sm', placeholder }: Props) => {
const [ filterQuery, setFilterQuery ] = useState(''); const [ filterQuery, setFilterQuery ] = useState('');
const inputRef = React.useRef<HTMLInputElement>(null); const inputRef = React.useRef<HTMLInputElement>(null);
const iconColor = useColorModeValue('blackAlpha.600', 'whiteAlpha.600');
const handleFilterQueryChange = useCallback((event: ChangeEvent<HTMLInputElement>) => { const handleFilterQueryChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
const { value } = event.target; const { value } = event.target;
...@@ -37,7 +38,7 @@ const FilterInput = ({ onChange, className, size = 'sm', placeholder }: Props) = ...@@ -37,7 +38,7 @@ const FilterInput = ({ onChange, className, size = 'sm', placeholder }: Props) =
<InputLeftElement <InputLeftElement
pointerEvents="none" pointerEvents="none"
> >
<Icon as={ searchIcon } color={ useColorModeValue('blackAlpha.600', 'whiteAlpha.600') }/> <Icon as={ searchIcon } color={ iconColor }/>
</InputLeftElement> </InputLeftElement>
<Input <Input
...@@ -50,18 +51,20 @@ const FilterInput = ({ onChange, className, size = 'sm', placeholder }: Props) = ...@@ -50,18 +51,20 @@ const FilterInput = ({ onChange, className, size = 'sm', placeholder }: Props) =
textOverflow="ellipsis" textOverflow="ellipsis"
/> />
<InputRightElement> { filterQuery ? (
<IconButton <InputRightElement>
colorScheme="gray" <IconButton
aria-label="Clear the filter input" colorScheme="gray"
title="Clear the filter input" aria-label="Clear the filter input"
w={ 6 } title="Clear the filter input"
h={ 6 } w={ 6 }
icon={ <Icon as={ crossIcon } w={ 4 } h={ 4 } color={ useColorModeValue('blackAlpha.600', 'whiteAlpha.600') }/> } h={ 6 }
size="sm" icon={ <Icon as={ crossIcon } w={ 4 } h={ 4 } color={ iconColor }/> }
onClick={ handleFilterQueryClear } size="sm"
/> onClick={ handleFilterQueryClear }
</InputRightElement> />
</InputRightElement>
) : null }
</InputGroup> </InputGroup>
); );
}; };
......
import { Box, Button, Grid, Heading, Text, useColorModeValue } from '@chakra-ui/react';
import React, { useCallback } from 'react';
import ChartWidgetGraph from './ChartWidgetGraph';
import { demoData } from './constants/demo-data';
type Props = {
apiMethodURL: string;
title: string;
description: string;
}
const ChartWidget = ({ title, description }: Props) => {
const [ isZoomResetInitial, setIsZoomResetInitial ] = React.useState(true);
const handleZoom = useCallback(() => {
setIsZoomResetInitial(false);
}, []);
const handleZoomResetClick = useCallback(() => {
setIsZoomResetInitial(true);
}, []);
return (
<Box
padding={{ base: 3, md: 4 }}
borderRadius="md"
border="1px"
borderColor={ useColorModeValue('gray.200', 'gray.600') }
>
<Grid>
<Heading
mb={ 1 }
size={{ base: 'xs', md: 'sm' }}
>
{ title }
</Heading>
<Text
mb={ 1 }
gridColumn={ 1 }
as="p"
variant="secondary"
fontSize="xs"
>
{ description }
</Text>
{ !isZoomResetInitial && (
<Button
gridColumn={ 2 }
justifySelf="end"
alignSelf="center"
gridRow="1/3"
size="sm"
variant="outline"
onClick={ handleZoomResetClick }
>
Reset zoom
</Button>
) }
</Grid>
<ChartWidgetGraph
items={ demoData }
onZoom={ handleZoom }
isZoomResetInitial={ isZoomResetInitial }
/>
</Box>
);
};
export default ChartWidget;
import { useToken } from '@chakra-ui/react';
import React, { useEffect, useMemo } from 'react';
import type { TimeChartItem } from 'ui/shared/chart/types';
import ChartArea from 'ui/shared/chart/ChartArea';
import ChartAxis from 'ui/shared/chart/ChartAxis';
import ChartGridLine from 'ui/shared/chart/ChartGridLine';
import ChartLine from 'ui/shared/chart/ChartLine';
import ChartOverlay from 'ui/shared/chart/ChartOverlay';
import ChartSelectionX from 'ui/shared/chart/ChartSelectionX';
import ChartTooltip from 'ui/shared/chart/ChartTooltip';
import useChartSize from 'ui/shared/chart/useChartSize';
import useTimeChartController from 'ui/shared/chart/useTimeChartController';
interface Props {
items: Array<TimeChartItem>;
onZoom: () => void;
isZoomResetInitial: boolean;
}
const CHART_MARGIN = { bottom: 20, left: 65, right: 30, top: 10 };
const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial }: Props) => {
const ref = React.useRef<SVGSVGElement>(null);
const [ range, setRange ] = React.useState<[ number, number ]>([ 0, Infinity ]);
const { width, height, innerWidth, innerHeight } = useChartSize(ref.current, CHART_MARGIN);
const overlayRef = React.useRef<SVGRectElement>(null);
const color = useToken('colors', 'blue.500');
const displayedData = useMemo(() => items.slice(range[0], range[1]).map((d) =>
({ ...d, date: new Date(d.date) })), [ items, range ]);
const chartData = [ { items: items, name: 'chart', color } ];
const { yTickFormat, xScale, yScale } = useTimeChartController({
data: [ { items: displayedData, name: 'chart', color } ],
width: innerWidth,
height: innerHeight,
});
const handleRangeSelect = React.useCallback((nextRange: [ number, number ]) => {
setRange([ range[0] + nextRange[0], range[0] + nextRange[1] ]);
onZoom();
}, [ onZoom, range ]);
useEffect(() => {
if (isZoomResetInitial) {
setRange([ 0, Infinity ]);
}
}, [ isZoomResetInitial ]);
return (
<svg width={ width || '100%' } height={ height || '100%' } ref={ ref } cursor="pointer">
<g transform={ `translate(${ CHART_MARGIN?.left || 0 },${ CHART_MARGIN?.top || 0 })` } opacity={ width ? 1 : 0 }>
<ChartGridLine
type="horizontal"
scale={ yScale }
ticks={ 3 }
size={ innerWidth }
disableAnimation
/>
<ChartArea
data={ displayedData }
color={ color }
xScale={ xScale }
yScale={ yScale }
/>
<ChartLine
data={ displayedData }
xScale={ xScale }
yScale={ yScale }
stroke={ color }
animation="left"
strokeWidth={ 3 }
/>
<ChartAxis
type="left"
scale={ yScale }
ticks={ 5 }
tickFormat={ yTickFormat }
disableAnimation
/>
<ChartOverlay ref={ overlayRef } width={ innerWidth } height={ innerHeight }>
<ChartAxis
type="bottom"
scale={ xScale }
transform={ `translate(0, ${ innerHeight })` }
ticks={ 5 }
anchorEl={ overlayRef.current }
disableAnimation
/>
<ChartTooltip
anchorEl={ overlayRef.current }
width={ innerWidth }
height={ innerHeight }
xScale={ xScale }
yScale={ yScale }
data={ chartData }
/>
<ChartSelectionX
anchorEl={ overlayRef.current }
height={ innerHeight }
scale={ xScale }
data={ chartData }
onSelect={ handleRangeSelect }
/>
</ChartOverlay>
</g>
</svg>
);
};
export default React.memo(ChartWidgetGraph);
import { Box, Button, Icon, Menu, MenuButton, MenuItemOption, MenuList, MenuOptionGroup } from '@chakra-ui/react';
import React, { useCallback } from 'react';
import eastMiniArrowIcon from 'icons/arrows/east-mini.svg';
type Props<T extends string> = {
items: Array<{id: T; value: string}>;
selectedId: T;
onSelect: (id: T) => void;
}
export function StatsDropdownMenu<T extends string>({ items, selectedId, onSelect }: Props<T>) {
const selectedCategory = items.find(category => category.id === selectedId);
const handleSelection = useCallback((id: string | Array<string>) => {
const selectedId = Array.isArray(id) ? id[0] : id;
onSelect(selectedId as T);
}, [ onSelect ]);
return (
<Menu
>
<MenuButton
as={ Button }
size="md"
variant="outline"
colorScheme="gray"
w="100%"
>
<Box
as="span"
display="flex"
alignItems="center"
>
{ selectedCategory?.value }
<Icon transform="rotate(-90deg)" ml={{ base: 'auto', sm: 1 }} as={ eastMiniArrowIcon } w={ 5 } h={ 5 }/>
</Box>
</MenuButton>
<MenuList zIndex={ 3 }>
<MenuOptionGroup
value={ selectedId }
type="radio"
onChange={ handleSelection }
>
{ items.map((item) => (
<MenuItemOption
key={ item.id }
value={ item.id }
>
{ item.value }
</MenuItemOption>
)) }
</MenuOptionGroup>
</MenuList>
</Menu>
);
}
export default StatsDropdownMenu;
import { Grid, GridItem } from '@chakra-ui/react';
import debounce from 'lodash/debounce';
import React, { useCallback, useState } from 'react';
import type { StatsInterval, StatsIntervalIds, StatsSection, StatsSectionIds } from 'types/client/stats';
import FilterInput from 'ui/shared/FilterInput';
import { STATS_INTERVALS, STATS_SECTIONS } from './constants';
import StatsDropdownMenu from './StatsDropdownMenu';
const sectionsList = Object.keys(STATS_SECTIONS).map((id: string) => ({
id: id,
value: STATS_SECTIONS[id as StatsSectionIds],
})) as Array<StatsSection>;
const intervalList = Object.keys(STATS_INTERVALS).map((id: string) => ({
id: id,
value: STATS_INTERVALS[id as StatsIntervalIds],
})) as Array<StatsInterval>;
const StatsFilters = () => {
const [ selectedSectionId, setSelectedSectionId ] = useState<StatsSectionIds>('all');
const [ selectedIntervalId, setSelectedIntervalId ] = useState<StatsIntervalIds>('all');
const [ , setFilterQuery ] = useState('');
// eslint-disable-next-line react-hooks/exhaustive-deps
const debounceFilterCharts = useCallback(debounce(q => setFilterQuery(q), 500), []);
return (
<Grid
gap={ 2 }
templateAreas={{
base: `"input input"
"section interval"`,
lg: `"input section interval"`,
}}
gridTemplateColumns={{ lg: '1fr auto auto' }}
>
<GridItem
w="100%"
area="input"
>
<FilterInput
onChange={ debounceFilterCharts }
placeholder="Find chart, metric..."/>
</GridItem>
<GridItem
w={{ base: '100%', lg: 'auto' }}
area="section"
>
<StatsDropdownMenu
items={ sectionsList }
selectedId={ selectedSectionId }
onSelect={ setSelectedSectionId }
/>
</GridItem>
<GridItem
w={{ base: '100%', lg: 'auto' }}
area="interval"
>
<StatsDropdownMenu
items={ intervalList }
selectedId={ selectedIntervalId }
onSelect={ setSelectedIntervalId }
/>
</GridItem>
</Grid>
);
};
export default StatsFilters;
import { Grid, GridItem, Heading, List, ListItem } from '@chakra-ui/react';
import React from 'react';
import ChartWidget from './ChartWidget';
import { statisticsChartsScheme } from './constants/charts-scheme';
const WidgetsList = () => {
return (
<List>
{
statisticsChartsScheme.map((section) => (
<ListItem
key={ section.id }
mb={ 8 }
_last={{
marginBottom: 0,
}}
>
<Heading
size="md"
mb={ 4 }
>
{ section.title }
</Heading>
<Grid
templateColumns={{
sm: 'repeat(2, 1fr)',
}}
gap={ 4 }
>
{ section.charts.map((chart) => (
<GridItem key={ chart.id }>
<ChartWidget
apiMethodURL={ chart.apiMethodURL }
title={ chart.title }
description={ chart.description }
/>
</GridItem>
)) }
</Grid>
</ListItem>
))
}
</List>
);
};
export default WidgetsList;
export const statisticsChartsScheme = [
{
id: 'blocks',
title: 'Blocks',
charts: [
{
id: 'new-blocks',
title: 'New blocks',
description: 'New blocks number per day',
apiMethodURL: '/node-api/stats/charts/transactions',
},
{
id: 'average-block-size',
title: 'Average block size',
description: 'Average size of blocks in bytes per day',
apiMethodURL: '/node-api/stats/charts/transactions',
},
],
},
{
id: 'transactions',
title: 'Transactions',
charts: [
{
id: 'transaction-fees',
title: 'Transaction fees',
description: 'Amount of tokens paid as fees per day',
apiMethodURL: '/node-api/stats/charts/transactions',
},
{
id: 'native-coin-holders-growth',
title: 'Native coin holders growth',
description: 'Total token holders number per day',
apiMethodURL: '/node-api/stats/charts/transactions',
},
],
},
];
import type { TimeChartItem } from '../../shared/chart/types';
export const demoData: Array<TimeChartItem> = [ { date: new Date('2022-10-17T00:00:00.000Z'), value: 432670 }, {
date: new Date('2022-10-18T00:00:00.000Z'),
value: 370100,
}, { date: new Date('2022-10-19T00:00:00.000Z'), value: 283234 }, { date: new Date('2022-10-20T00:00:00.000Z'), value: 420910 }, {
date: new Date('2022-10-21T00:00:00.000Z'),
value: 411988,
}, { date: new Date('2022-10-22T00:00:00.000Z'), value: 356269 }, { date: new Date('2022-10-23T00:00:00.000Z'), value: 389747 }, {
date: new Date('2022-10-24T00:00:00.000Z'),
value: 387130,
}, { date: new Date('2022-10-25T00:00:00.000Z'), value: 428785 }, { date: new Date('2022-10-26T00:00:00.000Z'), value: 63809 }, {
date: new Date('2022-10-27T00:00:00.000Z'),
value: 50518,
}, { date: new Date('2022-10-28T00:00:00.000Z'), value: 39087 }, { date: new Date('2022-10-29T00:00:00.000Z'), value: 36789 }, {
date: new Date('2022-10-30T00:00:00.000Z'),
value: 48569,
}, { date: new Date('2022-10-31T00:00:00.000Z'), value: 62519 }, { date: new Date('2022-11-01T00:00:00.000Z'), value: 152059 }, {
date: new Date('2022-11-02T00:00:00.000Z'),
value: 63743,
}, { date: new Date('2022-11-03T00:00:00.000Z'), value: 83667 }, { date: new Date('2022-11-04T00:00:00.000Z'), value: 91725 }, {
date: new Date('2022-11-05T00:00:00.000Z'),
value: 82897,
}, { date: new Date('2022-11-06T00:00:00.000Z'), value: 62477 }, { date: new Date('2022-11-07T00:00:00.000Z'), value: 58131 }, {
date: new Date('2022-11-08T00:00:00.000Z'),
value: 74197,
}, { date: new Date('2022-11-09T00:00:00.000Z'), value: 43691 }, { date: new Date('2022-11-10T00:00:00.000Z'), value: 92887 }, {
date: new Date('2022-11-11T00:00:00.000Z'),
value: 79493,
}, { date: new Date('2022-11-12T00:00:00.000Z'), value: 86764 }, { date: new Date('2022-11-13T00:00:00.000Z'), value: 22338 }, {
date: new Date('2022-11-14T00:00:00.000Z'),
value: 62266,
}, { date: new Date('2022-11-15T00:00:00.000Z'), value: 84084 }, { date: new Date('2022-11-16T00:00:00.000Z'), value: 75898 } ];
import type { StatsSectionIds, StatsIntervalIds } from 'types/client/stats';
export const STATS_SECTIONS: { [key in StatsSectionIds]: string } = {
all: 'All stats',
accounts: 'Accounts',
blocks: 'Blocks',
transactions: 'Transactions',
gas: 'Gas',
};
export const STATS_INTERVALS: { [key in StatsIntervalIds]: string } = {
all: 'All time',
oneMonth: '1 month',
threeMonths: '3 months',
sixMonths: '6 months',
oneYear: '1 year',
};
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