diff --git a/front/src/common/types.ts b/front/src/common/types.ts
index 16ccc65..ad884aa 100644
--- a/front/src/common/types.ts
+++ b/front/src/common/types.ts
@@ -14,6 +14,9 @@ export enum TransactionType {
DEPLOY_BOT_INPUT,
DEPOSIT_ERC20,
APPROVE_ERC20,
+ MINT_ERC20,
+ MINT_LP_NFT,
+ DEPOSIT_LP_NFT,
BOT_STEP,
MANAGER_BOT_INPUT,
RELEASE_FUNDS,
@@ -145,6 +148,23 @@ export interface ApproveErc20TransactionInfo extends BaseTransactionInfo {
amount: string;
}
+export interface MintErc20TransactionInfo extends BaseTransactionInfo {
+ type: TransactionType.MINT_ERC20;
+ tokenAddress: string;
+ tokenAmount: string;
+}
+
+export interface MintLpNftTransactionInfo extends BaseTransactionInfo {
+ type: TransactionType.MINT_LP_NFT;
+ stableAddress: string;
+ stableAmount: string;
+}
+
+export interface DepositLpNftTransactionInfo extends BaseTransactionInfo {
+ type: TransactionType.DEPOSIT_LP_NFT;
+ nftId: string;
+}
+
export interface BetTransactionInfo extends BaseTransactionInfo {
type: TransactionType.BET_INPUT;
gameId: string;
@@ -194,6 +214,9 @@ export type TransactionInfo =
| DeployBotTransactionInfo
| DepositErc20TransactionInfo
| ApproveErc20TransactionInfo
+ | MintErc20TransactionInfo
+ | MintLpNftTransactionInfo
+ | DepositLpNftTransactionInfo
| ResignGameTransactionInfo
| BotStepTransactionInfo
| ManagerBotTransactionInfo
diff --git a/front/src/components/BotManager.jsx b/front/src/components/BotManager.jsx
index a1aa9ee..46e816a 100644
--- a/front/src/components/BotManager.jsx
+++ b/front/src/components/BotManager.jsx
@@ -6,11 +6,17 @@
* See the file LICENSE for more information.
*/
+import { Spacer } from "@nextui-org/react";
import BotListView from "./list/BotList";
import { useAllBots } from "../state/game/hooks";
+import { useLpNfts } from "../state/game/hooks";
import { Text } from "./ui/Text";
import { styled } from "@stitches/react";
import ModalCreateBot from "./modals/ModalCreateBot";
+import ModalMintStables from "./modals/ModalMintStables";
+import ModalMintLpNft from "./modals/ModalMintLpNft";
+import ModalDepositLpNft from "./modals/ModalDepositLpNft";
+import LpNftListView from "./list/LpNftList";
import Separator from "./ui/Separator";
import { StitchesLogoIcon } from "@radix-ui/react-icons";
import { violet } from "@radix-ui/colors";
@@ -24,6 +30,7 @@ import { Spacer } from "@nextui-org/react";
export default () => {
const bots = useAllBots();
const dispatch = useDispatch();
+ const lpNfts = useLpNfts();
return (
@@ -75,6 +82,39 @@ export default () => {
Deploy bot
+
+
+
+
+
+
+
+ Mint stables
+
+ }
+ />
+
+
+ Mint LP NFT
+
+ }
+ />
+
+
+ Deposit LP NFT
+
+ }
+ />
+
+
+
+
diff --git a/front/src/components/modals/ModalDepositLpNft.tsx b/front/src/components/modals/ModalDepositLpNft.tsx
new file mode 100644
index 0000000..2e01f28
--- /dev/null
+++ b/front/src/components/modals/ModalDepositLpNft.tsx
@@ -0,0 +1,217 @@
+import { blackA, green, mauve, violet } from "@radix-ui/colors";
+import * as Dialog from "@radix-ui/react-dialog";
+import { Cross2Icon } from "@radix-ui/react-icons";
+import { keyframes, styled } from "@stitches/react";
+import { useWeb3React } from "@web3-react/core";
+import { useState } from "react";
+
+import { TransactionType } from "../../common/types";
+import { USDC_ADDRESS_ON_NETWORKS } from "../../ether/chains";
+import { useTokenFromList } from "../../hooks/token";
+import { useActionCreator } from "../../state/game/hooks";
+import AssetDisplay from "../AssetDisplay";
+
+export default ({ triggerElement }) => {
+ const { chainId } = useWeb3React();
+ const [tokenId, setTokenId] = useState("");
+ const token = useTokenFromList(USDC_ADDRESS_ON_NETWORKS[chainId]);
+
+ const addAction = useActionCreator();
+
+ const handleMint = async () => {
+ console.log(`Depositing token ID ${tokenId}`);
+ const [, wait] = await addAction({
+ type: TransactionType.DEPOSIT_LP_NFT,
+ nftId: tokenId,
+ });
+ await wait;
+ };
+
+ return (
+
+ {triggerElement}
+
+
+
+ Deposit LP NFT
+ Deposit LP NFT for testing.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const overlayShow = keyframes({
+ "0%": { opacity: 0 },
+ "100%": { opacity: 1 },
+});
+
+const contentShow = keyframes({
+ "0%": { opacity: 0, transform: "translate(-50%, -48%) scale(.96)" },
+ "100%": { opacity: 1, transform: "translate(-50%, -50%) scale(1)" },
+});
+
+const DialogOverlay = styled(Dialog.Overlay, {
+ backgroundColor: blackA.blackA9,
+ position: "fixed",
+ inset: 0,
+ animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
+});
+
+const DialogContent = styled(Dialog.Content, {
+ backgroundColor: "white",
+ borderRadius: 6,
+ boxShadow:
+ "hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px",
+ position: "fixed",
+ top: "50%",
+ left: "50%",
+ transform: "translate(-50%, -50%)",
+ width: "90vw",
+ maxWidth: "450px",
+ maxHeight: "85vh",
+ padding: 25,
+ animation: `${contentShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
+ "&:focus": { outline: "none" },
+});
+
+const DialogTitle = styled(Dialog.Title, {
+ margin: 0,
+ fontWeight: 500,
+ color: mauve.mauve12,
+ fontSize: 17,
+});
+
+const DialogDescription = styled(Dialog.Description, {
+ margin: "10px 0 20px",
+ color: mauve.mauve11,
+ fontSize: 15,
+ lineHeight: 1.5,
+});
+
+const RightSlot = styled("div", {
+ marginLeft: "auto",
+ paddingLeft: 0,
+ display: "flex",
+ color: violet.violet11,
+ "[data-highlighted] > &": { color: "white" },
+ "[data-disabled] &": { color: violet.violet4 },
+});
+
+const Flex = styled("div", { display: "flex" });
+
+const Button = styled("button", {
+ all: "unset",
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ borderRadius: 4,
+ padding: "0 15px",
+ fontSize: 15,
+ lineHeight: 1,
+ fontWeight: 500,
+ height: 35,
+
+ variants: {
+ variant: {
+ violet: {
+ backgroundColor: "white",
+ color: violet.violet11,
+ boxShadow: `0 2px 10px ${blackA.blackA7}`,
+ "&:hover": { backgroundColor: mauve.mauve3 },
+ "&:focus": { boxShadow: `0 0 0 2px black` },
+ },
+ green: {
+ backgroundColor: green.green4,
+ color: green.green11,
+ "&:hover": { backgroundColor: green.green5 },
+ "&:focus": { boxShadow: `0 0 0 2px ${green.green7}` },
+ },
+ },
+ },
+
+ defaultVariants: {
+ variant: "violet",
+ },
+});
+
+const IconButton = styled("button", {
+ all: "unset",
+ fontFamily: "inherit",
+ borderRadius: "100%",
+ height: 25,
+ width: 25,
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ color: violet.violet11,
+ position: "absolute",
+ top: 10,
+ right: 10,
+
+ "&:hover": { backgroundColor: violet.violet4 },
+ "&:focus": { boxShadow: `0 0 0 2px ${violet.violet7}` },
+});
+
+const Fieldset = styled("fieldset", {
+ all: "unset",
+ display: "flex",
+ gap: 20,
+ alignItems: "center",
+ marginBottom: 15,
+});
+
+const Label = styled("label", {
+ fontSize: 13,
+ lineHeight: 1,
+ marginBottom: 10,
+ color: violet.violet12,
+ display: "block",
+});
+
+const Input = styled("input", {
+ all: "unset",
+ width: "100%",
+ flex: "1",
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ borderRadius: 4,
+ padding: "0 10px",
+ fontSize: 15,
+ lineHeight: 1,
+ color: violet.violet11,
+ boxShadow: `0 0 0 1px ${violet.violet7}`,
+ height: 35,
+
+ "&:focus": { boxShadow: `0 0 0 2px ${violet.violet8}` },
+});
diff --git a/front/src/components/modals/ModalMintLpNft.tsx b/front/src/components/modals/ModalMintLpNft.tsx
new file mode 100644
index 0000000..1e219cc
--- /dev/null
+++ b/front/src/components/modals/ModalMintLpNft.tsx
@@ -0,0 +1,246 @@
+import { blackA, green, mauve, violet } from "@radix-ui/colors";
+import * as Dialog from "@radix-ui/react-dialog";
+import { Cross2Icon } from "@radix-ui/react-icons";
+import { keyframes, styled } from "@stitches/react";
+import { useWeb3React } from "@web3-react/core";
+import { useState } from "react";
+
+import { TransactionType } from "../../common/types";
+import { CHAINS } from "../../ether/chains";
+import { USDC_ADDRESS_ON_NETWORKS } from "../../ether/chains";
+import { CONTRACTS } from "../../ether/contracts";
+import { useTokenFromList } from "../../hooks/token";
+import { useActionCreator } from "../../state/game/hooks";
+import AssetDisplay from "./../AssetDisplay";
+
+export default ({ triggerElement }) => {
+ const { chainId, account } = useWeb3React();
+ const [amount, setAmount] = useState(0);
+ const token = useTokenFromList(USDC_ADDRESS_ON_NETWORKS[chainId]);
+
+ const addAction = useActionCreator();
+
+ const handleDeposit = async () => {
+ console.log(`Approving amount ${amount} tokenAddress ${token.address}`);
+
+ // Get network name
+ const CHAIN = CHAINS[chainId];
+ const networkName =
+ CHAIN && CHAIN.networkName ? CHAIN.networkName : "localhost";
+
+ // Fetch abi list
+ const contracts = CONTRACTS[networkName];
+ const abis =
+ contracts && contracts.InputFacet && contracts.ERC20PortalFacet
+ ? contracts
+ : CONTRACTS.localhost;
+
+ const [, wait] = await addAction({
+ type: TransactionType.APPROVE_ERC20,
+ tokenAddress: token.address,
+ spender: abis.UniV3Staker.address,
+ amount: amount.toString(),
+ });
+ await wait;
+
+ console.log(
+ `Minting LP NFT with amount ${amount} tokenAddress ${token.address}`
+ );
+ const [, wait2] = await addAction({
+ type: TransactionType.MINT_LP_NFT,
+ stableAddress: token.address,
+ stableAmount: amount.toString(),
+ });
+ await wait2;
+ };
+
+ return (
+
+ {triggerElement}
+
+
+
+ Mint LP NFT
+
+ Mint an LP NFT and deposit stablecoins.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const overlayShow = keyframes({
+ "0%": { opacity: 0 },
+ "100%": { opacity: 1 },
+});
+
+const contentShow = keyframes({
+ "0%": { opacity: 0, transform: "translate(-50%, -48%) scale(.96)" },
+ "100%": { opacity: 1, transform: "translate(-50%, -50%) scale(1)" },
+});
+
+const DialogOverlay = styled(Dialog.Overlay, {
+ backgroundColor: blackA.blackA9,
+ position: "fixed",
+ inset: 0,
+ animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
+});
+
+const DialogContent = styled(Dialog.Content, {
+ backgroundColor: "white",
+ borderRadius: 6,
+ boxShadow:
+ "hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px",
+ position: "fixed",
+ top: "50%",
+ left: "50%",
+ transform: "translate(-50%, -50%)",
+ width: "90vw",
+ maxWidth: "450px",
+ maxHeight: "85vh",
+ padding: 25,
+ animation: `${contentShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
+ "&:focus": { outline: "none" },
+});
+
+const DialogTitle = styled(Dialog.Title, {
+ margin: 0,
+ fontWeight: 500,
+ color: mauve.mauve12,
+ fontSize: 17,
+});
+
+const DialogDescription = styled(Dialog.Description, {
+ margin: "10px 0 20px",
+ color: mauve.mauve11,
+ fontSize: 15,
+ lineHeight: 1.5,
+});
+
+const RightSlot = styled("div", {
+ marginLeft: "auto",
+ paddingLeft: 0,
+ display: "flex",
+ color: violet.violet11,
+ "[data-highlighted] > &": { color: "white" },
+ "[data-disabled] &": { color: violet.violet4 },
+});
+
+const Flex = styled("div", { display: "flex" });
+
+const Button = styled("button", {
+ all: "unset",
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ borderRadius: 4,
+ padding: "0 15px",
+ fontSize: 15,
+ lineHeight: 1,
+ fontWeight: 500,
+ height: 35,
+
+ variants: {
+ variant: {
+ violet: {
+ backgroundColor: "white",
+ color: violet.violet11,
+ boxShadow: `0 2px 10px ${blackA.blackA7}`,
+ "&:hover": { backgroundColor: mauve.mauve3 },
+ "&:focus": { boxShadow: `0 0 0 2px black` },
+ },
+ green: {
+ backgroundColor: green.green4,
+ color: green.green11,
+ "&:hover": { backgroundColor: green.green5 },
+ "&:focus": { boxShadow: `0 0 0 2px ${green.green7}` },
+ },
+ },
+ },
+
+ defaultVariants: {
+ variant: "violet",
+ },
+});
+
+const IconButton = styled("button", {
+ all: "unset",
+ fontFamily: "inherit",
+ borderRadius: "100%",
+ height: 25,
+ width: 25,
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ color: violet.violet11,
+ position: "absolute",
+ top: 10,
+ right: 10,
+
+ "&:hover": { backgroundColor: violet.violet4 },
+ "&:focus": { boxShadow: `0 0 0 2px ${violet.violet7}` },
+});
+
+const Fieldset = styled("fieldset", {
+ all: "unset",
+ display: "flex",
+ gap: 20,
+ alignItems: "center",
+ marginBottom: 15,
+});
+
+const Label = styled("label", {
+ fontSize: 13,
+ lineHeight: 1,
+ marginBottom: 10,
+ color: violet.violet12,
+ display: "block",
+});
+
+const Input = styled("input", {
+ all: "unset",
+ width: "100%",
+ flex: "1",
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ borderRadius: 4,
+ padding: "0 10px",
+ fontSize: 15,
+ lineHeight: 1,
+ color: violet.violet11,
+ boxShadow: `0 0 0 1px ${violet.violet7}`,
+ height: 35,
+
+ "&:focus": { boxShadow: `0 0 0 2px ${violet.violet8}` },
+});
diff --git a/front/src/components/modals/ModalMintStables.tsx b/front/src/components/modals/ModalMintStables.tsx
new file mode 100644
index 0000000..aa1bee6
--- /dev/null
+++ b/front/src/components/modals/ModalMintStables.tsx
@@ -0,0 +1,218 @@
+import { blackA, green, mauve, violet } from "@radix-ui/colors";
+import * as Dialog from "@radix-ui/react-dialog";
+import { Cross2Icon } from "@radix-ui/react-icons";
+import { keyframes, styled } from "@stitches/react";
+import { useWeb3React } from "@web3-react/core";
+import { useState } from "react";
+
+import { TransactionType } from "../../common/types";
+import { USDC_ADDRESS_ON_NETWORKS } from "../../ether/chains";
+import { useTokenFromList } from "../../hooks/token";
+import { useActionCreator } from "../../state/game/hooks";
+import AssetDisplay from "./../AssetDisplay";
+
+export default ({ triggerElement }) => {
+ const { chainId } = useWeb3React();
+ const [amount, setAmount] = useState(0);
+ const token = useTokenFromList(USDC_ADDRESS_ON_NETWORKS[chainId]);
+
+ const addAction = useActionCreator();
+
+ const handleMint = async () => {
+ console.log(`minting amount ${amount} tokenAddress ${token.address}`);
+ const [, wait] = await addAction({
+ type: TransactionType.MINT_ERC20,
+ tokenAddress: token.address,
+ tokenAmount: amount.toString(),
+ });
+ await wait;
+ };
+
+ return (
+
+ {triggerElement}
+
+
+
+ Mint stablecoins
+ Mint stablecoins for testing.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const overlayShow = keyframes({
+ "0%": { opacity: 0 },
+ "100%": { opacity: 1 },
+});
+
+const contentShow = keyframes({
+ "0%": { opacity: 0, transform: "translate(-50%, -48%) scale(.96)" },
+ "100%": { opacity: 1, transform: "translate(-50%, -50%) scale(1)" },
+});
+
+const DialogOverlay = styled(Dialog.Overlay, {
+ backgroundColor: blackA.blackA9,
+ position: "fixed",
+ inset: 0,
+ animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
+});
+
+const DialogContent = styled(Dialog.Content, {
+ backgroundColor: "white",
+ borderRadius: 6,
+ boxShadow:
+ "hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px",
+ position: "fixed",
+ top: "50%",
+ left: "50%",
+ transform: "translate(-50%, -50%)",
+ width: "90vw",
+ maxWidth: "450px",
+ maxHeight: "85vh",
+ padding: 25,
+ animation: `${contentShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
+ "&:focus": { outline: "none" },
+});
+
+const DialogTitle = styled(Dialog.Title, {
+ margin: 0,
+ fontWeight: 500,
+ color: mauve.mauve12,
+ fontSize: 17,
+});
+
+const DialogDescription = styled(Dialog.Description, {
+ margin: "10px 0 20px",
+ color: mauve.mauve11,
+ fontSize: 15,
+ lineHeight: 1.5,
+});
+
+const RightSlot = styled("div", {
+ marginLeft: "auto",
+ paddingLeft: 0,
+ display: "flex",
+ color: violet.violet11,
+ "[data-highlighted] > &": { color: "white" },
+ "[data-disabled] &": { color: violet.violet4 },
+});
+
+const Flex = styled("div", { display: "flex" });
+
+const Button = styled("button", {
+ all: "unset",
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ borderRadius: 4,
+ padding: "0 15px",
+ fontSize: 15,
+ lineHeight: 1,
+ fontWeight: 500,
+ height: 35,
+
+ variants: {
+ variant: {
+ violet: {
+ backgroundColor: "white",
+ color: violet.violet11,
+ boxShadow: `0 2px 10px ${blackA.blackA7}`,
+ "&:hover": { backgroundColor: mauve.mauve3 },
+ "&:focus": { boxShadow: `0 0 0 2px black` },
+ },
+ green: {
+ backgroundColor: green.green4,
+ color: green.green11,
+ "&:hover": { backgroundColor: green.green5 },
+ "&:focus": { boxShadow: `0 0 0 2px ${green.green7}` },
+ },
+ },
+ },
+
+ defaultVariants: {
+ variant: "violet",
+ },
+});
+
+const IconButton = styled("button", {
+ all: "unset",
+ fontFamily: "inherit",
+ borderRadius: "100%",
+ height: 25,
+ width: 25,
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ color: violet.violet11,
+ position: "absolute",
+ top: 10,
+ right: 10,
+
+ "&:hover": { backgroundColor: violet.violet4 },
+ "&:focus": { boxShadow: `0 0 0 2px ${violet.violet7}` },
+});
+
+const Fieldset = styled("fieldset", {
+ all: "unset",
+ display: "flex",
+ gap: 20,
+ alignItems: "center",
+ marginBottom: 15,
+});
+
+const Label = styled("label", {
+ fontSize: 13,
+ lineHeight: 1,
+ marginBottom: 10,
+ color: violet.violet12,
+ display: "block",
+});
+
+const Input = styled("input", {
+ all: "unset",
+ width: "100%",
+ flex: "1",
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ borderRadius: 4,
+ padding: "0 10px",
+ fontSize: 15,
+ lineHeight: 1,
+ color: violet.violet11,
+ boxShadow: `0 0 0 1px ${violet.violet7}`,
+ height: 35,
+
+ "&:focus": { boxShadow: `0 0 0 2px ${violet.violet8}` },
+});
diff --git a/front/src/ether/chains.js b/front/src/ether/chains.js
index a32c3ac..28289d2 100644
--- a/front/src/ether/chains.js
+++ b/front/src/ether/chains.js
@@ -208,8 +208,8 @@ export const STABLECOIN_ADDRESS_ON_NETWORKS = {
421611: "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8",
5: "0x50b5a3f1cce4a5936e373e6828afdb073c79c1c7",
420: "0x17c8ad02b6eba2dd5aac672394588f8abd432513",
- 31337: CartesiToken.address,
- 1337: CartesiToken.address,
+ 31337: "0xea753456f554F59f70CD0E078FBd2FED058Cedcc",
+ 1337: "0xea753456f554F59f70CD0E078FBd2FED058Cedcc",
};
export const DEFAULT_NETWORK_URI = CHAINS[1].networkImg;
diff --git a/front/src/state/game/gameSlice.js b/front/src/state/game/gameSlice.js
index 6acbe69..5d59550 100644
--- a/front/src/state/game/gameSlice.js
+++ b/front/src/state/game/gameSlice.js
@@ -107,6 +107,7 @@ export const gameSlice = createSlice({
},
games: {},
bots: {},
+ lpNfts: {},
elo: {},
accounts: {},
tournaments: [],
@@ -154,11 +155,15 @@ export const gameSlice = createSlice({
setBots: (state, action) => {
state.bots = action.payload;
},
+ setLpNfts: (state, action) => {
+ state.lpNfts = action.payload;
+ },
setAppState: (state, action) => {
var {
elo,
game,
bots,
+ lpNfts,
accounts,
lastProcessedBlock,
actionList,
@@ -173,6 +178,7 @@ export const gameSlice = createSlice({
!deepEqual(state.elo, elo) ||
!deepEqual(state.games, game) ||
!deepEqual(state.bots, bots) ||
+ !deepEqual(state.lpNfts, lpNfts) ||
!deepEqual(state.accounts, accounts) ||
!deepEqual(state.tournaments, tournaments) ||
!deepEqual(state.challenges, challenges) ||
@@ -186,6 +192,7 @@ export const gameSlice = createSlice({
state.elo = elo;
state.games = game;
state.bots = bots;
+ state.lpNfts = lpNfts;
state.accounts = accounts;
state.tournaments = tournaments;
state.lastProcessedBlock = lastProcessedBlock;
@@ -297,6 +304,7 @@ export const {
setElo,
setAccounts,
setBots,
+ setLpNfts,
updateGame,
addGame,
removeGame,
diff --git a/front/src/state/game/hooks.ts b/front/src/state/game/hooks.ts
index 69e3051..077d751 100644
--- a/front/src/state/game/hooks.ts
+++ b/front/src/state/game/hooks.ts
@@ -9,7 +9,7 @@
import { TransactionResponse } from "@ethersproject/providers";
import { useWeb3React } from "@web3-react/core";
import { ethers } from "ethers";
-import { useCallback, useMemo } from "react";
+import { useCallback, useEffect, useMemo, useState } from "react";
import { useDispatch } from "react-redux";
import { TransactionInfo, TransactionType } from "../../common/types";
@@ -44,6 +44,7 @@ import {
BotProfile,
Challenge,
Game,
+ LpNftProfile,
Profile,
ProfileType,
Throne,
@@ -308,6 +309,64 @@ export function useAllBots(showRank = false): BotProfile[] {
});
}
+// Get all LP NFTs belonging to the user
+export function useLpNfts(): LpNftProfile[] {
+ const { chainId, account } = useWeb3React();
+
+ const [lpNfts, setLpNfts] = useState([]);
+
+ // Get network name
+ const CHAIN = CHAINS[chainId];
+ const networkName =
+ CHAIN && CHAIN.networkName ? CHAIN.networkName : "localhost";
+
+ // Fetch abi list
+ const contracts = CONTRACTS[networkName];
+ const abis =
+ contracts && contracts.InputFacet && contracts.ERC20PortalFacet
+ ? contracts
+ : CONTRACTS.localhost;
+
+ const lpSftContract = useContract(abis.LpSft.address, abis.LpSft.abi);
+
+ useEffect(() => {
+ const callFetch = async () => {
+ if (!lpSftContract) return;
+
+ const userLpNfts = [];
+
+ const tokenIds = await lpSftContract.getTokenIds(account);
+ for (const tokenId of tokenIds) {
+ const tokenUri = await lpSftContract.uri(tokenId);
+
+ // Decode the token URI
+ const tokenUriResponse = await fetch(tokenUri);
+ const tokenMetadata = await tokenUriResponse.json();
+
+ const tokenName = tokenMetadata.name;
+ const tokenDescription = tokenMetadata.description;
+ const tokenImageUri = tokenMetadata.image;
+
+ const lpNft = {
+ address: lpSftContract.address,
+ chainId: chainId,
+ tokenId: tokenId,
+ name: tokenName,
+ description: tokenDescription,
+ imageUri: tokenImageUri,
+ };
+ userLpNfts.push(lpNft);
+ }
+
+ setLpNfts(userLpNfts);
+ };
+
+ callFetch().catch(console.error);
+ }, [chainId, lpSftContract]);
+
+ return lpNfts;
+}
+
//get all user profiles
//similar to useAllBots
export function useAllUsers(showRank = false): UserProfile[] {
@@ -711,12 +770,19 @@ export function useActionCreator(): (
const dispatch = useDispatch();
const addAction = useAddAction();
const addTransaction = useTransactionAdder();
- // TODO: Handle DAPP_ADDRESSES[networkName] or CONTRACTS[networkName] not defined
+ // TODO: Handle dappAddress or abis not defined
const contract = useContract(dappAddress, abis.InputFacet.abi);
const erc20PortalContract = useContract(
dappAddress,
abis.ERC20PortalFacet.abi
);
+ const uniV3StakerContract = useContract(
+ abis.UniV3Staker.address,
+ abis.UniV3Staker.abi
+ );
+ const daiContract = useContract(abis.DAI.address, abis.DAI.abi);
+ const usdcContract = useContract(abis.USDC.address, abis.USDC.abi);
+ const usdtContract = useContract(abis.USDT.address, abis.USDT.abi);
return useCallback(
async (info: TransactionInfo) => {
@@ -730,7 +796,9 @@ export function useActionCreator(): (
id: id,
type:
info.type == TransactionType.APPROVE_ERC20 ||
- info.type == TransactionType.DEPOSIT_ERC20
+ info.type == TransactionType.DEPOSIT_ERC20 ||
+ info.type == TransactionType.MINT_ERC20 ||
+ info.type == TransactionType.MINT_LP_NFT
? ActionType.TRANSACTION
: ActionType.INPUT,
transactionInfo: info,
@@ -1007,6 +1075,73 @@ export function useActionCreator(): (
erc20Amount
);
break;
+ case TransactionType.MINT_ERC20: {
+ const { tokenAddress, tokenAmount } = info;
+
+ // Get the stable contract
+ let stableContract;
+ if (tokenAddress.toLowerCase() == daiContract.address.toLowerCase())
+ stableContract = daiContract;
+ else if (
+ tokenAddress.toLowerCase() == usdcContract.address.toLowerCase()
+ )
+ stableContract = usdcContract;
+ else if (
+ tokenAddress.toLowerCase() == usdtContract.address.toLowerCase()
+ )
+ stableContract = usdtContract;
+
+ // TODO: More efficient decimal handling
+ const decimals = await stableContract.decimals();
+
+ // Calculate ERC20 amount
+ var erc20Amount = ethers.BigNumber.from(
+ ethers.utils.parseUnits(tokenAmount, decimals)
+ );
+
+ // Mint tokens
+ result = await stableContract.mint(
+ await stableContract.signer.getAddress(),
+ erc20Amount
+ );
+
+ break;
+ }
+ case TransactionType.MINT_LP_NFT: {
+ const { stableAddress, stableAmount } = info;
+
+ // Get the stable index
+ let stableIndex;
+ if (
+ stableAddress.toLowerCase() == daiContract.address.toLowerCase()
+ )
+ stableIndex = 0;
+ else if (
+ stableAddress.toLowerCase() == usdcContract.address.toLowerCase()
+ )
+ stableIndex = 1;
+ else if (
+ stableAddress.toLowerCase() == usdtContract.address.toLowerCase()
+ )
+ stableIndex = 2;
+
+ // Mint the LP NFT
+ result = await uniV3StakerContract.stakeNFTOneStable(
+ stableIndex,
+ stableAmount
+ );
+
+ break;
+ }
+ case TransactionType.DEPOSIT_LP_NFT: {
+ const { nftId } = info;
+
+ alert(`Depositing token ${nftId}`);
+
+ //result = await uniV3StakerContract.stakeNFTOneToken(stableIndex, stableAmount);
+
+ break;
+ }
default:
break;
}
@@ -1032,7 +1167,9 @@ export function useActionCreator(): (
id: id,
type:
info.type == TransactionType.APPROVE_ERC20 ||
- info.type == TransactionType.DEPOSIT_ERC20
+ info.type == TransactionType.DEPOSIT_ERC20 ||
+ info.type == TransactionType.MINT_ERC20 ||
+ info.type == TransactionType.MINT_LP_NFT
? ActionType.TRANSACTION
: ActionType.INPUT,
transactionInfo: info,
diff --git a/front/src/state/game/types.ts b/front/src/state/game/types.ts
index aeb66f3..c9f8f85 100644
--- a/front/src/state/game/types.ts
+++ b/front/src/state/game/types.ts
@@ -141,6 +141,15 @@ export interface Balance {
amount: number;
}
+export interface LpNftProfile {
+ address: string;
+ chainId: number;
+ tokenId: string;
+ name: string;
+ description: string;
+ imageUri: string;
+}
+
export enum ProfileType {
HUMAN,
BOT,
diff --git a/front/src/utils/lists/ultrachess.tokenlists.json b/front/src/utils/lists/ultrachess.tokenlists.json
index 59e5de3..b31eaa5 100644
--- a/front/src/utils/lists/ultrachess.tokenlists.json
+++ b/front/src/utils/lists/ultrachess.tokenlists.json
@@ -49,7 +49,7 @@
},
{
"name": "USDC",
- "address": "0x7f8c8f5f1eefecb2bcbda3c2e183df9945e00e9d",
+ "address": "0xea753456f554F59f70CD0E078FBd2FED058Cedcc",
"symbol": "USDC",
"decimals": 6,
"chainId": 31337,