Merge branch 'main' into mixpanel-improvements

pull/208/head
Tim Pechersky 2021-08-31 13:17:33 +02:00
commit 88b4bbadfe
19 zmienionych plików z 378 dodań i 257 usunięć

Wyświetl plik

@ -1,13 +1,14 @@
import json import json
import logging import logging
from typing import Dict, Any, List, Optional from typing import Dict, Any, List, Optional
from enum import Enum
import boto3 # type: ignore import boto3 # type: ignore
from moonstreamdb.models import ( from moonstreamdb.models import (
EthereumAddress, EthereumAddress,
EthereumLabel, EthereumLabel,
) )
from sqlalchemy.orm import Session from sqlalchemy import text
from sqlalchemy.orm import Session, query, query_expression
from . import data from . import data
from .settings import ETHERSCAN_SMARTCONTRACTS_BUCKET from .settings import ETHERSCAN_SMARTCONTRACTS_BUCKET
@ -50,6 +51,62 @@ def get_contract_source_info(
return None return None
class LabelNames(Enum):
ETHERSCAN_SMARTCONTRACT = "etherscan_smartcontract"
COINMARKETCAP_TOKEN = "coinmarketcap_token"
def get_ethereum_address_info(
db_session: Session, address: str
) -> Optional[data.EthereumAddressInfo]:
query = db_session.query(EthereumAddress.id).filter(
EthereumAddress.address == address
)
id = query.one_or_none()
if id is None:
return None
address_info = data.EthereumAddressInfo(address=address)
etherscan_address_url = f"https://etherscan.io/address/{address}"
blockchain_com_url = f"https://www.blockchain.com/eth/address/{address}"
# Checking for token:
coinmarketcap_label: Optional[EthereumLabel] = (
db_session.query(EthereumLabel)
.filter(EthereumLabel.address_id == id[0])
.filter(EthereumLabel.label == LabelNames.COINMARKETCAP_TOKEN.value)
.order_by(text("created_at desc"))
.limit(1)
.one_or_none()
)
if coinmarketcap_label is not None:
address_info.token = data.EthereumTokenDetails(
name=coinmarketcap_label.label_data["name"],
symbol=coinmarketcap_label.label_data["symbol"],
external_url=[
coinmarketcap_label.label_data["coinmarketcap_url"],
etherscan_address_url,
blockchain_com_url,
],
)
# Checking for smart contract
etherscan_label: Optional[EthereumLabel] = (
db_session.query(EthereumLabel)
.filter(EthereumLabel.address_id == id[0])
.filter(EthereumLabel.label == LabelNames.ETHERSCAN_SMARTCONTRACT.value)
.order_by(text("created_at desc"))
.limit(1)
.one_or_none()
)
if etherscan_label is not None:
address_info.smart_contract = data.EthereumSmartContractDetails(
name=etherscan_label.label_data["name"],
external_url=[etherscan_address_url, blockchain_com_url],
)
return address_info
def get_address_labels( def get_address_labels(
db_session: Session, start: int, limit: int, addresses: Optional[str] = None db_session: Session, start: int, limit: int, addresses: Optional[str] = None
) -> data.AddressListLabelsResponse: ) -> data.AddressListLabelsResponse:

Wyświetl plik

@ -12,6 +12,7 @@ from .routes.subscriptions import app as subscriptions_api
from .routes.users import app as users_api from .routes.users import app as users_api
from .routes.txinfo import app as txinfo_api from .routes.txinfo import app as txinfo_api
from .routes.streams import app as streams_api from .routes.streams import app as streams_api
from .routes.address_info import app as addressinfo_api
from .settings import ORIGINS from .settings import ORIGINS
from .version import MOONSTREAM_VERSION from .version import MOONSTREAM_VERSION
@ -49,3 +50,4 @@ app.mount("/subscriptions", subscriptions_api)
app.mount("/users", users_api) app.mount("/users", users_api)
app.mount("/streams", streams_api) app.mount("/streams", streams_api)
app.mount("/txinfo", txinfo_api) app.mount("/txinfo", txinfo_api)
app.mount("/address_info", addressinfo_api)

Wyświetl plik

@ -157,6 +157,23 @@ class EthereumSmartContractSourceInfo(BaseModel):
compiler_version: str compiler_version: str
class EthereumTokenDetails(BaseModel):
name: Optional[str]
symbol: Optional[str]
external_url: List[str] = []
class EthereumSmartContractDetails(BaseModel):
name: Optional[str]
external_url: List[str] = []
class EthereumAddressInfo(BaseModel):
address: str
token: Optional[EthereumTokenDetails]
smart_contract: Optional[EthereumSmartContractDetails]
class TxinfoEthereumBlockchainResponse(BaseModel): class TxinfoEthereumBlockchainResponse(BaseModel):
tx: EthereumTransaction tx: EthereumTransaction
is_smart_contract_deployment: bool = False is_smart_contract_deployment: bool = False

Wyświetl plik

@ -91,6 +91,7 @@ class BugoutEventProvider:
""" """
is_query_constrained = query.subscription_types or query.subscriptions is_query_constrained = query.subscription_types or query.subscriptions
relevant_subscriptions = user_subscriptions.get(self.event_type) relevant_subscriptions = user_subscriptions.get(self.event_type)
if ( if (
is_query_constrained and self.event_type not in query.subscription_types is_query_constrained and self.event_type not in query.subscription_types
) or not relevant_subscriptions: ) or not relevant_subscriptions:
@ -268,10 +269,46 @@ class BugoutEventProvider:
return self.entry_event(search_results.results[0]) return self.entry_event(search_results.results[0])
class EthereumTXPoolProvider(BugoutEventProvider):
def __init__(
self,
event_type: str,
tags: Optional[List[str]] = None,
batch_size: int = 100,
timeout: float = 30.0,
):
super().__init__(event_type, tags, batch_size, timeout)
def parse_filters(
self, query: StreamQuery, user_subscriptions: Dict[str, List[BugoutResource]]
) -> Optional[List[str]]:
is_query_constrained = query.subscription_types or query.subscriptions
relevant_subscriptions = user_subscriptions.get(self.event_type)
if (
is_query_constrained and self.event_type not in query.subscription_types
) or not relevant_subscriptions:
return None
addresses = [
subscription.resource_data["address"]
for subscription in relevant_subscriptions
]
subscriptions_filters = []
for adress in addresses:
subscriptions_filters.extend(
[f"?#from_address:{adress}", f"?#to_address:{adress}"]
)
return subscriptions_filters
whalewatch_provider = BugoutEventProvider( whalewatch_provider = BugoutEventProvider(
event_type="ethereum_whalewatch", tags=["crawl_type:ethereum_trending"] event_type="ethereum_whalewatch", tags=["crawl_type:ethereum_trending"]
) )
ethereum_txpool_provider = BugoutEventProvider(
ethereum_txpool_provider = EthereumTXPoolProvider(
event_type="ethereum_txpool", tags=["client:ethereum-txpool-crawler-0"] event_type="ethereum_txpool", tags=["client:ethereum-txpool-crawler-0"]
) )

Wyświetl plik

@ -0,0 +1,87 @@
import logging
from typing import Dict, List, Optional
from sqlalchemy.sql.expression import true
from fastapi import FastAPI, Depends, Query, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from moonstreamdb.db import yield_db_session
from sqlalchemy.orm import Session
from .. import actions
from .. import data
from ..middleware import BroodAuthMiddleware
from ..settings import DOCS_TARGET_PATH, ORIGINS, DOCS_PATHS
from ..version import MOONSTREAM_VERSION
logger = logging.getLogger(__name__)
tags_metadata = [
{"name": "addressinfo", "description": "Address public information."},
{"name": "labels", "description": "Addresses label information."},
]
app = FastAPI(
title=f"Moonstream users API.",
description="User, token and password handlers.",
version=MOONSTREAM_VERSION,
openapi_tags=tags_metadata,
openapi_url="/openapi.json",
docs_url=None,
redoc_url=f"/{DOCS_TARGET_PATH}",
)
app.add_middleware(
CORSMiddleware,
allow_origins=ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
whitelist_paths: Dict[str, str] = {}
whitelist_paths.update(DOCS_PATHS)
app.add_middleware(BroodAuthMiddleware, whitelist=whitelist_paths)
@app.get(
"/ethereum_blockchain",
tags=["addressinfo"],
response_model=data.EthereumAddressInfo,
)
async def addressinfo_handler(
address: str,
db_session: Session = Depends(yield_db_session),
) -> Optional[data.EthereumAddressInfo]:
response = actions.get_ethereum_address_info(db_session, address)
return response
@app.get(
"/labels/ethereum_blockchain",
tags=["labels bul"],
response_model=data.AddressListLabelsResponse,
)
async def addresses_labels_bulk_handler(
addresses: Optional[str] = Query(None),
start: int = Query(0),
limit: int = Query(100),
db_session: Session = Depends(yield_db_session),
) -> data.AddressListLabelsResponse:
"""
Fetch labels with additional public information
about known addresses.
"""
if limit > 100:
raise HTTPException(
status_code=406, detail="The limit cannot exceed 100 addresses"
)
try:
addresses_response = actions.get_address_labels(
db_session=db_session, start=start, limit=limit, addresses=addresses
)
except Exception as err:
logger.error(f"Unable to get info about Ethereum addresses {err}")
raise HTTPException(status_code=500)
return addresses_response

Wyświetl plik

@ -10,7 +10,7 @@ from typing import Dict, Optional
from sqlalchemy.sql.expression import true from sqlalchemy.sql.expression import true
from fastapi import FastAPI, Depends, HTTPException, Query from fastapi import FastAPI, Depends
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from moonstreamdb.db import yield_db_session from moonstreamdb.db import yield_db_session
from moonstreamdb.models import EthereumAddress from moonstreamdb.models import EthereumAddress
@ -27,7 +27,6 @@ logger = logging.getLogger(__name__)
tags_metadata = [ tags_metadata = [
{"name": "txinfo", "description": "Ethereum transactions info."}, {"name": "txinfo", "description": "Ethereum transactions info."},
{"name": "address info", "description": "Addresses public information."},
] ]
app = FastAPI( app = FastAPI(
@ -92,31 +91,3 @@ async def txinfo_ethereum_blockchain_handler(
response.smart_contract_address = txinfo_request.tx.to_address response.smart_contract_address = txinfo_request.tx.to_address
response.is_smart_contract_call = True response.is_smart_contract_call = True
return response return response
@app.get(
"/addresses", tags=["address info"], response_model=data.AddressListLabelsResponse
)
async def addresses_labels_handler(
addresses: Optional[str] = Query(None),
start: int = Query(0),
limit: int = Query(100),
db_session: Session = Depends(yield_db_session),
) -> data.AddressListLabelsResponse:
"""
Fetch labels with additional public information
about known addresses.
"""
if limit > 100:
raise HTTPException(
status_code=406, detail="The limit cannot exceed 100 addresses"
)
try:
addresses_response = actions.get_address_labels(
db_session=db_session, start=start, limit=limit, addresses=addresses
)
except Exception as err:
logger.error(f"Unable to get info about Ethereum addresses {err}")
raise HTTPException(status_code=500)
return addresses_response

Wyświetl plik

@ -138,6 +138,7 @@ func PollTxpoolContent(gethClient *rpc.Client, interval int, reporter *humbug.Hu
fmt.Sprintf("max_priority_fee_per_gas:%d", pendingTx.Transaction.MaxPriorityFeePerGas.ToInt()), fmt.Sprintf("max_priority_fee_per_gas:%d", pendingTx.Transaction.MaxPriorityFeePerGas.ToInt()),
fmt.Sprintf("max_fee_per_gas:%d", pendingTx.Transaction.MaxFeePerGas.ToInt()), fmt.Sprintf("max_fee_per_gas:%d", pendingTx.Transaction.MaxFeePerGas.ToInt()),
fmt.Sprintf("gas:%d", pendingTx.Transaction.Gas), fmt.Sprintf("gas:%d", pendingTx.Transaction.Gas),
"crawl_type:ethereum_txpool",
} }
report := humbug.Report{ report := humbug.Report{
Title: ReportTitle, Title: ReportTitle,

Wyświetl plik

@ -1 +0,0 @@
python3

Wyświetl plik

@ -1 +0,0 @@
/usr/bin/python3

Wyświetl plik

@ -1 +0,0 @@
python3

Wyświetl plik

@ -1 +0,0 @@
lib

Wyświetl plik

@ -1,3 +0,0 @@
home = /home/neeraj/external/python/Python-3.9.6/bin
include-system-site-packages = false
version = 3.9.6

Wyświetl plik

@ -1,4 +1,5 @@
import argparse import argparse
import logging
import os import os
import time import time
@ -8,6 +9,9 @@ from sqlalchemy import text
from moonstreamdb.db import yield_db_session_ctx from moonstreamdb.db import yield_db_session_ctx
from moonstreamdb.models import EthereumAddress, EthereumLabel from moonstreamdb.models import EthereumAddress, EthereumLabel
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
COINMARKETCAP_API_KEY = os.environ.get("COINMARKETCAP_API_KEY") COINMARKETCAP_API_KEY = os.environ.get("COINMARKETCAP_API_KEY")
if COINMARKETCAP_API_KEY is None: if COINMARKETCAP_API_KEY is None:
raise ValueError("COINMARKETCAP_API_KEY environment variable must be set") raise ValueError("COINMARKETCAP_API_KEY environment variable must be set")
@ -50,29 +54,52 @@ def identities_cmc_add_handler(args: argparse.Namespace) -> None:
raise Exception(err) raise Exception(err)
if len(response["data"]) == 0: if len(response["data"]) == 0:
print("No more data, crawling finished") logger.info("No more data, crawling finished")
break break
with yield_db_session_ctx() as db_session: with yield_db_session_ctx() as db_session:
latest_address = ( latest_address = 1
latest_address_obj = (
db_session.query(EthereumAddress.id) db_session.query(EthereumAddress.id)
.order_by(text("id desc")) .order_by(text("id desc"))
.limit(1) .limit(1)
.one() .one_or_none()
)[0] )
if latest_address_obj is not None:
latest_address = latest_address_obj[0]
for coin in response["data"]: for coin in response["data"]:
if coin["platform"] is not None: if coin["platform"] is not None:
if ( if (
coin["platform"]["id"] == 1027 coin["platform"]["id"] == 1027
and coin["platform"]["token_address"] is not None and coin["platform"]["token_address"] is not None
): ):
token_address = coin["platform"]["token_address"]
# Check if address already exists
address = (
db_session.query(EthereumAddress)
.filter(EthereumAddress.address == token_address)
.one_or_none()
)
# Add new address
if address is None:
latest_address += 1 latest_address += 1
eth_token_id = latest_address eth_token_id = latest_address
eth_token = EthereumAddress( eth_token = EthereumAddress(
id=eth_token_id, id=eth_token_id,
address=coin["platform"]["token_address"], address=token_address,
) )
db_session.add(eth_token) db_session.add(eth_token)
logger.info(f"Added {coin['name']} token")
else:
eth_token_id = address.id
label = (
db_session.query(EthereumLabel)
.filter(EthereumLabel.address_id == eth_token_id)
.one_or_none()
)
if label is None:
eth_token_label = EthereumLabel( eth_token_label = EthereumLabel(
label="coinmarketcap_token", label="coinmarketcap_token",
address_id=eth_token_id, address_id=eth_token_id,
@ -83,12 +110,14 @@ def identities_cmc_add_handler(args: argparse.Namespace) -> None:
}, },
) )
db_session.add(eth_token_label) db_session.add(eth_token_label)
print(f"Added {coin['name']} token") logger.info(f"Added label for {coin['name']} token")
db_session.commit() db_session.commit()
start_n += limit_n start_n += limit_n
print(f"Loop ended, starting new from {start_n} to {start_n + limit_n - 1}") logger.info(
f"Loop ended, starting new from {start_n} to {start_n + limit_n - 1}"
)
time.sleep(1) time.sleep(1)

Wyświetl plik

@ -2,8 +2,7 @@
export MOONSTREAM_IPC_PATH=null export MOONSTREAM_IPC_PATH=null
export MOONSTREAM_CRAWL_WORKERS=4 export MOONSTREAM_CRAWL_WORKERS=4
export MOONSTREAM_DB_URI="postgresql://<username>:<password>@<db_host>:<db_port>/<db_name>" export MOONSTREAM_DB_URI="postgresql://<username>:<password>@<db_host>:<db_port>/<db_name>"
export MOONSTREAM_ETHERSCAN_TOKEN="<Token for etherscan>"
export MOONSTREAM_ETHERSCAN_TOKEN="TOKEN" export AWS_S3_SMARTCONTRACT_BUCKET="<AWS S3 bucket for smart contracts>"
export AWS_S3_SMARTCONTRACT_BUCKET=""
export MOONSTREAM_HUMBUG_TOKEN="<Token for crawlers store data via Humbug>" export MOONSTREAM_HUMBUG_TOKEN="<Token for crawlers store data via Humbug>"
export COINMARKETCAP_API_KEY="<API key to parse conmarketcap>" export COINMARKETCAP_API_KEY="<API key to parse conmarketcap>"

Wyświetl plik

@ -21,6 +21,7 @@ const DefaultLayout = dynamic(() => import("../src/layouts"), {
}); });
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import NProgress from "nprogress"; import NProgress from "nprogress";
import { WHITE_LOGO_W_TEXT_URL } from "../src/core/constants";
export default function CachingApp({ Component, pageProps }) { export default function CachingApp({ Component, pageProps }) {
const [queryClient] = useState(new QueryClient()); const [queryClient] = useState(new QueryClient());
@ -48,6 +49,10 @@ export default function CachingApp({ Component, pageProps }) {
const getLayout = const getLayout =
Component.getLayout || ((page) => <DefaultLayout>{page}</DefaultLayout>); Component.getLayout || ((page) => <DefaultLayout>{page}</DefaultLayout>);
const headLinks = [
{ rel: "preload", as: "image", href: WHITE_LOGO_W_TEXT_URL },
];
pageProps.preloads && headLinks.push(...pageProps.preloads);
return ( return (
<> <>
<style global jsx>{` <style global jsx>{`
@ -62,7 +67,7 @@ export default function CachingApp({ Component, pageProps }) {
} }
`}</style> `}</style>
{pageProps.metaTags && <HeadSEO {...pageProps.metaTags} />} {pageProps.metaTags && <HeadSEO {...pageProps.metaTags} />}
{pageProps.preloads && <HeadLinks links={pageProps.preloads} />} <HeadLinks links={headLinks} />
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<AppContext>{getLayout(<Component {...pageProps} />)}</AppContext> <AppContext>{getLayout(<Component {...pageProps} />)}</AppContext>
</QueryClientProvider> </QueryClientProvider>

Wyświetl plik

@ -7,8 +7,6 @@ import {
Stack, Stack,
chakra, chakra,
useMediaQuery, useMediaQuery,
UnorderedList,
ListItem,
useBreakpointValue, useBreakpointValue,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { DEFAULT_METATAGS, AWS_ASSETS_PATH } from "../../src/core/constants"; import { DEFAULT_METATAGS, AWS_ASSETS_PATH } from "../../src/core/constants";
@ -133,47 +131,54 @@ const Product = () => {
alignItems="center" alignItems="center"
pb={24} pb={24}
> >
<Stack mx={margin} my={12} maxW="1700px"> <Stack mx={margin} my={12} maxW="1700px" textAlign="justify">
<Heading as="h2" size="md" w="100%" px={12} py={2} borderTopRadius="xl"> <Heading
{`Why you'll love moonstream`} as="h2"
size="md"
placeSelf="center"
px={12}
py={2}
borderTopRadius="xl"
>
{`Why you'll love Moonstream`}
</Heading> </Heading>
<chakra.span pl={2} px={12} py={2}> <chakra.span pl={2} px={12} py={2}>
<Text mb={2}> <Text mb={3}>
We strive for financial inclusion. With cryptocurrencies becoming We strive for financial inclusion. With cryptocurrencies becoming
mainstream, now is the time for anyone with a computer and access to mainstream, now is the time for anyone with a computer and access to
the Internet to utilize this opportunity to make passive income. the Internet to utilize this opportunity to make passive income.
Were here to make it easier. Were here to make it easier.
</Text> </Text>
<Text mb={2}> <Text mb={3}>
Right now our source of data is Ethereum blockchain. Our goal is to Right now our source of data is Ethereum blockchain. Our goal is to
provide a live view of the transactions taking place on every public provide a live view of the transactions taking place on every public
blockchain - from the activity of specific accounts or smart blockchain - from the activity of specific accounts or smart
contracts to updates about general market movements. contracts to updates about general market movements.
</Text> </Text>
<Text mb={2}> <Text mb={3}>
This information comes from the blockchains themselves, from their This information comes from the blockchains themselves, from their
mempools/transaction pools, and from centralized exchanges, social mempools/transaction pools, and from centralized exchanges, social
media, and the news. This forms a stream of information tailored to media, and the news. This forms a stream of information tailored to
your specific needs. your specific needs.
</Text> </Text>
<Text mb={2}> <Text mb={3}>
Were giving you a macro view of the crypto market with direct Were giving you a macro view of the crypto market with direct
access from Moonstream dashboards to execute transactions. You can access from Moonstream dashboards to execute transactions. You can
also set up programs which execute (on- or off-chain) when your also set up programs which execute (on- or off-chain) when your
stream meets certain conditions. stream meets certain conditions.
</Text> </Text>
<Text mb={2}> <Text mb={3}>
Moonstream is accessible through dashboard, API and webhooks. Moonstream is accessible through dashboard, API and webhooks.
</Text> </Text>
<Text mb={2}> <Text mb={3}>
Moonstreams financial inclusion goes beyond providing access to Moonstreams financial inclusion goes beyond providing access to
data. All of our work is open source as we do not believe that data. All of our work is open source as we do not believe that
proprietary technologies are financially inclusive. proprietary technologies are financially inclusive.
</Text> </Text>
<Text mb={2}> <Text mb={3}>
You can read{" "} You can read{" "}
<Link <Link
textColor="primary.500" textColor="secondary.900"
isExternal isExternal
href="https://github.com/bugout-dev/moonstream" href="https://github.com/bugout-dev/moonstream"
> >
@ -181,7 +186,7 @@ const Product = () => {
</Link>{" "} </Link>{" "}
and keep track of our progress using{" "} and keep track of our progress using{" "}
<Link <Link
textColor="primary.500" textColor="secondary.900"
isExternal isExternal
href="https://github.com/bugout-dev/moonstream/milestones" href="https://github.com/bugout-dev/moonstream/milestones"
> >
@ -191,82 +196,6 @@ const Product = () => {
</Text> </Text>
</chakra.span> </chakra.span>
</Stack> </Stack>
<Stack mx={margin} mb={12} maxW="1700px" bgTra>
<Heading as="h2" size="md" w="100%" px={12} py={2} borderTopRadius="xl">
Product
</Heading>
<chakra.span pl={2} px={12} py={2}>
<Text mb={2}>
Moonstream is a product which helps anyone participate in
decentralized finance. From the most sophisticated flash
arbitrageurs to people looking for yield from currency that would
otherwise lie dormant in their exchange accounts.
</Text>
<Text mb={2}>
We aim to go far beyond raw transaction information, enriching our
view with context from centralized exchanges, the news, social
media, and smart contract analysis.
</Text>
<Text mb={2}>
Moonstream users can subscribe to events from any blockchain - from
the activity of specific accounts or smart contracts to updates
about general market movements. This information comes from the
blockchains themselves, from their <b>mempools/transaction</b>{" "}
pools, and from centralized exchanges, social media, and the news.
This forms a stream of information tailored to their specific needs.
</Text>
<Text mb={2}>
They can use this information to execute transactions directly from
the Moonstream frontend or they can set up programs which execute
(on- or off-chain) when their stream meets certain conditions.
</Text>
<Text mb={2}>
Moonstream will be accessible to software through our API and
webhooks.
</Text>
<chakra.span>
<Text>Moonstream customers are:</Text>
<UnorderedList w="75%" pl={4}>
<ListItem>
<b>Development teams deploying decentralized applications -</b>
They use Moonstream to analyze how users are calling their
dapps, and set up alerts for suspicious activity.{" "}
</ListItem>
<ListItem>
<b>Algorithmic funds - </b> They use Moonstream to execute
transactions directly on-chain under prespecified conditions.
</ListItem>
<ListItem>
<b>Crypto traders -</b> They use Moonstream to evaluate trading
strategies based on data from centralized exchanges, the
blockchain, and the transaction pool.
</ListItem>
</UnorderedList>
</chakra.span>
<Text my={2}>
Moonstreams financial inclusion goes beyond providing access to
data. We also help validators and stakers on proof of stake chains
earn rewards in excess of the validation rewards. We pay validators
to send mempool/transaction pool data back to Moonstream, and they
divide these payments between themselves and their stakers. This
helps validators attract more stake on proof of stake blockchains
like Algorand, Solana, and post-merge Ethereum. It also ensures that
Moonstream users have access to the freshest and most geographically
diverse transaction pool data on the market.
</Text>
<Text mb={2}>
All of our work is open source as we do not believe that proprietary
technologies are financially inclusive.{" "}
<Link
textColor="primary.500"
isExternal
href="https://github.com/bugout-dev/moonstream"
>
You can read our code on GitHub.
</Link>
</Text>
</chakra.span>
</Stack>
</Flex> </Flex>
); );
}; };

Wyświetl plik

@ -155,7 +155,6 @@ const Product = () => {
</Box> </Box>
<Box <Box
w="full" w="full"
h="full"
py={48} py={48}
backgroundImage={`url(${assets[`team`]})`} backgroundImage={`url(${assets[`team`]})`}
backgroundSize="cover" backgroundSize="cover"

Wyświetl plik

@ -1,13 +1,13 @@
import React, { Fragment, useContext } from "react"; import React, { Fragment, useContext } from "react";
import RouterLink from "next/link"; import RouterLink from "next/link";
import { import {
Flex,
Button, Button,
Image, Image,
ButtonGroup, ButtonGroup,
Spacer, Spacer,
Link, Link,
IconButton, IconButton,
Flex,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { HamburgerIcon } from "@chakra-ui/icons"; import { HamburgerIcon } from "@chakra-ui/icons";
import useModals from "../core/hooks/useModals"; import useModals from "../core/hooks/useModals";
@ -21,7 +21,6 @@ const LandingNavbar = () => {
const ui = useContext(UIContext); const ui = useContext(UIContext);
const { toggleModal } = useModals(); const { toggleModal } = useModals();
return ( return (
<>
<> <>
{ui.isMobileView && ( {ui.isMobileView && (
<> <>
@ -37,17 +36,22 @@ const LandingNavbar = () => {
<Flex <Flex
pl={ui.isMobileView ? 2 : 8} pl={ui.isMobileView ? 2 : 8}
justifySelf="flex-start" justifySelf="flex-start"
h="full" h="100%"
py={1} py={1}
flexBasis="200px"
flexGrow={1}
flexShirnk={1}
id="Logo Container"
> >
<RouterLink href="/" passHref> <RouterLink href="/" passHref>
<Link h="full"> <Link
<Image as={Image}
w="auto"
h="full" h="full"
justifyContent="left"
src={WHITE_LOGO_W_TEXT_URL} src={WHITE_LOGO_W_TEXT_URL}
alt="Moonstream logo" alt="Moonstream logo"
/> />
</Link>
</RouterLink> </RouterLink>
</Flex> </Flex>
@ -108,10 +112,7 @@ const LandingNavbar = () => {
</Button> </Button>
)} )}
{ui.isLoggedIn && ( {ui.isLoggedIn && (
<ChakraAccountIconButton <ChakraAccountIconButton variant="link" colorScheme="secondary" />
variant="link"
colorScheme="secondary"
/>
)} )}
</ButtonGroup> </ButtonGroup>
</> </>
@ -123,7 +124,6 @@ const LandingNavbar = () => {
</> </>
)} )}
</> </>
</>
); );
}; };

Wyświetl plik

@ -19,16 +19,11 @@ const Navbar = () => {
boxShadow={["sm", "md"]} boxShadow={["sm", "md"]}
alignItems="center" alignItems="center"
id="Navbar" id="Navbar"
minH={["3rem", "3rem", "3rem", "3rem", "3rem", "3rem"]} minH="3rem"
// overflow="initial" maxH="3rem"
bgColor="primary.1200" bgColor="primary.1200"
// flexWrap="wrap" direction="row"
direction={["row", "row", "row", null, "row"]}
// zIndex={100}
w="100%" w="100%"
minW="100%"
m={0}
p={0}
overflow="hidden" overflow="hidden"
> >
<Suspense fallback={""}> <Suspense fallback={""}>