Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a1462d5
remove unnecessary export keywords
yushih Oct 7, 2025
23626e6
wip
yushih Oct 8, 2025
aaf8635
debounce search
yushih Oct 8, 2025
755ec1a
wip - fix types
yushih Oct 9, 2025
a30eba7
wip
yushih Oct 9, 2025
7938ea8
use different backend URL for different networks
yushih Oct 21, 2025
884038e
fix pool id
yushih Oct 24, 2025
fa69b7f
fix csp
yushih Oct 31, 2025
19c9fb7
biased pools for mainnet only
yushih Oct 31, 2025
9068515
recommend EMURB in place of EMUR8
yushih Nov 5, 2025
37e2119
fix
yushih Nov 5, 2025
351f302
lint fixes
vsubhuman Nov 10, 2025
e3748be
wrap epoch in string
vsubhuman Nov 10, 2025
602993a
fix social handles
vsubhuman Nov 10, 2025
c38fc0f
flow fix
vsubhuman Nov 10, 2025
4e29dcb
resolving homepage
vsubhuman Nov 10, 2025
6f8fd8f
flow fix
vsubhuman Nov 10, 2025
a09af2f
fix hook deps
vsubhuman Nov 10, 2025
8006fdc
fixes
vsubhuman Nov 10, 2025
300375e
Fixed sorting options
vsubhuman Nov 10, 2025
76ca7b7
search fixes
vsubhuman Nov 10, 2025
a74bfe9
fixed random
vsubhuman Nov 10, 2025
3f1acd9
fix for sort check
vsubhuman Nov 11, 2025
276dfc2
changed pool fee fields to use active instead of live
vsubhuman Nov 13, 2025
b7a60d3
fixed saturation
vsubhuman Nov 17, 2025
c7884ad
Removed unused component file
vsubhuman Nov 17, 2025
2d850e6
Merge branch 'master' into chore/new-api
vsubhuman Nov 17, 2025
ddf17a9
reverted search function call on clear
vsubhuman Nov 17, 2025
ba3dbdb
fixing jdenticon in case no pool icon
vsubhuman Nov 19, 2025
1d05b6e
fixing jdenticon in case no pool icon
vsubhuman Nov 19, 2025
3712875
updated deprecated actions
vsubhuman Nov 19, 2025
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
2 changes: 1 addition & 1 deletion .env.development
Original file line number Diff line number Diff line change
@@ -1 +1 @@
REACT_APP_METATAG_CSP=default-src 'self' http://localhost:3000 https://kit-free.fontawesome.com https://*.cexplorer.io https://fonts.gstatic.com/s/rubik/; object-src 'none'; style-src 'unsafe-inline' https://fonts.googleapis.com/ https://kit-free.fontawesome.com/; script-src 'self'; connect-src https://*.cexplorer.io ws:
REACT_APP_METATAG_CSP=default-src 'self' http://localhost:3000 https://kit-free.fontawesome.com https://*.cexplorer.io https://fonts.gstatic.com/s/rubik/; object-src 'none'; style-src 'unsafe-inline' https://fonts.googleapis.com/ https://kit-free.fontawesome.com/; script-src 'self'; connect-src https://*.emurgornd.com https://zero.yoroiwallet.com ws:
2 changes: 1 addition & 1 deletion .env.production
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
REACT_APP_METATAG_CSP=default-src 'self' https://kit-free.fontawesome.com https://*.cexplorer.io https://fonts.gstatic.com/s/rubik/; object-src 'none'; style-src 'unsafe-inline' https://fonts.googleapis.com/ https://kit-free.fontawesome.com/; script-src 'self'; connect-src https://*.cexplorer.io
REACT_APP_METATAG_CSP=default-src 'self' https://kit-free.fontawesome.com https://*.cexplorer.io https://fonts.gstatic.com/s/rubik/; object-src 'none'; style-src 'unsafe-inline' https://fonts.googleapis.com/ https://kit-free.fontawesome.com/; script-src 'self'; connect-src https://*.emurgornd.com https://zero.yoroiwallet.com
INLINE_RUNTIME_CHUNK=false
115 changes: 68 additions & 47 deletions src/API/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

import axios from 'axios';
import seedrandom from 'seedrandom';
import { BACKEND_URL } from '../manifestEnvs';
import { BACKEND_URL_FOR_PREPROD, BACKEND_URL_FOR_MAINNET } from '../manifestEnvs';

const backendUrl: string = BACKEND_URL;
const SATURATION = 76293289283071;

