Merge branch 'main' into fix-state-crawler

pull/684/head
Andrey 2022-12-15 14:26:00 +02:00
commit 6a379c1b55
71 zmienionych plików z 2338 dodań i 448 usunięć

Wyświetl plik

@ -1,21 +1,42 @@
![github read me header](https://user-images.githubusercontent.com/8016073/203381867-f7b56861-04ca-4ae4-a5e6-53e97804817a.png)
# moonstream
\[[Live at https://moonstream.to/](https://moonstream.to)\] | \[[Join us on Discord](https://discord.gg/pYE65FuNSz)\]
[Website](https://moonstream.to)
[Join our Discord](https://discord.gg/pYE65FuNSz)
## What is Moonstream?
Moonstream makes tools that help people build, manage, and maintain their blockchain economies.
Moonstream creates economic infrastructure for web3 projects with a focus on blockchain games.
In order to provide this functionality, we build a lot of technology to crawl blockchains and makes sense of crawled transactions and events. This repository contains that code.
This repository contains Moonstream's complete data analysis stack. The emphasis of it is on collecting actionable data related to the blockchain. The repository contains:
1. Database management tools
2. Blockchain node management tools
3. Blockchain data crawlers
4. Access-controlled API which exposes collected data
## Important resources
1. [Documentation](https://docs.moonstream.to/)
2. [Status page](https://moonstream.to/status/)
3. [On-chain mechanics](https://github.com/bugout-dev/engine)
4. [How to create a dashboard to analyze a smart contract?](https://voracious-gerbil-120.notion.site/Creating-dashboard-for-a-smart-contract-288b1bfa64984b109b79895f69129fce)
## Who uses Moonstream?
Game designers and economists, data scientists, smart contract developers, backend engineers, and teams managing loyalty programs for blockchain projects.
People from different backgrounds who are interested in data, crypto and code.
Moonstream tools are often used by game designers and economists, data scientists, smart contract developers, backend engineers, and teams managing loyalty programs for blockchain projects.
Some of our prominents customers:
Some projects currently using Moonstream:
1. [Laguna Games](https://laguna.games), makers of [Crypto Unicorns](https://cryptounicorns.fun)
2. [RealtyBits](https://realtybits.com)
2. [Game7](https://game7.io)
3. [Champions Ascension](https://www.champions.io/)
Please read [the Game Master's Guide to Moonstream Solutions](https://docs.google.com/document/d/1mjfF8SgRrAZvtCVVxB2qNSUcbbmrH6dTEYSMfHKdEgc/view) if you want to know how Moonstream tools are applied in web3 games.
[Moonworm tool](https://github.com/bugout-dev/moonworm) is used to build datasets of on-chain data related to market activity. The dataset with on-chain activity from the Ethereum NFT market (April 1 to September 25, 2021) is available [on Kaggle](https://www.kaggle.com/datasets/simiotic/ethereum-nfts). The full report on it is published on [GitHub](https://github.com/bugout-dev/moonstream/blob/main/datasets/nfts/papers/ethereum-nfts.pdf).
## Free software
@ -62,4 +83,4 @@ docker-compose up --build
## Contributing
If you would like to contribute to Moonstream, please reach out to @zomglings on the [Moonstream Discord](https://discord.gg/pYE65FuNSz).
We are working on contributing guidelines. In the meantime, please reach out to @zomglings on the [Moonstream Discord](https://discord.gg/pYE65FuNSz).

Wyświetl plik

@ -23,11 +23,9 @@ PIP="${PYTHON_ENV_DIR}/bin/pip"
SCRIPT_DIR="$(realpath $(dirname $0))"
SECRETS_DIR="${SECRETS_DIR:-/home/ubuntu/moonstream-secrets}"
PARAMETERS_ENV_PATH="${SECRETS_DIR}/app.env"
AWS_SSM_PARAMETER_PATH="${AWS_SSM_PARAMETER_PATH:-/moonstream/prod}"
PARAMETERS_SCRIPT="${SCRIPT_DIR}/parameters.py"
# API server service file
SERVICE_FILE="${SCRIPT_DIR}/moonstreamapi.service"
MOONSTREAM_API_SERVICE_FILE="moonstream.service"
set -eu
@ -41,27 +39,21 @@ echo
echo -e "${PREFIX_INFO} Installing Python dependencies"
"${PIP}" install -e "${APP_BACKEND_DIR}/"
echo
echo
echo -e "${PREFIX_INFO} Retrieving deployment parameters"
mkdir -p "${SECRETS_DIR}"
AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}" "${PYTHON}" "${PARAMETERS_SCRIPT}" "${AWS_SSM_PARAMETER_PATH}" -o "${PARAMETERS_ENV_PATH}"
echo
echo
echo -e "${PREFIX_INFO} Install checkenv"
HOME=/root /usr/local/go/bin/go install github.com/bugout-dev/checkenv@latest
HOME=/home/ubuntu /usr/local/go/bin/go install github.com/bugout-dev/checkenv@latest
echo
echo
echo -e "${PREFIX_INFO} Retrieving addition deployment parameters"
AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}" /root/go/bin/checkenv show aws_ssm+Product:moonstream >> "${PARAMETERS_ENV_PATH}"
mkdir -p "${SECRETS_DIR}"
AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}" /home/ubuntu/go/bin/checkenv show aws_ssm+moonstream:true > "${PARAMETERS_ENV_PATH}"
echo
echo
echo -e "${PREFIX_INFO} Replacing existing Moonstream API service definition with ${SERVICE_FILE}"
chmod 644 "${SERVICE_FILE}"
cp "${SERVICE_FILE}" /etc/systemd/system/moonstreamapi.service
systemctl daemon-reload
systemctl restart moonstreamapi.service
systemctl status moonstreamapi.service
echo -e "${PREFIX_INFO} Replacing existing Moonstream API service definition with ${MOONSTREAM_API_SERVICE_FILE}"
chmod 644 "${SCRIPT_DIR}/${MOONSTREAM_API_SERVICE_FILE}"
cp "${SCRIPT_DIR}/${MOONSTREAM_API_SERVICE_FILE}" "/home/ubuntu/.config/systemd/user/${MOONSTREAM_API_SERVICE_FILE}"
XDG_RUNTIME_DIR="/run/user/1000" systemctl --user daemon-reload
XDG_RUNTIME_DIR="/run/user/1000" systemctl --user restart "${MOONSTREAM_API_SERVICE_FILE}"

Wyświetl plik

@ -1,14 +1,16 @@
[Unit]
Description=moonstreamapi-service
After=network.target
StartLimitIntervalSec=300
StartLimitBurst=3
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/moonstream/backend
EnvironmentFile=/home/ubuntu/moonstream-secrets/app.env
Restart=on-failure
RestartSec=15s
ExecStart=/home/ubuntu/moonstream-env/bin/uvicorn --proxy-headers --forwarded-allow-ips='127.0.0.1' --host 127.0.0.1 --port 7481 --workers 8 moonstreamapi.api:app
SyslogIdentifier=moonstreamapi
SyslogIdentifier=moonstream
[Install]
WantedBy=multi-user.target

Wyświetl plik

@ -22,9 +22,7 @@ PYTHON="${PYTHON_ENV_DIR}/bin/python"
PIP="${PYTHON_ENV_DIR}/bin/pip"
SECRETS_DIR="${SECRETS_DIR:-/home/ubuntu/moonstream-secrets}"
PARAMETERS_ENV_PATH="${SECRETS_DIR}/app.env"
AWS_SSM_PARAMETER_PATH="${AWS_SSM_PARAMETER_PATH:-/moonstream/prod}"
SCRIPT_DIR="$(realpath $(dirname $0))"
PARAMETERS_SCRIPT="${SCRIPT_DIR}/parameters.py"
# Service files
MOONCRAWL_SERVICE_FILE="mooncrawl.service"
@ -36,6 +34,7 @@ ETHEREUM_TRENDING_TIMER_FILE="ethereum-trending.timer"
ETHEREUM_TXPOOL_SERVICE_FILE="ethereum-txpool.service"
ETHEREUM_MISSING_SERVICE_FILE="ethereum-missing.service"
ETHEREUM_MISSING_TIMER_FILE="ethereum-missing.timer"
ETHEREUM_MOONWORM_CRAWLER_SERVICE_FILE="ethereum-moonworm-crawler.service"
# Polygon service files
POLYGON_SYNCHRONIZE_SERVICE="polygon-synchronize.service"
@ -51,6 +50,8 @@ POLYGON_STATE_CLEAN_SERVICE_FILE="polygon-state-clean.service"
POLYGON_STATE_CLEAN_TIMER_FILE="polygon-state-clean.timer"
POLYGON_METADATA_SERVICE_FILE="polygon-metadata.service"
POLYGON_METADATA_TIMER_FILE="polygon-metadata.timer"
POLYGON_CU_REPORTS_TOKENONOMICS_SERVICE_FILE="polygon-cu-reports-tokenonomics.service"
POLYGON_CU_REPORTS_TOKENONOMICS_TIMER_FILE="polygon-cu-reports-tokenonomics.timer"
# Mumbai service files
MUMBAI_SYNCHRONIZE_SERVICE="mumbai-synchronize.service"
@ -86,12 +87,6 @@ echo
echo -e "${PREFIX_INFO} Installing Python dependencies"
"${PIP}" install -e "${APP_CRAWLERS_DIR}/mooncrawl/"
echo
echo
echo -e "${PREFIX_INFO} Retrieving deployment parameters"
mkdir -p "${SECRETS_DIR}"
AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}" "${PYTHON}" "${PARAMETERS_SCRIPT}" extract -p "${AWS_SSM_PARAMETER_PATH}" -o "${PARAMETERS_ENV_PATH}"
echo
echo
echo -e "${PREFIX_INFO} Install checkenv"
@ -100,7 +95,8 @@ HOME=/root /usr/local/go/bin/go install github.com/bugout-dev/checkenv@latest
echo
echo
echo -e "${PREFIX_INFO} Retrieving addition deployment parameters"
AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}" /root/go/bin/checkenv show aws_ssm+Product:moonstream >> "${PARAMETERS_ENV_PATH}"
mkdir -p "${SECRETS_DIR}"
AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}" /root/go/bin/checkenv show aws_ssm+moonstream:true > "${PARAMETERS_ENV_PATH}"
echo
echo
@ -149,6 +145,14 @@ cp "${SCRIPT_DIR}/${ETHEREUM_MISSING_TIMER_FILE}" "/etc/systemd/system/${ETHEREU
systemctl daemon-reload
systemctl restart --no-block "${ETHEREUM_MISSING_TIMER_FILE}"
echo
echo
echo -e "${PREFIX_INFO} Replacing existing Ethereum moonworm crawler service definition with ${ETHEREUM_MOONWORM_CRAWLER_SERVICE_FILE}"
chmod 644 "${SCRIPT_DIR}/${ETHEREUM_MOONWORM_CRAWLER_SERVICE_FILE}"
cp "${SCRIPT_DIR}/${ETHEREUM_MOONWORM_CRAWLER_SERVICE_FILE}" "/etc/systemd/system/${ETHEREUM_MOONWORM_CRAWLER_SERVICE_FILE}"
systemctl daemon-reload
systemctl restart --no-block "${ETHEREUM_MOONWORM_CRAWLER_SERVICE_FILE}"
echo
echo
echo -e "${PREFIX_INFO} Replacing existing Polygon block with transactions syncronizer service definition with ${POLYGON_SYNCHRONIZE_SERVICE}"
@ -276,3 +280,12 @@ cp "${SCRIPT_DIR}/${POLYGON_METADATA_SERVICE_FILE}" "/etc/systemd/system/${POLYG
cp "${SCRIPT_DIR}/${POLYGON_METADATA_TIMER_FILE}" "/etc/systemd/system/${POLYGON_METADATA_TIMER_FILE}"
systemctl daemon-reload
systemctl restart --no-block "${POLYGON_METADATA_TIMER_FILE}"
echo
echo
echo -e "${PREFIX_INFO} Replacing existing Polygon CU reports tokenonomics service and timer with: ${POLYGON_CU_REPORTS_TOKENONOMICS_SERVICE_FILE}, ${POLYGON_CU_REPORTS_TOKENONOMICS_TIMER_FILE}"
chmod 644 "${SCRIPT_DIR}/${POLYGON_CU_REPORTS_TOKENONOMICS_SERVICE_FILE}" "${SCRIPT_DIR}/${POLYGON_CU_REPORTS_TOKENONOMICS_TIMER_FILE}"
cp "${SCRIPT_DIR}/${POLYGON_CU_REPORTS_TOKENONOMICS_SERVICE_FILE}" "/etc/systemd/system/${POLYGON_CU_REPORTS_TOKENONOMICS_SERVICE_FILE}"
cp "${SCRIPT_DIR}/${POLYGON_CU_REPORTS_TOKENONOMICS_TIMER_FILE}" "/etc/systemd/system/${POLYGON_CU_REPORTS_TOKENONOMICS_TIMER_FILE}"
systemctl daemon-reload
systemctl restart --no-block "${POLYGON_CU_REPORTS_TOKENONOMICS_TIMER_FILE}"

Wyświetl plik

@ -0,0 +1,19 @@
[Unit]
Description=Ethereum moonworm crawler
After=network.target
StartLimitIntervalSec=300
StartLimitBurst=3
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/moonstream/crawlers/mooncrawl
EnvironmentFile=/home/ubuntu/moonstream-secrets/app.env
Restart=on-failure
RestartSec=15s
ExecStart=/home/ubuntu/moonstream-env/bin/python -m mooncrawl.moonworm_crawler.cli --access-id "${NB_CONTROLLER_ACCESS_ID}" crawl -b ethereum
CPUWeight=70
SyslogIdentifier=ethereum-moonworm-crawler
[Install]
WantedBy=multi-user.target

Wyświetl plik

@ -2,7 +2,7 @@
Description=Fill missing blocks at Mumbai database
[Timer]
OnBootSec=30s
OnBootSec=120s
OnUnitActiveSec=15m
[Install]

Wyświetl plik

@ -0,0 +1,13 @@
[Unit]
Description=Runs custom crawler for CU tokenonomics
After=network.target
[Service]
Type=oneshot
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/moonstream/crawlers/mooncrawl
EnvironmentFile=/home/ubuntu/moonstream-secrets/app.env
ExecStart=/home/ubuntu/moonstream-env/bin/python -m mooncrawl.cu_reports_crawler.cli cu-reports --moonstream-token "${MOONSTREAM_PUBLIC_QUERIES_DATA_ACCESS_TOKEN}" queries run-tokenonomics
CPUWeight=60
SyslogIdentifier=polygon-cu-reports-tokenonomics

Wyświetl plik

@ -0,0 +1,9 @@
[Unit]
Description=Runs custom crawler for CU tokenonomics
[Timer]
OnBootSec=60s
OnUnitActiveSec=60m
[Install]
WantedBy=timers.target

Wyświetl plik

@ -2,7 +2,7 @@
Description=Fill missing blocks at Polygon database
[Timer]
OnBootSec=30s
OnBootSec=120s
OnUnitActiveSec=15m
[Install]

Wyświetl plik

@ -2,7 +2,7 @@
Description=Fill missing blocks at XDai database
[Timer]
OnBootSec=35s
OnBootSec=120s
OnUnitActiveSec=15m
[Install]

Wyświetl plik

@ -19,7 +19,7 @@ For a sample, view [`sample.env`](./sample.env).
### Ethereum Signature Database
This crawler retrieves Ethereum function signatures from the Ethereum Signature Database at
[https://4byte.directory](https://4byte.directory).
[https://www.4byte.directory](https://www.4byte.directory).
#### Crawling ESD function signatures

Wyświetl plik

@ -188,7 +188,7 @@ async def queries_data_update_handler(
# request.params validations
passed_params = {
key: value
key: queries.from_json_types(value)
for key, value in request_data.params.items()
if key in expected_query_parameters
}
@ -220,7 +220,7 @@ async def queries_data_update_handler(
query_id=f"{query_id}",
file_type=request_data.file_type,
query=valid_query,
params=request_data.params,
params=passed_params,
)
except Exception as e:

Wyświetl plik

@ -123,8 +123,6 @@ def add_block(db_session, block: Any, blockchain_type: AvailableBlockchainType)
)
if blockchain_type == AvailableBlockchainType.XDAI:
block_obj.author = block.author
block_obj.signature = block.signature
block_obj.step = block.step
db_session.add(block_obj)

Wyświetl plik

@ -13,7 +13,7 @@ class TestDeploymentCrawler(TestCase):
batch_size = 10
result = get_batch_block_range(from_block, to_block, batch_size)
last_end: Optional[int] = None
last_end: Optional[int] = None # type: ignore
for batch_start, batch_end in result:
if last_end is not None:
self.assertEqual(batch_start, last_end + 1)
@ -30,7 +30,7 @@ class TestDeploymentCrawler(TestCase):
batch_size = 10
result = get_batch_block_range(from_block, to_block, batch_size)
last_end: Optional[int] = None
last_end: Optional[int] = None # type: ignore
for batch_start, batch_end in result:
if last_end is not None:
self.assertEqual(batch_start, last_end - 1)

Wyświetl plik

@ -0,0 +1,635 @@
import argparse
import datetime
import logging
from moonstream.client import Moonstream # type: ignore
import time
import requests
import json
from typing import Any, Dict, Union
from uuid import UUID
from .queries import tokenomics_queries, cu_bank_queries
from ..settings import (
MOONSTREAM_S3_PUBLIC_DATA_BUCKET,
MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX,
)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
addresess_erc20_721 = {
"0x64060aB139Feaae7f06Ca4E63189D86aDEb51691": "ERC20", # UNIM
"0x431CD3C9AC9Fc73644BF68bF5691f4B83F9E104f": "ERC20", # RBW
"0xdC0479CC5BbA033B3e7De9F178607150B3AbCe1f": "NFT", # unicorns
"0xA2a13cE1824F3916fC84C65e559391fc6674e6e8": "NFT", # lands
"0xa7D50EE3D7485288107664cf758E877a0D351725": "NFT", # shadowcorns
}
addresess_erc1155 = ["0x99A558BDBdE247C2B2716f0D4cFb0E246DFB697D"]
def recive_S3_data_from_query(
client: Moonstream,
token: Union[str, UUID],
query_name: str,
params: Dict[str, Any],
time_await: int = 2,
max_retries: int = 30,
) -> Any:
"""
Await the query to be update data on S3 with if_modified_since and return new the data.
"""
keep_going = True
repeat = 0
if_modified_since_datetime = datetime.datetime.utcnow()
if_modified_since = if_modified_since_datetime.strftime("%a, %d %b %Y %H:%M:%S GMT")
time.sleep(2)
data_url = client.exec_query(
token=token,
name=query_name,
params=params,
) # S3 presign_url
while keep_going:
time.sleep(time_await)
try:
data_response = requests.get(
data_url.url,
headers={"If-Modified-Since": if_modified_since},
timeout=5,
)
except Exception as e:
logger.error(e)
continue
if data_response.status_code == 200:
break
repeat += 1
if repeat > max_retries:
logger.info("Too many retries")
break
return data_response.json()
def generate_report(
client: Moonstream,
token: Union[str, UUID],
query_name: str,
params: Dict[str, Any],
bucket_prefix: str,
bucket: str,
key: str,
):
"""
Generate the report.
"""
try:
json_data = recive_S3_data_from_query(
client=client,
token=token,
query_name=query_name,
params=params,
)
client.upload_query_results(
json.dumps(json_data),
bucket,
f"{bucket_prefix}/{key}",
)
logger.info(
f"Report generated and results uploaded at: https://{bucket}/{bucket_prefix}/{key}"
)
except Exception as err:
logger.error(
f"Cant recive or load data for s3, for query: {query_name}, bucket: {bucket}, key: {key}. End with error: {err}"
)
def create_user_query(
client: Moonstream,
token: Union[str, UUID],
query_name: str,
query: str,
):
"""
Create a user query.
"""
try:
client.create_query(token=token, name=query_name, query=query)
except Exception as err:
logger.error(f"Cant create user query: {query_name}. End with error: {err}")
def delete_user_query(client: Moonstream, token: str, query_name: str):
"""
Delete the user's queries.
"""
id = client.delete_query(
token=token,
name=query_name,
)
logger.info(f"Query with name:{query_name} and id: {id} was deleted")
def init_game_bank_queries_handler(args: argparse.Namespace):
"""
Create the game bank queries.
"""
client = Moonstream()
for query in cu_bank_queries:
try:
if args.overwrite:
try:
# delete
delete_user_query(
client=client,
token=args.moonstream_token,
query_name=query["name"],
)
except Exception as err:
logger.error(err)
# create
created_entry = client.create_query(
token=args.moonstream_token,
name=query["name"],
query=query["query"],
)
logger.info(
f"Created query {query['name']} please validate it in the UI url {created_entry.journal_url}/entries/{created_entry.id}/"
)
except Exception as e:
logger.error(e)
pass
def init_tokenomics_queries_handler(args: argparse.Namespace):
"""
Create the tokenomics queries.
"""
client = Moonstream()
for query in tokenomics_queries:
try:
if args.overwrite:
try:
# delete
delete_user_query(
client=client,
token=args.moonstream_token,
query_name=query["name"],
)
except Exception as err:
logger.error(err)
# create
created_entry = client.create_query(
token=args.moonstream_token,
name=query["name"],
query=query["query"],
)
logger.info(
f"Created query {query['name']} please validate it in the UI url {created_entry.journal_url}/entries/{created_entry.id}/"
)
except Exception as e:
logger.error(e)
pass
def run_tokenomics_queries_handler(args: argparse.Namespace):
client = Moonstream()
query_name = "erc20_721_volume"
### Run voluem query
ranges = [
{"time_format": "YYYY-MM-DD HH24", "time_range": "24 hours"},
{"time_format": "YYYY-MM-DD HH24", "time_range": "7 days"},
{"time_format": "YYYY-MM-DD", "time_range": "30 days"},
]
# volume of erc20 and erc721
for address, type in addresess_erc20_721.items():
for range in ranges:
params: Dict[str, Any] = {
"address": address,
"type": type,
"time_format": range["time_format"],
"time_range": range["time_range"],
}
generate_report(
client=client,
token=args.moonstream_token,
query_name=query_name,
params=params,
bucket_prefix=MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX,
bucket=MOONSTREAM_S3_PUBLIC_DATA_BUCKET,
key=f'{query_name}/{address}/{range["time_range"].replace(" ","_")}/data.json',
)
# volume change of erc20 and erc721
query_name = "volume_change"
for address, type in addresess_erc20_721.items():
for range in ranges:
params = {
"address": address,
"type": type,
"time_range": range["time_range"],
}
generate_report(
client=client,
token=args.moonstream_token,
query_name=query_name,
params=params,
bucket_prefix=MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX,
bucket=MOONSTREAM_S3_PUBLIC_DATA_BUCKET,
key=f'{query_name}/{address}/{range["time_range"].replace(" ","_")}/data.json',
)
query_name = "erc1155_volume"
# volume of erc1155
addresess_erc1155 = ["0x99A558BDBdE247C2B2716f0D4cFb0E246DFB697D"]
for address in addresess_erc1155:
for range in ranges:
params = {
"address": address,
"time_format": range["time_format"],
"time_range": range["time_range"],
}
generate_report(
client=client,
token=args.moonstream_token,
query_name=query_name,
params=params,
bucket_prefix=MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX,
bucket=MOONSTREAM_S3_PUBLIC_DATA_BUCKET,
key=f"{query_name}/{address}/{range['time_range'].replace(' ','_')}/data.json",
)
# most_recent_sale
query_name = "most_recent_sale"
for address, type in addresess_erc20_721.items():
if type == "NFT":
for amount in [10, 100]:
params = {
"address": address,
"amount": amount,
}
generate_report(
client=client,
token=args.moonstream_token,
query_name=query_name,
params=params,
bucket_prefix=MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX,
bucket=MOONSTREAM_S3_PUBLIC_DATA_BUCKET,
key=f"{query_name}/{address}/{amount}/data.json",
)
# most_active_buyers
query_name = "most_active_buyers"
for address, type in addresess_erc20_721.items():
if type == "NFT":
for range in ranges:
params = {
"address": address,
"time_range": range["time_range"],
}
generate_report(
client=client,
token=args.moonstream_token,
query_name=query_name,
params=params,
bucket_prefix=MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX,
bucket=MOONSTREAM_S3_PUBLIC_DATA_BUCKET,
key=f"{query_name}/{address}/{range['time_range'].replace(' ','_')}/data.json",
)
# most_active_sellers
query_name = "most_active_sellers"
for address, type in addresess_erc20_721.items():
if type == "NFT":
for range in ranges:
params = {
"address": address,
"time_range": range["time_range"],
}
generate_report(
client=client,
token=args.moonstream_token,
query_name=query_name,
params=params,
bucket_prefix=MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX,
bucket=MOONSTREAM_S3_PUBLIC_DATA_BUCKET,
key=f"{query_name}/{address}/{range['time_range'].replace(' ','_')}/data.json",
)
# lagerst_owners
query_name = "lagerst_owners"
for address, type in addresess_erc20_721.items():
if type == "NFT":
params = {
"address": address,
}
generate_report(
client=client,
token=args.moonstream_token,
query_name=query_name,
params=params,
bucket_prefix=MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX,
bucket=MOONSTREAM_S3_PUBLIC_DATA_BUCKET,
key=f"{query_name}/{address}/data.json",
)
# total_supply_erc721
query_name = "total_supply_erc721"
for address, type in addresess_erc20_721.items():
if type == "NFT":
params = {
"address": address,
}
generate_report(
client=client,
token=args.moonstream_token,
query_name=query_name,
params=params,
bucket_prefix=MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX,
bucket=MOONSTREAM_S3_PUBLIC_DATA_BUCKET,
key=f"{query_name}/{address}/data.json",
)
# total_supply_terminus
query_name = "total_supply_terminus"
for address in addresess_erc1155:
params = {
"address": address,
}
generate_report(
client=client,
token=args.moonstream_token,
query_name=query_name,
params=params,
bucket_prefix=MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX,
bucket=MOONSTREAM_S3_PUBLIC_DATA_BUCKET,
key=f"{query_name}/{address}/data.json",
)
logger.info("Done")
def list_user_queries_handler(args: argparse.Namespace):
"""
List the user's queries.
"""
client = Moonstream()
queries = client.list_queries(
token=args.moonstream_token,
)
for query in queries.queries:
logger.info(query.name, query.id)
def delete_user_query_handler(args: argparse.Namespace):
"""
Delete the user's queries.
"""
client = Moonstream()
delete_user_query(client=client, token=args.moonstream_token, query_name=args.name)
def create_user_query_handler(args: argparse.Namespace):
"""
Create the user's queries.
"""
client = Moonstream()
for query in tokenomics_queries:
if query["name"] == args.name:
create_user_query(
client=client,
token=args.moonstream_token,
query_name=query["name"],
query=query["query"],
)
def generate_game_bank_report(args: argparse.Namespace):
"""
han
Generate the game bank query.
"""
client = Moonstream()
for query in client.list_queries(
token=args.moonstream_token,
).queries:
params = {}
if (
query.name == "cu-bank-withdrawals-total"
or query.name == "cu-bank-withdrawals-events"
):
blocktimestamp = int(time.time())
params = {"block_timestamp": blocktimestamp}
keep_going = True
if_modified_since_datetime = datetime.datetime.utcnow()
if_modified_since = if_modified_since_datetime.strftime(
"%a, %d %b %Y %H:%M:%S GMT"
)
data_url = client.exec_query(
token=args.moonstream_token,
query_name=query.name,
params=params,
) # S3 presign_url
while keep_going:
data_response = requests.get(
data_url,
headers={"If-Modified-Since": if_modified_since},
timeout=10,
)
# push to s3
if data_response.status_code == 200:
json.dumps(data_response.json())
break
else:
time.sleep(2)
continue
pass
def main():
parser = argparse.ArgumentParser()
parser.set_defaults(func=lambda _: parser.print_help())
subparsers = parser.add_subparsers()
cu_reports_parser = subparsers.add_parser("cu-reports", help="CU Reports")
cu_reports_subparsers = cu_reports_parser.add_subparsers()
cu_reports_parser.add_argument(
"--moonstream-token",
required=True,
type=str,
)
queries_parser = cu_reports_subparsers.add_parser(
"queries",
help="Queries commands",
)
queries_parser.set_defaults(func=lambda _: queries_parser.print_help())
queries_subparsers = queries_parser.add_subparsers()
queries_subparsers.add_parser(
"list",
help="List all queries",
description="List all queries",
).set_defaults(func=list_user_queries_handler)
init_game_bank_parser = queries_subparsers.add_parser(
"init-game-bank",
help="Create all predifind query",
description="Create all predifind query",
)
init_game_bank_parser.add_argument("--overwrite", type=bool, default=False)
init_game_bank_parser.set_defaults(func=init_game_bank_queries_handler)
init_tokenonomics_parser = queries_subparsers.add_parser(
"init-tokenonomics",
help="Create all predifind query",
description="Create all predifind query",
)
init_tokenonomics_parser.add_argument("--overwrite", type=bool, default=False)
init_tokenonomics_parser.set_defaults(func=init_tokenomics_queries_handler)
generate_report = queries_subparsers.add_parser(
"run-tokenonomics",
help="Run tokenomics queries and push to S3 public backet",
description="Run tokenomics queries and push to S3 public backet",
)
generate_report.set_defaults(func=run_tokenomics_queries_handler)
delete_query = queries_subparsers.add_parser(
"delete",
help="Delete all predifind query",
description="Delete all predifind query",
)
delete_query.add_argument(
"--name",
required=True,
type=str,
)
delete_query.set_defaults(func=delete_user_query_handler)
create_query = queries_subparsers.add_parser(
"create",
help="Create all predifind query",
description="Create all predifind query",
)
create_query.add_argument(
"--name",
required=True,
type=str,
)
create_query.set_defaults(func=create_user_query_handler)
cu_bank_parser = cu_reports_subparsers.add_parser(
"generate-reports",
help="Generate cu-bank state reports",
)
cu_bank_parser.set_defaults(func=generate_game_bank_report)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()

Wyświetl plik

@ -0,0 +1,701 @@
cu_bank_queries = [
{
"name": "cu-bank-blances",
"query": """
WITH game_contract as (
SELECT
*
from
polygon_labels
where
address = '0x94f557dDdb245b11d031F57BA7F2C4f28C4A203e'
and label = 'moonworm-alpha'
)
SELECT
address,
div(sum(
CASE
WHEN result_balances.token_address = '0x64060aB139Feaae7f06Ca4E63189D86aDEb51691' THEN amount
ELSE 0
END
), 10^18::decimal) as UNIM_BALANCE,
div(sum(
CASE
WHEN result_balances.token_address = '0x431CD3C9AC9Fc73644BF68bF5691f4B83F9E104f' THEN amount
ELSE 0
END
), 10^18::decimal) as RBW_BALANCE
FROM
(
select
transaction_hash,
label_data -> 'args' ->> 'player' as address,
jsonb_array_elements(label_data -> 'args' -> 'tokenAddresses') ->> 0 as token_address,
- jsonb_array_elements(label_data -> 'args' -> 'tokenAmounts') :: decimal as amount
from
game_contract
where
label_data ->> 'name' = 'UnstashedMultiple'
union
ALL
select
transaction_hash,
label_data -> 'args' ->> 'player' as address,
label_data -> 'args' ->> 'token' as token_address,
-((label_data -> 'args' -> 'amount') :: decimal) as amount
from
game_contract
where
label_data ->> 'name' = 'Unstashed'
union
ALL
select
transaction_hash,
label_data -> 'args' ->> 'player' as address,
label_data -> 'args' ->> 'token' as token_address,
(label_data -> 'args' ->> 'amount') :: decimal as amount
from
game_contract
where
label_data ->> 'name' = 'Stashed'
union
ALL
select
transaction_hash,
label_data -> 'args' ->> 'player' as address,
jsonb_array_elements(label_data -> 'args' -> 'tokenAddresses') ->> 0 as token_address,
jsonb_array_elements(label_data -> 'args' -> 'tokenAmounts') :: decimal as amount
from
game_contract
where
label_data ->> 'name' = 'StashedMultiple'
) result_balances
group by
address
ORDER BY
UNIM_BALANCE DESC,
RBW_BALANCE DESC
""",
},
{
"name": "cu-bank-withdrawals-total",
"query": """
WITH game_contract as (
SELECT
*
from
polygon_labels
where
address = '0x94f557dDdb245b11d031F57BA7F2C4f28C4A203e'
and label = 'moonworm-alpha'
block_timestamp >= :block_timestamp
), withdoraws_total as (
SELECT
address,
div(sum(
CASE
WHEN result_balances.token_address = '0x64060aB139Feaae7f06Ca4E63189D86aDEb51691' THEN amount
ELSE 0
END
), 10^18::decimal) as UNIM_BALANCE,
div(sum(
CASE
WHEN result_balances.token_address = '0x431CD3C9AC9Fc73644BF68bF5691f4B83F9E104f' THEN amount
ELSE 0
END
), 10^18::decimal) as RBW_BALANCE
FROM
(
select
transaction_hash,
label_data -> 'args' ->> 'player' as address,
jsonb_array_elements(label_data -> 'args' -> 'tokenAddresses') ->> 0 as token_address,
jsonb_array_elements(label_data -> 'args' -> 'tokenAmounts') :: decimal as amount
from
game_contract
where
label_data ->> 'name' = 'UnstashedMultiple'
union
ALL
select
transaction_hash,
label_data -> 'args' ->> 'player' as address,
label_data -> 'args' ->> 'token' as token_address,
((label_data -> 'args' -> 'amount') :: decimal) as amount
from
game_contract
where
label_data ->> 'name' = 'Unstashed'
) result_balances
group by
address
ORDER BY
UNIM_BALANCE DESC,
RBW_BALANCE DESC
)
SELECT
address,
UNIM_BALANCE,
RBW_BALANCE,
UNIM_BALANCE + RBW_BALANCE as TOTAL
FROM
withdoraws_total
ORDER BY
TOTAL DESC;
""",
},
{
"name": "cu-bank-withdrawals-events",
"query": """
WITH game_contract as (
SELECT
*
from
polygon_labels
where
address = '0x94f557dDdb245b11d031F57BA7F2C4f28C4A203e'
and label = 'moonworm-alpha'
block_timestamp >= :block_timestamp
), withdoraws_total as (
SELECT
address,
CASE
WHEN result_balances.token_address = '0x64060aB139Feaae7f06Ca4E63189D86aDEb51691' THEN 'UNIM'
WHEN result_balances.token_address = '0x431CD3C9AC9Fc73644BF68bF5691f4B83F9E104f' THEN 'RBW'
END as currency,
div(amount, 10^18::decimal) as amount
FROM
(
select
transaction_hash,
label_data -> 'args' ->> 'player' as address,
jsonb_array_elements(label_data -> 'args' -> 'tokenAddresses') ->> 0 as token_address,
jsonb_array_elements(label_data -> 'args' -> 'tokenAmounts') :: decimal as amount
from
game_contract
where
label_data ->> 'name' = 'UnstashedMultiple'
union
ALL
select
transaction_hash,
label_data -> 'args' ->> 'player' as address,
label_data -> 'args' ->> 'token' as token_address,
((label_data -> 'args' -> 'amount') :: decimal) as amount
from
game_contract
where
label_data ->> 'name' = 'Unstashed'
) result_balances
)
SELECT
address,
currency,
amount,
FROM
withdoraws_total
ORDER BY
amount DESC
""",
},
]
tokenomics_queries = [
{
"name": "volume_change",
"query": """
with all_transfers as (
select
transaction_hash,
CASE
WHEN type: ='NFT' THEN 1
ELSE (label_data->'args'->>'value')::decimal
END as value,
block_timestamp as block_timestamp
from polygon_labels
where label='moonworm-alpha'
and address= :address
and label_data->>'name'='Transfer'
), after_range_transfer as (
select
*
FROM
all_transfers
where block_timestamp >= extract(epoch from now() - interval :time_range)::int
), current_volume as (
SELECT
sum(all_transfers.value) as value,
sum(
CASE
WHEN to_address in ('0xF715bEb51EC8F63317d66f491E37e7BB048fCc2d','0xfede379e48C873C75F3cc0C81F7C784aD730a8F7','0x00000000006c3852cbef3e08e8df289169ede581')
THEN 1
else 0
END
) as os_sales
from all_transfers
LEFT JOIN polygon_transactions ON all_transfers.transaction_hash = polygon_transactions.hash
), volume_different as (
select
sum(after_range_transfer.value) as value,
sum(
CASE
WHEN to_address in ('0xF715bEb51EC8F63317d66f491E37e7BB048fCc2d','0xfede379e48C873C75F3cc0C81F7C784aD730a8F7','0x00000000006c3852cbef3e08e8df289169ede581')
THEN 1
else 0
END
) as os_sales
from after_range_transfer
LEFT JOIN polygon_transactions ON after_range_transfer.transaction_hash = polygon_transactions.hash
)
SELECT
volume_different.value as diff,
volume_different.os_sales as os_diff,
current_volume.value as current,
current_volume.os_sales as os_current
from current_volume, volume_different
""",
},
{
"name": "erc20_721_volume",
"query": """
with interval_transfers as (
select
transaction_hash,
CASE
WHEN :type ='NFT' THEN 1
ELSE (label_data->'args'->>'value')::decimal
END as value,
label_data->'args'->>'to' as buyer,
label_data->'args'->>'from' as seller,
to_char(to_timestamp(block_timestamp), :time_format) as time
from polygon_labels
where label='moonworm-alpha'
and address= :address
and label_data->>'name'='Transfer'
and block_timestamp >= extract(epoch from now() - interval :time_range)::int
)
SELECT
time as time,
sum(interval_transfers.value) as value,
sum(
CASE
WHEN to_address in ('0xF715bEb51EC8F63317d66f491E37e7BB048fCc2d','0xfede379e48C873C75F3cc0C81F7C784aD730a8F7','0x00000000006c3852cbef3e08e8df289169ede581')
THEN 1
else 0
END
) as os_sales
from interval_transfers
LEFT JOIN polygon_transactions ON interval_transfers.transaction_hash = polygon_transactions.hash
GROUP BY time
""",
},
{
"name": "erc1155_volume",
"query": """
with labels_data as (
select
*
from
polygon_labels
where address= :address
AND label='moonworm-alpha'
AND block_timestamp >= extract(epoch from now() - interval :time_range)::int
),
nfts_data as (
select
transaction_hash,
label_data->'args'->>'to' as buyer,
label_data->'args'->>'from' as seller,
jsonb_array_elements(label_data -> 'args' -> 'values') :: decimal as value,
jsonb_array_elements(label_data -> 'args' -> 'ids')->>0 as token_id,
to_char(to_timestamp(block_timestamp), :time_format) as time
from
labels_data
where
label_data ->> 'name' = 'TransferBatch'
UNION ALL
select
transaction_hash,
label_data->'args'->>'to' as buyer,
label_data->'args'->>'from' as seller,
(label_data -> 'args' ->> 'value') :: decimal as value,
label_data -> 'args' ->> 'id' as token_id,
to_char(to_timestamp(block_timestamp), :time_format) as time
from
labels_data
where
label_data ->> 'name' = 'TransferSingle'
)
SELECT
time as time,
token_id as token_id,
sum(nfts_data.value) as value,
sum(
CASE
WHEN to_address in ('0xF715bEb51EC8F63317d66f491E37e7BB048fCc2d','0xfede379e48C873C75F3cc0C81F7C784aD730a8F7','0x00000000006c3852cbef3e08e8df289169ede581')
THEN 1
else 0
END
) as os_sales
from nfts_data
LEFT JOIN polygon_transactions ON nfts_data.transaction_hash = polygon_transactions.hash
GROUP BY
time,
token_id
ORDER BY token_id::int, time DESC
""",
},
{
"name": "most_recent_sale",
"query": """
with contract_erc721_transfers as (
select
transaction_hash,
label_data->'args'->>'tokenId' as token_id,
label_data->'args'->>'to' as buyer,
label_data->'args'->>'from' as seller,
block_timestamp as block_timestamp
from polygon_labels
where label='moonworm-alpha'
and address= :address
and label_data->>'name'='Transfer'
order by block_number desc
)
SELECT
polygon_transactions.hash as transaction_hash,
contract_erc721_transfers.block_timestamp as block_timestamp,
contract_erc721_transfers.token_id as token_id,
contract_erc721_transfers.buyer as buyer,
contract_erc721_transfers.seller as seller
from polygon_transactions
inner JOIN contract_erc721_transfers ON contract_erc721_transfers.transaction_hash = polygon_transactions.hash
where polygon_transactions.to_address in ('0xF715bEb51EC8F63317d66f491E37e7BB048fCc2d','0xfede379e48C873C75F3cc0C81F7C784aD730a8F7', '0x00000000006c3852cbef3e08e8df289169ede581')
limit :amount
""",
},
{
"name": "most_active_buyers",
"query": """
with contracts_data as (
select
*
from polygon_labels
where label='moonworm-alpha'
and address= :address
and block_timestamp >= extract(epoch from now() - interval :time_range)::int
), contract_nfts_transfers as (
select
transaction_hash,
label_data->'args'->>'to' as buyer,
label_data->'args'->>'from' as seller
from contracts_data
where label_data->>'name'='Transfer'
UNION ALL
select
transaction_hash,
label_data->'args'->>'to' as buyer,
label_data->'args'->>'from' as seller
from
contracts_data
where
label_data ->> 'name' = 'TransferBatch'
UNION ALL
select
transaction_hash,
label_data->'args'->>'to' as buyer,
label_data->'args'->>'from' as seller
from
contracts_data
where
label_data ->> 'name' = 'TransferSingle'
)
SELECT
contract_nfts_transfers.buyer as buyer,
count(*) as sale_count
from polygon_transactions
inner JOIN contract_nfts_transfers ON contract_nfts_transfers.transaction_hash = polygon_transactions.hash
where polygon_transactions.to_address in ('0xF715bEb51EC8F63317d66f491E37e7BB048fCc2d','0xfede379e48C873C75F3cc0C81F7C784aD730a8F7','0x00000000006c3852cbEf3e08E8dF289169EdE581')
group by contract_nfts_transfers.buyer
order by sale_count desc
""",
},
{
"name": "most_active_sellers",
"query": """
with contracts_data as (
select
*
from polygon_labels
where label='moonworm-alpha'
and address= :address
and block_timestamp >= extract(epoch from now() - interval :time_range)::int
), contract_nfts_transfers as (
select
transaction_hash,
label_data->'args'->>'to' as buyer,
label_data->'args'->>'from' as seller
from contracts_data
where label_data->>'name'='Transfer'
UNION ALL
select
transaction_hash,
label_data->'args'->>'to' as buyer,
label_data->'args'->>'from' as seller
from
contracts_data
where
label_data ->> 'name' = 'TransferBatch'
UNION ALL
select
transaction_hash,
label_data->'args'->>'to' as buyer,
label_data->'args'->>'from' as seller
from
contracts_data
where
label_data ->> 'name' = 'TransferSingle'
)
SELECT
contract_nfts_transfers.seller as seller,
count(*) as sale_count
from polygon_transactions
inner JOIN contract_nfts_transfers ON contract_nfts_transfers.transaction_hash = polygon_transactions.hash
where polygon_transactions.to_address in ('0xF715bEb51EC8F63317d66f491E37e7BB048fCc2d','0xfede379e48C873C75F3cc0C81F7C784aD730a8F7','0x00000000006c3852cbEf3e08E8dF289169EdE581')
group by contract_nfts_transfers.seller
order by sale_count desc
""",
},
{
"name": "lagerst_owners",
"query": """
WITH erc_1155_721_contracts_transfers_with_trashhold as (
SELECT
label_data as label_data,
block_timestamp as block_timestamp,
address as address
from
polygon_labels
WHERE
polygon_labels.label = 'moonworm-alpha'
AND polygon_labels.address = :address
),
own_erc_1155_721_count as (
Select
difference.address,
(
difference.transfers_in - difference.transfers_out
) as owned_nfts
from
(
SELECT
total.address,
sum(total.transfer_out) as transfers_out,
sum(total.transfer_in) as transfers_in
from
(
SELECT
label_data -> 'args' ->> 'from' as address,
jsonb_array_elements(label_data -> 'args' -> 'values') :: decimal as transfer_out,
0 as transfer_in
from
erc_1155_721_contracts_transfers_with_trashhold
where
label_data ->> 'name' = 'TransferBatch'
UNION
ALL
SELECT
label_data -> 'args' ->> 'from' as address,
(label_data -> 'args' ->> 'value') :: decimal as transfer_out,
0 as transfer_in
from
erc_1155_721_contracts_transfers_with_trashhold
where
label_data ->> 'name' = 'TransferSingle'
UNION
ALL
select
label_data -> 'args' ->> 'to' as address,
0 as transfer_out,
(label_data -> 'args' ->>'value') :: decimal as transfer_in
from
erc_1155_721_contracts_transfers_with_trashhold
where
label_data ->> 'name' = 'TransferSingle'
UNION
ALL
select
label_data -> 'args' ->> 'to' as address,
0 as transfer_out,
jsonb_array_elements(label_data -> 'args' -> 'values') :: decimal as transfer_in
from
erc_1155_721_contracts_transfers_with_trashhold
where
label_data ->> 'name' = 'TransferBatch'
UNION
ALL
select
label_data -> 'args' ->> 'from' as address,
1 as transfer_out,
0 as transfer_in
from
erc_1155_721_contracts_transfers_with_trashhold
where
label_data ->> 'name' = 'Transfer'
UNION
ALL
select
label_data -> 'args' ->> 'to' as address,
0 as transfer_out,
1 as transfer_in
from
erc_1155_721_contracts_transfers_with_trashhold
where
label_data ->> 'name' = 'Transfer'
) as total
group by
address
) difference
order by
owned_nfts desc
)
SELECT
*
from
own_erc_1155_721_count
WHERE
address not in (
'0x000000000000000000000000000000000000dEaD',
'0x0000000000000000000000000000000000000000'
)
order by
owned_nfts desc
""",
},
{
"name": "total_supply_erc721",
"query": """
select
count(*) as total_supply
from(
SELECT DISTINCT ON((label_data->'args'->>'tokenId')::INT) (label_data->'args'->>'tokenId')::INT as token_id,
label_data->'args'->>'to' as current_owner
FROM polygon_labels
WHERE address = :address
AND (label = 'moonworm' or label = 'moonworm-alpha')
AND block_number >= 21418707
AND label_data->>'type' = 'event'
AND label_data->>'name' = 'Transfer'
AND label_data->'args'->>'to' != '0x8d528e98A69FE27b11bb02Ac264516c4818C3942'
AND label_data->'args'->>'from' != '0x8d528e98A69FE27b11bb02Ac264516c4818C3942'
ORDER BY (label_data->'args'->>'tokenId')::INT ASC,
block_number::INT DESC,
log_index::INT DESC
) as total_supply
where current_owner not in ('0x000000000000000000000000000000000000dEaD','0x0000000000000000000000000000000000000000')
""",
},
{
"name": "total_supply_terminus",
"query": """
WITH erc_1155_721_contracts_transfers_with_trashhold as (
SELECT
label_data as label_data,
block_timestamp as block_timestamp,
address as address
from
polygon_labels
WHERE
polygon_labels.label = 'moonworm-alpha'
AND polygon_labels.address = :address
),
own_erc_1155_721_count as (
Select
difference.address,
token_id,
(
difference.transfers_in - difference.transfers_out
) as owned_nfts
from
(
SELECT
total.address,
token_id,
sum(total.transfer_out) as transfers_out,
sum(total.transfer_in) as transfers_in
from
(
SELECT
label_data -> 'args' ->> 'from' as address,
jsonb_array_elements(label_data -> 'args' -> 'ids')->>0 as token_id,
jsonb_array_elements(label_data -> 'args' -> 'values') :: decimal as transfer_out,
0 as transfer_in
from
erc_1155_721_contracts_transfers_with_trashhold
where
label_data ->> 'name' = 'TransferBatch'
UNION
ALL
SELECT
label_data -> 'args' ->> 'from' as address,
label_data -> 'args' ->> 'id' as token_id,
(label_data -> 'args' ->> 'value') :: decimal as transfer_out,
0 as transfer_in
from
erc_1155_721_contracts_transfers_with_trashhold
where
label_data ->> 'name' = 'TransferSingle'
UNION
ALL
select
label_data -> 'args' ->> 'to' as address,
label_data -> 'args' ->> 'id' as token_id,
0 as transfer_out,
(label_data -> 'args' ->>'value') :: decimal as transfer_in
from
erc_1155_721_contracts_transfers_with_trashhold
where
label_data ->> 'name' = 'TransferSingle'
UNION
ALL
select
label_data -> 'args' ->> 'to' as address,
jsonb_array_elements(label_data -> 'args' -> 'ids')->>0 as token_id,
0 as transfer_out,
jsonb_array_elements(label_data -> 'args' -> 'values') :: decimal as transfer_in
from
erc_1155_721_contracts_transfers_with_trashhold
where
label_data ->> 'name' = 'TransferBatch'
) as total
group by
address, token_id
) difference
order by
owned_nfts desc
)
SELECT
token_id as token_id,
sum(owned_nfts) as total_supply
from
own_erc_1155_721_count
WHERE
address not in (
'0x000000000000000000000000000000000000dEaD',
'0x0000000000000000000000000000000000000000'
)
group by
token_id
order by
token_id::int desc
""",
},
]

Wyświetl plik

@ -19,7 +19,7 @@ class MoonstreamHTTPException(HTTPException):
status_code: int,
detail: Any = None,
headers: Optional[Dict[str, Any]] = None,
internal_error: Exception = None,
internal_error: Optional[Exception] = None,
):
super().__init__(status_code, detail, headers)
if internal_error is not None:

Wyświetl plik

@ -252,6 +252,7 @@ def continuous_crawler(
start_block = end_block + 1
failed_count = 0
except Exception as e:
db_session.rollback()
logger.error(f"Internal error: {e}")
logger.exception(e)
failed_count += 1

Wyświetl plik

@ -156,7 +156,7 @@ def get_crawl_job_entries(
subscription_type: SubscriptionTypes,
crawler_type: str,
journal_id: str = MOONSTREAM_MOONWORM_TASKS_JOURNAL,
created_at_filter: int = None,
created_at_filter: Optional[int] = None,
limit: int = 200,
) -> List[BugoutSearchResult]:
"""

Wyświetl plik

@ -193,3 +193,20 @@ multicall_contracts: Dict[AvailableBlockchainType, str] = {
AvailableBlockchainType.MUMBAI: "0xe9939e7Ea7D7fb619Ac57f648Da7B1D425832631",
AvailableBlockchainType.ETHEREUM: "0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696",
}
# Custom Crawler
MOONSTREAM_S3_PUBLIC_DATA_BUCKET = os.environ.get(
"MOONSTREAM_S3_PUBLIC_DATA_BUCKET", ""
) # S3 bucket for storing custom crawler data
if MOONSTREAM_S3_PUBLIC_DATA_BUCKET == "":
raise ValueError(
"MOONSTREAM_S3_PUBLIC_DATA_BUCKET environment variable must be set"
)
MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX = os.environ.get(
"MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX", "dev"
)

Wyświetl plik

@ -64,6 +64,16 @@ def to_json_types(value):
return str(value)
def from_json_types(value):
if isinstance(value, (str, int, tuple, dict)):
return value
elif isinstance(value, list): # psycopg2 issue with list support
return tuple(value)
else:
return str(value)
def data_generate(
bucket: str,
query_id: str,
@ -125,6 +135,7 @@ def data_generate(
bucket=bucket,
)
except Exception as err:
logger.error(f"Error while generating data: {err}")
db_session.rollback()
reporter.error_report(
err,

Wyświetl plik

@ -2,4 +2,4 @@
Moonstream crawlers version.
"""
MOONCRAWL_VERSION = "0.2.4"
MOONCRAWL_VERSION = "0.2.5"

Wyświetl plik

@ -31,3 +31,9 @@ export MOONSTREAM_S3_QUERIES_BUCKET_PREFIX="dev"
# 3rd parties environment variables
export MOONSTREAM_ETHERSCAN_TOKEN="<Token_for_etherscan>"
export COINMARKETCAP_API_KEY="<API_key_to_parse_conmarketcap>"
# Custom crawler
export MOONSTREAM_S3_PUBLIC_DATA_BUCKET="<public_bucket>"
export MOONSTREAM_S3_PUBLIC_DATA_BUCKET_PREFIX="dev"
export MOONSTREAM_PUBLIC_QUERIES_DATA_ACCESS_TOKEN="<access token for run queries for public dashboards>"

Wyświetl plik

@ -38,6 +38,7 @@ setup(
"chardet",
"fastapi",
"moonstreamdb>=0.3.2",
"moonstream>=0.1.1",
"moonworm[moonstream]>=0.5.2",
"humbug",
"pydantic==1.9.2",
@ -64,6 +65,7 @@ setup(
"statistics=mooncrawl.stats_worker.dashboard:main",
"state-crawler=mooncrawl.state_crawler.cli:main",
"metadata-crawler=mooncrawl.metadata_crawler.cli:main",
"custom-crawler=mooncrawl.cu_reports_crawler.cli:main",
]
},
)

1
frontend/.gitignore vendored
Wyświetl plik

@ -41,3 +41,4 @@ package-lock.json
# vercel
.vercel
.todo

Wyświetl plik

@ -72,7 +72,7 @@ const Tokens = () => {
<ScaleFade in>
<Modal isOpen={isOpen} onClose={onClose} size="lg" trapFocus={false}>
<ModalOverlay />
<ModalContent>
<ModalContent bg="black.300">
<ModalHeader>New API access token</ModalHeader>
<ModalCloseButton />
<ModalBody>
@ -85,6 +85,7 @@ const Tokens = () => {
<Stack direction={["column", "row", null]} w="100%">
<InputGroup size="sm" variant="outline">
<Input
variant="bw"
type="search"
maxW="300px"
flexBasis="50px"

Wyświetl plik

@ -36,7 +36,9 @@ const Contact = () => {
mb={8}
minHeight="100vh"
textColor="black"
bgColor="white"
position="relative"
pt="72px"
>
<Icon
as={BiArrowBack}

Wyświetl plik

@ -160,7 +160,8 @@ const Analytics = () => {
})
}
size="md"
colorScheme="blue"
color="orange.1000"
borderColor="1000"
variant="outline"
icon={<BsGear />}
/>

Wyświetl plik

@ -23,6 +23,7 @@ const Features = () => {
id="container"
maxW="container.xl"
px={["10%", "10%", "7%", "0"]}
mt="72px"
>
<Heading as="h1" hidden>
Moonstream feautes

Wyświetl plik

@ -721,7 +721,7 @@ const Homepage = () => {
<Heading {...HEADING_PROPS} textAlign="center" as="h2" pb={10}>
FAQ
</Heading>
<Accordion defaultIndex={[0]} allowMultiple allowToggle>
<Accordion defaultIndex={[-1]} allowMultiple allowToggle>
<FAQCard
heading="Im a game designer. What can Moonstream engine do for me?"
headingProps={HEADING_PROPS}

Wyświetl plik

@ -4,7 +4,7 @@ import { DEFAULT_METATAGS } from "../../src/core/constants";
import { getLayout, getLayoutProps } from "../../src/layouts/WideInfoPage";
const PrivacyPolicy = () => (
<Box>
<Box mt="72px">
<Box px="1.5rem" m="auto" mb={8} maxWidth="1199.98px" minHeight="60vh">
<Heading my={8} as="h1">
Privacy Policy for Moonstream

Wyświetl plik

@ -6,7 +6,7 @@ import { getLayout, getLayoutProps } from "../../src/layouts/InfoPageLayout";
const Status = () => {
const healthyStatusText = "Available";
const downStatusText = "Unavailable";
const healthyStatusColor = "green.900";
const healthyStatusColor = "green.1000";
const downStatusColor = "red.600";
const { serverListStatusCache } = useStatus();
@ -58,6 +58,7 @@ const Status = () => {
px={12}
py={2}
borderTopRadius="xl"
mt="72px"
>
{`Status page`}
</Heading>

Wyświetl plik

@ -63,7 +63,8 @@ const Subscriptions = () => {
<Flex
h="3rem"
w="100%"
bgColor="blue.50"
bgColor="black.300"
borderColor="white"
borderTopRadius="xl"
justifyContent="flex-end"
alignItems="center"

Wyświetl plik

@ -49,7 +49,7 @@ const Team = () => {
alignItems="center"
w="100%"
>
<Stack mx={margin} maxW="1700px" w="100%">
<Stack mt="72px" mx={margin} maxW="1700px" w="100%">
<SimpleGrid
px={12}
alignItems="start"

Wyświetl plik

@ -5,9 +5,9 @@ import { DEFAULT_METATAGS } from "../../src/core/constants";
import { getLayout, getLayoutProps } from "../../src/layouts/WideInfoPage";
const TermsOfService = () => (
<Box>
<Box mt="72px">
<Box px="1.5rem" m="auto" mb={8} maxWidth="1199.98px" minHeight="60vh">
<Heading my={8} as="h1">
<Heading textAlign="start" my={8} as="h1">
Moonstream Terms of Service
</Heading>
<Text fontSize="md">

Wyświetl plik

@ -6,9 +6,11 @@ import { MdPictureAsPdf } from "react-icons/md";
const Papers = () => {
return (
<VStack>
<Heading py={12}>Whitepapers</Heading>
<Heading pb={12} pt="72px">
Whitepapers
</Heading>
<Link
color="orange.900"
color="orange.1000"
href="https://github.com/bugout-dev/moonstream/blob/main/datasets/nfts/papers/ethereum-nfts.pdf"
>
An analysis of 7,020,950 NFT transactions on the Ethereum blockchain -

Wyświetl plik

@ -5,11 +5,11 @@
<g id="04-01-register" transform="translate(-895.000000, -362.000000)">
<g id="Group" transform="translate(891.000000, 356.000000)">
<rect id="Rectangle-Copy-5" x="0" y="0" width="24" height="24"></rect>
<g id="Group-6" transform="translate(5.000000, 7.000000)" stroke="#83859E" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
<g id="Group-6" transform="translate(5.000000, 7.000000)" stroke="#FFFFFF" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
<path d="M0,5.09090909 C0,5.09090909 2.54545455,-2.57571742e-14 7,-2.57571742e-14 C11.4545455,-2.57571742e-14 14,5.09090909 14,5.09090909 C14,5.09090909 11.4545455,10.1818182 7,10.1818182 C2.54545455,10.1818182 0,5.09090909 0,5.09090909 Z" id="Path"></path>
<circle id="Oval" cx="7" cy="5.09090909" r="1.90909091"></circle>
</g>
</g>
</g>
</g>
</svg>
</svg>

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 1.1 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.1 KiB

Wyświetl plik

@ -144,6 +144,30 @@ const variantWhiteOnOrange = () => {
};
};
const variantPlainOrange = () => {
return {
alignItems: "center",
justifyContent: "center",
border: "solid transparent",
borderRadius: "30px",
variant: "solid",
fontSize: ["md", "md", "lg", "lg", "xl", "xl"],
textColor: "white",
bg: "#F56646",
fontWeight: "700",
padding: "10px 30px",
_hover: {
backgroundColor: "#F4532F",
},
_focus: {
backgroundColor: "#F4532F",
},
_active: {
backgroundColor: "#F4532F",
},
};
};
const Button = {
// 1. We can update the base styles
baseStyle: () => ({
@ -183,6 +207,7 @@ const Button = {
link: variantLink,
orangeAndBlue: variantOrangeAndBlue,
whiteOnOrange: variantWhiteOnOrange,
plainOrange: variantPlainOrange,
},
};
export default Button;

Wyświetl plik

@ -21,6 +21,31 @@ const flushedVariant = (props) => {
};
};
const bwVariant = () => {
return {
field: {
border: "1px solid white",
borderRadius: "7px",
color: "white",
backgroundColor: "black.300",
errorBorderColor: "#EE8686",
_hover: {
backgroundColor: "black.300",
},
_focus: {
backgroundColor: "black.300",
},
_placeholder: { color: "#CDCDCD" },
_autofill: {
backgroundColor: "black.300",
textFillColor: "white",
boxShadow: "0 0 0px 1000px black.300 inset",
transition: "background-color 5000s ease-in-out 0s",
},
},
};
};
const outlineVariant = (props) => {
const { colorScheme: c, theme } = props;
const bgColor = transparentize(`${c}.50`, 0.8)(theme);
@ -79,6 +104,7 @@ const Input = {
flushed: flushedVariant,
newTag: newTagVariant,
filled: filledVariant,
bw: bwVariant,
},
defaultProps: {

Wyświetl plik

@ -0,0 +1,26 @@
const mobileVariant = () => {
return {
_active: {
backgroundColor: "black.300",
color: "white",
textDecoration: "none",
},
_focus: {
backgroundColor: "black.300",
color: "white",
textDecoration: "none",
},
color: "white",
fontSize: "sm",
margin: "0px",
padding: "0px",
};
};
const MenuButton = {
variants: {
mobile: mobileVariant,
},
};
export default MenuButton;

Wyświetl plik

@ -2,6 +2,7 @@ import { extendTheme } from "@chakra-ui/react";
import Button from "./Button";
import Tag from "./Tag";
import Menu from "./Menu";
import MenuButton from "./MenuButton";
import Input from "./Input";
// import Spinner from "./Spinner";
import NumberInput from "./NumberInput";
@ -59,6 +60,7 @@ const theme = extendTheme({
Spinner,
Tooltip,
Heading,
MenuButton,
},
fonts: {
@ -100,6 +102,7 @@ const theme = extendTheme({
600: "#6469b1",
700: "#4d54a6",
800: "#373e9b",
850: "#358BF5",
900: "#212990",
1000: "#1e2582",
1100: "#1a2173",
@ -165,6 +168,7 @@ const theme = extendTheme({
700: "#fd7835",
800: "#fd671b",
900: "#FD5602",
1000: "#F56646",
},
green: {
@ -179,11 +183,14 @@ const theme = extendTheme({
700: "#a8d973",
800: "#9dd562",
900: "#92D050",
1000: "#46C370",
},
black: {
100: "#333399",
200: "#111442",
300: "#1A1D22",
400: "#292929",
},
},
});

Wyświetl plik

@ -6,7 +6,6 @@ import {
MenuList,
MenuItem,
MenuGroup,
MenuDivider,
IconButton,
chakra,
Portal,
@ -33,20 +32,24 @@ const AccountIconButton = (props) => {
/>
<Portal>
<MenuList
zIndex="dropdown"
width={["100vw", "100vw", "18rem", "20rem", "22rem", "24rem"]}
borderRadius={0}
color="black"
zIndex={100}
bg="#1A1D22"
w="auto"
minW="auto"
borderRadius="10px"
p="20px 20px 10px 20px"
border="1px solid white"
>
<MenuGroup>
<RouterLink href="/account/security" passHref>
<MenuItem>Security</MenuItem>
<div className="desktop-menu-item">Security</div>
</RouterLink>
<RouterLink href="/account/tokens" passHref>
<MenuItem>API tokens</MenuItem>
<div className="desktop-menu-item" title="API tokens">
API tokens
</div>
</RouterLink>
</MenuGroup>
<MenuDivider />
{ui.isMobileView &&
SITEMAP.map((item, idx) => {
if (item.type !== PAGETYPE.FOOTER_CATEGORY && item.children) {
@ -54,7 +57,22 @@ const AccountIconButton = (props) => {
<MenuGroup key={`AccountIconButton-MenuGroup-${idx}`}>
{item.children.map((child, idx) => {
return (
<MenuItem key={`AccountIconButton-SITEMAP-${idx}`}>
<MenuItem
key={`AccountIconButton-SITEMAP-${idx}`}
m={0}
color="white"
fontWeight="400"
fontSize="16px"
px="0px"
mb="10px"
h="22px"
_hover={{
backgroundColor: "#1A1D22",
color: "#F56646",
fontWeight: "700",
}}
_focus={{ backgroundColor: "#1A1D22" }}
>
<RouterLink href={child.path}>
{child.title}
</RouterLink>
@ -65,14 +83,14 @@ const AccountIconButton = (props) => {
);
}
})}
<MenuDivider />
<MenuItem
<div
className="desktop-menu-item"
onClick={() => {
logout();
}}
>
Logout
</MenuItem>
</div>
</MenuList>
</Portal>
</Menu>

Wyświetl plik

@ -107,9 +107,20 @@ const AppNavbar = () => {
<>
{!ui.isMobileView && (
<>
<Flex px={2}>
<Flex px={2} minH="72px" maxH="72px" alignItems="center">
<RouterLink href="/" passHref>
<Image
w="160px"
py="0.75rem"
pl={1}
ml="15px"
src={PRIMARY_MOON_LOGO_URL}
alt="Moonstream To"
cursor="pointer"
/>
</RouterLink>
<Spacer />
<Flex placeSelf="flex-end">
<Flex h="100%" alignItems="center">
<ButtonGroup variant="link" spacing={4}>
{SITEMAP.map((item, idx) => {
if (
@ -133,11 +144,24 @@ const AppNavbar = () => {
key={`menu-button-${idx}`}
as={Button}
rightIcon={<ChevronDownIcon />}
color="white"
fontWeight="500"
_expanded={{ color: "white", fontWeight: "700" }}
_focus={{ textDecoration: "none" }}
_hover={{ textDecoration: "none", fontWeight: "700" }}
>
{item.title}
</MenuButton>
<Portal>
<MenuList zIndex={100}>
<MenuList
zIndex={100}
bg="#1A1D22"
w="auto"
minW="auto"
borderRadius="10px"
p="20px 20px 10px 20px"
border="1px solid white"
>
{item.children.map((child, idx) => (
<RouterLink
shallow={true}
@ -145,8 +169,23 @@ const AppNavbar = () => {
href={child.path}
passHref
>
<MenuItem key={`menu-${idx}`} as={"a"} m={0}>
<Text color="black">{child.title}</Text>
<MenuItem
key={`menu-${idx}`}
as={"a"}
m={0}
color="white"
fontWeight="400"
px="0px"
mb="10px"
h="22px"
_hover={{
backgroundColor: "black.300",
color: "#F56646",
fontWeight: "700",
}}
_focus={{ backgroundColor: "black.300" }}
>
{child.title}
</MenuItem>
</RouterLink>
))}
@ -164,7 +203,7 @@ const AppNavbar = () => {
href={item.path}
isActive={!!(router.nextRouter.pathname === item.path)}
>
{item.title}
<Text color="white">{item.title}</Text>
</RouteButton>
);
})}

Wyświetl plik

@ -53,8 +53,13 @@ const AutoCompleter = ({
return (
<Box pos="relative">
<Box {...getRootProps({}, { suppressRefError: true })}>
<InputGroup>
<InputGroup
border="1px solid white"
bg="black.300"
borderRadius="7px"
>
<InputLeftAddon
borderStyle="none"
isTruncated
maxW="60px"
fontSize={ui.isMobileView ? "xs" : "sm"}
@ -75,6 +80,9 @@ const AutoCompleter = ({
</InputLeftAddon>
<Input
variant="bw"
borderStyle="none none none"
borderLeft="1px solid white"
placeholder={placeholder}
isTruncated
fontSize="sm"
@ -82,9 +90,10 @@ const AutoCompleter = ({
// defaultValue: getDefaultValue(selectedItem),
})}
></Input>
<InputRightAddon>
<InputRightAddon bg="black.300">
{" "}
<button
style={{ backgroundColor: "black.300" }}
{...getToggleButtonProps()}
aria-label={"toggle menu"}
>
@ -99,7 +108,7 @@ const AutoCompleter = ({
direction="column"
className="menuListTim"
{...getMenuProps()}
bgColor="gray.300"
bgColor="black.300"
borderRadius="md"
boxShadow="lg"
pos="absolute"
@ -123,6 +132,7 @@ const AutoCompleter = ({
px={4}
py={1}
alignItems="center"
border="1px solid white"
key={`autocomplete-item-${index}`}
{...getItemProps({
index,
@ -130,13 +140,8 @@ const AutoCompleter = ({
})}
direction="row"
w="100%"
bgColor={
index === highlightedIndex
? "orange.900"
: "inherit"
}
color={
index === highlightedIndex ? "gray.100" : "inherit"
fontWeight={
index === highlightedIndex ? "600" : "inherit"
}
>
{dropdownItem(item)}

Wyświetl plik

@ -28,7 +28,7 @@ const CheckboxGroupped = ({
px={2}
key={`list-item-checkbox-${idx}`}
direction="row"
bgColor={idx % 2 == 0 ? "gray.50" : "gray.100"}
bgColor={idx % 2 == 0 ? "black.400" : "black.300"}
>
<Checkbox
isChecked={isItemChecked(listItem)}

Wyświetl plik

@ -16,7 +16,7 @@ const ConfirmationRequest = (props) => {
{({ onClose }) => (
<Fragment>
<PopoverTrigger>{props.children}</PopoverTrigger>
<PopoverContent zIndex={100} bg="White">
<PopoverContent zIndex={100} bg="White" color="Black">
<PopoverCloseButton />
<PopoverHeader fontWeight="bold">{props.header}</PopoverHeader>
<PopoverBody fontSize="md">{props.bodyMessage}</PopoverBody>

Wyświetl plik

@ -222,12 +222,15 @@ const EntriesNavigation = () => {
overflow="hidden"
direction="column"
flexGrow={1}
mt="10px"
mr="5px"
ml="5px"
>
{streamCache && !eventsIsLoading ? (
<>
<Drawer onClose={onClose} isOpen={isOpen} size="lg">
<DrawerOverlay />
<DrawerContent bgColor="gray.100">
<DrawerContent bgColor="gray.100" color="Black">
<DrawerCloseButton />
<DrawerHeader>{`Filter results`}</DrawerHeader>
<DrawerBody>
@ -433,7 +436,7 @@ const EntriesNavigation = () => {
w="100%"
//onScroll={(e) => handleScroll(e)}
>
<Stack direction="row" justifyContent="space-between">
<Stack mt="5px" direction="row" justifyContent="space-around">
{!loadNewerEventsIsFetching && !nextEventIsFetching ? (
<Button
onClick={() => {
@ -452,53 +455,53 @@ const EntriesNavigation = () => {
colorScheme="green"
></Button>
)}
</Stack>
{streamCache
.slice(
cursor,
streamCache.length <= cursor + PAGE_SIZE
? streamCache.length
: cursor + PAGE_SIZE
)
.map((entry, idx) => (
<StreamEntry
showOnboardingTooltips={false}
key={`entry-list-${idx}`}
entry={entry}
disableDelete={!canDelete}
disableCopy={!canCreate}
filterCallback={handleFilterStateCallback}
filterConstants={{ DIRECTIONS, CONDITION, FILTER_TYPES }}
/>
))}
{previousEvent &&
!loadOlderEventsIsFetching &&
!previousEventIsFetching ? (
<Center>
<Button
onClick={() => {
loadPreviousEventHandler();
}}
variant="outline"
colorScheme="green"
>
Load older events
</Button>
</Center>
) : (
<Center>
{!previousEventIsFetching && !loadOlderEventsIsFetching ? (
"Тransactions not found. You can subscribe to more addresses in Subscriptions menu."
) : (
{streamCache
.slice(
cursor,
streamCache.length <= cursor + PAGE_SIZE
? streamCache.length
: cursor + PAGE_SIZE
)
.map((entry, idx) => (
<StreamEntry
showOnboardingTooltips={false}
key={`entry-list-${idx}`}
entry={entry}
disableDelete={!canDelete}
disableCopy={!canCreate}
filterCallback={handleFilterStateCallback}
filterConstants={{ DIRECTIONS, CONDITION, FILTER_TYPES }}
/>
))}
{previousEvent &&
!loadOlderEventsIsFetching &&
!previousEventIsFetching ? (
<Center>
<Button
isLoading
loadingText="Loading"
onClick={() => {
loadPreviousEventHandler();
}}
variant="outline"
colorScheme="green"
></Button>
)}
</Center>
)}
>
Load older events
</Button>
</Center>
) : (
<Center>
{!previousEventIsFetching && !loadOlderEventsIsFetching ? (
"Тransactions not found. You can subscribe to more addresses in Subscriptions menu."
) : (
<Button
isLoading
loadingText="Loading"
variant="outline"
colorScheme="green"
></Button>
)}
</Center>
)}
</Stack>
</Flex>
</Flex>
</>

Wyświetl plik

@ -1,15 +1,7 @@
import React, { useEffect } from "react";
import { useForm } from "react-hook-form";
import { useToast, useForgotPassword } from "../core/hooks";
import {
FormControl,
InputGroup,
FormErrorMessage,
Button,
Input,
InputRightElement,
} from "@chakra-ui/react";
import CustomIcon from "./CustomIcon";
import { FormControl, InputGroup, Button, Input } from "@chakra-ui/react";
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
const ForgotPassword = ({ toggleModal }) => {
@ -19,34 +11,39 @@ const ForgotPassword = ({ toggleModal }) => {
useEffect(() => {
if (!data) return;
toggleModal({ type: MODAL_TYPES.OFF });
}, [data, toggleModal, toast]);
return (
<form onSubmit={handleSubmit(forgotPassword)}>
<div
style={{
fontSize: "18px",
fontWeight: 400,
color: errors.username ? "#EE8686" : "white",
}}
>
{errors.email ? errors.email.message : "Email"}
</div>
<FormControl isInvalid={errors.email} my={4}>
<InputGroup>
<Input
colorScheme="blue"
variant="filled"
placeholder="Your email here"
variant="bw"
placeholder="Enter your email"
name="email"
ref={register({ required: "Email is required!" })}
autoComplete="email"
ref={register({ required: "Email is required" })}
/>
<InputRightElement>
<CustomIcon icon="name" />
</InputRightElement>
</InputGroup>
<FormErrorMessage color="red.400" pl="1">
{errors.email && errors.email.message}
</FormErrorMessage>
</FormControl>
<Button
mt="30px"
mb="10px"
fontSize="lg"
h="46px"
type="submit"
variant="solid"
colorScheme="blue"
width="100%"
variant="plainOrange"
isLoading={isLoading}
>
Send

Wyświetl plik

@ -0,0 +1,164 @@
import React, { useContext } from "react";
import RouterLink from "next/link";
import {
Button,
Image,
ButtonGroup,
Spacer,
Link,
Flex,
Menu,
MenuButton,
MenuList,
MenuItem,
Portal,
Text,
Box,
} from "@chakra-ui/react";
import { PAGETYPE, SITEMAP, PRIMARY_MOON_LOGO_URL } from "../core/constants";
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
import useModals from "../core/hooks/useModals";
import UIContext from "../core/providers/UIProvider/context";
import PlainButton from "./atoms/PlainButton";
import ChakraAccountIconButton from "./AccountIconButton";
const LandingBarMobile = () => {
const ui = useContext(UIContext);
const { toggleModal } = useModals();
return (
<Flex
h={ui.isAppView ? "72px" : "89px"}
direction="column"
width={"100%"}
justifyContent={ui.isLoggedIn ? "center" : "space-between"}
>
<Flex
width={"100%"}
alignItems="center"
flex="flex: 0 0 100%"
pl="10px"
pr="27px"
mt={ui.isLoggedIn ? "0px" : "12px"}
>
<RouterLink href="/" passHref>
<Link
as={Image}
w={"160px"}
h={"23px"}
justifyContent="left"
src={PRIMARY_MOON_LOGO_URL}
alt="Moonstream logo"
/>
</RouterLink>
<Spacer />
{!ui.isLoggedIn && (
<PlainButton
style={{
marginRight: "12px",
fontSize: "14px",
padding: "2px 10px",
}}
onClick={() => toggleModal({ type: MODAL_TYPES.SIGNUP })}
>
Sign up
</PlainButton>
)}
{!ui.isLoggedIn && (
<Text
color="white"
bg="transparent"
onClick={() => toggleModal({ type: MODAL_TYPES.LOGIN })}
fontWeight="400"
p="0px"
m="0px"
_focus={{ backgroundColor: "transparent" }}
_hover={{ backgroundColor: "transparent" }}
>
Log in
</Text>
)}
{ui.isLoggedIn && (
<RouterLink href="/welcome" passHref>
<Box
bg="orange.1000"
alignSelf={"center"}
as={Link}
color="white"
size="sm"
fontWeight="700"
borderRadius="15px"
w="47px"
h="25px"
textAlign="center"
fontSize="14px"
>
<Text lineHeight="25px">App</Text>
</Box>
</RouterLink>
)}
{ui.isLoggedIn && ui.isMobileView && (
<>
<ChakraAccountIconButton variant="link" colorScheme="orange" />
</>
)}
</Flex>
<ButtonGroup
justifyContent="center"
w="100%"
variant="link"
spacing={4}
flexGrow={0.5}
>
{SITEMAP.map((item, idx) => {
return (
<React.Fragment key={`Fragment-${idx}`}>
{item.type !== PAGETYPE.FOOTER_CATEGORY && item.children && (
<Menu>
<MenuButton variant="mobile" mb="0px" p="0px" as={Button}>
{item.title}
</MenuButton>
<Portal>
<MenuList
zIndex={100}
bg="black.300"
w="auto"
minW="auto"
borderRadius="10px"
p="10px 20px 10px 20px"
border="1px solid white"
>
{item.children.map((child, idx) => (
<RouterLink
shallow={true}
key={`${idx}-${item.title}-menu-links`}
href={child.path}
passHref
>
<MenuItem
color="white"
key={`menu-${idx}`}
as={"a"}
m={0}
fontSize="sm"
_focus={{ backgroundColor: "black.300" }}
_active={{ backgroundColor: "black.300" }}
_hover={{ backgroundColor: "black.300" }}
>
{child.title}
</MenuItem>
</RouterLink>
))}
</MenuList>
</Portal>
</Menu>
)}
</React.Fragment>
);
})}
</ButtonGroup>
</Flex>
);
};
export default LandingBarMobile;

Wyświetl plik

@ -4,70 +4,54 @@ import {
Button,
Image,
ButtonGroup,
Spacer,
Link,
IconButton,
Flex,
Menu,
MenuButton,
MenuList,
MenuItem,
Portal,
Box,
Text,
} from "@chakra-ui/react";
import { ChevronDownIcon, HamburgerIcon } from "@chakra-ui/icons";
import { ChevronDownIcon } from "@chakra-ui/icons";
import useModals from "../core/hooks/useModals";
import UIContext from "../core/providers/UIProvider/context";
import ChakraAccountIconButton from "./AccountIconButton";
import RouteButton from "./RouteButton";
import {
PAGETYPE,
SITEMAP,
PRIMARY_MOON_LOGO_URL,
BACKGROUND_COLOR,
} from "../core/constants";
import { PAGETYPE, SITEMAP, PRIMARY_MOON_LOGO_URL } from "../core/constants";
import router from "next/router";
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
import LandingBarMobile from "./LandingBarMobile";
const LandingNavbar = () => {
const ui = useContext(UIContext);
const { toggleModal } = useModals();
return (
<>
{ui.isMobileView && (
<>
<IconButton
alignSelf="flex-start"
colorScheme="blackAlpha"
bgColor={BACKGROUND_COLOR}
variant="solid"
onClick={() => ui.setSidebarToggled(!ui.sidebarToggled)}
icon={<HamburgerIcon />}
/>
</>
)}
<Flex
pl={ui.isMobileView ? 2 : "60px"}
justifySelf="flex-start"
h="48px"
py={1}
flexBasis="200px"
flexGrow={0.6}
id="Logo Container"
alignItems="center"
>
<RouterLink href="/" passHref>
<Link
as={Image}
w={"160px"}
justifyContent="left"
src={PRIMARY_MOON_LOGO_URL}
alt="Moonstream logo"
/>
</RouterLink>
</Flex>
{ui.isMobileView && <LandingBarMobile />}
{!ui.isMobileView && (
<>
<Flex
pl={ui.isMobileView ? 2 : "60px"}
justifySelf="flex-start"
h={ui.isMobileView && !ui.isAppView ? "89px" : "72px"}
py={1}
flexBasis="200px"
flexGrow={0.6}
id="Logo Container"
alignItems="center"
>
<RouterLink href="/" passHref>
<Link
as={Image}
w={"160px"}
justifyContent="left"
src={PRIMARY_MOON_LOGO_URL}
alt="Moonstream logo"
/>
</RouterLink>
</Flex>
<ButtonGroup variant="link" spacing={4} pr={16} flexGrow={0.5}>
{SITEMAP.map((item, idx) => {
return (
@ -78,18 +62,36 @@ const LandingNavbar = () => {
variant="link"
href={item.path}
color="black"
fontSize="16px"
isActive={!!(router.pathname === item.path)}
>
{item.title}
</RouteButton>
)}
{item.type !== PAGETYPE.FOOTER_CATEGORY && item.children && (
<Menu colorScheme="blackAlpha">
<MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
<Menu autoSelect="false">
<MenuButton
as={Button}
rightIcon={<ChevronDownIcon />}
color="white"
fontWeight="500"
fontSize="16px"
_expanded={{ color: "white", fontWeight: "700" }}
_focus={{ textDecoration: "none" }}
_hover={{ textDecoration: "none", fontWeight: "700" }}
>
{item.title}
</MenuButton>
<Portal>
<MenuList zIndex={100}>
<MenuList
zIndex={100}
bg="black.300"
w="auto"
minW="auto"
borderRadius="10px"
p="20px 20px 10px 20px"
border="1px solid white"
>
{item.children.map((child, idx) => (
<RouterLink
shallow={true}
@ -97,7 +99,23 @@ const LandingNavbar = () => {
href={child.path}
passHref
>
<MenuItem key={`menu-${idx}`} as={"a"} m={0}>
<MenuItem
key={`menu-${idx}`}
as={"a"}
m={0}
color="white"
fontWeight="400"
fontSize="16px"
px="0px"
mb="10px"
h="22px"
_hover={{
backgroundColor: "black.300",
color: "orange.1000",
fontWeight: "700",
}}
_focus={{ backgroundColor: "black.300" }}
>
{child.title}
</MenuItem>
</RouterLink>
@ -113,28 +131,38 @@ const LandingNavbar = () => {
<ButtonGroup variant="link" spacing={4} pr={16}>
{ui.isLoggedIn && (
<RouterLink href="/welcome" passHref>
<Button
<Box
bg="orange.1000"
alignSelf={"center"}
as={Link}
colorScheme="orange"
variant="outline"
size="sm"
fontWeight="400"
borderRadius="2xl"
fontWeight="700"
borderRadius="15px"
w="51px"
h="32px"
textAlign="center"
px="10px"
cursor="pointer"
_hover={{
backgroundColor: "#F4532F",
}}
>
App
</Button>
<Text fontSize="16px" lineHeight="32px">
App
</Text>
</Box>
</RouterLink>
)}
{!ui.isLoggedIn && (
<Button
bg="#F56646"
bg="orange.1000"
variant="solid"
onClick={() => toggleModal({ type: MODAL_TYPES.SIGNUP })}
size="sm"
fontWeight="bold"
fontWeight="700"
borderRadius="2xl"
textColor="white"
_hover={{
backgroundColor: "#F4532F",
}}
>
Sign up
</Button>
@ -154,12 +182,6 @@ const LandingNavbar = () => {
</ButtonGroup>
</>
)}
{ui.isLoggedIn && ui.isMobileView && (
<>
<Spacer />
<ChakraAccountIconButton variant="link" colorScheme="orange" />
</>
)}
</>
);
};

Wyświetl plik

@ -7,7 +7,7 @@ import LandingNavbar from "./LandingNavbar";
const AppNavbar = React.lazy(() => import("./AppNavbar"));
const Navbar = () => {
const { isAppView, isLoggedIn } = useContext(UIContext);
const { isAppView, isLoggedIn, isMobileView } = useContext(UIContext);
return (
<Flex
@ -15,8 +15,8 @@ const Navbar = () => {
zIndex={1}
alignItems="center"
id="Navbar"
minH="3rem"
maxH="3rem"
minH={isMobileView && !isAppView ? "89px" : "72px"}
maxH={isMobileView && !isAppView ? "89px" : "72px"}
bgColor={BACKGROUND_COLOR}
borderBottom="1px solid white"
direction="row"

Wyświetl plik

@ -4,7 +4,6 @@ import {
FormLabel,
Stack,
Button,
Badge,
Spinner,
Accordion,
AccordionItem,
@ -13,6 +12,7 @@ import {
AccordionIcon,
Box,
IconButton,
Text,
} from "@chakra-ui/react";
import { useSubscriptions } from "../core/hooks";
import color from "color";
@ -78,7 +78,7 @@ const NewDashboardChart = () => {
});
}
return (
<AccordionItem key={`new-chart-component-${idx}`}>
<AccordionItem pt="5px" key={`new-chart-component-${idx}`}>
{subscribedItem?.subscription_id &&
subscriptionItemFromCache && (
<>
@ -244,33 +244,25 @@ const NewDashboardChart = () => {
</Button>
)}
dropdownItem={(item) => {
const badgeColor = color(`${item.color}`);
return (
<>
<Stack cursor="pointer" direction="row">
<chakra.span whiteSpace="nowrap">
{item.label}
</chakra.span>
<Badge
size="sm"
placeSelf="self-end"
colorScheme={item.abi ? "green" : "gray"}
<Text
fontSize="md"
color={item.abi ? "white" : "gray"}
>
ABI
</Badge>
<Badge
</Text>
<Text
isTruncated
size="sm"
placeSelf="self-end"
bgColor={item.color}
color={
badgeColor.isDark()
? badgeColor.lighten(100).hex()
: badgeColor.darken(0.6).hex()
}
fontSize="md"
placeSelf="self-center"
>
{item.address}
</Badge>
</>
</Text>
</Stack>
);
}}
/>
@ -299,7 +291,8 @@ const NewDashboardChart = () => {
)}
<Button
colorScheme="green"
variant="plainOrange"
fontSize="md"
size="md"
onClick={() =>
ui.dispatchDashboardUpdate({

Wyświetl plik

@ -57,9 +57,12 @@ const NewDashboardName = (props) => {
return (
<>
<Stack direction={["column", "row", null]}>
<InputGroup>
<InputLeftAddon>Name:</InputLeftAddon>
<InputGroup border="1px solid white" borderRadius="7px">
<InputLeftAddon bg="black.300">Name:</InputLeftAddon>
<Input
borderStyle="none none none"
borderLeft="1px solid white"
variant="bw"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder={placeholder}

Wyświetl plik

@ -158,12 +158,15 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
// style={comboboxStyles}
{...getRootProps({}, { suppressRefError: true })}
>
<InputGroup>
<InputGroup
border="1px solid white"
borderRadius="7px"
>
<InputLeftAddon
isTruncated
maxW="60px"
fontSize="sm"
bgColor={"gray.100"}
bgColor="black.300"
>
<Image h="24px" src={selectedItem?.icon_url} />
</InputLeftAddon>
@ -172,15 +175,24 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
placeholder="What do you want to subscribe to"
isTruncated
fontSize="sm"
variant="bw"
{...getInputProps()}
></Input>
<InputRightAddon p={0}>
<Button
variant="outline"
// variant="none"
borderStyle="none"
borderColor="black"
w="100%"
m={0}
p={0}
colorScheme="gray"
bg="black.300"
_hover={{
backgroundColor: "black.300",
}}
_focus={{
backgroundColor: "black.300",
}}
{...getToggleButtonProps({})}
aria-label={"toggle menu"}
>
@ -194,7 +206,7 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
direction="column"
className="menuListTim"
{...getMenuProps()}
bgColor="gray.300"
bgColor="black.300"
borderRadius="md"
boxShadow="lg"
pos="absolute"
@ -213,6 +225,7 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
px={4}
py={1}
alignItems="center"
cursor="pointer"
key={item.value}
{...getItemProps({
key: item.value,
@ -221,14 +234,9 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
})}
direction="row"
w="100%"
bgColor={
index === highlightedIndex
? "orange.900"
: "inherit"
}
color={
index === highlightedIndex
? "gray.100"
? "#F56646"
: "inherit"
}
justifyContent="space-between"
@ -237,6 +245,11 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
h="24px"
src={item.icon_url}
alignSelf="flex-start"
ml={
index === highlightedIndex
? "3px"
: "inherit"
}
/>
<chakra.span
whiteSpace="nowrap"
@ -272,8 +285,13 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
<VStack w="100%" spacing={0}>
<Flex w="100%">
<FormControl isInvalid={errors?.address}>
<InputGroup my={2} fontSize="xs">
<InputLeftAddon>
<InputGroup
my={2}
fontSize="xs"
border="1px solid white"
borderRadius="7px"
>
<InputLeftAddon bg="black.300">
<FormLabel
fontWeight="600"
// alignSelf="flex-start"
@ -285,6 +303,7 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
<Input
type="text"
autoComplete="off"
variant="bw"
placeholder="Address to subscribe to"
name="address"
value={address}
@ -292,7 +311,7 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
ref={register({ required: "address is required!" })}
></Input>
</InputGroup>
<FormErrorMessage color="red.400" pl="1">
<FormErrorMessage color="#EE8686" pl="1">
{errors?.address && errors?.address.message}
</FormErrorMessage>
</FormControl>
@ -306,8 +325,13 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
<VStack w="100%" spacing={0}>
<Flex w="100%">
<FormControl isInvalid={errors?.label}>
<InputGroup my={2} fontSize="xs">
<InputLeftAddon>
<InputGroup
my={2}
fontSize="xs"
border="1px solid white"
borderRadius="7px"
>
<InputLeftAddon bgColor="black.300">
<FormLabel
fontWeight="600"
// alignSelf="flex-start"
@ -319,6 +343,7 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
<Input
type="text"
autoComplete="off"
variant="bw"
placeholder="Name your label"
name="label"
value={label}
@ -326,7 +351,7 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
ref={register({ required: "label is required!" })}
></Input>
</InputGroup>
<FormErrorMessage color="red.400" pl="1">
<FormErrorMessage color="#EE8686" pl="1">
{errors?.label && errors?.label.message}
</FormErrorMessage>
</FormControl>
@ -336,7 +361,7 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
)}
{type && (
<FormControl isInvalid={errors?.color}>
<FormControl isInvalid={errors?.color} bg="black.300">
{!isModal ? (
<Flex
direction="row"
@ -355,7 +380,6 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
>
<IconButton
size="md"
// colorScheme="blue"
color={"white.100"}
_hover={{ bgColor: { color } }}
bgColor={color}
@ -364,6 +388,7 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
icon={<BiRefresh />}
/>
<Input
variant="bw"
type="input"
placeholder="color"
name="color"
@ -401,6 +426,7 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
type="input"
placeholder="color"
name="color"
variant="bw"
ref={register({ required: "color is required!" })}
value={color}
onChange={() => null}
@ -408,7 +434,20 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
></Input>
</Stack>
<GithubPicker onChangeComplete={handleChangeColorComplete} />
<GithubPicker
styles={{
default: {
card: {
background: "black.300",
border: "1px solid white",
},
triangle: {
borderBottomColor: "white",
},
},
}}
onChangeComplete={handleChangeColorComplete}
/>
</>
)}
<FormErrorMessage color="red.400" pl="1">

Wyświetl plik

@ -27,10 +27,12 @@ const Scrollable = (props) => {
}
}
if (dir === -1) {
document.getElementById("Navbar").style.top = "-48px";
const navbar = document.getElementById("Navbar");
if (dir === -1 && e.target.scrollTop > 0) {
navbar.style.top = `${-navbar.offsetHeight}px`;
} else {
document.getElementById("Navbar").style.top = "-0";
navbar.style.top = "-0";
}
setY(currentScroll);
if (currentScroll > scrollDepth) {
@ -69,6 +71,7 @@ const Scrollable = (props) => {
ref={scrollerRef}
overflowY="scroll"
onScroll={(e) => handleScroll(e)}
// pt="72px"
>
{props.children}
</Box>

Wyświetl plik

@ -2,35 +2,17 @@ import {
ProSidebar,
Menu,
MenuItem,
SidebarHeader,
SidebarFooter,
SidebarContent,
} from "react-pro-sidebar";
import { useContext } from "react";
import RouterLink from "next/link";
import {
Flex,
Image,
IconButton,
Divider,
Text,
Button,
} from "@chakra-ui/react";
import { Divider, Text, Button } from "@chakra-ui/react";
import UIContext from "../core/providers/UIProvider/context";
import React from "react";
import {
HamburgerIcon,
ArrowLeftIcon,
ArrowRightIcon,
LockIcon,
} from "@chakra-ui/icons";
import { LockIcon } from "@chakra-ui/icons";
import { MdSettings, MdDashboard, MdTimeline } from "react-icons/md";
import {
PRIMARY_MOON_LOGO_URL,
SITEMAP,
PAGETYPE,
BACKGROUND_COLOR,
} from "../core/constants";
import { SITEMAP, PAGETYPE, BACKGROUND_COLOR } from "../core/constants";
import useDashboard from "../core/hooks/useDashboard";
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
import OverlayContext from "../core/providers/OverlayProvider/context";
@ -48,41 +30,8 @@ const Sidebar = () => {
onToggle={ui.setSidebarToggled}
collapsed={ui.sidebarCollapsed}
hidden={!ui.sidebarVisible}
className={ui.isMobileView ? "t40" : "t0"}
>
<SidebarHeader>
<Flex>
<IconButton
ml={4}
justifySelf="flex-start"
colorScheme="blackAlpha"
bgColor={BACKGROUND_COLOR}
aria-label="App navigation"
icon={
ui.isMobileView ? (
<HamburgerIcon />
) : ui.sidebarCollapsed ? (
<ArrowRightIcon />
) : (
<ArrowLeftIcon />
)
}
onClick={() => {
ui.isMobileView
? ui.setSidebarToggled(!ui.sidebarToggled)
: ui.setSidebarCollapsed(!ui.sidebarCollapsed);
}}
/>
<RouterLink href="/" passHref>
<Image
w="160px"
py="0.75rem"
pl={1}
src={PRIMARY_MOON_LOGO_URL}
alt="Moonstream To"
/>
</RouterLink>
</Flex>
</SidebarHeader>
<SidebarContent>
<Divider borderColor={BACKGROUND_COLOR} />
<Menu iconShape="square">

Wyświetl plik

@ -1,24 +1,23 @@
import React, { useEffect } from "react";
import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import {
Text,
Stack,
Box,
FormControl,
FormErrorMessage,
InputGroup,
Button,
Input,
InputRightElement,
Button,
} from "@chakra-ui/react";
import CustomIcon from "./CustomIcon";
import { useLogin } from "../core/hooks";
import PasswordInput from "./PasswordInput";
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
const SignIn = ({ toggleModal }) => {
const { handleSubmit, errors, register } = useForm();
const { login, isLoading, data } = useLogin();
const [showPassword, setShowPassword] = useState(false);
useEffect(() => {
if (!data) {
@ -30,70 +29,87 @@ const SignIn = ({ toggleModal }) => {
return (
<>
<Text color="gray.1200" fontSize="md">
To your Moonstream account
</Text>
<form onSubmit={handleSubmit(login)}>
<Stack width="100%" pt={4} spacing={3}>
<Stack width="100%" spacing={3}>
<div
style={{
fontSize: "18px",
fontWeight: 400,
color: errors.username ? "#EE8686" : "white",
}}
>
{errors.username ? errors.username.message : "Username"}
</div>
<FormControl isInvalid={errors.username}>
<InputGroup>
<InputGroup bg="black">
<Input
_placeholder={{ textColor: "gray.1200" }}
autoComplete="username"
variant="filled"
colorScheme="blue"
placeholder="Your Moonstream username"
variant="bw"
placeholder="Enter your username or email"
name="username"
{...register("username", { required: true })}
ref={register({ required: "Username is required!" })}
ref={register({ required: "Username is required" })}
/>
<InputRightElement>
<CustomIcon icon="name" />
</InputGroup>
</FormControl>
<div
style={{
fontSize: "18px",
color: errors.password ? "#EE8686" : "white",
}}
>
{errors.password ? errors.password.message : "Password"}
</div>
<FormControl isInvalid={errors.password}>
<InputGroup bg="black">
<Input
autoComplete="current-password"
variant="bw"
placeholder="Enter your password"
name="password"
type={showPassword ? "text" : "password"}
ref={register({ required: "Password is required" })}
/>
<InputRightElement
onClick={() => setShowPassword(!showPassword)}
style={{ cursor: "pointer" }}
>
<CustomIcon icon="password" />
</InputRightElement>
</InputGroup>
<FormErrorMessage color="red.400" pl="1">
{errors.username && errors.username.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.password}>
<PasswordInput
placeholder="Your Moonstream password"
name="password"
ref={register({ required: "Password is required!" })}
/>
<FormErrorMessage color="red.400" pl="1">
{errors.password && errors.password.message}
</FormErrorMessage>
</FormControl>
<Text textAlign="start" fontSize="18px">
{" "}
<Box
cursor="pointer"
color="#EE8686"
as="span"
onClick={() => toggleModal({ type: MODAL_TYPES.FORGOT })}
>
Forgot your password?
</Box>
</Text>
</Stack>
<Button
my={8}
mt="30px"
mb="10px"
h="46px"
fontSize="lg"
variant="plainOrange"
type="submit"
width="100%"
variant="solid"
colorScheme="blue"
isLoading={isLoading}
>
Login
</Button>
</form>
<Text textAlign="center" fontSize="md" color="gray.1200">
{" "}
<Box
cursor="pointer"
color="blue.800"
as="span"
onClick={() => toggleModal({ type: MODAL_TYPES.FORGOT })}
>
Forgot your password?
</Box>
<Box height="1px" width="100%" background="#eaebf8" mb="1.875rem" />
</Text>
<Text textAlign="center" fontSize="md" color="gray.1200">
<Text textAlign="center" fontSize="md" color="white">
Don`t have an account?{" "}
<Box
cursor="pointer"
color="blue.800"
color="#EE8686"
as="span"
onClick={() => toggleModal({ type: MODAL_TYPES.SIGNUP })}
>

Wyświetl plik

@ -1,11 +1,10 @@
import React, { useEffect } from "react";
import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import {
Text,
Stack,
Box,
FormControl,
FormErrorMessage,
InputGroup,
Button,
Input,
@ -13,12 +12,12 @@ import {
} from "@chakra-ui/react";
import CustomIcon from "./CustomIcon";
import { useSignUp } from "../core/hooks";
import PasswordInput from "./PasswordInput";
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
const SignUp = ({ toggleModal }) => {
const { handleSubmit, errors, register } = useForm();
const { signUp, isLoading, isSuccess } = useSignUp();
const [showPassword, setShowPassword] = useState(false);
useEffect(() => {
if (isSuccess) {
@ -28,76 +27,94 @@ const SignUp = ({ toggleModal }) => {
return (
<>
<Text color="gray.1200" fontSize="md">
Sign up for free
</Text>
<form onSubmit={handleSubmit(signUp)}>
<Stack width="100%" pt={4} spacing={3}>
<Stack width="100%" spacing={3}>
<div
style={{
fontSize: "18px",
fontWeight: 400,
color: errors.username ? "#EE8686" : "white",
}}
>
{errors.username ? errors.username.message : "Username"}
</div>
<FormControl isInvalid={errors.username}>
<InputGroup>
<Input
variant="filled"
colorScheme="blue"
placeholder="Your username here"
name="username"
autoComplete="username"
ref={register({ required: "Username is required!" })}
variant="bw"
placeholder="Enter your username or email"
name="username"
{...register("username", { required: true })}
ref={register({ required: "Username is required" })}
/>
<InputRightElement>
<CustomIcon icon="name" />
</InputRightElement>
</InputGroup>
<FormErrorMessage color="red.400" pl="1">
{errors.username && errors.username.message}
</FormErrorMessage>
</FormControl>
<div
style={{
fontSize: "18px",
fontWeight: 400,
color: errors.username ? "#EE8686" : "white",
}}
>
{errors.email ? errors.email.message : "Email"}
</div>
<FormControl isInvalid={errors.email}>
<InputGroup>
<Input
variant="filled"
colorScheme="blue"
placeholder="Your email here"
variant="bw"
placeholder="Enter your email"
name="email"
autoComplete="email"
ref={register({ required: "Email is required!" })}
ref={register({ required: "Email is required" })}
/>
<InputRightElement>
<CustomIcon icon="name" />
</InputGroup>
</FormControl>
<div
style={{
fontSize: "18px",
color: errors.password ? "#EE8686" : "white",
}}
>
{errors.password ? errors.password.message : "Password"}
</div>
<FormControl isInvalid={errors.password}>
<InputGroup bg="black">
<Input
autoComplete="current-password"
variant="bw"
placeholder="Enter your password"
name="password"
type={showPassword ? "text" : "password"}
ref={register({ required: "Password is required" })}
/>
<InputRightElement
onClick={() => setShowPassword(!showPassword)}
style={{ cursor: "pointer" }}
>
<CustomIcon icon="password" />
</InputRightElement>
</InputGroup>
<FormErrorMessage color="red.400" pl="1">
{errors.email && errors.email.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.password}>
<PasswordInput
placeholder="Add password"
name="password"
autoComplete="new-password"
ref={register({ required: "Password is required!" })}
/>
<FormErrorMessage color="red.400" pl="1">
{errors.password && errors.password.message}
</FormErrorMessage>
</FormControl>
</Stack>
<Button
my={8}
variant="solid"
colorScheme="blue"
width="100%"
mt="30px"
mb="10px"
fontSize="lg"
h="46px"
type="submit"
width="100%"
variant="plainOrange"
isLoading={isLoading}
>
Register
</Button>
</form>
<Box height="1px" width="100%" background="#eaebf8" mb="1.875rem" />
<Text textAlign="center" fontSize="md" color="gray.1200" pb={8}>
<Text textAlign="center" fontSize="md" color="white">
Already have an account?{" "}
<Box
cursor="pointer"
color="blue.400"
color="#EE8686"
as="span"
onClick={() => toggleModal({ type: MODAL_TYPES.LOGIN })}
>

Wyświetl plik

@ -61,7 +61,6 @@ const SubscriptionCard = ({ subscription, isDesktopView, iconLink }) => {
<>
{!isDesktopView && (
<AccordionItem
bgColor="blue.50"
borderBottomColor="blue.500"
key={`token-row-${subscription.id}`}
>
@ -108,7 +107,7 @@ const SubscriptionCard = ({ subscription, isDesktopView, iconLink }) => {
<AccordionIcon />
</AccordionButton>
</h2>
<AccordionPanel pb={4} bgColor="blue.100" boxShadow="md">
<AccordionPanel pb={4} boxShadow="md">
<Stack>
<Stack fontSize="sm" h="min-content" pr={0}>
<Text placeSelf="flex-start">Address:</Text>

Wyświetl plik

@ -27,9 +27,8 @@ export default function SocialProfileSimple({
maxW={"320px"}
h="420px"
w={"full"}
bg={useColorModeValue("white.50", "gray.900")}
boxShadow={"2xl"}
rounded={"lg"}
borderRadius="20px"
border="1px solid white"
p={6}
textAlign={"center"}
>
@ -56,7 +55,7 @@ export default function SocialProfileSimple({
<Heading fontSize={"2xl"} fontFamily={"body"}>
{name}
</Heading>
<Text fontWeight={600} color={"gray.900"} mb={4}>
<Text fontWeight={600} color={"blue.850"} mb={4}>
{atName}
</Text>
<Text textAlign={"center"} px={3}>

Wyświetl plik

@ -64,6 +64,7 @@ const TokenRequest = ({ setNewToken, onClose }) => {
<chakra.label for="pwd">API key label:</chakra.label>
<Input
w="100%"
variant="bw"
ref={register}
name="token_note"
placeholder="My API key label"
@ -81,7 +82,7 @@ const TokenRequest = ({ setNewToken, onClose }) => {
<Input
id="pwd"
colorScheme="blue"
variant="filled"
variant="bw"
isDisabled={createToken.isLoading}
placeholder="This action requires your password to confirm"
name="password"
@ -114,12 +115,7 @@ const TokenRequest = ({ setNewToken, onClose }) => {
>
Submit
</Button>
<Button
variant="solid"
colorScheme="red"
type="submit"
onClick={() => onClose()}
>
<Button variant="solid" colorScheme="red" onClick={() => onClose()}>
Cancel
</Button>
</Stack>

Wyświetl plik

@ -55,6 +55,7 @@ const MobileFiledInput = ({
return (
<>
<Input
variant="bw"
ref={inputRef}
type="text"
value={value}

Wyświetl plik

@ -0,0 +1,16 @@
.button {
background-color: #F56646;
font-weight: 700;
border-radius: 15px;
text-align: center;
padding: 5px 10px;
cursor: pointer;
font-size: 16px;
justify-content: center;
color: #ffffff;
}
.button:hover {
background-color: #F4532F;
}

Wyświetl plik

@ -0,0 +1,16 @@
/* eslint-disable react/react-in-jsx-scope */
import styles from "./PlainButton.module.css";
const PlainButton = (props) => {
return (
<div
onClick={props.onClick}
className={styles.button}
style={props.style}
type={props.type}
>
{props.children}
</div>
);
};
export default PlainButton;

Wyświetl plik

@ -77,7 +77,7 @@ export const SITEMAP = [
children: [
{
title: "Docs",
path: "/docs",
path: "https://docs.moonstream.to/",
type: PAGETYPE.CONTENT,
},
{

Wyświetl plik

@ -30,7 +30,6 @@ import {
AlertDialogOverlay,
Button,
Spinner,
Divider,
} from "@chakra-ui/react";
import UIContext from "../UIProvider/context";
import useDashboard from "../../hooks/useDashboard";
@ -264,14 +263,26 @@ const OverlayProvider = ({ children }) => {
<Modal
isOpen={modalDisclosure.isOpen}
onClose={() => toggleModal({ type: MODAL_TYPES.OFF })}
size="2xl"
size={modal.type === MODAL_TYPES.LOGIN ? "lg" : "2xl"}
scrollBehavior="outside"
trapFocus={false}
>
<ModalOverlay />
<ModalOverlay backdropFilter="auto" backdropBrightness="60%" />
<ModalContent textColor="black">
<ModalHeader bgColor="white.200" py={2} fontSize="lg">
<ModalContent
bg="black.300"
borderRadius="15px"
border="1px white solid"
p="30px"
textColor="white"
>
<ModalHeader
p="0px"
fontSize="24px"
lineHeight="24px"
fontWeight="700"
mb="30px"
>
{modal.type === MODAL_TYPES.NEW_SUBSCRIPTON &&
"Subscribe to a new address"}
{modal.type === MODAL_TYPES.FORGOT && "Forgot Password"}
@ -283,9 +294,9 @@ const OverlayProvider = ({ children }) => {
"Would you like to give it a name?"}
{modal.type === MODAL_TYPES.MOBILE_INPUT_FIELD && modal.props.title}
</ModalHeader>
<Divider />
<ModalCloseButton />
<ModalCloseButton color="white" top="25px" right="25px" />
<ModalBody
p="0px"
zIndex={100002}
bgColor={
modal.type === MODAL_TYPES.UPLOAD_ABI ? "white.200" : undefined
@ -299,7 +310,9 @@ const OverlayProvider = ({ children }) => {
{...modal.props}
/>
)}
{modal.type === MODAL_TYPES.FORGOT && <ForgotPassword />}
{modal.type === MODAL_TYPES.FORGOT && (
<ForgotPassword toggleModal={toggleModal} />
)}
{modal.type === MODAL_TYPES.HUBSPOT && (
<HubspotForm
toggleModal={toggleModal}
@ -344,8 +357,8 @@ const OverlayProvider = ({ children }) => {
}
}}
>
<DrawerOverlay />
<DrawerContent overflowY="scroll" textColor="black">
<DrawerOverlay backdropFilter="auto" backdropBrightness="60%" />
<DrawerContent overflowY="scroll" textColor="white" bg="black.300">
<DrawerCloseButton />
<DrawerHeader borderBottomWidth="1px">
{drawer.type === DRAWER_TYPES.NEW_DASHBOARD && "New dashboard"}

Wyświetl plik

@ -1,9 +1,10 @@
import { Flex, Spinner, Box } from "@chakra-ui/react";
import { getLayout as getSiteLayout } from "./RootLayout";
import React, { useContext, useEffect } from "react";
import React, { Suspense, useContext, useEffect } from "react";
import UIContext from "../core/providers/UIProvider/context";
import AppNavbar from "../components/AppNavbar";
import { BACKGROUND_COLOR } from "../core/constants";
import Sidebar from "../components/Sidebar";
const AppLayout = ({ children }) => {
const ui = useContext(UIContext);
@ -25,6 +26,7 @@ const AppLayout = ({ children }) => {
w="100%"
overflow="hidden"
direction="column"
pb="85px"
>
{(!ui.isAppReady || !ui.isLoggedIn) && (
<Spinner
@ -55,7 +57,19 @@ const AppLayout = ({ children }) => {
>
<AppNavbar />
</Flex>
{ui.isAppReady && ui.isLoggedIn && children}
<Flex
direction="row"
// id="Bugout"
className="Main"
w="100%"
h="100%"
maxH="100%"
>
<Suspense fallback="">
<Sidebar />
</Suspense>
{ui.isAppReady && ui.isLoggedIn && children}
</Flex>
</Flex>
);
};

Wyświetl plik

@ -1,6 +1,6 @@
import { CloseIcon } from "@chakra-ui/icons";
import { Flex, Center, Text, Link, IconButton } from "@chakra-ui/react";
import React, { Suspense, useContext, useState } from "react";
import React, { useContext, useState } from "react";
import UIContext from "../core/providers/UIProvider/context";
const Sidebar = React.lazy(() => import("../components/Sidebar"));
@ -17,9 +17,6 @@ const RootLayout = (props) => {
h="100%"
maxH="100%"
>
<Suspense fallback="">
<Sidebar />
</Suspense>
<Flex
direction="column"
flexGrow={1}

Wyświetl plik

@ -29,6 +29,14 @@
}
}
.t40 {
top: 40px;
}
.t0 {
top: 0px;
}
.pro-sidebar {
height: 100%;
width: 270px;

Wyświetl plik

@ -267,3 +267,32 @@ code {
linear-gradient(to right, transparent 6px, white 6px),
linear-gradient(to bottom, #444 1px, transparent 1px);
}
.desktop-menu-item {
color: white;
font-weight: 400;
padding-left: 0px;
padding-right: 0px;
margin-bottom: 10px;
height: 22px;
cursor: pointer;
}
.desktop-menu-item:hover {
color: #F56646;
font-weight: 700;
}
/* to dont change div size when bolding text.
Need 'title' attr in the longest item's tag*/
.desktop-menu-item::after {
display: block;
content: attr(title);
font-weight: 700;
height: 1px;
color: transparent;
overflow: hidden;
visibility: hidden;
}