Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Faucet: Add handling for chains API #685

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion apps/faucet/.env.sample
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Faucet API Endpoint override
NAMADA_INTERFACE_FAUCET_API_URL=http://127.0.0.1:5000
NAMADA_INTERFACE_FAUCET_API_URL=http://127.0.0.1:5000/
NAMADA_INTERFACE_FAUCET_API_ENDPOINT=/api/v1/faucet
NAMADA_INTERFACE_CHAIN_ID=shielded-expedition.88f17d1d14
NAMADA_INTERFACE_CHAINS_URL=https://chains.heliax.click/

# Faucet limit, as defined in genesis toml
NAMADA_INTERFACE_FAUCET_LIMIT=1000
Expand Down
47 changes: 38 additions & 9 deletions apps/faucet/src/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,32 @@ import { FaucetForm } from "App/Faucet";
import { chains } from "@namada/chains";
import { useUntil } from "@namada/hooks";
import { Account, AccountType } from "@namada/types";
import { API } from "utils";
import { API, ChainsAPI } from "utils";
import dotsBackground from "../../public/bg-dots.svg";
import { CallToActionCard } from "./CallToActionCard";
import { CardsContainer } from "./Card.components";
import { Faq } from "./Faq";

const DEFAULT_URL = "http://localhost:5000";
const DEFAULT_URL = "http://localhost:5000/";
const DEFAULT_ENDPOINT = "/api/v1/faucet";
const DEFAULT_FAUCET_LIMIT = "1000";
const DEFAULT_CHAIN_ID = "shielded-expedition.88f17d1d14";
const DEFAULT_CHAINS_URL = "";

const {
NAMADA_INTERFACE_FAUCET_API_URL: faucetApiUrl = DEFAULT_URL,
NAMADA_INTERFACE_FAUCET_API_ENDPOINT: faucetApiEndpoint = DEFAULT_ENDPOINT,
NAMADA_INTERFACE_FAUCET_LIMIT: faucetLimit = DEFAULT_FAUCET_LIMIT,
NAMADA_INTERFACE_CHAIN_ID: chainId = DEFAULT_CHAIN_ID,
NAMADA_INTERFACE_CHAINS_URL: chainsURL = DEFAULT_CHAINS_URL,
NAMADA_INTERFACE_PROXY: isProxied,
NAMADA_INTERFACE_PROXY_PORT: proxyPort = 9000,
} = process.env;

const apiUrl = isProxied ? `http://localhost:${proxyPort}/proxy` : faucetApiUrl;
const url = `${apiUrl}${faucetApiEndpoint}`;
const url = `${apiUrl}${chainId}${faucetApiEndpoint}`;
const api = new API(url);
const chainsApi = new ChainsAPI(chainsURL, chainId)
const limit = parseInt(faucetLimit);
const runFullNodeUrl = "https://docs.namada.net/operators/ledger";
const becomeBuilderUrl = "https://docs.namada.net/integrating-with-namada";
Expand Down Expand Up @@ -124,7 +129,7 @@ export const App: React.FC = () => {
);

useEffect(() => {
const { startsAt } = settings;
let { startsAt } = settings;
const now = new Date();
const nowUTC = Date.UTC(
now.getUTCFullYear(),
Expand All @@ -133,14 +138,37 @@ export const App: React.FC = () => {
now.getUTCHours(),
now.getUTCMinutes()
);
const startsAtToMilliseconds = startsAt * 1000;
if (nowUTC < startsAtToMilliseconds) {
setIsTestnetLive(false);
}

// Fetch settings from faucet API
(async () => {
try {
let startsAtToMilliseconds = startsAt * 1000;
let startsAtText = defaults.startsAtText
try{
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be nice to move this try...catch block to the separete function. I think that would make code more readable. Also it should help with getting rid off mutations in multiple places. At the moment it's a bit difficult to follow what is going on.

const chainsData = await chainsApi.chainsData()
let validFrom = 0;
chainsData["chains"].filter(chain => chain.chain_id === chainId).forEach(chain => {
validFrom = Date.parse(chain.valid_from) / 1000;
})
validFrom != 0 ? startsAt = validFrom : startsAt = START_TIME_UTC
startsAtToMilliseconds = startsAt * 1000;
startsAtText = new Date(startsAtToMilliseconds).toLocaleString(
"en-gb",
{
timeZone: "UTC",
month: "long",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "numeric",
}
);
} catch(e) {}

if (nowUTC < startsAtToMilliseconds) {
setIsTestnetLive(false);
}

const { difficulty, tokens_alias_to_address: tokens } = await api
.settings()
.catch((e) => {
Expand All @@ -152,9 +180,10 @@ export const App: React.FC = () => {
});
// Append difficulty level and tokens to settings
setSettings({
...settings,
difficulty,
tokens,
startsAt,
startsAtText
});
} catch (e) {
setSettingsError(`Failed to load settings! ${e}`);
Expand Down
66 changes: 66 additions & 0 deletions apps/faucet/src/utils/chainsApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can't you just reuse the current api.ts ?

ChallengeResponse,
Data,
ErrorResponse,
ChainsResponse,
} from "./types";

enum Endpoint {
ChainsData = "",
}

export class ChainsAPI {
constructor(protected readonly url: string, protected readonly chainId: string) {}

/**
* Wrapper for fetch requests to handle ReadableStream response when errors are received from API
*
* @param {string} endpoint
* @param {RequestInit} options
*
* @returns Object
*/
async request<T = unknown>(
endpoint: string,
options: RequestInit = { method: "GET" }
): Promise<T> {
return await fetch(new URL(`${this.url}${endpoint}`), {
...options,
})
.then((response) => {
if (response.ok) {
return response.json();
}
const reader = response?.body?.getReader();
const errors = reader?.read().then((data): Promise<ErrorResponse> => {
const response = JSON.parse(
new TextDecoder().decode(data.value)
) as ErrorResponse;
// If code 429 is received on any request, rate limiting is blocking
// requests from this this IP, so provide a specific message:
if (response.code === 429) {
response.message = "Too many requests! Try again in one hour.";
}
return Promise.reject(response);
});
if (!errors) {
throw new Error("Unable to parse error response");
}
return errors;
})
.catch((e) => {
console.error(e);
return Promise.reject(e);
});
}

/**
* Request faucet settings
*
* @returns Object
*/
async chainsData(): Promise<ChainsResponse> {
return this.request(Endpoint.ChainsData);
}
}

1 change: 1 addition & 0 deletions apps/faucet/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./api";
export * from "./chainsApi";
export * from "./pow";
export * from "./types";
12 changes: 12 additions & 0 deletions apps/faucet/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ export type SettingsResponse = {
tokens_alias_to_address: Record<string, string>;
};

export type ChainsResponse = {
chains: ChainData[]
}

export type ChainData = {
chain_id: string;
valid_from: string;
valid_until: string;
network_type: string;
namada_version: string;
}

export type TransferDetails = {
target: string;
token: string;
Expand Down