const BIAS_POOL_IDS = [
'dbda39c8d064ff9801e376f8350efafe67c07e9e9244dd613aee5125', // EMURA
// '359d3f8e355c873b0b5cae1e18eb12e44dcfc2ad212706d93ac314ab', // EMURB
'8efb053977341471256685b1069d67f4aca7166bc3f94e27ebad217f', // EMUR7
'0ef7aa564933ce75b695cdad66be4a39b43a22726de7c58908e0e033', // EMUR8
// '0ef7aa564933ce75b695cdad66be4a39b43a22726de7c58908e0e033', // EMUR8
'359d3f8e355c873b0b5cae1e18eb12e44dcfc2ad212706d93ac314ab', // EMURB
'2a8294ad7538b15353b9ffd81e26dafe846ffc3f6b9e331d4c1dc030', // YORO1
'b19f2d9498845652ae6eea5da77952b37e2bca9f59b2a98c56694cae', // YORO2
];
Expand All @@ -28,13 +28,13 @@ const brackets = [
{ startIndex: 53, positionGap: 27 },
];

export type HistBPE = {|
type HistBPE = {|
+val: string,
+time: string,
+e: number,
|};

export type SocialMediaHandles = {|
type SocialMediaHandles = {|
tw: ?string,
tg: ?string,
fb: ?string,
Expand Down Expand Up @@ -74,14 +74,7 @@ export type Pool = {|
+saturation: number,
|};

export type World = {|
+epoch: string,
+slot: string,
+stake: string,
+supply: number,
+pools: string,
+price: number,
+delegators: string,
type World = {|
+saturation: number,
|};

Expand All @@ -102,7 +95,7 @@ export const SortingDirections = Object.freeze({
});

export type SortingEnum = $Values<typeof Sorting>;
export type SortingDirEnum = $Values<typeof SortingDirections>;
type SortingDirEnum = $Values<typeof SortingDirections>;

export type SearchParams = {|
limit?: number,
Expand All @@ -111,38 +104,69 @@ export type SearchParams = {|
sortDirection?: SortingDirEnum,
|};

export type ApiPoolsResponse = {|
world?: World,
pools?: {| [string]: Pool |},
type ApiPoolsResponse = {|
world: World,
pools: Array<Pool>,
|};

const toPoolArray: (?{| [string]: Pool |}) => Array<Pool> = (pools) => {
if (pools == null) return [];
return Object.keys(pools)
.map((key) => pools[key])
.filter((x) => x != null);
};

export function getPools(body: SearchParams): Promise<ApiPoolsResponse> {
function transformData(poolsResponse) {
return {
world: {
saturation: SATURATION,
},
pools: poolsResponse?.data?.data?.map((pool) => (
{
id: pool.pool_id_hash_raw,
id_bech: pool.pool_id,
db_ticker: pool.pool_name.ticker,
db_name: pool.pool_name.name,
pledge: String(pool.pledged),
pledge_real: String(pool.pledged),
total_stake: String(pool.live_stake),
tax_fix: String(pool.pool_update.live.fixed_cost),
tax_ratio: String(pool.pool_update.live.margin),
blocks_epoch: pool.blocks.epoch,
roa: String(pool.stats.lifetime.roa),
handles: {},
saturation: pool.live_stake / SATURATION,
pool_pic: `https://ix.cexplorer.io/${pool.pool_id}`,
}
)) ?? [],
};
}

function getPools(network: 'mainnet' | 'preprod', body: SearchParams, bias: ?string = null): Promise<ApiPoolsResponse> {
const requestBody = {
...{ search: '', sort: Sorting.SCORE, limit: 250 },
...{ limit: 250},
...body,
...{ sort: 'ranking' }
};

const encodeForm = (data) => {
return (Object.keys(data): any)
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
.join('&');
};
const searchParams = new URLSearchParams();
if (requestBody.sort === 'ranking') {
searchParams.append('order', 'ranking');
}
if (requestBody.limit) {
searchParams.append('limit', String(requestBody.limit));
}
if (requestBody.sortDirection) {
searchParams.append('sort', requestBody.sortDirection);
}
if (requestBody.search) {
searchParams.append('name', requestBody.search);
}
if (bias) {
searchParams.append('poolId', bias);
}
const backendUrl = {
preprod: BACKEND_URL_FOR_PREPROD,
mainnet: BACKEND_URL_FOR_MAINNET,
}[network];

return axios(`${backendUrl}`, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
method: 'post',
data: encodeForm(requestBody),
})
return axios(`${backendUrl}?${searchParams.toString()}`)
.then((response) => {
const poolsResponse: ApiPoolsResponse = response.data;
return poolsResponse;
return transformData(response.data);
})
.catch((error) => {
console.error('API::getPools Error: ', error);
Expand Down Expand Up @@ -181,15 +205,16 @@ export type ListBiasedPoolsResponse = {|
|};

export async function listBiasedPools(
network: 'mainnet' | 'preprod',
externalSeed: string,
searchParams: SearchParams,
): Promise<ListBiasedPoolsResponse> {
const unbiasedPoolsResponse = await getPools(searchParams);
const originalPools = toPoolArray(unbiasedPoolsResponse.pools);
const unbiasedPoolsResponse = await getPools(network, searchParams);
const originalPools = unbiasedPoolsResponse.pools;

const saturationLimit = unbiasedPoolsResponse.world?.saturation;

if (searchParams.search || searchParams.sort === Sorting.TICKER) {
if (searchParams.search || searchParams.sort === Sorting.TICKER || network !== 'mainnet') {
// If user searched or sorted explicitly - then we don't bias
return { pools: originalPools, saturationLimit };
}
Expand All @@ -202,9 +227,9 @@ export async function listBiasedPools(
const internalSeed = tail(p1?.id) + tail(p2?.id) + tail(p3?.id);

try {
const biasedPoolsResponse = await getPools({ search: BIAS_POOLS_SEARCH_QUERY });
const biasedPoolsResponse = await getPools(network, ({}: any), BIAS_POOLS_SEARCH_QUERY);
if (!biasedPoolsResponse) return { pools: unbiasedPools, saturationLimit };
const biasedPools = toPoolArray(biasedPoolsResponse.pools)
const biasedPools = biasedPoolsResponse.pools
.filter((x) => x.id && BIAS_POOL_IDS.indexOf(x.id) >= 0)
.sort((a, b) => {
// this sorting is to ensure that changes in the backend response order is not affecting the final ordering
Expand Down Expand Up @@ -242,7 +267,3 @@ export async function listBiasedPools(
return { pools: unbiasedPools, saturationLimit };
}
}

export function listPools(): Promise<ApiPoolsResponse> {
return getPools(({}: any));
}
3 changes: 2 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { Node } from 'react';
import GlobalStyle from './helpers/globalStyles';
import Home from './containers/Home';
import HomeRevamp from './containers/HomeRevamp';
import type { UrlParams } from './containers/Home';
import type { UrlParams } from './types';

const parseIds = (array: ?string): Array<string> => {
if (array == null) return [];
Expand All @@ -25,6 +25,7 @@ const extractParams = (locationSearch: string): UrlParams => {
layout: params.get('layout'),
bias: params.get('bias'),
theme: params.get('theme'),
network: params.get('network') === 'preprod' ? 'preprod' : 'mainnet',
};
};

Expand Down
20 changes: 17 additions & 3 deletions src/components/SearchRevamp.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow

import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import type { Node } from 'react';
import styled from 'styled-components';
import searchIcon from '../assets/search-icon.svg';
Expand Down Expand Up @@ -76,12 +76,16 @@ type Props = {|
isLight?: boolean,
|};

const SEARCH_DEBOUNCE_DELAY = 500;

const SearchRevamp = ({ filter, isDark, isLight }: Props): Node => {
const [prevSearch, setPrevSearch] = useState('');
const [searchValue, setSearchValue] = useState('');

const callSearchFunction = (e) => {
e.preventDefault();
if (e) {
e.preventDefault();
}

if (prevSearch !== searchValue) {
filter(searchValue);
Expand All @@ -90,10 +94,20 @@ const SearchRevamp = ({ filter, isDark, isLight }: Props): Node => {
};

const handleSearchInputChanges = (e) => {
e.preventDefault();
setSearchValue(e.target.value);
callSearchFunction(e);
};

useEffect(() => {
const handle = setTimeout(() => {
callSearchFunction();
}, SEARCH_DEBOUNCE_DELAY);

return () => {
clearTimeout(handle);
};
}, [searchValue]);

return (
<Form className="search">
<SearchBtn onClick={callSearchFunction} type="submit">
Expand Down
18 changes: 4 additions & 14 deletions src/containers/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Modal from '../components/common/Modal';
import SaturatedPoolAlert from '../components/SaturatedPoolAlert';
import cexplorerIconMini from '../assets/cexplorer-logo-mini.svg';
import cexplorerIcon from '../assets/cexplorer-logo-extend.svg';
import type { UrlParams } from '../types';

const Header = styled.div`
display: flex;
Expand Down Expand Up @@ -71,17 +72,6 @@ const CreditSection = styled.div`
height: 32px;
}
`;
export type UrlParams = {|
chromeId: ?string,
mozId: ?string,
source: ?string,
selectedPoolIds: ?Array<string>,
lang: ?string,
totalAda: ?number,
layout: ?string,
bias: ?string,
theme: ?string,
|};

export type HomeProps = {|
urlParams: UrlParams,
Expand Down Expand Up @@ -111,7 +101,7 @@ function Home(props: HomeProps): Node {

useEffect(() => {
setStatus('pending');
listBiasedPools(seed, {})
listBiasedPools(props.urlParams.network, seed, {})
.then((resp: ListBiasedPoolsResponse) => {
setStatus('resolved');
setRowData(resp.pools);
Expand All @@ -130,7 +120,7 @@ function Home(props: HomeProps): Node {
};
setFilterOptions(newSearch);
setStatus('pending');
listBiasedPools(seed, newSearch)
listBiasedPools(props.urlParams.network, seed, newSearch)
.then((resp: ListBiasedPoolsResponse) => {
setStatus('resolved');
setRowData(resp.pools);
Expand All @@ -148,7 +138,7 @@ function Home(props: HomeProps): Node {
};
setFilterOptions(newSearch);
setStatus('pending');
listBiasedPools(seed, newSearch)
listBiasedPools(props.urlParams.network, seed, newSearch)
.then((resp: ListBiasedPoolsResponse) => {
setStatus('resolved');
setRowData(resp.pools);
Expand Down
17 changes: 3 additions & 14 deletions src/containers/HomeRevamp.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import DesktopTableRevamp from '../components/DesktopTableRevamp';
import SearchRevamp from '../components/SearchRevamp';
import MobileTableRevamp from '../components/MobileTableRevamp';
import { formatCostLabel } from '../utils/utils';
import type { UrlParams } from '../types';

const Header = styled.div`
display: flex;
Expand Down Expand Up @@ -89,18 +90,6 @@ const CreditSection = styled.div`
height: 32px;
}
`;
export type UrlParams = {|
chromeId: ?string,
mozId: ?string,
source: ?string,
selectedPoolIds: ?Array<string>,
lang: ?string,
totalAda: ?number,
layout: ?string,
bias: ?string,
theme: ?string,
|};

export type HomeProps = {|
urlParams: UrlParams,
|};
Expand Down Expand Up @@ -168,7 +157,7 @@ function Home(props: HomeProps): Node {

useEffect(() => {
setStatus('pending');
listBiasedPools(seed, {})
listBiasedPools(props.urlParams.network, seed, {})
.then((resp: ListBiasedPoolsResponse) => {
setStatus('resolved');
setRowData(resp.pools);
Expand All @@ -189,7 +178,7 @@ function Home(props: HomeProps): Node {
};
setFilterOptions(newSearch);
setStatus('pending');
listBiasedPools(seed, newSearch)
listBiasedPools(props.urlParams.network, seed, newSearch)
.then((resp: ListBiasedPoolsResponse) => {
setStatus('resolved');
setRowData(resp.pools);
Expand Down
8 changes: 4 additions & 4 deletions src/manifestEnvs.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// @flow

const BACKEND_URL /*: string */ = process.env.BACKEND_URL != null
? process.env.BACKEND_URL
: 'https://a.cexplorer.io/yoroi-api/';
const BACKEND_URL_FOR_PREPROD = 'https://yoroi-backend-zero-preprod-prod.emurgornd.com/cexplorer-pool-list';
const BACKEND_URL_FOR_MAINNET = 'https://zero.yoroiwallet.com/cexplorer-pool-list'

module.exports = {
BACKEND_URL,
BACKEND_URL_FOR_PREPROD,
BACKEND_URL_FOR_MAINNET,
};
14 changes: 14 additions & 0 deletions src/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @flow

export type UrlParams = {|
chromeId: ?string,
mozId: ?string,
source: ?string,
selectedPoolIds: ?Array<string>,
lang: ?string,
totalAda: ?number,
layout: ?string,
bias: ?string,
theme: ?string,
network: 'mainnet' | 'preprod',
|};