Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
e16425d
feat: add data to market consts
alcercu Oct 1, 2025
1512442
feat: complete details component
alcercu Oct 1, 2025
0365755
chore: change name of the markets to shortName
alcercu Oct 1, 2025
7d6475c
chore: change title of page
alcercu Oct 1, 2025
62f8978
chore: update colors
alcercu Oct 1, 2025
e009b66
chore: change texts
alcercu Oct 1, 2025
3f31abb
chore: update texts
alcercu Oct 3, 2025
e46164a
seer credits
tractorss Jan 8, 2026
306cd9d
Merge branch 'master' into feat/realt-details-component
tractorss Jan 8, 2026
0813204
chore: update-realt-details
tractorss Jan 8, 2026
7d8b30c
chore: embed-image
tractorss Jan 8, 2026
95fd590
chore: logo-update
tractorss Jan 8, 2026
236bbaf
Merge branch 'master' into feat/realt-details-component
tractorss Jan 9, 2026
346826d
feat: logo-update
tractorss Jan 13, 2026
abf2fde
Merge branch 'master' into feat/realt-details-component
tractorss Jan 13, 2026
29928d9
chore: update-readme
tractorss Jan 14, 2026
6a82f8c
chore: remove-web3-metadata
tractorss Jan 14, 2026
596642a
chore: update-seo
tractorss Jan 14, 2026
ec572ed
chore: update-projects-chosen
tractorss Jan 19, 2026
8847dfa
Merge branch 'master' into realt
tractorss Jan 20, 2026
10f28a8
Merge branch 'master' into realt
tractorss Jan 26, 2026
e16efd1
realT changes
tractorss Feb 9, 2026
258c2b1
chore: update-guide-svg
tractorss Feb 9, 2026
8d8e92f
feat: initial-realt-proxy
tractorss Feb 9, 2026
4c89ae8
chore: move-proxy-to-functions
tractorss Feb 9, 2026
d4da0de
chore: add-log-for-cache-serve
tractorss Feb 9, 2026
04c8df1
chore: separate-token-calls
tractorss Feb 9, 2026
eba7a33
chore: update-realt-proxy-header
tractorss Feb 9, 2026
aad172a
chore: update-realt-proxy-function
tractorss Feb 9, 2026
b124863
Merge branch 'master' into realt
tractorss Feb 9, 2026
20b1e65
Merge branch 'master' into realt
tractorss Feb 10, 2026
7268d62
chore: update-realt-guide
tractorss Feb 10, 2026
6618945
feat: realt-dynamic-details
tractorss Feb 10, 2026
e079bf6
chore: display-gross-rent-month-in-misc-data
tractorss Feb 10, 2026
4868f6e
feat: realt-data-download
tractorss Feb 10, 2026
c08058f
fix: correct-question-description
tractorss Feb 13, 2026
81c8aba
Merge branch 'master' into realt
tractorss Feb 16, 2026
d1d0bff
fix: slider-clipping
tractorss Feb 16, 2026
5f3b900
Merge branch 'master' into realt
tractorss Feb 19, 2026
211d279
Merge branch 'master' into realt
tractorss Feb 24, 2026
9915324
Merge branch 'master' into realt
tractorss Feb 25, 2026
c8b422e
fix: remove-unused-import
tractorss Feb 25, 2026
b777597
Merge branch 'master' into realt
tractorss Feb 26, 2026
2b1e3c6
Merge branch 'master' into realt
tractorss Feb 27, 2026
457831f
Merge branch 'master' into realt
tractorss Mar 5, 2026
9a5cba1
chore: unique-persistence-id
tractorss Mar 5, 2026
4f84133
Merge branch 'master' into realt
tractorss Mar 13, 2026
2754275
chore: update-participation-details-for-realt
tractorss Mar 13, 2026
7319619
Merge branch 'master' into realt
tractorss Mar 16, 2026
e0a9926
Merge branch 'master' into realt
tractorss Mar 17, 2026
e9ed73f
refactor: redundant-details-info
tractorss Mar 20, 2026
9d83c0d
feat: show-min-and-max-value-on-slider
tractorss Mar 20, 2026
bb1f175
Merge branch 'master' into realt
tractorss Mar 25, 2026
399a177
Merge branch 'master' into realt
tractorss Mar 26, 2026
7c34e91
feat: dynamic-range
tractorss Apr 8, 2026
ca1c7df
Merge branch 'master' into realt
tractorss Apr 16, 2026
2d0da17
Merge branch 'master' into realt
tractorss May 12, 2026
9752648
Merge branch 'master' into realt
tractorss May 13, 2026
084910d
Merge branch 'master' into realt
tractorss May 14, 2026
ce0fc16
Merge branch 'master' into realt
tractorss May 16, 2026
ee3563a
Merge branch 'master' into realt
tractorss May 18, 2026
3278d63
Merge branch 'master' into realt
tractorss May 18, 2026
eb15ecc
feat: realt-experimental-setup
tractorss May 18, 2026
260c39b
chore: update-realt-market-data
tractorss May 21, 2026
4087409
Merge branch 'master' into realt
tractorss May 22, 2026
3776015
Merge branch 'master' into realt
tractorss May 22, 2026
4aff44a
fix: clamp-values-and-properly-handle-resolution-display
tractorss May 25, 2026
6d33163
Merge branch 'master' into realt
tractorss May 29, 2026
a87aa52
Merge branch 'master' into realt
tractorss May 30, 2026
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: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Futarchy

