diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 472092c9..a791487b 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -5,13 +5,12 @@ name: Node.js CI on: push: - branches: [ develop ] + branches: [develop] pull_request: - branches: [ master, develop ] + branches: [master, develop] jobs: build: - runs-on: ubuntu-latest strategy: @@ -20,19 +19,20 @@ jobs: # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 - with: - node-version: ${{ matrix.node-version }} - cache: 'yarn' - - run: yarn install - - run: yarn workspace @tender/shared build - - run: yarn workspace @tender/shared lint - - run: yarn workspace @tender/shared pretty - - run: yarn workspace @tender/app build - - run: yarn workspace @tender/app lint - - run: yarn workspace @tender/app pretty - - run: yarn workspace @tender/landing build - - run: yarn workspace @tender/landing lint - - run: yarn workspace @tender/landing pretty + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + cache: "yarn" + - run: yarn install + - run: yarn workspace @tender/shared build + - run: yarn workspace @tender/shared lint + - run: yarn workspace @tender/shared pretty + - run: yarn workspace @tender/app build + - run: yarn workspace @tender/app test + - run: yarn workspace @tender/app lint + - run: yarn workspace @tender/app pretty + - run: yarn workspace @tender/landing build + - run: yarn workspace @tender/landing lint + - run: yarn workspace @tender/landing pretty diff --git a/.gitignore b/.gitignore index df5db0f4..bbc214dc 100644 --- a/.gitignore +++ b/.gitignore @@ -26,5 +26,6 @@ yarn-error.log* # subgraph packages/subgraph/build/ +packages/subgraph/data/ packages/subgraph/src/types/ packages/subgraph/subgraph.yaml diff --git a/packages/app/jest.config.js b/packages/app/jest.config.js new file mode 100644 index 00000000..a169f217 --- /dev/null +++ b/packages/app/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + preset: "ts-jest", + transform: { "^.+\\.ts?$": "ts-jest" }, + testEnvironment: "node", + testRegex: "/test/.*\\.(test|spec)?\\.(ts|tsx)$", + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], + automock: false, + setupFiles: ["./setupJest.js"], +}; diff --git a/packages/app/package.json b/packages/app/package.json index b652951b..bc425385 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -7,6 +7,7 @@ "startprod": "next start", "start": "next dev", "lint": "next lint", + "test": "yarn jest", "eject": "react-scripts eject", "pretty": "npx prettier --check 'src/**/*.{ts,tsx,js,jsx}'", "format": "npx prettier --write 'src/**/*.{ts,tsx,js,jsx}'" @@ -19,7 +20,7 @@ "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", - "@types/jest": "^26.0.15", + "@types/jest": "^29.2.5", "@types/lru-cache": "^5.1.1", "@types/node": "^12.0.0", "@types/react": "^17.0.0", @@ -35,6 +36,7 @@ "framer-motion": "^4.1.17", "grommet": "^2.19.1", "grommet-icons": "^4.7.0", + "jest": "^29.3.1", "lru-cache": "^6.0.0", "next": "12.0.8", "polished": "^4.1.3", @@ -44,6 +46,7 @@ "react-scripts": "4.0.3", "styled-components": "^5.3.3", "swr": "^1.0.1", + "ts-jest": "^29.0.3", "typescript": "^4.6.4", "utf-8-validate": "^5.0.8", "web-vitals": "^1.0.1" diff --git a/packages/app/setupJest.js b/packages/app/setupJest.js new file mode 100644 index 00000000..e69de29b diff --git a/packages/app/src/components/BrandedALink.tsx b/packages/app/src/components/BrandedALink.tsx new file mode 100644 index 00000000..1ba4426f --- /dev/null +++ b/packages/app/src/components/BrandedALink.tsx @@ -0,0 +1,8 @@ +import { theme } from "@tender/shared/src"; +import { normalizeColor } from "grommet/utils"; +import styled from "styled-components"; + +const brandColor = normalizeColor("brand", theme); +export const BrandedALink = styled.a` + color: ${brandColor}; +`; diff --git a/packages/app/src/components/ChangeChainWarning.tsx b/packages/app/src/components/ChangeChainWarning.tsx new file mode 100644 index 00000000..5bb4cc9d --- /dev/null +++ b/packages/app/src/components/ChangeChainWarning.tsx @@ -0,0 +1,28 @@ +import { FC } from "react"; +import { stakers } from "@tender/shared/src/index"; +import { useIsCorrectChain } from "utils/useEnsureRinkebyConnect"; +import { SwitchNetwork } from "components/account/SwitchNetwork"; +import { ProtocolName } from "@tender/shared/src/data/stakers"; +import { Box } from "grommet"; +import { useEthers } from "@usedapp/core"; + +const ChangeChainWarning: FC<{ protocolName: ProtocolName }> = ({ children, protocolName }) => { + const { account } = useEthers(); + + const requiredChain = stakers[protocolName].chainId; + const isCorrectChain = useIsCorrectChain(requiredChain); + + return ( + <> + {!isCorrectChain && account ? ( + + + + ) : ( + <>{children} + )} + + ); +}; + +export default ChangeChainWarning; diff --git a/packages/app/src/components/account/AccountModal.tsx b/packages/app/src/components/account/AccountModal.tsx index 9fe6a461..5b4876ff 100644 --- a/packages/app/src/components/account/AccountModal.tsx +++ b/packages/app/src/components/account/AccountModal.tsx @@ -3,8 +3,7 @@ import styled from "styled-components"; import { useEthers, useEtherBalance, getChainById, useTokenBalance } from "@usedapp/core"; import { addresses } from "@tender/contracts/src/index"; import { TransactionsList } from "../transactions"; -import { formatEther } from "@ethersproject/units"; -import { BigNumber, constants } from "ethers"; +import { constants } from "ethers"; import { ShareIcon } from "../transactions/Icons"; import { Link } from "../base"; import { @@ -29,14 +28,7 @@ import { import { Staker, stakers } from "@tender/shared/src/index"; import { AddToken } from "./AddToken"; import { FormClose } from "grommet-icons"; - -const formatter = new Intl.NumberFormat("en-us", { - minimumFractionDigits: 4, - maximumFractionDigits: 4, -}); - -const formatBalance = (balance: BigNumber | undefined) => - formatter.format(parseFloat(formatEther(balance ?? BigNumber.from("0")))); +import { formatBalance } from "components/formatting"; type AccountModalProps = { showModal: boolean; diff --git a/packages/app/src/components/deposit/ConfirmDepositModal.tsx b/packages/app/src/components/deposit/ConfirmDepositModal.tsx index 876cdaeb..0c7f1f13 100644 --- a/packages/app/src/components/deposit/ConfirmDepositModal.tsx +++ b/packages/app/src/components/deposit/ConfirmDepositModal.tsx @@ -17,7 +17,7 @@ import { } from "grommet"; import { FormClose } from "grommet-icons"; -import { stakers, theme } from "@tender/shared/src/index"; +import { stakers } from "@tender/shared/src/index"; import { weiToEthWithDecimals } from "utils/amountFormat"; import { useBalanceValidation } from "utils/inputValidation"; import { AmountInputFooter } from "components/AmountInputFooter"; @@ -26,8 +26,7 @@ import { isPendingTransaction } from "utils/transactions"; import { TransactionStatus } from "@usedapp/core"; import { LoadingButtonContent } from "components/LoadingButtonContent"; import { useCalcDepositOut } from "utils/tenderDepositHooks"; -import styled from "styled-components"; -import { normalizeColor } from "grommet/utils"; +import { BrandedALink } from "components/BrandedALink"; type Props = { show: boolean; @@ -142,9 +141,9 @@ const ConfirmDepositModal: FC = ({ Notice: staking GRT infers a 0.5%{" "} - + delegation - {" "} + {" "} fee towards the Graph Protocol. @@ -179,8 +178,3 @@ const ConfirmDepositModal: FC = ({ }; export default ConfirmDepositModal; - -const brandColor = normalizeColor("brand", theme); -const StyledA = styled.a` - color: ${brandColor}; -`; diff --git a/packages/app/src/components/deposit/UnstakeModal.tsx b/packages/app/src/components/deposit/UnstakeModal.tsx new file mode 100644 index 00000000..0c0031b8 --- /dev/null +++ b/packages/app/src/components/deposit/UnstakeModal.tsx @@ -0,0 +1,156 @@ +import { FC, MouseEventHandler, useState } from "react"; +import { utils, BigNumberish } from "ethers"; +import { + Button, + Box, + Card, + CardHeader, + CardBody, + CardFooter, + Layer, + Form, + FormField, + Image, + TextInput, + Text, + Heading, +} from "grommet"; + +import { FormClose } from "grommet-icons"; +import { stakers } from "@tender/shared/src/index"; +import { weiToEthWithDecimals } from "utils/amountFormat"; +import { isLargerThanMax, isPositive, useBalanceValidation } from "utils/inputValidation"; +import { AmountInputFooter } from "components/AmountInputFooter"; +import { ProtocolName } from "@tender/shared/src/data/stakers"; +import { useUnstake } from "utils/tenderDepositHooks"; +import { BrandedALink } from "components/BrandedALink"; + +type Props = { + show: boolean; + tenderTokenBalance: BigNumberish; + protocolName: ProtocolName; + onDismiss: () => void; +}; + +const UnstakeModal: FC = ({ show, tenderTokenBalance, protocolName, onDismiss }) => { + const staker = stakers[protocolName]; + const symbol = staker.symbol; + const bwTenderLogo = `/${staker.bwTenderLogo}`; + + const [unstakeInput, setUnstakeInput] = useState(""); + + const { validationMessage } = useBalanceValidation(unstakeInput, tenderTokenBalance, symbol); + + const maxUnstake = () => { + setUnstakeInput(utils.formatEther(tenderTokenBalance.toString())); + }; + + const handleInputChange = (e: any) => { + const val = e.target.value; + if (val && !val.match(/^(\d+\.?\d*|\.\d+)$/)) return; + setUnstakeInput(val); + }; + + const { unstake } = useUnstake(protocolName); + + const handleUnstake: MouseEventHandler = async (e) => { + e.preventDefault(); + await unstake(utils.parseEther(unstakeInput || "0")); + setUnstakeInput(""); + onDismiss(); + }; + + return ( + <> + {show && ( + + +