diff --git a/package.json b/package.json
index 642cf87..6253ff3 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
},
"dependencies": {
"@cowprotocol/cow-sdk": "^5.10.3",
- "@kleros/ui-components-library": "^3.6.0",
+ "@kleros/ui-components-library": "^3.7.0",
"@reown/appkit": "^1.7.11",
"@reown/appkit-adapter-wagmi": "^1.7.11",
"@swapr/sdk": "https://github.com/seer-pm/swapr-sdk#6dea7e63f7e05c84a4374717ee1ad5baca86f7de",
@@ -21,6 +21,7 @@
"@yornaath/batshit": "^0.11.1",
"clsx": "^2.1.1",
"ethers": "5.8.0",
+ "framer-motion": "^12.23.26",
"graphql-request": "^7.3.1",
"graphql-tag": "^2.12.6",
"lightweight-charts": "^5.0.8",
diff --git a/src/abi/CreditsManager.ts b/src/abi/CreditsManager.ts
new file mode 100644
index 0000000..150bac4
--- /dev/null
+++ b/src/abi/CreditsManager.ts
@@ -0,0 +1,94 @@
+import { Abi } from "viem";
+
+export const CreditsManagerAbi: Abi = [
+ {
+ inputs: [
+ { internalType: "contract ERC20", name: "_token", type: "address" },
+ {
+ internalType: "contract SeerCredits",
+ name: "_seerCredits",
+ type: "address",
+ },
+ ],
+ stateMutability: "nonpayable",
+ type: "constructor",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "_user", type: "address" },
+ { internalType: "uint256", name: "_amount", type: "uint256" },
+ ],
+ name: "canSpendCredits",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "_governor", type: "address" }],
+ name: "changeGovernor",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ { internalType: "contract ERC20", name: "outputToken", type: "address" },
+ ],
+ name: "execute",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "governor",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "seerCredits",
+ outputs: [
+ { internalType: "contract SeerCredits", name: "", type: "address" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "_contract", type: "address" },
+ { internalType: "bool", name: "_whitelisted", type: "bool" },
+ ],
+ name: "setWhitelistedContract",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "contract ERC20", name: "_token", type: "address" },
+ ],
+ name: "sweepTokens",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "token",
+ outputs: [{ internalType: "contract ERC20", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "", type: "address" }],
+ name: "whitelistedContracts",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+] as const;
diff --git a/src/abi/wrappedXDAI.ts b/src/abi/wrappedXDAI.ts
new file mode 100644
index 0000000..5d4829e
--- /dev/null
+++ b/src/abi/wrappedXDAI.ts
@@ -0,0 +1,155 @@
+import { Abi } from "viem";
+
+export const wrappedXDAIAbi: Abi = [
+ {
+ constant: true,
+ inputs: [],
+ name: "name",
+ outputs: [{ name: "", type: "string" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [
+ { name: "guy", type: "address" },
+ { name: "wad", type: "uint256" },
+ ],
+ name: "approve",
+ outputs: [{ name: "", type: "bool" }],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: "totalSupply",
+ outputs: [{ name: "", type: "uint256" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [
+ { name: "src", type: "address" },
+ { name: "dst", type: "address" },
+ { name: "wad", type: "uint256" },
+ ],
+ name: "transferFrom",
+ outputs: [{ name: "", type: "bool" }],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [{ name: "wad", type: "uint256" }],
+ name: "withdraw",
+ outputs: [],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: "decimals",
+ outputs: [{ name: "", type: "uint8" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [{ name: "", type: "address" }],
+ name: "balanceOf",
+ outputs: [{ name: "", type: "uint256" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: "symbol",
+ outputs: [{ name: "", type: "string" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [
+ { name: "dst", type: "address" },
+ { name: "wad", type: "uint256" },
+ ],
+ name: "transfer",
+ outputs: [{ name: "", type: "bool" }],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [],
+ name: "deposit",
+ outputs: [],
+ payable: true,
+ stateMutability: "payable",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [
+ { name: "", type: "address" },
+ { name: "", type: "address" },
+ ],
+ name: "allowance",
+ outputs: [{ name: "", type: "uint256" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ { payable: true, stateMutability: "payable", type: "fallback" },
+ {
+ anonymous: false,
+ inputs: [
+ { indexed: true, name: "src", type: "address" },
+ { indexed: true, name: "guy", type: "address" },
+ { indexed: false, name: "wad", type: "uint256" },
+ ],
+ name: "Approval",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ { indexed: true, name: "src", type: "address" },
+ { indexed: true, name: "dst", type: "address" },
+ { indexed: false, name: "wad", type: "uint256" },
+ ],
+ name: "Transfer",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ { indexed: true, name: "dst", type: "address" },
+ { indexed: false, name: "wad", type: "uint256" },
+ ],
+ name: "Deposit",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ { indexed: true, name: "src", type: "address" },
+ { indexed: false, name: "wad", type: "uint256" },
+ ],
+ name: "Withdrawal",
+ type: "event",
+ },
+] as const;
diff --git a/src/app/(homepage)/components/Header/index.tsx b/src/app/(homepage)/components/Header/index.tsx
index 13ff5ea..92f6b42 100644
--- a/src/app/(homepage)/components/Header/index.tsx
+++ b/src/app/(homepage)/components/Header/index.tsx
@@ -6,13 +6,15 @@ import SeerLogo from "@/components/SeerLogo";
import SeerHeaderBackground from "@/assets/png/seer-header-bg.png";
import ChartBar from "@/assets/svg/chart-bar.svg";
+import { metadata } from "@/consts/markets";
+
import Countdown from "./Countdown";
const Header: React.FC = () => {
return (
- Session 1 - Movies Experiment
+ {metadata.name}
@@ -31,6 +33,7 @@ const Header: React.FC = () => {
className={clsx(
"relative mt-8 box-border w-full overflow-hidden rounded-xl",
"border-gradient-purple-blue",
+ "flex flex-col gap-2",
)}
>
{
alt="Seer header background"
className="absolute -z-2 size-full object-cover max-md:opacity-35"
/>
-
+
- If watched, what score will Clément give to the movie?
+ {metadata.question}
+
+ {metadata.questionDescription}
+
);
diff --git a/src/app/(homepage)/components/ParticipateSection/CsvUpload/CsvDownload.tsx b/src/app/(homepage)/components/ParticipateSection/CsvUpload/CsvDownload.tsx
new file mode 100644
index 0000000..720396a
--- /dev/null
+++ b/src/app/(homepage)/components/ParticipateSection/CsvUpload/CsvDownload.tsx
@@ -0,0 +1,26 @@
+import { useCallback } from "react";
+
+import { useMarketsStore } from "@/store/markets";
+
+import LightButton from "@/components/LightButton";
+
+import { downloadCsvFile, generateMarketCsv } from "@/utils/csv";
+
+const CsvDownload: React.FC = () => {
+ const markets = useMarketsStore((state) => state.markets);
+ const handleDownload = useCallback(() => {
+ const csv = generateMarketCsv(markets);
+ downloadCsvFile("market-predictions.csv", csv);
+ }, [markets]);
+
+ return (
+
+ );
+};
+
+export default CsvDownload;
diff --git a/src/app/(homepage)/components/ParticipateSection/CsvUpload/index.tsx b/src/app/(homepage)/components/ParticipateSection/CsvUpload/index.tsx
index 04a9acb..77af54b 100644
--- a/src/app/(homepage)/components/ParticipateSection/CsvUpload/index.tsx
+++ b/src/app/(homepage)/components/ParticipateSection/CsvUpload/index.tsx
@@ -7,7 +7,9 @@ import { useToggle } from "react-use";
import { useMarketsStore } from "@/store/markets";
import { isUndefined } from "@/utils";
-import { parseMarketCSV } from "@/utils/parseCsvFile";
+import { parseMarketCSV } from "@/utils/csv";
+
+import CsvDownload from "./CsvDownload";
interface ICsvUploadPopup {
isOpen: boolean;
@@ -65,13 +67,13 @@ const CsvUploadPopup: React.FC
= ({
)}
>
- marketId,score
+ marketName,score
- 0x105d957043ee12f7705efa072af11e718f8c5b83,49.45
+ Judge Dredd (1995),49.45
- 0x68af0afe82dda5c9c26e6a458a143caad35708d6,53.52
+ Bacurau (2019),53.52
...
@@ -82,6 +84,7 @@ const CsvUploadPopup: React.FC = ({
Gnosis ecosystem.
+
{
diff --git a/src/app/(homepage)/components/ParticipateSection/TradeWallet/DepositInterface.tsx b/src/app/(homepage)/components/ParticipateSection/TradeWallet/DepositInterface.tsx
index 71db3fd..a854bb8 100644
--- a/src/app/(homepage)/components/ParticipateSection/TradeWallet/DepositInterface.tsx
+++ b/src/app/(homepage)/components/ParticipateSection/TradeWallet/DepositInterface.tsx
@@ -7,7 +7,7 @@ import { useAccount, useBalance } from "wagmi";
import { useDepositToTradeExecutor } from "@/hooks/tradeWallet/useDepositToTradeExecutor";
import { useTokenBalance } from "@/hooks/useTokenBalance";
-import AmountInput, { TokenType } from "@/components/AmountInput";
+import AmountInput from "@/components/AmountInput";
import LightButton from "@/components/LightButton";
import CloseIcon from "@/assets/svg/close-icon.svg";
@@ -15,6 +15,7 @@ import CloseIcon from "@/assets/svg/close-icon.svg";
import { isUndefined } from "@/utils";
import { collateral } from "@/consts";
+import { TokenType } from "@/consts/tokens";
interface DepositInterfaceProps {
isOpen: boolean;
diff --git a/src/app/(homepage)/components/ParticipateSection/TradeWallet/ProjectBalances/index.tsx b/src/app/(homepage)/components/ParticipateSection/TradeWallet/ProjectBalances/index.tsx
index 4298cdf..69c8e5f 100644
--- a/src/app/(homepage)/components/ParticipateSection/TradeWallet/ProjectBalances/index.tsx
+++ b/src/app/(homepage)/components/ParticipateSection/TradeWallet/ProjectBalances/index.tsx
@@ -21,7 +21,7 @@ const ProjectBalances: React.FC = () => {
);
return (
= ({
isOpen,
toggleIsOpen,
}) => {
- const [amount, setAmount] = useState();
+ const [amount, setAmount] = useState();
+ const [selectedToken, setSelectedToken] = useState(TokenType.sDAI);
const { address: account } = useAccount();
- const { data: balanceData, isLoading: isBalanceLoading } = useTokenBalance({
+ const { data: balanceData } = useTokenBalance({
+ address: tradeExecutor,
+ token: Tokens[selectedToken].address,
+ });
+ const { data: balanceXDai } = useBalance({
address: tradeExecutor,
- token: collateral.address,
});
- const balance =
- balanceData && formatUnits(balanceData.value, balanceData.decimals);
+
+ const balance = useMemo(() => {
+ if (selectedToken === TokenType.xDAI) {
+ return balanceXDai?.value ?? 0n;
+ }
+ return balanceData?.value ?? 0n;
+ }, [balanceXDai, balanceData, selectedToken]);
const withdrawFromTradeExecutor = useWithdrawFromTradeExecutor(() => {
setAmount(undefined);
@@ -50,25 +52,17 @@ export const WithdrawInterface: React.FC = ({
const onSubmit = (e: FormEvent) => {
e.preventDefault();
- const data = Object.fromEntries(new FormData(e.currentTarget));
- const depositAmount = data["amount"];
-
- if (!account) return;
+ if (!account || !amount) return;
withdrawFromTradeExecutor.mutate({
account,
- tokens: [collateral.address],
- amounts: [parseUnits(depositAmount as string, collateral.decimals)],
+ tokens: [Tokens[selectedToken].address],
+ amounts: [amount],
+ isXDai: selectedToken === TokenType.xDAI,
tradeExecutor,
});
};
- const handleMaxClick = () => {
- if (balance) {
- setAmount(balance);
- }
- };
-
return (
= ({
- Withdraw sDAI
+ Withdraw
Withdraw from trade wallet to your account
@@ -95,37 +89,12 @@ export const WithdrawInterface: React.FC = ({
-
+
@@ -119,19 +119,27 @@ export const TradeWallet = () => {
text="Redeem outcome tokens"
/>
) : null}
-
-
-
-
@@ -158,46 +166,56 @@ export const TradeWallet = () => {
)}
-
-
-
-
-
+ {isDepositOpen ? (
+
+ ) : null}
+ {isWithdrawOpen ? (
+
+ ) : null}
+ {isRedeemOpen ? (
+
+ ) : null}
+ {isMintOpen ? (
+
+ ) : null}
+ {isMergeOpen ? (
+
+ ) : null}
>
);
};
diff --git a/src/app/(homepage)/components/PredictAll/PredictAllPopup/Header.tsx b/src/app/(homepage)/components/PredictAll/PredictAllPopup/Header.tsx
index 0f6a99d..ddb6cba 100644
--- a/src/app/(homepage)/components/PredictAll/PredictAllPopup/Header.tsx
+++ b/src/app/(homepage)/components/PredictAll/PredictAllPopup/Header.tsx
@@ -1,9 +1,15 @@
import React from "react";
import clsx from "clsx";
+import { AnimatePresence, motion } from "framer-motion";
+
+import { useMarketsStore } from "@/store/markets";
import { usePredictionMarkets } from "@/hooks/usePredictionMarkets";
+import LightButton from "@/components/LightButton";
+
+import CloseIcon from "@/assets/svg/close-icon.svg";
import ArrowDown from "@/assets/svg/long-arrow-down.svg";
import ArrowUp from "@/assets/svg/long-arrow-up.svg";
@@ -11,6 +17,7 @@ import { isUndefined } from "@/utils";
const Header: React.FC = () => {
const markets = usePredictionMarkets();
+ const removeMarket = useMarketsStore((state) => state.removeMarket);
return (
@@ -23,49 +30,66 @@ const Header: React.FC = () => {
"scroll-shadows max-h-58 overflow-hidden overflow-y-scroll",
)}
>
- {markets.map((market) => (
-
-
-
-
- {market.name}
-
-
- Score
-
-
- {market.prediction}
-
-
- {!isUndefined(market?.prediction) &&
- !isUndefined(market?.marketEstimate) ? (
-
- ) : null}
-
- ))}
+
+ {markets.map((market) => (
+
+
+
+
+ {market.name}
+
+
+ Score
+
+
+ {market.prediction}
+
+
+ {!isUndefined(market?.prediction) &&
+ !isUndefined(market?.marketEstimate) ? (
+
+ ) : null}
+ {markets.length > 1 ? (
+
+ }
+ onPress={() => removeMarket(market.marketId)}
+ />
+ ) : null}
+
+ ))}
+
);
diff --git a/src/app/(homepage)/components/PredictAll/PredictAllPopup/index.tsx b/src/app/(homepage)/components/PredictAll/PredictAllPopup/index.tsx
index f17d6f4..5f1543a 100644
--- a/src/app/(homepage)/components/PredictAll/PredictAllPopup/index.tsx
+++ b/src/app/(homepage)/components/PredictAll/PredictAllPopup/index.tsx
@@ -1,9 +1,11 @@
import React, { useMemo, useState } from "react";
import { Button, Modal } from "@kleros/ui-components-library";
+import { useToggle } from "react-use";
import { useAccount, useBalance } from "wagmi";
import {
+ seerCreditsAddress,
useReadSDaiPreviewDeposit,
useReadSDaiPreviewRedeem,
} from "@/generated";
@@ -14,7 +16,6 @@ import { usePredictionMarkets } from "@/hooks/usePredictionMarkets";
import { useTokenBalance } from "@/hooks/useTokenBalance";
import { useTokensBalances } from "@/hooks/useTokenBalances";
-import { TokenType } from "@/components/AmountInput";
import { PredictAmountSection } from "@/components/Predict/PredictAmountSection";
import PredictSteps from "@/components/Predict/PredictSteps";
@@ -23,6 +24,7 @@ import { isUndefined } from "@/utils";
import { collateral } from "@/consts";
import Header from "./Header";
+import { TokenType } from "@/consts/tokens";
interface IPredictAllPopup {
isOpen: boolean;
toggleIsOpen: () => void;
@@ -36,6 +38,7 @@ export const PredictAllPopup: React.FC = ({
const [amount, setAmount] = useState();
const [selectedToken, setSelectedToken] = useState(TokenType.sDAI);
+ const [isUsingSeerCredits, toggleIsUsingCredits] = useToggle(false);
const isXDai = selectedToken === TokenType.xDAI;
@@ -55,6 +58,10 @@ export const PredictAllPopup: React.FC = ({
address: account,
token: collateral.address,
});
+ const { data: userSeerCreditsBalanceData } = useTokenBalance({
+ address: account,
+ token: seerCreditsAddress,
+ });
const { data: userXDaiBalanceData } = useBalance({
address: account,
});
@@ -64,6 +71,19 @@ export const PredictAllPopup: React.FC = ({
token: collateral.address,
});
+ const { data: seerCreditsEquivalentXDAI } = useReadSDaiPreviewRedeem({
+ args: [userSeerCreditsBalanceData?.value ?? 0n],
+ query: {
+ enabled:
+ !isUndefined(userSeerCreditsBalanceData) &&
+ userSeerCreditsBalanceData.value > 0 &&
+ isXDai,
+ retry: false,
+ },
+ });
+
+ const seerCreditsBalance = userSeerCreditsBalanceData?.value ?? 0n;
+
const { data: tokensBalances } = useTokensBalances(
tradeExecutor,
markets.flatMap((market) => [market.upToken, market.downToken]),
@@ -95,21 +115,45 @@ export const PredictAllPopup: React.FC = ({
},
});
+ // the total amount of collateral being supplied in sDAI
+ // accounts for all sources of collateral including seer credits
const sDAIDepositAmount = useMemo(() => {
if (!isXDai) return amount;
return resultingDeposit;
}, [resultingDeposit, amount, isXDai]);
- //sDAI required
+ // additional sDAI required to be deposited, accounts for Seer credits if being used
const toBeAdded = useMemo(() => {
if (isUndefined(sDAIDepositAmount)) return 0n;
- return sDAIDepositAmount > (walletSDaiBalanceData?.value ?? 0n)
- ? sDAIDepositAmount - (walletSDaiBalanceData?.value ?? 0n)
- : 0n;
- }, [sDAIDepositAmount, walletSDaiBalanceData]);
+ // account for wallet balance
+ const sDAIDepositWalletBalanceOffset =
+ sDAIDepositAmount > (walletSDaiBalanceData?.value ?? 0n)
+ ? sDAIDepositAmount - (walletSDaiBalanceData?.value ?? 0n)
+ : 0n;
+ //account for Seer Credits
+ if (isUsingSeerCredits) {
+ return sDAIDepositWalletBalanceOffset - seerCreditsBalance > 0
+ ? sDAIDepositWalletBalanceOffset - seerCreditsBalance
+ : 0n;
+ }
+ return sDAIDepositWalletBalanceOffset;
+ }, [
+ sDAIDepositAmount,
+ walletSDaiBalanceData,
+ seerCreditsBalance,
+ isUsingSeerCredits,
+ ]);
+
+ const toBeAddedSeerCredits = useMemo(() => {
+ if (!isUsingSeerCredits) return 0n;
+ // sDAIDepositAmount is alrd adjusted in case xDAI is selected
+ return (sDAIDepositAmount ?? 0n) > seerCreditsBalance
+ ? seerCreditsBalance
+ : sDAIDepositAmount;
+ }, [seerCreditsBalance, sDAIDepositAmount, isUsingSeerCredits]);
// when using xDAI input, we need to convert the additional sDAI amount required,
- // back to xDAI to take what's necessary
+ // back to xDAI to take what's necessary
const { data: toBeAddedXDai } = useReadSDaiPreviewRedeem({
args: [toBeAdded],
query: {
@@ -120,16 +164,25 @@ export const PredictAllPopup: React.FC = ({
// can be either xDAI or sDAI
const availableBalance = useMemo(() => {
- return selectedToken === TokenType.sDAI
+ const seerCreditBalanceEquivalent = isXDai
+ ? (seerCreditsEquivalentXDAI ?? 0n)
+ : seerCreditsBalance;
+ return !isXDai
? (userSDaiBalanceData?.value ?? 0n) +
- (walletSDaiBalanceData?.value ?? 0n)
- : (userXDaiBalanceData?.value ?? 0n) + (walletXDaiBalance ?? 0n);
+ (walletSDaiBalanceData?.value ?? 0n) +
+ (isUsingSeerCredits ? seerCreditBalanceEquivalent : 0n)
+ : (userXDaiBalanceData?.value ?? 0n) +
+ (walletXDaiBalance ?? 0n) +
+ (isUsingSeerCredits ? seerCreditBalanceEquivalent : 0n);
}, [
- selectedToken,
+ isXDai,
userSDaiBalanceData,
walletSDaiBalanceData,
userXDaiBalanceData,
walletXDaiBalance,
+ seerCreditsBalance,
+ isUsingSeerCredits,
+ seerCreditsEquivalentXDAI,
]);
const {
@@ -138,11 +191,15 @@ export const PredictAllPopup: React.FC = ({
isCreatingWallet,
isAddingCollateral,
isCollateralAdded,
+ isAddingSeerCredits,
+ isSeerCreditsAdded,
isProcessingMarkets,
isLoadingQuotes,
isPredictionSuccessful,
isSending,
error,
+ frozenToBeAdded,
+ frozenToBeAddedSeerCredits,
tradeExecutorPredictAll,
} = usePredictAllFlow({
account,
@@ -152,6 +209,7 @@ export const PredictAllPopup: React.FC = ({
sDAIDepositAmount,
toBeAdded,
toBeAddedXDai,
+ toBeAddedSeerCredits,
walletUnderlyingBalances: underlyingTokensBalances,
walletTokensBalances: tokensBalances,
onDone: () => {
@@ -186,16 +244,23 @@ export const PredictAllPopup: React.FC = ({
toBeAdded,
toBeAddedXDai,
isXDai,
+ toggleIsUsingCredits,
+ isUsingSeerCredits,
+ seerCreditsBalance,
}}
isWalletCreated={checkTradeExecutorResult?.isCreated ?? false}
/>
{
@@ -22,12 +24,14 @@ const PredictAll: React.FC = () => {
Predict all the estimates above
-
-
+
+
+
+
);
};
diff --git a/src/app/(homepage)/components/ProjectFunding/PredictButton.tsx b/src/app/(homepage)/components/ProjectFunding/PredictButton.tsx
deleted file mode 100644
index 09139d3..0000000
--- a/src/app/(homepage)/components/ProjectFunding/PredictButton.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-"use client";
-import React from "react";
-
-import { Button } from "@kleros/ui-components-library";
-import { useToggle } from "react-use";
-
-import { useMarketContext } from "@/context/MarketContext";
-
-import { PredictPopup } from "./PredictPopup";
-
-const PredictButton: React.FC = ({}) => {
- const [isOpen, toggleIsOpen] = useToggle(false);
- const { hasLiquidity } = useMarketContext();
- return (
- <>
-
-
- >
- );
-};
-export default PredictButton;
diff --git a/src/app/(homepage)/components/ProjectFunding/PredictPopup/Header.tsx b/src/app/(homepage)/components/ProjectFunding/PredictPopup/Header.tsx
deleted file mode 100644
index fbbba1d..0000000
--- a/src/app/(homepage)/components/ProjectFunding/PredictPopup/Header.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import React from "react";
-
-import clsx from "clsx";
-
-import { useMarketContext } from "@/context/MarketContext";
-
-import ArrowDown from "@/assets/svg/long-arrow-down.svg";
-import ArrowUp from "@/assets/svg/long-arrow-up.svg";
-
-const Header: React.FC = () => {
- const { market, prediction, isUpPredict } = useMarketContext();
-
- return (
-
-
- Predict
-
-
-
-
-
- {market.name}
-
-
- Score
-
-
- {prediction}
-
-
-
-
-
- );
-};
-
-export default Header;
diff --git a/src/app/(homepage)/components/ProjectFunding/PredictPopup/index.tsx b/src/app/(homepage)/components/ProjectFunding/PredictPopup/index.tsx
deleted file mode 100644
index 06d8e02..0000000
--- a/src/app/(homepage)/components/ProjectFunding/PredictPopup/index.tsx
+++ /dev/null
@@ -1,231 +0,0 @@
-import React, { useMemo, useState } from "react";
-
-import { Button, Modal } from "@kleros/ui-components-library";
-import { useAccount, useBalance } from "wagmi";
-
-import {
- useReadSDaiPreviewDeposit,
- useReadSDaiPreviewRedeem,
-} from "@/generated";
-
-import { useMarketContext } from "@/context/MarketContext";
-import { usePredictFlow } from "@/hooks/predict/usePredictFlow";
-import { useCheckTradeExecutorCreated } from "@/hooks/tradeWallet/useCheckTradeExecutorCreated";
-import { useTokenBalance } from "@/hooks/useTokenBalance";
-
-import { TokenType } from "@/components/AmountInput";
-import { PredictAmountSection } from "@/components/Predict/PredictAmountSection";
-import PredictSteps from "@/components/Predict/PredictSteps";
-
-import { isUndefined } from "@/utils";
-
-import { collateral } from "@/consts";
-
-import Header from "./Header";
-
-interface IPredictPopup {
- isOpen: boolean;
- toggleIsOpen: () => void;
-}
-
-export const PredictPopup: React.FC = ({
- isOpen,
- toggleIsOpen,
-}) => {
- const [amount, setAmount] = useState();
- const [selectedToken, setSelectedToken] = useState(TokenType.sDAI);
-
- const isXDai = selectedToken === TokenType.xDAI;
-
- const { market, predictedPrice } = useMarketContext();
- const resetUI = () => {
- setAmount(undefined);
- setSelectedToken(TokenType.sDAI);
- };
-
- // checking to see if user alrd has trade wallet
- const { address: account } = useAccount();
- const { data: checkTradeExecutorResult } =
- useCheckTradeExecutorCreated(account);
- const tradeExecutor = checkTradeExecutorResult?.predictedAddress;
-
- // balances
- const { data: userSDaiBalanceData } = useTokenBalance({
- address: account,
- token: collateral.address,
- });
- const { data: userXDaiBalanceData } = useBalance({
- address: account,
- });
-
- const { data: walletSDaiBalanceData } = useTokenBalance({
- address: tradeExecutor,
- token: collateral.address,
- });
- const { data: walletUPBalanceData } = useTokenBalance({
- address: tradeExecutor,
- token: market.upToken,
- });
-
- const { data: walletDOWNBalanceData } = useTokenBalance({
- address: tradeExecutor,
- token: market.downToken,
- });
-
- // wallet only holds sDAI, this gives the equivalent amount in xDAI
- // to inform user how much equivalent xDAI they have
- const { data: walletXDaiBalance } = useReadSDaiPreviewRedeem({
- args: [walletSDaiBalanceData?.value ?? 0n],
- query: {
- enabled:
- !isUndefined(walletSDaiBalanceData) &&
- walletSDaiBalanceData.value > 0 &&
- isXDai,
- retry: false,
- },
- });
-
- const { data: walletUnderlyingBalanceData } = useTokenBalance({
- address: tradeExecutor,
- token: market.underlyingToken,
- });
-
- // tells us the resulting sDAI
- const { data: resultingDeposit } = useReadSDaiPreviewDeposit({
- args: [amount ?? 0n],
- query: {
- enabled: !isUndefined(amount) && amount > 0,
- retry: false,
- },
- });
-
- const sDAIDepositAmount = useMemo(() => {
- if (!isXDai) return amount;
- return resultingDeposit;
- }, [resultingDeposit, amount, isXDai]);
-
- //sDAI required
- const toBeAdded = useMemo(() => {
- if (isUndefined(sDAIDepositAmount)) return 0n;
- return sDAIDepositAmount > (walletSDaiBalanceData?.value ?? 0n)
- ? sDAIDepositAmount - (walletSDaiBalanceData?.value ?? 0n)
- : 0n;
- }, [sDAIDepositAmount, walletSDaiBalanceData]);
-
- // when using xDAI input, we need to convert the additional sDAI amount required,
- // back to xDAI to take what's necessary
- const { data: toBeAddedXDai } = useReadSDaiPreviewRedeem({
- args: [toBeAdded],
- query: {
- enabled: !isUndefined(toBeAdded) && toBeAdded > 0 && isXDai,
- retry: false,
- },
- });
-
- // combined balance (user + wallet)
- const availableBalance = useMemo(() => {
- return selectedToken === TokenType.sDAI
- ? (userSDaiBalanceData?.value ?? 0n) +
- (walletSDaiBalanceData?.value ?? 0n)
- : (userXDaiBalanceData?.value ?? 0n) + (walletXDaiBalance ?? 0n);
- }, [
- selectedToken,
- userSDaiBalanceData,
- walletSDaiBalanceData,
- userXDaiBalanceData,
- walletXDaiBalance,
- ]);
-
- const {
- handlePredict,
- createdTradeWallet,
- isCreatingWallet,
- isAddingCollateral,
- isCollateralAdded,
- isProcessingMarkets,
- isLoadingQuotes,
- isPredictionSuccessful,
- isSending,
- error,
- tradeExecutorPredict,
- } = usePredictFlow({
- market,
- predictedPrice,
- account,
- tradeExecutor,
- checkTradeExecutorResult,
- isXDai,
- sDAIDepositAmount,
- toBeAdded,
- toBeAddedXDai,
- walletUnderlyingBalance: walletUnderlyingBalanceData?.value,
- walletUPBalance: walletUPBalanceData?.value,
- walletDOWNBalance: walletDOWNBalanceData?.value,
- onDone: () => {
- toggleIsOpen();
- resetUI();
- },
- });
-
- const disabled =
- isSending || (!isUndefined(amount) && amount > availableBalance);
-
- return (
-
-
-
- );
-};
diff --git a/src/app/(homepage)/components/ProjectFunding/index.tsx b/src/app/(homepage)/components/ProjectFunding/index.tsx
index 276a8e0..d6fdb7e 100644
--- a/src/app/(homepage)/components/ProjectFunding/index.tsx
+++ b/src/app/(homepage)/components/ProjectFunding/index.tsx
@@ -1,123 +1,91 @@
import React from "react";
-import { Card, Accordion, NumberField } from "@kleros/ui-components-library";
+import { Accordion, CustomAccordion } from "@kleros/ui-components-library";
import clsx from "clsx";
+import { motion } from "framer-motion";
-import { useCardInteraction } from "@/context/CardInteractionContext";
import { useMarketContext } from "@/context/MarketContext";
import { useTradeWallet } from "@/context/TradeWalletContext";
-import { isUndefined } from "@/utils";
-
import Details from "./Details";
import PositionValue from "./PositionValue";
-import PredictButton from "./PredictButton";
import PredictionSlider from "./PredictionSlider";
const ProjectFunding: React.FC = () => {
- const { setActiveCardId } = useCardInteraction();
- const {
- isUpPredict,
- market,
- prediction,
- setPrediction,
- showEstimateVariant,
- hasLiquidity,
- } = useMarketContext();
- const {
- name,
- color,
- upToken,
- downToken,
- precision,
- details,
- marketId,
- underlyingToken,
- minValue,
- maxValue,
- } = market;
+ const { market } = useMarketContext();
+ const { name, color, upToken, downToken, details, underlyingToken } = market;
const { tradeExecutor } = useTradeWallet();
return (
- setActiveCardId(marketId)}
- >
-
-
-
-
-
-
-
setPrediction(e * precision)}
- />
-
-
-
-
-
-
-
- {tradeExecutor ? (
-
- ) : null}
-
}]}
- />
-
-
+ items={[
+ {
+ title: (
+ <>
+
+ >
+ ),
+ body: (
+
+
+ {tradeExecutor ? (
+
+ ) : null}
+
}]}
+ />
+
+ ),
+ expandButton: ({ expanded, toggle }) => (
+ // hydration error, because of button in button, workaround
+ e.key === "Enter" && toggle()}
+ className={clsx(
+ "bg-klerosUIComponentsPrimaryBlue cursor-pointer",
+ "rounded-base inline-flex items-center px-6 py-1.5",
+ "text-klerosUIComponentsWhiteBackground text-sm font-semibold",
+ )}
+ whileTap={{ scale: 0.9, y: 1 }}
+ >
+ {expanded ? "Close" : "Predict"}
+
+ ),
+ },
+ ]}
+ />
);
};
diff --git a/src/app/(homepage)/page.tsx b/src/app/(homepage)/page.tsx
index 80005e4..655395f 100644
--- a/src/app/(homepage)/page.tsx
+++ b/src/app/(homepage)/page.tsx
@@ -4,12 +4,10 @@ import React from "react";
import { useReadGnosisRouterGetWinningOutcomes } from "@/generated";
-import { CardInteractionProvider } from "@/context/CardInteractionContext";
import MarketContextProvider from "@/context/MarketContext";
import { TradeWalletProvider } from "@/context/TradeWalletContext";
import { useChartData } from "@/hooks/useChartData";
-import EnsureChain from "@/components/EnsureChain";
import Loader from "@/components/Loader";
import { isUndefined } from "@/utils";
@@ -45,25 +43,21 @@ export default function Home() {
-
-
-
-
-
- {markets.map((market, i) => (
-
-
-
- ))}
-
-
-
-
-
+
+
+
+ {markets.map((market, i) => (
+
+
+
+ ))}
+
+
+
diff --git a/src/assets/svg/seer-credits.svg b/src/assets/svg/seer-credits.svg
new file mode 100644
index 0000000..97027a7
--- /dev/null
+++ b/src/assets/svg/seer-credits.svg
@@ -0,0 +1,10 @@
+
diff --git a/src/components/AmountInput.tsx b/src/components/AmountInput.tsx
index 92ee26a..9f75dbf 100644
--- a/src/components/AmountInput.tsx
+++ b/src/components/AmountInput.tsx
@@ -4,16 +4,11 @@ import { BigNumberField, DropdownSelect } from "@kleros/ui-components-library";
import clsx from "clsx";
import { parseUnits, formatUnits } from "viem";
-import DAIIcon from "@/assets/svg/dai.svg";
-
import { cn, formatValue, isUndefined } from "@/utils";
import LightButton from "./LightButton";
+import { Tokens, TokenType } from "@/consts/tokens";
-export enum TokenType {
- sDAI,
- xDAI,
-}
interface IAmountInput {
setAmount: (amount: bigint) => void;
setSelectedToken: (token: TokenType) => void;
@@ -23,6 +18,7 @@ interface IAmountInput {
selectedToken: TokenType;
className?: string;
inputProps?: React.ComponentProps;
+ isWithdraw?: boolean;
}
const AmountInput: React.FC = ({
@@ -34,6 +30,7 @@ const AmountInput: React.FC = ({
balance,
className,
inputProps,
+ isWithdraw,
}) => {
const notEnoughBalance = useMemo(() => {
if (!isUndefined(value) && !isUndefined(balance) && value > balance)
@@ -47,6 +44,44 @@ const AmountInput: React.FC = ({
}
};
+ const items = useMemo(() => {
+ const SDAIIcon = Tokens[TokenType.sDAI].Icon;
+ const SeerCreditsIcon = Tokens[TokenType.SeerCredits].Icon;
+ const tokens = [
+ {
+ text: "sDAI",
+ itemValue: TokenType.sDAI,
+ id: TokenType.sDAI,
+ icon: ,
+ },
+ {
+ text: "xDAI",
+ itemValue: TokenType.xDAI,
+ id: TokenType.xDAI,
+ icon: ,
+ },
+ ];
+ if (isWithdraw) {
+ tokens.push(
+ ...[
+ {
+ text: "wxDAI",
+ itemValue: TokenType.WXDAI,
+ id: TokenType.WXDAI,
+ icon: ,
+ },
+ {
+ text: "Seer Credits",
+ itemValue: TokenType.SeerCredits,
+ id: TokenType.SeerCredits,
+ icon: ,
+ },
+ ],
+ );
+ }
+ return tokens;
+ }, [isWithdraw]);
+
return (
@@ -81,20 +116,7 @@ const AmountInput: React.FC = ({
setSelectedToken(item.itemValue);
}}
defaultSelectedKey={selectedToken}
- items={[
- {
- text: "sDAI",
- itemValue: TokenType.sDAI,
- id: TokenType.sDAI,
- icon: ,
- },
- {
- text: "xDAI",
- itemValue: TokenType.xDAI,
- id: TokenType.xDAI,
- icon: ,
- },
- ]}
+ items={items}
isDisabled={inputProps?.isReadOnly}
/>
@@ -111,7 +133,7 @@ const AmountInput: React.FC
= ({
{!notEnoughBalance && (
{!isUndefined(balance)
- ? `Available: ${formatValue(balance)} ${selectedToken === TokenType.xDAI ? "xDAI" : "sDAI"}`
+ ? `Available: ${formatValue(balance)}`
: "Loading..."}
)}
diff --git a/src/components/EnsureChain.tsx b/src/components/EnsureChain.tsx
index 579512b..6b6d6a2 100644
--- a/src/components/EnsureChain.tsx
+++ b/src/components/EnsureChain.tsx
@@ -8,9 +8,7 @@ const EnsureChain: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { address } = useAccount();
return isUndefined(address) ? (
-
-
-
+
) : (
children
);
diff --git a/src/components/Predict/PredictAmountSection.tsx b/src/components/Predict/PredictAmountSection.tsx
index acdce6d..0a4fe8e 100644
--- a/src/components/Predict/PredictAmountSection.tsx
+++ b/src/components/Predict/PredictAmountSection.tsx
@@ -1,14 +1,18 @@
import React from "react";
+import { Checkbox } from "@kleros/ui-components-library";
import clsx from "clsx";
-import AmountInput, { TokenType } from "@/components/AmountInput";
+import AmountInput from "@/components/AmountInput";
import ArrowDownIcon from "@/assets/svg/arrow-down.svg";
import { formatValue } from "@/utils";
+import { MIN_SEER_CREDITS_USAGE } from "@/consts";
+
import AmountDisplay from "./AmountDisplay";
+import { TokenType } from "@/consts/tokens";
interface IPredictAmountSection {
amount: bigint | undefined;
@@ -22,6 +26,7 @@ interface IPredictAmountSection {
walletXDaiBalance?: bigint;
walletSDaiBalanceData?: { value: bigint };
+ seerCreditsBalance: bigint;
walletUnderlyingBalanceData?: { value: bigint };
sDAIDepositAmount?: bigint;
@@ -30,6 +35,8 @@ interface IPredictAmountSection {
isXDai: boolean;
isWalletCreated: boolean;
+ isUsingSeerCredits: boolean;
+ toggleIsUsingCredits: (value?: boolean) => void;
}
export const PredictAmountSection: React.FC = ({
@@ -41,17 +48,20 @@ export const PredictAmountSection: React.FC = ({
isSending,
walletXDaiBalance,
walletSDaiBalanceData,
+ seerCreditsBalance,
walletUnderlyingBalanceData,
sDAIDepositAmount,
toBeAdded,
toBeAddedXDai,
isXDai,
isWalletCreated,
+ isUsingSeerCredits,
+ toggleIsUsingCredits,
}) => {
return (
{/* Amount input */}
-
+
You pay
@@ -80,6 +90,16 @@ export const PredictAmountSection: React.FC = ({
>
) : null}
+ {/* Seer credits checkbox */}
+ {seerCreditsBalance > MIN_SEER_CREDITS_USAGE ? (
+
+ ) : null}
= ({
tradeExecutor,
toBeAdded,
+ toBeAddedSeerCredits,
isCreatingWallet,
isAddingCollateral,
isCollateralAdded,
+ isAddingSeerCredits,
+ isSeerCreditsAdded,
isProcessingMarkets,
isLoadingQuotes,
isMakingPrediction,
@@ -76,6 +82,19 @@ const PredictSteps: React.FC = ({
});
}
+ if (toBeAddedSeerCredits && toBeAddedSeerCredits > 0n) {
+ steps.push({
+ title: "Add Seer Credits to wallet",
+ party: isAddingSeerCredits ? : "",
+ subtitle: `Adding ${formatValue(toBeAddedSeerCredits)} Seer Credits`,
+ Icon: isSeerCreditsAdded ? CheckOutline : CircleOutline,
+ state: isAddingSeerCredits
+ ? "loading"
+ : error || toBeAddedSeerCredits === 0n
+ ? "disabled"
+ : undefined,
+ });
+ }
steps.push({
title: "Add Collateral to wallet",
party: isAddingCollateral ? : "",
@@ -121,7 +140,10 @@ const PredictSteps: React.FC = ({
isCreatingWallet,
isAddingCollateral,
isCollateralAdded,
+ isAddingSeerCredits,
+ isSeerCreditsAdded,
toBeAdded,
+ toBeAddedSeerCredits,
isMakingPrediction,
isPredictionSuccessful,
predictionProgressText,
diff --git a/src/consts/index.ts b/src/consts/index.ts
index b741535..0a63126 100644
--- a/src/consts/index.ts
+++ b/src/consts/index.ts
@@ -1,11 +1,13 @@
-import { Address, type Abi } from "viem";
+import { Address, erc20Abi, type Abi } from "viem";
import { gnosis } from "viem/chains";
import { CondtionalRouterAbi } from "@/abi/ConditionalRouter";
import { CowSwapAbi } from "@/abi/CowSwap";
+import { CreditsManagerAbi } from "@/abi/CreditsManager";
import { RouterAbi } from "@/abi/Router";
import { sDAIAbi } from "@/abi/sDAI";
import { sDAIAdapterAbi } from "@/abi/sDAIAdapter";
+import { wrappedXDAIAbi } from "@/abi/wrappedXDAI";
export const reownProjectId = process.env.NEXT_PUBLIC_REOWN_PROJECTID;
@@ -43,6 +45,21 @@ const contracts = {
abi: CondtionalRouterAbi,
name: "conditionalRouter",
},
+ seerCreditsManager: {
+ address: "0xB29D0C9875D93483891c0645fdC13D665a4d2D70",
+ abi: CreditsManagerAbi,
+ name: "CreditsManager",
+ },
+ seerCredits: {
+ address: "0xEDd48e43EBd4E2b31238a5CBA8FD548fC051aCAF",
+ abi: erc20Abi,
+ name: "SeerCredits",
+ },
+ wrappedXDai: {
+ address: "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d",
+ abi: wrappedXDAIAbi,
+ name: "WXDAI",
+ },
} satisfies Record;
export const getContractInfo = (
@@ -65,3 +82,4 @@ export const collateral = {
};
export const DECIMALS = 18;
export const VOLUME_MIN = 0.01;
+export const MIN_SEER_CREDITS_USAGE = 0.01;
diff --git a/src/consts/markets.ts b/src/consts/markets.ts
index 88ecc21..f5ba5d7 100644
--- a/src/consts/markets.ts
+++ b/src/consts/markets.ts
@@ -36,6 +36,13 @@ export interface IMarket {
conditionId: `0x${string}`;
}
+export const metadata = {
+ name: "Session 1 - Movies Experiment",
+ question: "If watched, what score will Clément give to the movie?",
+ questionDescription:
+ "Which movies will Clément watch as part of the “Distilled Clément’s Judgement experiment”? \nAnd for each movie, what rating will he give to that movie?",
+};
+
export const markets: Array = [
{
name: "Judge Dredd (1995)",
diff --git a/src/consts/tokens.ts b/src/consts/tokens.ts
new file mode 100644
index 0000000..caba49a
--- /dev/null
+++ b/src/consts/tokens.ts
@@ -0,0 +1,33 @@
+import DAIIcon from "@/assets/svg/dai.svg";
+import SeerCreditsIcon from "@/assets/svg/seer-credits.svg";
+import { sDaiAddress, seerCreditsAddress, wxdaiAddress } from "@/generated";
+import { Address } from "viem";
+
+export enum TokenType {
+ sDAI = "sDAI",
+ xDAI = "xDAI",
+ WXDAI = "WXDAI",
+ SeerCredits = "SEER_CREDITS",
+}
+
+export const Tokens: Record<
+ TokenType,
+ { address: Address; Icon: React.FC> }
+> = {
+ [TokenType.sDAI]: {
+ address: sDaiAddress,
+ Icon: DAIIcon,
+ },
+ [TokenType.xDAI]: {
+ address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
+ Icon: DAIIcon,
+ },
+ [TokenType.WXDAI]: {
+ address: wxdaiAddress,
+ Icon: DAIIcon,
+ },
+ [TokenType.SeerCredits]: {
+ address: seerCreditsAddress,
+ Icon: SeerCreditsIcon,
+ },
+};
diff --git a/src/hooks/predict/usePredictAllFlow.ts b/src/hooks/predict/usePredictAllFlow.ts
index a4d8bff..3d20942 100644
--- a/src/hooks/predict/usePredictAllFlow.ts
+++ b/src/hooks/predict/usePredictAllFlow.ts
@@ -3,6 +3,7 @@ import { useEffect, useMemo } from "react";
import { useQueryClient } from "@tanstack/react-query";
import { Address } from "viem";
+import { seerCreditsAddress } from "@/generated";
import { useMarketsStore } from "@/store/markets";
import { useCreateTradeExecutor } from "@/hooks/tradeWallet/useCreateTradeExecutor";
@@ -11,7 +12,7 @@ import { fetchTokenBalance } from "@/hooks/useTokenBalance";
import { isUndefined } from "@/utils";
import { formatError } from "@/utils/formatError";
-import { getQuotes } from "@/utils/getQuotes";
+import { getQuotes, getSDaiToWXdaiData } from "@/utils/getQuotes";
import { processMarket } from "@/utils/processMarket";
import { collateral } from "@/consts";
@@ -35,6 +36,7 @@ interface UsePredictAllFlowArgs {
sDAIDepositAmount?: bigint;
toBeAdded: bigint;
toBeAddedXDai?: bigint;
+ toBeAddedSeerCredits?: bigint;
walletUnderlyingBalances?: bigint[];
walletTokensBalances?: bigint[];
@@ -50,6 +52,7 @@ export function usePredictAllFlow({
sDAIDepositAmount,
toBeAdded,
toBeAddedXDai,
+ toBeAddedSeerCredits,
walletUnderlyingBalances,
walletTokensBalances,
onDone,
@@ -94,8 +97,8 @@ export function usePredictAllFlow({
}, [checkTradeExecutorResult?.isCreated, walletUnderlyingBalances]);
const hasDepositCollateral = useMemo(() => {
- return (sDAIDepositAmount ?? 0n) > 0n;
- }, [sDAIDepositAmount]);
+ return (sDAIDepositAmount ?? 0n) + (toBeAddedSeerCredits ?? 0n) > 0n;
+ }, [sDAIDepositAmount, toBeAddedSeerCredits]);
const hasPosition = useMemo(() => {
return walletTokensBalances?.some((v) => v > 0n);
@@ -108,11 +111,15 @@ export function usePredictAllFlow({
initialSDAIDeposit?: bigint;
initialToBeAdded?: bigint;
initialToBeAddedXDai?: bigint;
+ initialToBeAddedSeerCredits?: bigint;
} = {
initialSDAIDeposit: sDAIDepositAmount,
initialToBeAdded: toBeAdded,
initialToBeAddedXDai: toBeAddedXDai,
+ initialToBeAddedSeerCredits: toBeAddedSeerCredits,
};
+ setFlag("frozenToBeAdded", toBeAdded);
+ setFlag("frozenToBeAddedSeerCredits", toBeAddedSeerCredits);
if (!hasWalletCollateral && !hasDepositCollateral && !hasPosition) {
setFlag("error", "Require collateral to trade");
@@ -148,7 +155,25 @@ export function usePredictAllFlow({
setFlag("createdTradeWallet", tradeWallet);
}
- // deposit if needed
+ // deposit SeerCredits if needed
+ if (
+ !isUndefined(snapshot.initialToBeAddedSeerCredits) &&
+ snapshot.initialToBeAddedSeerCredits > 0n
+ ) {
+ setFlag("isAddingSeerCredits", true);
+
+ await depositToTradeExecutor.mutateAsync({
+ token: seerCreditsAddress,
+ amount: snapshot.initialToBeAddedSeerCredits,
+ tradeExecutor: tradeWallet,
+ isXDai: false,
+ });
+
+ setFlag("isAddingSeerCredits", false);
+ setFlag("isSeerCreditsAdded", true);
+ }
+
+ // deposit sDAI/xDAI if needed
if (
!isUndefined(snapshot.initialToBeAdded) &&
snapshot.initialToBeAdded > 0n
@@ -177,15 +202,32 @@ export function usePredictAllFlow({
setFlag("isCollateralAdded", true);
}
- // process markets (UP & DOWN)
setFlag("isProcessingMarkets", true);
+
+ const sDaiToWXDaiData = await getSDaiToWXdaiData(
+ tradeWallet!,
+ toBeAddedSeerCredits,
+ );
+
+ // the expected/equivalent sDAI received by using SeerCredits can be less than initially calculated
+ // so adjusting
+ if (
+ sDaiToWXDaiData &&
+ sDaiToWXDaiData.slippage > 0n &&
+ snapshot.initialSDAIDeposit
+ ) {
+ snapshot.initialSDAIDeposit =
+ snapshot.initialSDAIDeposit - sDaiToWXDaiData.slippage;
+ }
+
+ // process markets (UP & DOWN)
const processedMarkets = await Promise.all(
markets.map(async (market) => {
const upProcessed = await processMarket({
underlying: market.underlyingToken,
outcome: market.upToken,
tradeExecutor: tradeWallet!,
- mintAmount: snapshot.initialSDAIDeposit,
+ mintAmount: snapshot.initialSDAIDeposit ?? 0n,
targetPrice: market?.predictedPrice ?? 0,
});
@@ -193,7 +235,7 @@ export function usePredictAllFlow({
underlying: market.underlyingToken,
outcome: market.downToken,
tradeExecutor: tradeWallet!,
- mintAmount: snapshot.initialSDAIDeposit,
+ mintAmount: snapshot.initialSDAIDeposit ?? 0n,
targetPrice: 1 - (market?.predictedPrice ?? 0),
});
@@ -231,7 +273,10 @@ export function usePredictAllFlow({
await tradeExecutorPredictAll.mutateAsync({
marketsData: quotesPerMarket,
tradeExecutor: tradeWallet!,
- mintAmount: snapshot.initialSDAIDeposit,
+ mintAmount:
+ (snapshot.initialSDAIDeposit ?? 0n) -
+ (sDaiToWXDaiData?.minSDaiReceived ?? 0n),
+ seerCreditsSwapQuote: sDaiToWXDaiData?.quote,
});
setFlag("isPredictionSuccessful", true);
@@ -239,10 +284,13 @@ export function usePredictAllFlow({
setTimeout(() => {
onDone();
reset();
- resetPredictionMarkets();
- queryClient.refetchQueries({
- queryKey: ["useTicksData"],
- });
+ queryClient
+ .refetchQueries({
+ queryKey: ["useTicksData"],
+ })
+ .then(() => {
+ resetPredictionMarkets();
+ });
}, 3000);
} catch (e) {
if (e instanceof Error) {
diff --git a/src/hooks/predict/usePredictState.ts b/src/hooks/predict/usePredictState.ts
index 88b86bd..e64a323 100644
--- a/src/hooks/predict/usePredictState.ts
+++ b/src/hooks/predict/usePredictState.ts
@@ -7,9 +7,13 @@ export interface PredictState {
isCreatingWallet: boolean;
isAddingCollateral: boolean;
isCollateralAdded: boolean;
+ isAddingSeerCredits: boolean;
+ isSeerCreditsAdded: boolean;
isProcessingMarkets: boolean;
isLoadingQuotes: boolean;
isPredictionSuccessful: boolean;
+ frozenToBeAdded?: bigint;
+ frozenToBeAddedSeerCredits?: bigint;
error?: string;
isSending: boolean;
}
@@ -18,10 +22,14 @@ const initialState: PredictState = {
isCreatingWallet: false,
isAddingCollateral: false,
isCollateralAdded: false,
+ isAddingSeerCredits: false,
+ isSeerCreditsAdded: false,
isProcessingMarkets: false,
isLoadingQuotes: false,
isPredictionSuccessful: false,
isSending: false,
+ frozenToBeAdded: undefined,
+ frozenToBeAddedSeerCredits: undefined,
createdTradeWallet: undefined,
error: undefined,
};
diff --git a/src/hooks/tradeWallet/useTradeExecutorPredictAll.ts b/src/hooks/tradeWallet/useTradeExecutorPredictAll.ts
index 4296fbd..0b00512 100644
--- a/src/hooks/tradeWallet/useTradeExecutorPredictAll.ts
+++ b/src/hooks/tradeWallet/useTradeExecutorPredictAll.ts
@@ -1,9 +1,19 @@
+import { SwaprV3Trade } from "@swapr/sdk";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { writeContract } from "@wagmi/core";
+import { type BytesLike } from "ethers";
import { Address, encodeFunctionData, erc20Abi, parseUnits } from "viem";
import { TradeExecutorAbi } from "@/contracts/abis/TradeExecutorAbi";
-import { gnosisRouterAddress } from "@/generated";
+import {
+ creditsManagerAbi,
+ creditsManagerAddress,
+ gnosisRouterAbi,
+ gnosisRouterAddress,
+ sDaiAddress,
+ wxdaiAbi,
+ wxdaiAddress,
+} from "@/generated";
import { config } from "@/wagmiConfig";
import { isUndefined } from "@/utils";
@@ -11,10 +21,11 @@ import { GetQuotesResult } from "@/utils/getQuotes";
import { waitForTransaction } from "@/utils/waitForTransaction";
import { DECIMALS, DEFAULT_CHAIN } from "@/consts";
-import { IMarket } from "@/consts/markets";
+import { IMarket, parentMarket } from "@/consts/markets";
import { mergeFromRouter, splitFromRouter } from "./useTradeExecutorPredict";
import { getSplitFromTradeExecutorCalls } from "./useTradeExecutorSplit";
+import { getMinimumAmountOut } from "@/utils/swapr";
interface MarketsData {
marketInfo: IMarket;
@@ -23,6 +34,7 @@ interface MarketsData {
// instead the amount of underlying tokens the wallet will have, in case of a split from collateral
// example: if user provided collateral to mint/ split tokens in parent market, then we add that mintAmount in here,
// since the txns will include a split txn that will end up giving this mintAmount to the Wallet as underlying token
+ // also factors in underlyingTokens from minting using SeerCredits
// This value is handled in utils/processMarket
amount: bigint;
}
@@ -32,6 +44,76 @@ interface PredictAllProps {
marketsData: MarketsData[];
// defined if collateral needs to minted to Parent Market
mintAmount?: bigint;
+ seerCreditsSwapQuote?: SwaprV3Trade;
+}
+
+interface Call {
+ to: Address | string;
+ data: string | BytesLike;
+ value?: bigint;
+}
+
+// Use the available SeerCredits in TradeWallet to Mint Parent market tokens
+// swap sDAI to Wxdai => Convert Wxdai to xdai (wxdai.withdraw()) => mint tokens with xdai
+async function getMintFromSeerCreditsCalls(
+ tradeExecutor: Address,
+ seerCreditSwapQuote: SwaprV3Trade,
+): Promise {
+ const quote = seerCreditSwapQuote;
+
+ const approveCall = {
+ to: sDaiAddress,
+ data: encodeFunctionData({
+ abi: erc20Abi,
+ functionName: "approve",
+ args: [
+ quote.approveAddress as Address,
+ parseUnits(quote.maximumAmountIn().toExact(), DECIMALS),
+ ],
+ }),
+ };
+
+ const swapTxn = await quote.swapTransaction({ recipient: tradeExecutor });
+ const executeCall = {
+ to: creditsManagerAddress,
+ data: encodeFunctionData({
+ abi: creditsManagerAbi,
+ functionName: "execute",
+ args: [
+ swapTxn.to! as `0x${string}`,
+ swapTxn.data! as `0x${string}`,
+ parseUnits(quote.maximumAmountIn().toExact(), DECIMALS),
+ wxdaiAddress,
+ ],
+ }),
+ };
+
+ const availableWxdai = await getMinimumAmountOut(quote);
+ if (!availableWxdai) {
+ throw new Error("Unable to fetch Wrapped xDAI balance.");
+ }
+
+ const withdrawCall = {
+ to: wxdaiAddress,
+ data: encodeFunctionData({
+ abi: wxdaiAbi,
+ functionName: "withdraw",
+ args: [availableWxdai],
+ }),
+ };
+
+ // splitPosition with xDAI
+ const splitCall = {
+ to: gnosisRouterAddress,
+ data: encodeFunctionData({
+ abi: gnosisRouterAbi,
+ functionName: "splitFromBase",
+ args: [parentMarket],
+ }),
+ value: availableWxdai,
+ };
+
+ return [approveCall, executeCall, withdrawCall, splitCall];
}
const getApproveCalls = async ({
@@ -130,8 +212,20 @@ async function getTradeExecutorCalls({
tradeExecutor,
marketsData,
mintAmount,
+ seerCreditsSwapQuote,
}: PredictAllProps) {
- const calls = [];
+ const calls: Call[] = [];
+
+ // Note that mintAmount will be already be offset taking into account the available SeerCredits
+ // so if the collateral amount is 10, then 7 can be SeerCredits and then mintAmount will be 3.
+ if (seerCreditsSwapQuote) {
+ const mintFromSeerCreditsCalls = await getMintFromSeerCreditsCalls(
+ tradeExecutor,
+ seerCreditsSwapQuote,
+ );
+
+ calls.push(...mintFromSeerCreditsCalls);
+ }
// Adds a split call if the user entered an amount to mint parent market tokens
if (!isUndefined(mintAmount) && mintAmount > 0n) {
@@ -195,18 +289,20 @@ async function predictAllFromTradeExecutor({
tradeExecutor,
marketsData,
mintAmount,
+ seerCreditsSwapQuote,
}: PredictAllProps) {
const calls = await getTradeExecutorCalls({
tradeExecutor,
marketsData,
mintAmount,
+ seerCreditsSwapQuote,
});
const writePromise = writeContract(config, {
address: tradeExecutor,
abi: TradeExecutorAbi,
- functionName: "batchExecute",
- args: [calls],
+ functionName: "batchValueExecute",
+ args: [calls.map((call) => ({ ...call, value: call?.value ?? 0n }))],
value: 0n,
chainId: DEFAULT_CHAIN.id,
});
diff --git a/src/hooks/tradeWallet/useWithdrawFromTradeExecutor.ts b/src/hooks/tradeWallet/useWithdrawFromTradeExecutor.ts
index 97f991f..525ca31 100644
--- a/src/hooks/tradeWallet/useWithdrawFromTradeExecutor.ts
+++ b/src/hooks/tradeWallet/useWithdrawFromTradeExecutor.ts
@@ -14,6 +14,7 @@ interface WithdrawProps {
tradeExecutor: Address;
tokens: Address[];
amounts: bigint[];
+ isXDai?: boolean;
}
async function withdrawFromTradeExecutor({
@@ -21,21 +22,30 @@ async function withdrawFromTradeExecutor({
tradeExecutor,
tokens,
amounts,
+ isXDai = false,
}: WithdrawProps) {
- const calls = tokens.map((token, index) => {
- return {
- to: token,
- data: encodeFunctionData({
- abi: erc20Abi,
- functionName: "transfer",
- args: [account, amounts[index]],
- }),
- };
- });
+ const calls = isXDai
+ ? [
+ {
+ to: account,
+ data: "0x",
+ value: amounts[0],
+ },
+ ]
+ : tokens.map((token, index) => {
+ return {
+ to: token,
+ data: encodeFunctionData({
+ abi: erc20Abi,
+ functionName: "transfer",
+ args: [account, amounts[index]],
+ }),
+ };
+ });
const writePromise = writeContract(config, {
address: tradeExecutor,
abi: TradeExecutorAbi,
- functionName: "batchExecute",
+ functionName: isXDai ? "batchValueExecute" : "batchExecute",
args: [calls],
value: 0n,
chainId: DEFAULT_CHAIN.id,
diff --git a/src/store/markets.ts b/src/store/markets.ts
index b0871cb..99d3e0e 100644
--- a/src/store/markets.ts
+++ b/src/store/markets.ts
@@ -16,6 +16,7 @@ interface MarketsStore {
setPrediction: (marketId: string, prediction: number) => void;
setMarketEstimate: (marketId: string, estimate: number) => void;
resetPredictionMarkets: () => void;
+ removeMarket: (marketId: string) => void;
}
export const useMarketsStore = create((set) => ({
@@ -74,4 +75,19 @@ export const useMarketsStore = create((set) => ({
return { markets: updatedMarkets };
}),
+
+ removeMarket: (marketId) =>
+ set((state) => {
+ const market = state.markets[marketId];
+ if (isUndefined(market)) return state;
+
+ return {
+ markets: {
+ ...state.markets,
+ // Setting the prediction to marketEstimate,
+ // removes it from the predicable markets (@/hooks/usePredictionMarkets)
+ [marketId]: { ...market, prediction: market?.marketEstimate },
+ },
+ };
+ }),
}));
diff --git a/src/utils/csv.ts b/src/utils/csv.ts
new file mode 100644
index 0000000..ce58cb9
--- /dev/null
+++ b/src/utils/csv.ts
@@ -0,0 +1,99 @@
+import { PredictionMarket } from "@/store/markets";
+
+import marketFromName from "./marketIdFromName";
+
+import { isUndefined } from ".";
+
+export const parseMarketCSV = (csvText: string): Record => {
+ const lines = csvText.trim().split("\n");
+
+ if (lines.length < 2) {
+ throw new Error("CSV must have at least a header row and one data row");
+ }
+
+ const headers = lines[0].split(",").map((h) => h.trim());
+
+ // Check for required columns
+ if (headers.length !== 2) {
+ throw new Error("CSV must have exactly 2 columns: marketName, score");
+ }
+
+ if (!headers.includes("marketName") || !headers.includes("score")) {
+ throw new Error("CSV must have columns: marketName, score");
+ }
+
+ const result: Record = {};
+
+ for (let i = 1; i < lines.length; i++) {
+ const line = lines[i].trim();
+ if (!line) continue; // Skip empty lines
+
+ const values = line.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g);
+
+ if (isUndefined(values) || values.length !== 2) {
+ throw new Error(
+ `Row ${i + 1}: Expected 2 columns, found ${values?.length}`,
+ );
+ }
+
+ // remove quotes
+ const marketName = values[0].replace(/^"|"$/g, "");
+ const scoreStr = values[1];
+ // Check for empty values
+ if (!marketName || !scoreStr) {
+ throw new Error(`Row ${i + 1}: All columns must have values`);
+ }
+ const market = marketFromName(marketName);
+
+ if (!market) {
+ throw new Error(`Row ${i + 1}: No market found named: ${marketName}`);
+ }
+
+ const marketId = market.marketId;
+
+ // Validate score
+ const score = parseFloat(scoreStr);
+ if (isNaN(score)) {
+ throw new Error(
+ `Row ${i + 1}: Score "${scoreStr}" is not a valid number`,
+ );
+ }
+
+ if (score < 0) {
+ throw new Error(`Row ${i + 1}: Score cannot be negative`);
+ }
+
+ result[marketId] = score;
+ }
+
+ if (Object.values(result).length === 0) {
+ throw new Error("CSV contains no valid data rows");
+ }
+
+ return result;
+};
+
+export function generateMarketCsv(markets: Record) {
+ const header = ["marketName", "score"];
+
+ const rows = Object.values(markets).map((market) => [
+ `"${market.name}"`,
+ market.prediction ?? market?.marketEstimate,
+ ]);
+
+ const csvContent = [header, ...rows]
+ .map((row) => row.map((v) => `${v}`).join(","))
+ .join("\n");
+
+ return csvContent;
+}
+
+export function downloadCsvFile(filename: string, content: string) {
+ const blob = new Blob([content], { type: "text/csv" });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = filename;
+ link.click();
+ URL.revokeObjectURL(url);
+}
diff --git a/src/utils/getQuotes.ts b/src/utils/getQuotes.ts
index 896f965..3fdd0c5 100644
--- a/src/utils/getQuotes.ts
+++ b/src/utils/getQuotes.ts
@@ -1,11 +1,13 @@
import { SwaprV3Trade } from "@swapr/sdk";
import { Address, formatUnits, parseUnits } from "viem";
+import { sDaiAddress, wxdaiAddress } from "@/generated";
+
import { ProcessedMarket } from "@/hooks/useProcessMarkets";
import { DECIMALS, DEFAULT_CHAIN, VOLUME_MIN } from "@/consts";
-import { getSwaprQuote } from "./swapr";
+import { getMinimumAmountOut, getSwaprQuote } from "./swapr";
import { minBigIntArray } from ".";
@@ -179,3 +181,41 @@ export const getQuotes = async ({
mergeAmount: collateralFromMerge,
};
};
+
+export const getSDaiToWXdaiData = async (account: Address, amount?: bigint) => {
+ if (!amount) return;
+ const quoteSDaiToWXDai = await getSwaprQuote({
+ address: account,
+ chain: DEFAULT_CHAIN.id,
+ outcomeToken: wxdaiAddress,
+ collateralToken: sDaiAddress,
+ amount: formatUnits(amount, DECIMALS),
+ }).catch((e) => {
+ throw e;
+ });
+
+ if (!quoteSDaiToWXDai) {
+ throw new Error("No route found for sDAI <> WXDAI");
+ }
+
+ const minWXDaiReceived = await getMinimumAmountOut(quoteSDaiToWXDai);
+ const quoteWXDaiToSDai = await getSwaprQuote({
+ address: account,
+ chain: DEFAULT_CHAIN.id,
+ outcomeToken: sDaiAddress,
+ collateralToken: wxdaiAddress,
+ amount: formatUnits(minWXDaiReceived, DECIMALS),
+ }).catch((e) => {
+ throw e;
+ });
+ if (!quoteWXDaiToSDai) {
+ throw new Error("No route found for WXDAI <> sDAI");
+ }
+ const minSDaiReceived = await getMinimumAmountOut(quoteWXDaiToSDai);
+
+ return {
+ quote: quoteSDaiToWXDai,
+ minSDaiReceived,
+ slippage: amount - minSDaiReceived,
+ };
+};
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 4923fff..87870a3 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -21,7 +21,7 @@ export const formatValue = (value: bigint, decimals = 18) => {
return "0";
} else {
const parsedValue = parseFloat(formattedValue);
- return parsedValue > 0.01 ? commify(parsedValue.toFixed(2)) : "<0.01";
+ return parsedValue >= 0.01 ? commify(parsedValue.toFixed(2)) : "<0.01";
}
};
diff --git a/src/utils/marketIdFromName.ts b/src/utils/marketIdFromName.ts
new file mode 100644
index 0000000..7abebb6
--- /dev/null
+++ b/src/utils/marketIdFromName.ts
@@ -0,0 +1,11 @@
+import { markets } from "@/consts/markets";
+
+const marketFromName = (name: string) => {
+ const processedName = name.trim().toLowerCase();
+
+ return markets.find(
+ (market) => market.name.trim().toLowerCase() === processedName,
+ );
+};
+
+export default marketFromName;
diff --git a/src/utils/parseCsvFile.ts b/src/utils/parseCsvFile.ts
deleted file mode 100644
index d90d801..0000000
--- a/src/utils/parseCsvFile.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { isAddress } from "viem";
-
-export const parseMarketCSV = (csvText: string): Record => {
- const lines = csvText.trim().split("\n");
-
- if (lines.length < 2) {
- throw new Error("CSV must have at least a header row and one data row");
- }
-
- const headers = lines[0].split(",").map((h) => h.trim());
-
- // Check for required columns
- if (headers.length !== 2) {
- throw new Error("CSV must have exactly 2 columns: marketId, score");
- }
-
- if (!headers.includes("marketId") || !headers.includes("score")) {
- throw new Error("CSV must have columns: marketId, score");
- }
-
- const result: Record = {};
-
- for (let i = 1; i < lines.length; i++) {
- const line = lines[i].trim();
- if (!line) continue; // Skip empty lines
-
- const values = line.split(",");
-
- if (values.length !== 2) {
- throw new Error(
- `Row ${i + 1}: Expected 2 columns, found ${values.length}`,
- );
- }
-
- const marketId = values[0];
- const scoreStr = values[1];
-
- // Check for empty values
- if (!marketId || !scoreStr) {
- throw new Error(`Row ${i + 1}: All columns must have values`);
- }
-
- // Validate score
- const score = parseFloat(scoreStr);
- if (isNaN(score)) {
- throw new Error(
- `Row ${i + 1}: Score "${scoreStr}" is not a valid number`,
- );
- }
-
- if (score < 0) {
- throw new Error(`Row ${i + 1}: Score cannot be negative`);
- }
-
- if (!isAddress(marketId)) {
- throw new Error(`Row ${i + 1}: marketId needs to be the market address`);
- }
- result[marketId.trim()] = score;
- }
-
- if (Object.values(result).length === 0) {
- throw new Error("CSV contains no valid data rows");
- }
-
- return result;
-};
diff --git a/src/utils/swapr.ts b/src/utils/swapr.ts
index 2080aae..657f173 100644
--- a/src/utils/swapr.ts
+++ b/src/utils/swapr.ts
@@ -4,12 +4,14 @@ import {
encodeAbiParameters,
getCreate2Address,
keccak256,
+ parseUnits,
type Address,
} from "viem";
import { gnosis } from "viem/chains";
import { getTradeArgs } from "./trade";
import { getTradeExactOutArgs } from "./tradeExactOut";
+import { DECIMALS } from "@/consts";
type GetQuoteArgs = {
address?: Address;
@@ -19,6 +21,10 @@ type GetQuoteArgs = {
collateralToken: string;
};
+export const getMinimumAmountOut = async (quote: SwaprV3Trade) => {
+ return parseUnits(quote.minimumAmountOut().toExact(), DECIMALS);
+};
+
export const getSwaprQuote = async ({
address,
chain = gnosis.id,
diff --git a/wagmi.config.ts b/wagmi.config.ts
index 611c0ab..bf71883 100644
--- a/wagmi.config.ts
+++ b/wagmi.config.ts
@@ -8,6 +8,9 @@ const gnosisRouter = getContractInfo("gnosisRouter");
const sDAI = getContractInfo("sDAI");
const sDAIAdapter = getContractInfo("sDAIAdapter");
const conditionalRouter = getContractInfo("conditionalRouter");
+const creditsManager = getContractInfo("seerCreditsManager");
+const seerCredits = getContractInfo("seerCredits");
+const WXDAI = getContractInfo("wrappedXDai");
export default defineConfig({
out: "src/generated.ts",
@@ -16,6 +19,9 @@ export default defineConfig({
{ ...sDAI },
{ ...sDAIAdapter },
{ ...conditionalRouter },
+ { ...creditsManager },
+ { ...seerCredits },
+ { ...WXDAI },
{ name: "ERC20", abi: erc20Abi },
],
plugins: [react()],
diff --git a/yarn.lock b/yarn.lock
index 7fbf599..c112f80 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3444,9 +3444,9 @@ __metadata:
languageName: node
linkType: hard
-"@kleros/ui-components-library@npm:^3.6.0":
- version: 3.6.0
- resolution: "@kleros/ui-components-library@npm:3.6.0"
+"@kleros/ui-components-library@npm:^3.7.0":
+ version: 3.7.0
+ resolution: "@kleros/ui-components-library@npm:3.7.0"
dependencies:
"@internationalized/date": "npm:^3.7.0"
bignumber.js: "npm:^9.1.2"
@@ -3467,7 +3467,7 @@ __metadata:
react-dom: ^18.0.0
react-is: ^18.0.0
tailwindcss: ^4.0.11
- checksum: 10c0/d76e481f3e72f76d40037a784ac65f97c411ce287fb7720aebb084f81032681b7f19cad0bd5269a719db148069dd18f17280083bd44938225e7e1dd3d51547d4
+ checksum: 10c0/25b41deb28a338d98f3740c62ed81def420af8b90e74135f2712bc2be5aebe7eb810878e61fe9b44341e6d9430cf3a5fc85b9bd040431f8c894358f66107e9c4
languageName: node
linkType: hard
@@ -10990,6 +10990,28 @@ __metadata:
languageName: node
linkType: hard
+"framer-motion@npm:^12.23.26":
+ version: 12.23.26
+ resolution: "framer-motion@npm:12.23.26"
+ dependencies:
+ motion-dom: "npm:^12.23.23"
+ motion-utils: "npm:^12.23.6"
+ tslib: "npm:^2.4.0"
+ peerDependencies:
+ "@emotion/is-prop-valid": "*"
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ "@emotion/is-prop-valid":
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ checksum: 10c0/7dbbc4a392b969804e04a056e3d89a55bf31572dc7f6cd79050b90616fbb84a1762e4ac4e2537c735d347bbf4ceebea126f5c090234149b12cffa3ea6c518a34
+ languageName: node
+ linkType: hard
+
"fs-minipass@npm:^3.0.0":
version: 3.0.3
resolution: "fs-minipass@npm:3.0.3"
@@ -11062,7 +11084,7 @@ __metadata:
"@graphql-codegen/typescript": "npm:^5.0.2"
"@graphql-codegen/typescript-graphql-request": "npm:^6.3.0"
"@graphql-codegen/typescript-operations": "npm:^5.0.2"
- "@kleros/ui-components-library": "npm:^3.6.0"
+ "@kleros/ui-components-library": "npm:^3.7.0"
"@reown/appkit": "npm:^1.7.11"
"@reown/appkit-adapter-wagmi": "npm:^1.7.11"
"@svgr/webpack": "npm:^8.1.0"
@@ -11082,6 +11104,7 @@ __metadata:
eslint-config-prettier: "npm:^10.1.2"
eslint-plugin-prettier: "npm:^5.2.6"
ethers: "npm:5.8.0"
+ framer-motion: "npm:^12.23.26"
graphql-request: "npm:^7.3.1"
graphql-tag: "npm:^2.12.6"
lightweight-charts: "npm:^5.0.8"
@@ -13403,6 +13426,22 @@ __metadata:
languageName: node
linkType: hard
+"motion-dom@npm:^12.23.23":
+ version: 12.23.23
+ resolution: "motion-dom@npm:12.23.23"
+ dependencies:
+ motion-utils: "npm:^12.23.6"
+ checksum: 10c0/139705731085063519b88f23fcc5b1c13e15707a4ff3365da02ef9a4bf2a2d8ebed9a151c57e7f215ccd9e822365d93c16e28e619fbf25611f61dcff5ee81d75
+ languageName: node
+ linkType: hard
+
+"motion-utils@npm:^12.23.6":
+ version: 12.23.6
+ resolution: "motion-utils@npm:12.23.6"
+ checksum: 10c0/c058e8ba6423b3baa63e985bcc669877ee7d9579d938f5348b4e60c5ea1b4b33dd7f4877434436a4a5807f3cf00370d3fd4079a6fdd6309c5c87aa62b311a897
+ languageName: node
+ linkType: hard
+
"ms@npm:^2.1.1, ms@npm:^2.1.3":
version: 2.1.3
resolution: "ms@npm:2.1.3"