This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

## Getting Started
Expand Down
132 changes: 132 additions & 0 deletions functions/realt-proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { getStore } from "@netlify/blobs";

/**
* Cache TTL in milliseconds (24 hours).
*/
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;

/**
* RealT property UUIDs to fetch.
*/
const REALT_UUIDS = [
"0x0f388d7e65a969dbcbfab21bc3ab6629af78f4cf",
"0x5c4e3fa9704d4212c6434190af6379cfbda47e13",
"0x854a0cfa24012937d3d15682ecc3d5b474bfa97e",
"0xd8b19f31186fc7350be018651aa1383175923bb3",
"0xc7697f5e86a102eaf4000719a2dc477d65beea7d",
"0x4ae9d3343bbc6a894b7ee7f843c224c953f1661b",
"0x90d280b6456f8233e115e6aabb2ca89249dafd39",
"0x19f824662ba9df78e368022f085b708fccc201c8",
"0xa83cbd26964ea953f86c741871a1ab2a256cb82d",
];

/**
* Edge route that proxies a rate-limited upstream API
* (allowed to be called once per 24 hours), caches the
* response using Netlify Blobs, and serves cached data
* to all clients.
*
* @returns RealT properties data
*/
export default async function handler(): Promise<Response> {
const store = getStore("realt-api-cache");
const now = Date.now();
type CachedResponse = {
data: Record<string, unknown>;
fetchedAt: number;
};
const apiUrl = process.env.REALT_API_URL;
const apiKey = process.env.REALT_API_KEY;

if (!apiUrl || !apiKey) {
console.error("[realt-proxy] Missing required env vars", {
UPSTREAM_API_URL: !!apiUrl,
UPSTREAM_API_KEY: !!apiKey,
});

return new Response("Server misconfigured", { status: 500 });
}

// Read cached response
const cached = (await store.get("response", {
type: "json",
})) as CachedResponse | null;

// Serve cached response if still valid
if (cached && now - cached.fetchedAt < CACHE_TTL_MS) {
console.log("[realt-proxy] Serving from cache");
return Response.json(cached.data, {
headers: {
"Cache-Control": "public, max-age=300",
},
});
}

try {
// Call realT API (should happen ~once per day)
console.log("[realt-proxy] Calling RealT API");

const res = await fetch(apiUrl, {
headers: {
"X-AUTH-REALT-TOKEN": apiKey,
},
});

if (!res.ok) {
throw new Error(`RealT API failed with status ${res.status}`);
}

const allProperties = (await res.json()) as Array<{
uuid: string;
[key: string]: unknown;
}>;

/**
* Filter only the UUIDs we need
*/
const uuidSet = new Set(REALT_UUIDS.map((uuid) => uuid.toLowerCase()));

const filtered = allProperties.filter(
(item) =>
typeof item.uuid === "string" && uuidSet.has(item.uuid.toLowerCase()),
);

const normalized = filtered.map((item) => ({
...item,
uuid: item.uuid.toLowerCase(),
}));

const byUuid = Object.fromEntries(
normalized.map((item) => [item.uuid, item]),
);

console.log(
`[realt-proxy] Filtered ${filtered.length} / ${allProperties.length} properties`,
);

// Persist fresh response
await store.setJSON("response", {
data: byUuid,
fetchedAt: now,
} satisfies CachedResponse);

return Response.json(byUuid, {
headers: {
"Cache-Control": "public, max-age=300",
},
});
} catch (error) {
console.error("[realt-proxy] RealT API fetch failed:", error);

// serve stale cache if available
if (cached) {
return Response.json(cached.data, {
headers: {
"Cache-Control": "public, max-age=300",
},
});
}

return new Response("RealT API unavailable", { status: 502 });
}
}
2 changes: 2 additions & 0 deletions netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[functions]
directory = "functions"
7 changes: 7 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ const nextConfig = {
pathname: "/**",
search: "",
},
{
protocol: "https",
hostname: "realt.co",
port: "",
pathname: "/**",
search: "",
},
],
},
webpack: (config) => {
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@cowprotocol/cow-sdk": "^5.10.3",
"@kleros/kleros-app": "^2.3.0",
"@kleros/ui-components-library": "^3.7.0",
"@netlify/blobs": "^10.6.0",
"@reown/appkit": "^1.7.11",
"@reown/appkit-adapter-wagmi": "^1.7.11",
"@swapr/sdk": "https://github.com/seer-pm/swapr-sdk/archive/6dea7e63f7e05c84a4374717ee1ad5baca86f7de.tar.gz",
Expand All @@ -37,6 +38,7 @@
"react-jdenticon": "^1.4.0",
"react-toastify": "^11.1.0",
"react-use": "^17.6.0",
"swiper": "^12.0.2",
"viem": "^2.31.4",
"wagmi": "^2.15.6",
"zustand": "^5.0.8"
Expand Down
Binary file modified public/futarchy_kleros.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/icon1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 41 additions & 3 deletions src/app/(homepage)/components/AdvancedSection.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
import { useCallback } from "react";

import { Card } from "@kleros/ui-components-library";
import clsx from "clsx";
import Link from "next/link";

import { useAllRealtMarketData } from "@/hooks/useRealtMarketData";

import LightButton from "@/components/LightButton";
import SeerLogo from "@/components/SeerLogo";

import DownloadIcon from "@/assets/svg/download.svg";
import ExternalArrow from "@/assets/svg/external-arrow.svg";

import { downloadCsvFile, generateRealtDataCsv } from "@/utils/csv";

import { markets, seerMarketLink } from "@/consts/markets";

const contractAddresses = markets.map((m) => m.realtContract);

const AdvancedSection: React.FC = () => {
const { data: allRealtData, isLoading } = useAllRealtMarketData();

const handleDownloadCsv = useCallback(() => {
if (!allRealtData) return;
const csv = generateRealtDataCsv(allRealtData, contractAddresses);
downloadCsvFile("realt-market-data.csv", csv);
}, [allRealtData]);

return (
<Card
round
Expand All @@ -24,16 +44,34 @@ const AdvancedSection: React.FC = () => {
Check the opportunities if you want to LP or Trade specific outcome
tokens in Seer.&nbsp;
<Link
href={
"https://app.seer.pm/markets/100/which-movies-will-clement-watch-as-part-of-the-distilled-clements-judgement-expe-2"
}
href={seerMarketLink}
target="_blank"
rel="noreferrer noopener"
className="text-klerosUIComponentsPrimaryBlue items-center text-sm"
>
Check it out <ExternalArrow className="ml-2 inline size-4" />
</Link>
</p>
<div>
<span className="text-klerosUIComponentsSecondaryText mr-1 text-sm">
Download the latest data (updated in the last 24 hours) for the 9
properties in CSV format
</span>
<LightButton
text={isLoading ? "Loading..." : "here"}
onPress={handleDownloadCsv}
isDisabled={isLoading || !allRealtData}
small
className={clsx(
"inline-flex flex-row-reverse p-0",
"[&_.button-text]:text-klerosUIComponentsPrimaryBlue [&_.button-text]:text-sm",
"hover:bg-transparent",
)}
icon={
<DownloadIcon className="[&_path]:fill-klerosUIComponentsPrimaryBlue! ml-2" />
}
/>
</div>
</div>
<SeerLogo className="shrink-0" />
</Card>
Expand Down
4 changes: 3 additions & 1 deletion src/app/(homepage)/components/Chart/Legend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import clsx from "clsx";
import ChartBarIcon from "@/assets/svg/chart-bar.svg";
import StatsBarIcon from "@/assets/svg/stats-bar.svg";

import { formatCompactUsd } from "@/utils/formatCompactUsd";

import { type MarketsData } from ".";

interface ILegend {
Expand Down Expand Up @@ -38,7 +40,7 @@ const Legend: React.FC<ILegend> = ({
onMouseLeave={() => onHoverMarket?.(null)}
>
<Tag
text={`${name} ${data.at(-1)?.value?.toFixed(2) ?? "0.00"}%`}
text={`${name} ${formatCompactUsd(data.at(-1)?.value ?? 0)}`}
active={isVisible}
onClick={() => onToggleMarket(name)}
className={clsx(
Expand Down
10 changes: 6 additions & 4 deletions src/app/(homepage)/components/Chart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { type IChartData } from "@/hooks/useChartData";

import { shortenName } from "@/utils";
import { formatCompactUsd } from "@/utils/formatCompactUsd";

import { IMarket, startTime, endTime } from "@/consts/markets";

Expand Down Expand Up @@ -123,12 +124,13 @@ const Chart: React.FC<{ data: IChartData[] }> = ({ data }) => {
Object.entries(marketsData).forEach(([marketName, marketInfo]) => {
const { data, market } = marketInfo;

const maxValue = market.maxValue;
const { minValue, maxValue } = market;

// Find valid start index (skip extreme values)
let validStartIndex = 0;
for (let i = 0; i < data.length; i++) {
const isExtreme = data[i].value === maxValue || data[i].value < 0.1;
const isExtreme =
data[i].value === maxValue || data[i].value < minValue + 0.1;
if (!isExtreme && i > 0) {
validStartIndex = i;
break;
Expand Down Expand Up @@ -188,7 +190,7 @@ const Chart: React.FC<{ data: IChartData[] }> = ({ data }) => {
ensureEdgeTickMarksVisible: true,
},
localization: {
priceFormatter: (val: number) => `${val.toFixed(2)}%`,
priceFormatter: (val: number) => formatCompactUsd(val),
},
leftPriceScale: {
borderVisible: false,
Expand Down Expand Up @@ -252,7 +254,7 @@ const Chart: React.FC<{ data: IChartData[] }> = ({ data }) => {
onHoverMarket={handleHoverMarket}
/>
<h2 className="text-klerosUIComponentsPrimaryText mt-6 mb-4 text-base font-semibold">
Market Estimate Scores
Market estimates (USD)
</h2>
<div ref={chartContainerRef} />
</div>
Expand Down
10 changes: 1 addition & 9 deletions src/app/(homepage)/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Image from "next/image";

import { useWinningAnswers } from "@/hooks/useWinningAnswers";

import ExternalLink from "@/components/ExternalLink";
import SeerLogo from "@/components/SeerLogo";

import SeerHeaderBackground from "@/assets/png/seer-header-bg.png";
Expand Down Expand Up @@ -56,14 +55,7 @@ const Header: React.FC = () => {
</p>
</div>
<p className="text-klerosUIComponentsSecondaryText px-6 pb-3.75 text-xs whitespace-pre-line">
You can look at{" "}
<ExternalLink
text="previous assessments"
url="https://www.criticker.com/profile/clesaege/"
showIcon={false}
className="text-xs"
/>
, to get an idea of what he would like/dislike.
{marketMetadata.questionDescription}
</p>
</div>
{!isLoading && winningMarkets.length > 0 ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ const CsvUploadPopup: React.FC<ICsvUploadPopup> = ({
const csvText = await file.text();
const records = parseMarketCSV(csvText);

Object.entries(records).forEach(([marketId, score]) => {
setPrediction(marketId, score);
Object.entries(records).forEach(([marketId, predictionUsd]) => {
setPrediction(marketId, predictionUsd);
});

toggleIsOpen();
Expand All @@ -48,7 +48,7 @@ const CsvUploadPopup: React.FC<ICsvUploadPopup> = ({
{...{ isOpen }}
>
<div className="flex size-full flex-col justify-center gap-6">
<h2 className="w-full. text-klerosUIComponentsPrimaryText text-center text-2xl font-semibold">
<h2 className="text-klerosUIComponentsPrimaryText w-full text-center text-2xl font-semibold">
Upload CSV Predictions
</h2>
<div
Expand All @@ -70,18 +70,18 @@ const CsvUploadPopup: React.FC<ICsvUploadPopup> = ({
marketName,score
</span>
<span className="text-klerosUIComponentsPrimaryText text-sm">
Judge Dredd (1995),49.45
23750 W 7 Mile,1200000
</span>
<span className="text-klerosUIComponentsPrimaryText text-sm">
Bacurau (2019),53.52
18881 Mound,450000
</span>
<span className="text-klerosUIComponentsSecondaryText text-sm">
...
</span>
</div>
<span className="text-klerosUIComponentsPrimaryText text-sm">
Each row represents a prediction for a movie&apos;s score in the
Gnosis ecosystem.
Each row is a property name and a predicted price in USD (whole
dollars, within that property&apos;s min/max range).
</span>
</div>
<CsvDownload />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import clsx from "clsx";
import { useTradeWallet } from "@/context/TradeWalletContext";
import { useTokensBalances } from "@/hooks/useTokenBalances";

import MovieIcon from "@/assets/svg/movie.svg";
import HomeIcon from "@/assets/svg/homes.svg";

import { markets } from "@/consts/markets";

Expand All @@ -26,9 +26,9 @@ const ProjectBalances: React.FC = () => {
{
title: (
<div className="flex items-center gap-2">
<MovieIcon className="size-6" />
<HomeIcon className="size-6" />
<label className="text-klerosUIComponentsPrimaryText text-sm">
Movie tokens
Property tokens
</label>
</div>
),
Expand Down
Loading