diff --git a/.github/workflows/audit_build_verify.yml b/.github/workflows/audit_build_verify.yml index 49abbd2641..354575e56e 100644 --- a/.github/workflows/audit_build_verify.yml +++ b/.github/workflows/audit_build_verify.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # pin@v2 + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v2 - name: Audit dependencies run: npm audit --audit-level=critical @@ -35,14 +35,14 @@ jobs: steps: - name: Checkout - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # pin@v2 + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v2 - name: Set npm cache directory run: npm config set cache .npm-cache --global continue-on-error: true - name: Cache node modules - uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # pin@v3 + uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # pin@v3 with: path: | .npm-cache @@ -70,7 +70,7 @@ jobs: - name: Upload lint results # run if lint failed and only on main/dev branch and pull requests if: always() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || github.event_name == 'pull_request') - uses: github/codeql-action/upload-sarif@86f3159a697a097a813ad9bfa0002412d97690a4 # pin@codeql-bundle-20220322 + uses: github/codeql-action/upload-sarif@e0e5ded33cabb451ae0a9768fc7b0410bad9ad44 # pin@codeql-bundle-20220322 with: sarif_file: lint-results.sarif continue-on-error: true @@ -91,14 +91,14 @@ jobs: steps: - name: Checkout - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # pin@v2 + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v2 - name: Set npm cache directory run: npm config set cache .npm-cache --global continue-on-error: true - name: Cache node modules - uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # pin@v3 + uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # pin@v3 with: path: | .npm-cache @@ -119,7 +119,7 @@ jobs: npm install --prefer-offline --no-audit --legacy-peer-deps - name: Cache next.js build cache - uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # pin@v2 + uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # pin@v2 with: path: .next/cache key: ${{ runner.os }}-next-cache-${{ hashFiles('**/package-lock.json') }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 67eedc4983..a38ad5191b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -18,16 +18,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # pin@v2 + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v2 - name: Initialize CodeQL - uses: github/codeql-action/init@86f3159a697a097a813ad9bfa0002412d97690a4 # pin@codeql-bundle-20220322 + uses: github/codeql-action/init@e0e5ded33cabb451ae0a9768fc7b0410bad9ad44 # pin@codeql-bundle-20220322 with: queries: security-and-quality languages: javascript - name: Autobuild - uses: github/codeql-action/autobuild@86f3159a697a097a813ad9bfa0002412d97690a4 # pin@codeql-bundle-20220322 + uses: github/codeql-action/autobuild@e0e5ded33cabb451ae0a9768fc7b0410bad9ad44 # pin@codeql-bundle-20220322 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@86f3159a697a097a813ad9bfa0002412d97690a4 # pin@codeql-bundle-20220322 + uses: github/codeql-action/analyze@e0e5ded33cabb451ae0a9768fc7b0410bad9ad44 # pin@codeql-bundle-20220322 diff --git a/.github/workflows/lighthouse.yml b/.github/workflows/lighthouse.yml index 5fd7a5ed8e..7a4046b97b 100644 --- a/.github/workflows/lighthouse.yml +++ b/.github/workflows/lighthouse.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # pin@v2 + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v2 - name: Run lighthouse run: | diff --git a/.gitsigners b/.gitsigners new file mode 100644 index 0000000000..92c4d801c4 --- /dev/null +++ b/.gitsigners @@ -0,0 +1 @@ +hybrtdx9bqhe14wdkrnxouagdcqep3awpsureoi8tcxphbsz38tbne ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEkRv+F3ES1QaiCfCeDDY5Dc4o20yIhU8WPbwNr5PEIk diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a717b6daaf..7803afcb03 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributors Guide -Kwenta is a dApp enabling derivates trading with infinite liquidity - powered by the Synthetix protocol. We're community driven and welcome all contribution. We aim to provide a constructive, respectful and fun environment for collaboration. +Kwenta is a dApp enabling derivates trading with infinite liquidity - powered by the Synthetix protocol. We're community driven and welcome all contributions. We aim to provide a constructive, respectful and fun environment for collaboration. If you wish to help out, please first join the **Kwenta devDAO** on our [Discord](https://discord.gg/kwenta) `#community-dev` channel. For more information, see [devDAO](#devdao) below. @@ -31,15 +31,15 @@ Be welcome to join the Kwenta devDAO on our [Discord](https://discord.gg/kwenta) This is where discussions take place, new tickets will be announced by the community PM and assigned to the respective community members and roles on a first come, first served base. -There are different roles depending on the severety of a ticket. As a new community member, you should watch out for *bounty hunter* tickets are work your way up from there. +There are different roles depending on the severity of a ticket. As a new community member, you should watch out for *bounty hunter* tickets are work your way up from there. ### General Work-Flow We recommend the following work-flow for contributors: -1. **Find an open ticket** to work on in our Discord, either because it's interesting or suitable to your skill-set. Use the `#community-dev channel` to communicate your intentions and ask questions. +1. **Find an open ticket** to work on in our Discord, either because it's interesting or suitable to your skill set. Use the `#community-dev channel` to communicate your intentions and ask questions. 2. **Work in a feature branch** of your personal fork (github.com/YOUR_NAME/kwenta) of the main repository (github.com/kwenta/kwenta). -3. Once you feel you have addressed the issue, **create a pull-request** to merge your changes in to the main repository. In case of any doubts, don't hesitate to contact the community PM or ask away in the channel. +3. Once you feel you have addressed the issue, **create a pull-request** to merge your changes into the main repository. In case of any doubts, don't hesitate to contact the community PM or ask away in the channel. 4. Wait for a CC or auditor to **review your changes** to ensure the issue is addressed satisfactorily. Optionally, mention your PR on [Discord](https://discord.gg/kwenta). 5. If the issue is addressed the repository maintainers will **merge your pull-request** and you'll be an official contributor! @@ -66,7 +66,7 @@ There's a great [guide](https://akrabat.com/the-beginners-guide-to-contributing- ### I don't think I have anything to add -There's lots to be done and there's all sorts of tasks. You can do anything from correcting typos through to writing core dApp code. If you reach out, we'll include you. +There's lots to be done and there's all sorts of tasks. You can do anything from correcting typos to writing core dApp code. If you reach out, we'll include you. ### I'm not sure my programming level is good enough diff --git a/assets/png/features/permissionless.png b/assets/png/features/opensource.png similarity index 100% rename from assets/png/features/permissionless.png rename to assets/png/features/opensource.png diff --git a/assets/svg/app/dropdown-arrow.svg b/assets/svg/app/dropdown-arrow.svg index 487405cfc4..52b944dd0c 100644 --- a/assets/svg/app/dropdown-arrow.svg +++ b/assets/svg/app/dropdown-arrow.svg @@ -1,3 +1,3 @@ - - + + diff --git a/assets/svg/futures/switch-arrows.svg b/assets/svg/futures/switch-arrows.svg new file mode 100644 index 0000000000..8a7ce4189e --- /dev/null +++ b/assets/svg/futures/switch-arrows.svg @@ -0,0 +1,3 @@ + + + diff --git a/components/Button/Button.tsx b/components/Button/Button.tsx index 4f990f8c8a..ad8cab586d 100644 --- a/components/Button/Button.tsx +++ b/components/Button/Button.tsx @@ -1,8 +1,19 @@ import styled, { css } from 'styled-components'; +// TODO: Clean up these styles +export type ButtonVariant = + | 'primary' + | 'secondary' + | 'flat' + | 'alt' + | 'success' + | 'danger' + | 'text' + | 'select'; + type ButtonProps = { size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'; - variant?: 'primary' | 'secondary' | 'flat' | 'alt' | 'success' | 'danger' | 'text' | 'select'; + variant?: ButtonVariant; isActive?: boolean; isRounded?: boolean; mono?: boolean; diff --git a/components/Error/Error.tsx b/components/Error/Error.tsx index be4abdd272..22f070e679 100644 --- a/components/Error/Error.tsx +++ b/components/Error/Error.tsx @@ -1,15 +1,21 @@ import React, { FC, useMemo } from 'react'; import styled from 'styled-components'; +import Button from 'components/Button'; +import Spacer from 'components/Spacer'; import { formatRevert, isUserDeniedError } from 'utils/formatters/error'; import { truncateString } from 'utils/formatters/string'; type ErrorProps = { message: string; formatter?: 'revert' | undefined; + retryButton?: { + onClick: () => void; + label: string; + }; }; -export const Error: FC = ({ message, formatter }) => { +export const Error: FC = ({ message, formatter, retryButton }) => { const formattedMessage = useMemo(() => { switch (formatter) { case 'revert': @@ -21,7 +27,19 @@ export const Error: FC = ({ message, formatter }) => { if (isUserDeniedError(message) || !message) return null; - return {truncateString(formattedMessage)}; + return ( + +
{truncateString(formattedMessage)}
+ {retryButton && ( + <> + + + + )} + + ); }; const ErrorContainer = styled.div` diff --git a/components/InfoBox/InfoBox.tsx b/components/InfoBox/InfoBox.tsx index f1c75f9a09..facaa81590 100644 --- a/components/InfoBox/InfoBox.tsx +++ b/components/InfoBox/InfoBox.tsx @@ -7,12 +7,12 @@ export type DetailedInfo = { value: string | React.ReactNode; keyNode?: React.ReactNode; valueNode?: React.ReactNode; - color?: 'green' | 'red' | 'gold'; + color?: 'green' | 'red' | 'gold' | undefined; spaceBeneath?: boolean; }; type InfoBoxProps = { - details: Record; + details: Record; style?: React.CSSProperties; className?: string; disabled?: boolean; @@ -23,7 +23,7 @@ const InfoBox: React.FC = ({ details, style, className, disabled, return ( {Object.entries(details).map(([key, value], index) => { - if (value !== null) { + if (value) { return (
diff --git a/components/Input/CustomInput.tsx b/components/Input/CustomInput.tsx index eb645de943..eb00df6d89 100644 --- a/components/Input/CustomInput.tsx +++ b/components/Input/CustomInput.tsx @@ -14,6 +14,7 @@ type CustomInputProps = { defaultValue?: any; dataTestId?: string; textAlign?: string; + invalid?: boolean; }; const INVALID_CHARS = ['-', '+', 'e']; @@ -30,6 +31,7 @@ const CustomInput: React.FC = ({ id, defaultValue, dataTestId, + invalid, textAlign = 'left', }) => { const handleChange = (e: React.ChangeEvent) => { @@ -37,7 +39,12 @@ const CustomInput: React.FC = ({ }; return ( - + {typeof left === 'string' ? {left} : left} = ({ ); }; -const CustomInputContainer = styled.div<{ textAlign: string }>` +const CustomInputContainer = styled.div<{ textAlign: string; invalid?: boolean }>` display: flex; align-items: center; justify-content: space-between; @@ -69,6 +76,9 @@ const CustomInputContainer = styled.div<{ textAlign: string }>` background: ${(props) => props.theme.colors.selectedTheme.input.secondary.background}; box-shadow: ${(props) => props.theme.colors.selectedTheme.input.shadow}; border: ${(props) => props.theme.colors.selectedTheme.border}; + border-color: ${(props) => + props.invalid ? props.theme.colors.selectedTheme.red : props.theme.colors.selectedTheme.border}; + border-radius: 10px; padding: 0 10px; @@ -82,7 +92,10 @@ const CustomInputContainer = styled.div<{ textAlign: string }>` background-color: transparent; border: none; text-align: ${(props) => props.textAlign || 'left'}; - color: ${(props) => props.theme.colors.selectedTheme.button.text.primary}; + color: ${(props) => + props.invalid + ? props.theme.colors.selectedTheme.red + : props.theme.colors.selectedTheme.button.text.primary}; width: 100%; &:focus { diff --git a/components/Input/InputTitle.tsx b/components/Input/InputTitle.tsx new file mode 100644 index 0000000000..36ddf7f0ff --- /dev/null +++ b/components/Input/InputTitle.tsx @@ -0,0 +1,12 @@ +import styled from 'styled-components'; + +const InputTitle = styled.div<{ margin?: string }>` + color: ${(props) => props.theme.colors.selectedTheme.button.text.primary}; + font-size: 13px; + margin: ${(props) => props.margin || '0'}; + span { + color: ${(props) => props.theme.colors.selectedTheme.gray}; + } +`; + +export default InputTitle; diff --git a/components/ProgressSteps/ProgressSteps.tsx b/components/ProgressSteps/ProgressSteps.tsx index 3c0f6e2a76..317fb95b01 100644 --- a/components/ProgressSteps/ProgressSteps.tsx +++ b/components/ProgressSteps/ProgressSteps.tsx @@ -16,6 +16,7 @@ const ProgressSteps: FC = ({ step, totalSteps, complete }) => { const relStep = step - 1; return ( ); diff --git a/components/SegmentedControl/SegmentedControl.tsx b/components/SegmentedControl/SegmentedControl.tsx index b20acda125..da48e4d35a 100644 --- a/components/SegmentedControl/SegmentedControl.tsx +++ b/components/SegmentedControl/SegmentedControl.tsx @@ -1,69 +1,102 @@ import React from 'react'; -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; + +type StyleType = 'tab' | 'check' | 'button'; interface SegmentedControlProps { values: string[]; selectedIndex: number; - onChange(index: number): void; style?: React.CSSProperties; className?: string; + styleType?: StyleType; + suffix?: string; + onChange(index: number): void; } -const SegmentedControl: React.FC = ({ +function SegmentedControl({ values, selectedIndex, + suffix, onChange, + styleType = 'tab', ...props -}) => ( - - {values.map((value, index) => ( - onChange(index)} - > - {value} - - ))} - -); +}: SegmentedControlProps) { + return ( + + {values.map((value, index) => ( + onChange(index)} + > + {styleType === 'check' && } + {value} + {suffix} + + ))} + + ); +} -const SegmentedControlContainer = styled.div<{ $length: number }>` +const SegmentedControlContainer = styled.div<{ $length: number; styleType: StyleType }>` display: grid; grid-template-columns: repeat(${(props) => props.$length}, 1fr); box-sizing: border-box; grid-gap: 14px; width: 100%; - height: 38px; - padding: 4px; - background: ${(props) => props.theme.colors.selectedTheme.segmented.background}; - border: ${(props) => props.theme.colors.selectedTheme.border}; + height: ${(props) => (props.styleType === 'tab' ? '38px' : '24px')}; + padding: ${(props) => (props.styleType === 'tab' ? '4px' : '0')}; + background: ${(props) => + props.styleType === 'tab' ? props.theme.colors.selectedTheme.segmented.background : 'none'}; + border: ${(props) => + props.styleType === 'tab' ? props.theme.colors.selectedTheme.border : 'none'}; border-radius: 8px; `; -const SegmentedControlOption = styled.button<{ isSelected: boolean }>` +const SegmentedControlOption = styled.button<{ isSelected: boolean; styleType: StyleType }>` font-size: 13px; - font-family: ${(props) => props.theme.fonts.regular}; + font-family: ${(props) => + (props.styleType === 'tab' && props.isSelected) || props.styleType === 'button' + ? props.theme.fonts.bold + : props.theme.fonts.regular}; cursor: pointer; + text-transform: capitalize; + text-align: ${(props) => (props.styleType === 'check' ? 'left' : 'center')}; + display: ${(props) => (props.styleType === 'check' ? 'flex' : 'inherit')}; + align-items: center; + border: ${(props) => { + if ((props.isSelected && props.styleType === 'tab') || props.styleType === 'button') + return props.theme.colors.selectedTheme.border; + return 'none'; + }}; + border-radius: ${(props) => (props.styleType === 'button' ? '20px' : '6px')}; + border-color: ${(props) => + props.styleType === 'button' && props.isSelected + ? props.theme.colors.selectedTheme.yellow + : undefined}; + color: ${(props) => + props.isSelected && props.styleType === 'button' + ? props.theme.colors.selectedTheme.yellow + : props.isSelected + ? props.theme.colors.selectedTheme.button.text.primary + : props.theme.colors.selectedTheme.segmented.button.inactive.color}; + + background: ${(props) => + props.isSelected && props.styleType === 'tab' + ? props.theme.colors.selectedTheme.segmented.button.background + : 'transparent'}; +`; - ${(props) => - props.isSelected - ? css` - background: ${(props) => props.theme.colors.selectedTheme.segmented.button.background}; - position: relative; - border: ${(props) => props.theme.colors.selectedTheme.border}; - border-radius: 6px; - color: ${(props) => props.theme.colors.selectedTheme.button.text.primary}; - font-family: ${(props) => props.theme.fonts.bold}; - &::before { - border-radius: 6px; - } - ` - : css` - background: transparent; - border: none; - color: ${(props) => props.theme.colors.selectedTheme.segmented.button.inactive.color}; - `} +const CheckBox = styled.div<{ selected: boolean }>` + margin-right: 10px; + outline: ${(props) => props.theme.colors.selectedTheme.outlineBorder}; + border-radius: 2px; + outline-width: 3px; + height: 15px; + width: 15px; + background: ${(props) => + props.selected ? props.theme.colors.selectedTheme.yellow : 'transparent'}; `; export default SegmentedControl; diff --git a/constants/address.ts b/constants/address.ts index de33fe825b..2c325ea8da 100644 --- a/constants/address.ts +++ b/constants/address.ts @@ -6,12 +6,12 @@ export const SYNTH_SWAP_OPTIMISM_ADDRESS = '0x6d6273f52b0C8eaB388141393c1e8cfDB3 export const CROSS_MARGIN_ACCOUNT_FACTORY: Record = { 69: '0xB2e8d9832C8a22C6fB6D2c92c7E2a69d654749CB', - 420: '0x9BaCA1f3304Ff606703c3e0d7433741D92a015E4', + 420: '0xf6Be6F3b4DDC804978169F5B96A6D6dDA9212168', // 10: awaiting mainnet }; export const CROSS_MARGIN_BASE_SETTINGS: Record = { 69: '0x026B0DA8B453967D39227748066Eb094a415b696', - 420: '0x0b95CD442447848583Ac8a377aee41fa8ecd68D4', + 420: '0x6909951d28133e7c26C24035e2163BD4c4B973E8', // 10: awaiting mainnet }; diff --git a/constants/defaults.ts b/constants/defaults.ts index f85402c20c..6499641688 100644 --- a/constants/defaults.ts +++ b/constants/defaults.ts @@ -28,8 +28,10 @@ export const DEFAULT_FIAT_DECIMALS = 2; export const DEFAULT_NUMBER_DECIMALS = 2; export const DEFAULT_PERCENT_DECIMALS = 2; export const DEFAULT_TOKEN_DECIMALS = 18; + // for DEX aggregators like 1inch export const DEFAULT_SLIPPAGE = 1; +export const DEFAULT_1INCH_SLIPPAGE = 3; // for Trading History export const DEFAULT_NUMBER_OF_TRADES: number = 16; diff --git a/constants/futures.ts b/constants/futures.ts new file mode 100644 index 0000000000..de93bd353b --- /dev/null +++ b/constants/futures.ts @@ -0,0 +1,7 @@ +import { wei } from '@synthetixio/wei'; + +export const ISOLATED_MARGIN_ORDER_TYPES = ['market', 'next-price']; +export const CROSS_MARGIN_ORDER_TYPES = ['market', 'limit', 'stop']; +export const ORDER_KEEPER_ETH_DEPOSIT = wei(0.1); +export const DEFAULT_MAX_LEVERAGE = wei(10); +export const MAX_POSITION_BUFFER = 0.01; diff --git a/constants/queryKeys.ts b/constants/queryKeys.ts index 7f957abaa7..bd51cabb86 100644 --- a/constants/queryKeys.ts +++ b/constants/queryKeys.ts @@ -307,11 +307,12 @@ export const QUERY_KEYS = { wallet: string, crossMarginBaseAddress: string ) => ['futures', 'cross-margin-account-overview', networkId, wallet, crossMarginBaseAddress], - CrossMarginAccount: ( - crossMarginAddress: string, - wallet: string, - selectedAccountType: string - ) => ['futures', 'cross-margin-account', crossMarginAddress, wallet, selectedAccountType], + CrossMarginAccount: (wallet: string, factoryAddress: string) => [ + 'futures', + 'cross-margin-account', + wallet, + factoryAddress, + ], CrossMarginSettings: (networkId: NetworkId) => ['futures', 'cross-margin-settings', networkId], }, Files: { diff --git a/contexts/RefetchContext.tsx b/contexts/RefetchContext.tsx index 27b8ef68b1..5c76c4c4db 100644 --- a/contexts/RefetchContext.tsx +++ b/contexts/RefetchContext.tsx @@ -1,4 +1,5 @@ import React, { useEffect } from 'react'; +import { UseQueryResult } from 'react-query'; import { useRecoilValue, useSetRecoilState } from 'recoil'; import { Period } from 'constants/period'; @@ -23,12 +24,16 @@ type RefetchType = | 'account-margin-change' | 'cross-margin-account-change'; +type RefetchUntilType = 'wallet-balance-change' | 'cross-margin-account-change'; + type RefetchContextType = { handleRefetch: (refetchType: RefetchType, timeout?: number) => void; + refetchUntilUpdate: (refetchType: RefetchUntilType) => Promise; }; const RefetchContext = React.createContext({ handleRefetch: () => {}, + refetchUntilUpdate: () => Promise.resolve(), }); export const RefetchProvider: React.FC = ({ children }) => { @@ -90,7 +95,61 @@ export const RefetchProvider: React.FC = ({ children }) => { }, timeout ?? 5000); }; - return {children}; + const refetchUntilUpdate = async (refetchType: RefetchUntilType) => { + switch (refetchType) { + case 'cross-margin-account-change': + return refetchWithComparator(crossMarginAccountQuery, (prev, next) => { + return !next || prev === next; + }); + case 'wallet-balance-change': + return refetchWithComparator(synthsBalancesQuery, (prev, next) => { + return !next || prev?.susdWalletBalance === next?.susdWalletBalance; + }); + } + }; + + return ( + + {children} + + ); +}; + +// Takes a comparitor which should return a bool condition to +// signal to continue retrying, comparing prev and new query result + +const refetchWithComparator = async ( + query: UseQueryResult, + comparator: (previous: any, current: any) => boolean, + interval = 1000, + max = 20 +) => { + const prev = query?.data; + + return new Promise((res, rej) => { + let count = 1; + + const refetch = async () => { + const timeout = setTimeout(async () => { + if (count > max) { + clearTimeout(timeout); + rej(new Error('Refetch timed out')); + } else { + const cur = query?.data || null; + const next = await query.refetch(); + + count += 1; + if (!comparator(prev, next.data)) { + clearTimeout(timeout); + res(cur); + } else { + refetch(); + } + } + }, interval); + }; + refetch(); + }); }; export const useRefetchContext = () => { diff --git a/hooks/useCrossMarginKeeperEthBal.ts b/hooks/useCrossMarginKeeperEthBal.ts new file mode 100644 index 0000000000..50ca324afe --- /dev/null +++ b/hooks/useCrossMarginKeeperEthBal.ts @@ -0,0 +1,27 @@ +import { wei } from '@synthetixio/wei'; +import { useCallback, useEffect, useState } from 'react'; +import { useRecoilValue } from 'recoil'; + +import Connector from 'containers/Connector'; +import { futuresAccountState } from 'store/futures'; +import { zeroBN } from 'utils/formatters/number'; + +export default function useCrossMarginKeeperEthBal() { + const { provider } = Connector.useContainer(); + + const { crossMarginAddress } = useRecoilValue(futuresAccountState); + + const [keeperEthBal, setKeeperEthBal] = useState(zeroBN); + + const getKeeperEthBal = useCallback(async () => { + if (!crossMarginAddress) return zeroBN; + const bal = await provider.getBalance(crossMarginAddress); + setKeeperEthBal(wei(bal)); + }, [crossMarginAddress, provider]); + + useEffect(() => { + getKeeperEthBal(); + }, [getKeeperEthBal]); + + return { keeperEthBal, getKeeperEthBal }; +} diff --git a/hooks/useExchange.ts b/hooks/useExchange.ts index d21d31106e..2eae9e9cbc 100644 --- a/hooks/useExchange.ts +++ b/hooks/useExchange.ts @@ -15,7 +15,7 @@ import { ETH_ADDRESS, ETH_COINGECKO_ADDRESS, } from 'constants/currency'; -import { DEFAULT_CRYPTO_DECIMALS } from 'constants/defaults'; +import { DEFAULT_1INCH_SLIPPAGE, DEFAULT_CRYPTO_DECIMALS } from 'constants/defaults'; import { ATOMIC_EXCHANGE_SLIPPAGE } from 'constants/exchange'; import ROUTES from 'constants/routes'; import Connector from 'containers/Connector'; @@ -653,14 +653,6 @@ const useExchange = ({ showNoSynthsCard = false }: ExchangeCardProps) => { // eslint-disable-next-line }, [exchangeTxn.hash]); - const oneInchSlippage = useMemo(() => { - // ETH swaps often fail with lower slippage - if (txProvider === '1inch' && (baseCurrencyKey === 'ETH' || quoteCurrencyKey === 'ETH')) { - return 3; - } - return slippage; - }, [txProvider, baseCurrencyKey, quoteCurrencyKey, slippage]); - const getGasEstimateForExchange = useCallback(async () => { if (!isL2) return null; if (txProvider === 'synthswap') { @@ -693,7 +685,7 @@ const useExchange = ({ showNoSynthsCard = false }: ExchangeCardProps) => { baseCurrencyTokenAddress!, quoteCurrencyAmount, quoteDecimals, - oneInchSlippage + DEFAULT_1INCH_SLIPPAGE ); const metaTx = await swap1Inch( @@ -701,7 +693,7 @@ const useExchange = ({ showNoSynthsCard = false }: ExchangeCardProps) => { baseCurrencyTokenAddress!, quoteCurrencyAmount, quoteDecimals, - oneInchSlippage, + DEFAULT_1INCH_SLIPPAGE, true ); const l1Fee = await getL1SecurityFee({ @@ -723,7 +715,6 @@ const useExchange = ({ showNoSynthsCard = false }: ExchangeCardProps) => { slippage, txProvider, gasPrice?.gasPrice, - oneInchSlippage, quoteDecimals, swap1Inch, swap1InchGasEstimate, @@ -795,7 +786,7 @@ const useExchange = ({ showNoSynthsCard = false }: ExchangeCardProps) => { baseCurrencyTokenAddress!, quoteCurrencyAmount, quoteDecimals, - oneInchSlippage + DEFAULT_1INCH_SLIPPAGE ); } else if (txProvider === 'synthswap') { tx = await swapSynthSwap( @@ -850,7 +841,6 @@ const useExchange = ({ showNoSynthsCard = false }: ExchangeCardProps) => { oneInchTokensMap, allTokensMap, exchangeTxn, - oneInchSlippage, monitorExchangeTxn, setHasOrdersNotification, setOrders, diff --git a/hooks/useFuturesData.ts b/hooks/useFuturesData.ts index 43e64e97d2..873559926e 100644 --- a/hooks/useFuturesData.ts +++ b/hooks/useFuturesData.ts @@ -4,21 +4,21 @@ import { ethers } from 'ethers'; import { formatBytes32String } from 'ethers/lib/utils'; import { debounce } from 'lodash'; import { useRouter } from 'next/router'; -import React, { useState, useEffect, useMemo, useCallback } from 'react'; +import { useState, useEffect, useMemo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; -import { CROSS_MARGIN_ENABLED, DEFAULT_LEVERAGE } from 'constants/defaults'; +import { DEFAULT_LEVERAGE } from 'constants/defaults'; +import { + CROSS_MARGIN_ORDER_TYPES, + ISOLATED_MARGIN_ORDER_TYPES, + ORDER_KEEPER_ETH_DEPOSIT, +} from 'constants/futures'; import Connector from 'containers/Connector'; import TransactionNotifier from 'containers/TransactionNotifier'; import { useRefetchContext } from 'contexts/RefetchContext'; import { KWENTA_TRACKING_CODE } from 'queries/futures/constants'; -import { - FuturesAccountType, - PositionSide, - TradeFees, - FuturesTradeInputs, -} from 'queries/futures/types'; +import { PositionSide, TradeFees, FuturesTradeInputs } from 'queries/futures/types'; import useGetCrossMarginAccountOverview from 'queries/futures/useGetCrossMarginAccountOverview'; import useGetFuturesPotentialTradeDetails from 'queries/futures/useGetFuturesPotentialTradeDetails'; import { getFuturesMarketContract } from 'queries/futures/utils'; @@ -40,8 +40,11 @@ import { simulatedTradeState, crossMarginTotalMarginState, potentialTradeDetailsState, + futuresOrderPriceState, + orderFeeCapState, + isAdvancedOrderState, } from 'store/futures'; -import { zeroBN, floorNumber } from 'utils/formatters/number'; +import { zeroBN, floorNumber, weiToString } from 'utils/formatters/number'; import { getDisplayAsset } from 'utils/futures'; import logError from 'utils/logError'; @@ -62,13 +65,15 @@ const ZERO_FEES = { staticFee: zeroBN, crossMarginFee: zeroBN, dynamicFeeRate: zeroBN, + keeperEthDeposit: zeroBN, + limitStopOrderFee: zeroBN, total: zeroBN, }; const useFuturesData = () => { const router = useRouter(); const { t } = useTranslation(); - const { defaultSynthetixjs: synthetixjs, network } = Connector.useContainer(); + const { defaultSynthetixjs: synthetixjs, network, provider } = Connector.useContainer(); const { useSynthetixTxn } = useSynthetixQueries(); const getPotentialTrade = useGetFuturesPotentialTradeDetails(); @@ -87,25 +92,30 @@ const useFuturesData = () => { const [tradeFees, setTradeFees] = useRecoilState(tradeFeesState); const leverageSide = useRecoilValue(leverageSideState); const [orderType, setOrderType] = useRecoilState(orderTypeState); + const feeCap = useRecoilValue(orderFeeCapState); const position = useRecoilValue(positionState); const market = useRecoilValue(marketInfoState); const totalMargin = useRecoilValue(crossMarginTotalMarginState); const maxLeverage = useRecoilValue(maxLeverageState); - const { tradeFee: crossMarginTradeFee } = useRecoilValue(crossMarginSettingsState); - const { crossMarginAvailable } = useRecoilValue(futuresAccountState); + const { crossMarginAvailable, crossMarginAddress } = useRecoilValue(futuresAccountState); + const { tradeFee: crossMarginTradeFee, stopOrderFee, limitOrderFee } = useRecoilValue( + crossMarginSettingsState + ); + const isAdvancedOrder = useRecoilValue(isAdvancedOrderState); const marketAssetRate = useRecoilValue(marketAssetRateState); + const orderPrice = useRecoilValue(futuresOrderPriceState); const setPotentialTradeDetails = useSetRecoilState(potentialTradeDetailsState); - const [selectedAccountType, setSelectedAccountType] = usePersistedRecoilState( - futuresAccountTypeState - ); + const selectedAccountType = useRecoilValue(futuresAccountTypeState); const [preferredLeverage] = usePersistedRecoilState(preferredLeverageState); const [maxFee, setMaxFee] = useState(zeroBN); const [error, setError] = useState(null); - const routerAccountType = useMemo(() => { - return typeof router.query.accountType === 'string' ? router.query.accountType : 'cross_margin'; - }, [router.query.accountType]); + const tradePrice = useMemo(() => wei(isAdvancedOrder ? orderPrice || zeroBN : marketAssetRate), [ + orderPrice, + marketAssetRate, + isAdvancedOrder, + ]); const crossMarginAccount = useMemo(() => { return crossMarginAvailable @@ -181,6 +191,21 @@ const useFuturesData = () => { [synthetixjs, marketAsset] ); + const getCrossMarginEthBal = useCallback(async () => { + if (!crossMarginAddress) return zeroBN; + const bal = await provider.getBalance(crossMarginAddress); + return wei(bal); + }, [crossMarginAddress, provider]); + + const calculateCrossMarginFee = useCallback( + (susdSizeDelta: Wei) => { + if (orderType !== 'limit' && orderType !== 'stop') return zeroBN; + const advancedOrderFeeRate = orderType === 'limit' ? limitOrderFee : stopOrderFee; + return susdSizeDelta.abs().mul(advancedOrderFeeRate); + }, + [orderType, stopOrderFee, limitOrderFee] + ); + const calculateFees = useCallback( async (susdSizeDelta: Wei, nativeSizeDelta: Wei) => { if (!synthetixjs) return ZERO_FEES; @@ -193,10 +218,17 @@ const useFuturesData = () => { getTradeFee(nativeSizeDelta), ]); + const currentDeposit = + orderType === 'limit' || orderType === 'market' ? await getCrossMarginEthBal() : zeroBN; + const requiredDeposit = currentDeposit.lt(ORDER_KEEPER_ETH_DEPOSIT) + ? ORDER_KEEPER_ETH_DEPOSIT.sub(currentDeposit) + : zeroBN; + const crossMarginFee = selectedAccountType === 'cross_margin' ? susdSizeDelta.abs().mul(crossMarginTradeFee) : zeroBN; + const limitStopOrderFee = calculateCrossMarginFee(susdSizeDelta); const orderFeeWei = wei(orderFee.fee); const volatilityFeeWei = wei(volatilityFee.feeRate); @@ -204,30 +236,53 @@ const useFuturesData = () => { staticFee: orderFeeWei, crossMarginFee: crossMarginFee, dynamicFeeRate: volatilityFeeWei, - total: orderFeeWei.add(crossMarginFee), + keeperEthDeposit: requiredDeposit, + limitStopOrderFee: limitStopOrderFee, + total: orderFeeWei.add(crossMarginFee).add(limitStopOrderFee), }; setTradeFees(fees); return fees; }, - [crossMarginTradeFee, selectedAccountType, marketAsset, synthetixjs, setTradeFees, getTradeFee] + [ + crossMarginTradeFee, + selectedAccountType, + marketAsset, + synthetixjs, + orderType, + calculateCrossMarginFee, + setTradeFees, + getTradeFee, + getCrossMarginEthBal, + ] ); const calculateMarginDelta = useCallback( async (nextTrade: FuturesTradeInputs, fees: TradeFees) => { + if (nextTrade.nativeSizeDelta.add(position?.position?.size || 0).eq(zeroBN)) return zeroBN; const currentSize = position?.position?.notionalValue || zeroBN; const newNotionalValue = currentSize.add(nextTrade.susdSizeDelta); const fullMargin = newNotionalValue.abs().div(nextTrade.leverage); - let marginDelta = fullMargin.sub(position?.remainingMargin || '0'); - return marginDelta.add(fees.total); + + let marginDelta = fullMargin.sub(position?.remainingMargin || '0').add(fees.total); + return crossMarginAccount?.freeMargin?.sub(marginDelta).lt(0) + ? crossMarginAccount?.freeMargin + : marginDelta; }, - [position?.position?.notionalValue, position?.remainingMargin] + [ + position?.position?.notionalValue, + position?.position?.size, + position?.remainingMargin, + crossMarginAccount?.freeMargin, + ] ); // eslint-disable-next-line const debounceFetchPreview = useCallback( debounce(async (nextTrade: FuturesTradeInputs, fromLeverage = false) => { + setError(null); + // if (((orderType === 'stop' || orderType === 'limit') && !orderPrice) || !nextTrade.nativeSize) + // return; try { - setError(null); const fees = await calculateFees(nextTrade.susdSizeDelta, nextTrade.nativeSizeDelta); let nextMarginDelta = zeroBN; if (selectedAccountType === 'cross_margin') { @@ -237,7 +292,12 @@ const useFuturesData = () => { : zeroBN; setCrossMarginMarginDelta(nextMarginDelta); } - getPotentialTrade(nextTrade.nativeSizeDelta, nextMarginDelta, Number(nextTrade.leverage)); + getPotentialTrade( + nextTrade.nativeSizeDelta, + nextMarginDelta, + Number(nextTrade.leverage), + nextTrade.orderPrice + ); } catch (err) { setError(t('futures.market.trade.preview.error')); logError(err); @@ -248,6 +308,8 @@ const useFuturesData = () => { calculateFees, getPotentialTrade, calculateMarginDelta, + orderPrice, + orderType, selectedAccountType, logError, setCrossMarginMarginDelta, @@ -263,70 +325,54 @@ const useFuturesData = () => { [setTradeInputs, setSimulatedTrade, debounceFetchPreview] ); - const onTradeAmountChange = React.useCallback( - (value: string, fromLeverage: boolean = false) => { + const onTradeAmountChange = useCallback( + ( + value: string, + currencyType: 'usd' | 'native', + options?: { simulateChange?: boolean; crossMarginLeverage?: Wei } + ) => { + if (!value || tradePrice.eq(0)) { + resetTradeState(); + return; + } + + const positiveTrade = leverageSide === PositionSide.LONG; + const nativeSize = currencyType === 'native' ? wei(value) : wei(value).div(tradePrice); + const usdSize = currencyType === 'native' ? tradePrice.mul(value) : wei(value); const changeEnabled = remainingMargin.gt(0) && value !== ''; - const size = fromLeverage ? (value === '' ? '' : wei(value).toNumber().toString()) : value; - const sizeSusdWei = marketAssetRate.mul(value || 0); - const isolatedMarginLeverage = changeEnabled ? sizeSusdWei.div(remainingMargin) : zeroBN; + const isolatedMarginLeverage = changeEnabled ? usdSize.div(remainingMargin) : zeroBN; + const inputLeverage = - selectedAccountType === 'cross_margin' ? wei(selectedLeverage) : isolatedMarginLeverage; - const leverage = value === '' || remainingMargin.eq(0) ? zeroBN : inputLeverage; - - onStagePositionChange({ - nativeSize: size, - susdSize: changeEnabled ? sizeSusdWei.toString() : '', - nativeSizeDelta: size ? wei(leverageSide === PositionSide.LONG ? size : -size) : zeroBN, - susdSizeDelta: leverageSide === PositionSide.LONG ? sizeSusdWei : sizeSusdWei.neg(), - leverage: - leverage.gt(0) && marketMaxLeverage.gt(leverage) ? String(floorNumber(leverage)) : '', - }); - }, - [ - marketAssetRate, - remainingMargin, - marketMaxLeverage, - leverageSide, - selectedLeverage, - selectedAccountType, - onStagePositionChange, - ] - ); + selectedAccountType === 'cross_margin' + ? options?.crossMarginLeverage ?? wei(selectedLeverage) + : isolatedMarginLeverage; + let leverage = remainingMargin.eq(0) ? zeroBN : inputLeverage; + leverage = marketMaxLeverage.gt(leverage) ? leverage : marketMaxLeverage; + + const newTradeInputs = { + nativeSize: changeEnabled ? weiToString(nativeSize) : '', + susdSize: changeEnabled ? weiToString(usdSize) : '', + nativeSizeDelta: positiveTrade ? nativeSize : nativeSize.neg(), + susdSizeDelta: positiveTrade ? usdSize : usdSize.neg(), + orderPrice: tradePrice, + leverage: String(floorNumber(leverage)), + }; - const onTradeAmountSUSDChange = useCallback( - (value: string, commitChange = true, crossMarginLeverage?: string) => { - if (marketAssetRate.gt(0)) { - const changeEnabled = remainingMargin.gt(0) && value !== ''; - const size = changeEnabled ? wei(value).div(marketAssetRate).toNumber().toString() : ''; - const leverage = - selectedAccountType === 'cross_margin' - ? crossMarginLeverage || selectedLeverage - : changeEnabled - ? String(floorNumber(wei(value).div(remainingMargin))) - : ''; - - const newSize = { - nativeSize: size, - susdSize: value, - susdSizeDelta: value ? wei(leverageSide === PositionSide.LONG ? value : -value) : zeroBN, - nativeSizeDelta: size ? wei(leverageSide === PositionSide.LONG ? size : -size) : zeroBN, - leverage: leverage, - }; - - if (commitChange) { - onStagePositionChange(newSize); - } else { - // Allows us to keep it snappy updating the input values - setSimulatedTrade(newSize); - } + if (options?.simulateChange) { + // Allows us to keep it snappy updating the input values + setSimulatedTrade(newTradeInputs); + } else { + onStagePositionChange(newTradeInputs); } }, [ - marketAssetRate, + tradePrice, remainingMargin, - leverageSide, + marketMaxLeverage, selectedLeverage, selectedAccountType, + leverageSide, + resetTradeState, setSimulatedTrade, onStagePositionChange, ] @@ -375,7 +421,9 @@ const useFuturesData = () => { if (position?.position) { onChangeOpenPosLeverage(leverage); } else { - onTradeAmountSUSDChange('', true, String(leverage)); + onTradeAmountChange('', 'usd', { + crossMarginLeverage: wei(leverage), + }); } } else { if (leverage <= 0) { @@ -385,7 +433,7 @@ const useFuturesData = () => { marketAssetRate.eq(0) || remainingMargin.eq(0) ? '' : wei(leverage).mul(remainingMargin).div(marketAssetRate).toString(); - onTradeAmountChange(newTradeSize, true); + onTradeAmountChange(newTradeSize, 'native'); } } }, @@ -397,13 +445,22 @@ const useFuturesData = () => { resetTradeState, onTradeAmountChange, onChangeOpenPosLeverage, - onTradeAmountSUSDChange, ] ); + const onTradeOrderPriceChange = useCallback( + (price: string) => { + if (price && tradeInputs.susdSize) { + // Recalc the trade + onTradeAmountChange(tradeInputs.susdSize, 'usd'); + } + }, + [tradeInputs, onTradeAmountChange] + ); + const orderTxn = useSynthetixTxn( `FuturesMarket${getDisplayAsset(marketAsset)}`, - orderType === 1 ? 'submitNextPriceOrderWithTracking' : 'modifyPositionWithTracking', + orderType === 'next-price' ? 'submitNextPriceOrderWithTracking' : 'modifyPositionWithTracking', [tradeInputs.nativeSizeDelta.toBN(), KWENTA_TRACKING_CODE], {}, { @@ -417,22 +474,42 @@ const useFuturesData = () => { } ); - const submitCrossMarginOrder = useCallback(async () => { - if (!crossMarginAccountContract) return; - const newPosition = [ - { - marketKey: formatBytes32String(marketAsset), - marginDelta: crossMarginMarginDelta.toBN(), - sizeDelta: tradeInputs.nativeSizeDelta.toBN(), - }, - ]; - return await crossMarginAccountContract.distributeMargin(newPosition); - }, [ - crossMarginAccountContract, - marketAsset, - crossMarginMarginDelta, - tradeInputs.nativeSizeDelta, - ]); + const submitCrossMarginOrder = useCallback( + async (fromEditLeverage?: boolean) => { + if (!crossMarginAccountContract) return; + if (orderType === 'market' || fromEditLeverage) { + const newPosition = [ + { + marketKey: formatBytes32String(marketAsset), + marginDelta: crossMarginMarginDelta.toBN(), + sizeDelta: tradeInputs.nativeSizeDelta.toBN(), + }, + ]; + return await crossMarginAccountContract.distributeMargin(newPosition); + } + const enumType = orderType === 'limit' ? 0 : 1; + + return await crossMarginAccountContract.placeOrderWithFeeCap( + formatBytes32String(marketAsset), + crossMarginMarginDelta.toBN(), + tradeInputs.nativeSizeDelta.toBN(), + wei(orderPrice).toBN(), + enumType, + feeCap.toBN(), + { value: tradeFees.keeperEthDeposit.toBN() } + ); + }, + [ + crossMarginAccountContract, + marketAsset, + orderPrice, + orderType, + feeCap, + crossMarginMarginDelta, + tradeInputs.nativeSizeDelta, + tradeFees.keeperEthDeposit, + ] + ); const submitIsolatedMarginOrder = useCallback(() => { orderTxn.mutate(); @@ -453,14 +530,14 @@ const useFuturesData = () => { useEffect(() => { const getMaxFee = async () => { - if (remainingMargin.eq(0) || marketAssetRate.eq(0)) { + if (remainingMargin.eq(0) || tradePrice.eq(0)) { return; } try { const currentValue = position?.position?.notionalValue || zeroBN; let maxUsd = remainingMargin.mul(selectedLeverage); maxUsd = leverageSide === 'long' ? maxUsd.sub(currentValue) : maxUsd.add(currentValue); - const maxSize = maxUsd.gt(0) ? maxUsd.div(marketAssetRate) : zeroBN; + const maxSize = maxUsd.gt(0) ? maxUsd.div(tradePrice) : zeroBN; const maxOrderFee = await getTradeFee(maxSize); setMaxFee(wei(maxOrderFee.fee)); } catch (e) { @@ -470,40 +547,33 @@ const useFuturesData = () => { getMaxFee(); }, [ setMaxFee, - leverageSide, getTradeFee, + leverageSide, position?.position?.notionalValue, remainingMargin, - marketAssetRate, + tradePrice, selectedLeverage, ]); useEffect(() => { - // TODO: Can remove once cross margin is fully integrated - if (!CROSS_MARGIN_ENABLED && selectedAccountType === 'cross_margin') { - setSelectedAccountType('isolated_margin'); + if (selectedAccountType === 'cross_margin' && !CROSS_MARGIN_ORDER_TYPES.includes(orderType)) { + setOrderType('market'); + } else if ( + selectedAccountType === 'isolated_margin' && + !ISOLATED_MARGIN_ORDER_TYPES.includes(orderType) + ) { + setOrderType('market'); } - }, [selectedAccountType, setSelectedAccountType]); + onTradeAmountChange(tradeInputs.susdSize, 'usd'); - useEffect(() => { - const validType = ['cross_margin', 'isolated_margin'].includes(routerAccountType); - if (validType) { - setSelectedAccountType( - CROSS_MARGIN_ENABLED ? (routerAccountType as FuturesAccountType) : 'isolated_margin' - ); - if (routerAccountType === 'cross_margin' && orderType === 1) { - setOrderType(0); - } - } - }, [routerAccountType, orderType, network.id, setSelectedAccountType, setOrderType]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedAccountType, orderType, network.id]); useEffect(() => { const handleRouteChange = () => { resetTradeState(); }; - router.events.on('routeChangeStart', handleRouteChange); - return () => { router.events.off('routeChangeStart', handleRouteChange); }; @@ -511,29 +581,24 @@ const useFuturesData = () => { useEffect(() => { if (tradeInputs.susdSizeDelta.eq(0)) return; - const nextTrade = { - ...tradeInputs, - susdSizeDelta: - leverageSide === PositionSide.LONG - ? tradeInputs.susdSizeDelta.abs() - : tradeInputs.susdSizeDelta.neg(), - nativeSizeDelta: - leverageSide === PositionSide.LONG - ? tradeInputs.nativeSizeDelta.abs() - : tradeInputs.nativeSizeDelta.neg(), - }; - onStagePositionChange(nextTrade); + onTradeAmountChange(tradeInputs.susdSize, 'usd'); // Only want to react to leverage side change // eslint-disable-next-line react-hooks/exhaustive-deps }, [leverageSide]); + useEffect(() => { + resetTradeState(); + // Clear trade state when switching address + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [crossMarginAddress]); + return { onLeverageChange, onTradeAmountChange, - onTradeAmountSUSDChange, submitIsolatedMarginOrder, submitCrossMarginOrder, resetTradeState, + onTradeOrderPriceChange, marketAssetRate, position, marketAsset, diff --git a/hooks/useSUSDBalance.ts b/hooks/useSUSDBalance.ts deleted file mode 100644 index c00d314c4e..0000000000 --- a/hooks/useSUSDBalance.ts +++ /dev/null @@ -1,12 +0,0 @@ -import useSynthetixQueries from '@synthetixio/queries'; - -import Connector from 'containers/Connector'; -import { zeroBN } from 'utils/formatters/number'; - -export default function useSUSDBalance(address?: string) { - const { walletAddress } = Connector.useContainer(); - - const { useSynthsBalancesQuery } = useSynthetixQueries(); - const synthsBalancesQuery = useSynthsBalancesQuery(address || walletAddress); - return synthsBalancesQuery?.data?.balancesMap?.['sUSD']?.balance ?? zeroBN; -} diff --git a/lib/abis/CrossMarginAccountFactory.json b/lib/abis/CrossMarginAccountFactory.json index 512a4bbbb2..aedc4899e3 100644 --- a/lib/abis/CrossMarginAccountFactory.json +++ b/lib/abis/CrossMarginAccountFactory.json @@ -1,143 +1,143 @@ [ - { - "inputs": [ - { - "internalType": "string", - "name": "_version", - "type": "string" - }, - { - "internalType": "address", - "name": "_marginAsset", - "type": "address" - }, - { - "internalType": "address", - "name": "_addressResolver", - "type": "address" - }, - { - "internalType": "address", - "name": "_marginBaseSettings", - "type": "address" - }, - { - "internalType": "address payable", - "name": "_ops", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "NewAccount", - "type": "event" - }, - { - "inputs": [], - "name": "addressResolver", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "implementation", - "outputs": [ - { - "internalType": "contract MarginBase", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "marginAsset", - "outputs": [ - { - "internalType": "contract IERC20", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "marginBaseSettings", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "newAccount", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "ops", - "outputs": [ - { - "internalType": "address payable", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "version", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - } - ] \ No newline at end of file + { + "inputs": [ + { + "internalType": "string", + "name": "_version", + "type": "string" + }, + { + "internalType": "address", + "name": "_marginAsset", + "type": "address" + }, + { + "internalType": "address", + "name": "_addressResolver", + "type": "address" + }, + { + "internalType": "address", + "name": "_marginBaseSettings", + "type": "address" + }, + { + "internalType": "address payable", + "name": "_ops", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "NewAccount", + "type": "event" + }, + { + "inputs": [], + "name": "addressResolver", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "contract MarginBase", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "marginAsset", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "marginBaseSettings", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "newAccount", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "ops", + "outputs": [ + { + "internalType": "address payable", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/lib/abis/CrossMarginBase.json b/lib/abis/CrossMarginBase.json index 8500575787..f2adaf54f9 100644 --- a/lib/abis/CrossMarginBase.json +++ b/lib/abis/CrossMarginBase.json @@ -1,597 +1,870 @@ [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "EthWithdrawalFailed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "available", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "required", - "type": "uint256" - } - ], - "name": "InsufficientFreeMargin", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "numberOfNewPositions", - "type": "uint256" - } - ], - "name": "MaxNewPositionsExceeded", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "marketKey", - "type": "bytes32" - } - ], - "name": "MissingMarketKey", - "type": "error" - }, - { - "inputs": [], - "name": "OrderInvalid", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "valueName", - "type": "bytes32" - } - ], - "name": "ValueCannotBeZero", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Deposit", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Withdraw", - "type": "event" - }, - { - "inputs": [], - "name": "ETH", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "activeMarketKeys", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "activeMarketPositions", - "outputs": [ - { - "internalType": "bytes32", - "name": "marketKey", - "type": "bytes32" - }, - { - "internalType": "uint128", - "name": "margin", - "type": "uint128" - }, - { - "internalType": "int128", - "name": "size", - "type": "int128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_orderId", - "type": "uint256" - } - ], - "name": "cancelOrder", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_orderId", - "type": "uint256" - } - ], - "name": "checker", - "outputs": [ - { - "internalType": "bool", - "name": "canExec", - "type": "bool" - }, - { - "internalType": "bytes", - "name": "execPayload", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "committedMargin", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_amount", - "type": "uint256" - } - ], - "name": "deposit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "marketKey", - "type": "bytes32" - }, - { - "internalType": "int256", - "name": "marginDelta", - "type": "int256" - }, - { - "internalType": "int256", - "name": "sizeDelta", - "type": "int256" - } - ], - "internalType": "struct IMarginBaseTypes.UpdateMarketPositionSpec[]", - "name": "_newPositions", - "type": "tuple[]" - } - ], - "name": "distributeMargin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_orderId", - "type": "uint256" - } - ], - "name": "executeOrder", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "freeMargin", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "gelato", - "outputs": [ - { - "internalType": "address payable", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getAllActiveMarketPositions", - "outputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "marketKey", - "type": "bytes32" - }, - { - "internalType": "uint128", - "name": "margin", - "type": "uint128" - }, - { - "internalType": "int128", - "name": "size", - "type": "int128" - } - ], - "internalType": "struct IMarginBaseTypes.ActiveMarketPosition[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getNumberOfActivePositions", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_marginAsset", - "type": "address" - }, - { - "internalType": "address", - "name": "_addressResolver", - "type": "address" - }, - { - "internalType": "address", - "name": "_marginBaseSettings", - "type": "address" - }, - { - "internalType": "address payable", - "name": "_ops", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "marginAsset", - "outputs": [ - { - "internalType": "contract IERC20", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "marginBaseSettings", - "outputs": [ - { - "internalType": "contract MarginBaseSettings", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "ops", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "orderId", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "orders", - "outputs": [ - { - "internalType": "bytes32", - "name": "marketKey", - "type": "bytes32" - }, - { - "internalType": "int256", - "name": "marginDelta", - "type": "int256" - }, - { - "internalType": "int256", - "name": "sizeDelta", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "desiredPrice", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "gelatoTaskId", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_marketKey", - "type": "bytes32" - }, - { - "internalType": "int256", - "name": "_marginDelta", - "type": "int256" - }, - { - "internalType": "int256", - "name": "_sizeDelta", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "_limitPrice", - "type": "uint256" - } - ], - "name": "placeOrder", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_orderId", - "type": "uint256" - } - ], - "name": "validOrder", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_amount", - "type": "uint256" - } - ], - "name": "withdraw", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_amount", - "type": "uint256" - } - ], - "name": "withdrawEth", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ] \ No newline at end of file + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "CannotPayFee", + "type": "error" + }, + { + "inputs": [], + "name": "CannotRescueMarginAsset", + "type": "error" + }, + { + "inputs": [], + "name": "EthWithdrawalFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minimum", + "type": "uint256" + } + ], + "name": "InsufficientEthBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "available", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "required", + "type": "uint256" + } + ], + "name": "InsufficientFreeMargin", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPrice", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "numberOfNewPositions", + "type": "uint256" + } + ], + "name": "MaxNewPositionsExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "OrderInvalid", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "valueName", + "type": "bytes32" + } + ], + "name": "ValueCannotBeZero", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "FeeImposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "orderId", + "type": "uint256" + } + ], + "name": "OrderCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "orderId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fillPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "keeperFee", + "type": "uint256" + } + ], + "name": "OrderFilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "orderId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "marketKey", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "int256", + "name": "marginDelta", + "type": "int256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "sizeDelta", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "targetPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "enum IMarginBaseTypes.OrderTypes", + "name": "orderType", + "type": "uint8" + } + ], + "name": "OrderPlaced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Rescued", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Withdraw", + "type": "event" + }, + { + "inputs": [], + "name": "ETH", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "activeMarketKeys", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_orderId", + "type": "uint256" + } + ], + "name": "cancelOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_orderId", + "type": "uint256" + } + ], + "name": "checker", + "outputs": [ + { + "internalType": "bool", + "name": "canExec", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "execPayload", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "committedMargin", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "marketKey", + "type": "bytes32" + }, + { + "internalType": "int256", + "name": "marginDelta", + "type": "int256" + }, + { + "internalType": "int256", + "name": "sizeDelta", + "type": "int256" + } + ], + "internalType": "struct IMarginBaseTypes.NewPosition[]", + "name": "_newPositions", + "type": "tuple[]" + } + ], + "name": "depositAndDistribute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "marketKey", + "type": "bytes32" + }, + { + "internalType": "int256", + "name": "marginDelta", + "type": "int256" + }, + { + "internalType": "int256", + "name": "sizeDelta", + "type": "int256" + } + ], + "internalType": "struct IMarginBaseTypes.NewPosition[]", + "name": "_newPositions", + "type": "tuple[]" + } + ], + "name": "distributeMargin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_orderId", + "type": "uint256" + } + ], + "name": "executeOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "freeMargin", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gelato", + "outputs": [ + { + "internalType": "address payable", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNumberOfInternalPositions", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_marketKey", + "type": "bytes32" + } + ], + "name": "getPosition", + "outputs": [ + { + "internalType": "uint64", + "name": "id", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "fundingIndex", + "type": "uint64" + }, + { + "internalType": "uint128", + "name": "margin", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "lastPrice", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "size", + "type": "int128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_marginAsset", + "type": "address" + }, + { + "internalType": "address", + "name": "_addressResolver", + "type": "address" + }, + { + "internalType": "address", + "name": "_marginBaseSettings", + "type": "address" + }, + { + "internalType": "address payable", + "name": "_ops", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "marginAsset", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "marginBaseSettings", + "outputs": [ + { + "internalType": "contract MarginBaseSettings", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "marketKeyIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ops", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "orderId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "orders", + "outputs": [ + { + "internalType": "bytes32", + "name": "marketKey", + "type": "bytes32" + }, + { + "internalType": "int256", + "name": "marginDelta", + "type": "int256" + }, + { + "internalType": "int256", + "name": "sizeDelta", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "targetPrice", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "gelatoTaskId", + "type": "bytes32" + }, + { + "internalType": "enum IMarginBaseTypes.OrderTypes", + "name": "orderType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "maxDynamicFee", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_marketKey", + "type": "bytes32" + }, + { + "internalType": "int256", + "name": "_marginDelta", + "type": "int256" + }, + { + "internalType": "int256", + "name": "_sizeDelta", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "_targetPrice", + "type": "uint256" + }, + { + "internalType": "enum IMarginBaseTypes.OrderTypes", + "name": "_orderType", + "type": "uint8" + } + ], + "name": "placeOrder", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_marketKey", + "type": "bytes32" + }, + { + "internalType": "int256", + "name": "_marginDelta", + "type": "int256" + }, + { + "internalType": "int256", + "name": "_sizeDelta", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "_targetPrice", + "type": "uint256" + }, + { + "internalType": "enum IMarginBaseTypes.OrderTypes", + "name": "_orderType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "_maxDynamicFee", + "type": "uint256" + } + ], + "name": "placeOrderWithFeeCap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" + } + ], + "name": "rescueERC20", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_orderId", + "type": "uint256" + } + ], + "name": "validOrder", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdrawEth", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/lib/abis/CrossMarginBaseSettings.json b/lib/abis/CrossMarginBaseSettings.json index 7193aaaf55..53ce133f22 100644 --- a/lib/abis/CrossMarginBaseSettings.json +++ b/lib/abis/CrossMarginBaseSettings.json @@ -1,252 +1,252 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "_treasury", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_tradeFee", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_limitOrderFee", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_stopLossFee", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "fee", - "type": "uint256" - } - ], - "name": "InvalidFee", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroAddress", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "fee", - "type": "uint256" - } - ], - "name": "LimitOrderFeeChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "fee", - "type": "uint256" - } - ], - "name": "StopLossFeeChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "fee", - "type": "uint256" - } - ], - "name": "TradeFeeChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "treasury", - "type": "address" - } - ], - "name": "TreasuryAddressChanged", - "type": "event" - }, - { - "inputs": [], - "name": "limitOrderFee", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_fee", - "type": "uint256" - } - ], - "name": "setLimitOrderFee", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, + { + "inputs": [ { - "inputs": [ - { - "internalType": "uint256", - "name": "_fee", - "type": "uint256" - } - ], - "name": "setStopLossFee", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" + "internalType": "address", + "name": "_treasury", + "type": "address" }, { - "inputs": [ - { - "internalType": "uint256", - "name": "_fee", - "type": "uint256" - } - ], - "name": "setTradeFee", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" + "internalType": "uint256", + "name": "_tradeFee", + "type": "uint256" }, { - "inputs": [ - { - "internalType": "address", - "name": "_treasury", - "type": "address" - } - ], - "name": "setTreasury", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" + "internalType": "uint256", + "name": "_limitOrderFee", + "type": "uint256" }, { - "inputs": [], - "name": "stopLossFee", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "tradeFee", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], + "internalType": "uint256", + "name": "_stopOrderFee", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "InvalidFee", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "LimitOrderFeeChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "StopOrderFeeChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "TradeFeeChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", "name": "treasury", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" + "type": "address" + } + ], + "name": "TreasuryAddressChanged", + "type": "event" + }, + { + "inputs": [], + "name": "limitOrderFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_fee", + "type": "uint256" + } + ], + "name": "setLimitOrderFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_fee", + "type": "uint256" + } + ], + "name": "setStopOrderFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_fee", + "type": "uint256" + } + ], + "name": "setTradeFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_treasury", + "type": "address" + } + ], + "name": "setTreasury", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stopOrderFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tradeFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "treasury", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" } + ], + "stateMutability": "view", + "type": "function" + } ] \ No newline at end of file diff --git a/lib/abis/types/CrossMarginBase.ts b/lib/abis/types/CrossMarginBase.ts index 73cb3882fa..f274698f5b 100644 --- a/lib/abis/types/CrossMarginBase.ts +++ b/lib/abis/types/CrossMarginBase.ts @@ -29,56 +29,47 @@ import type { } from "./common"; export declare namespace IMarginBaseTypes { - export type UpdateMarketPositionSpecStruct = { + export type NewPositionStruct = { marketKey: PromiseOrValue; marginDelta: PromiseOrValue; sizeDelta: PromiseOrValue; }; - export type UpdateMarketPositionSpecStructOutput = [ - string, - BigNumber, - BigNumber - ] & { marketKey: string; marginDelta: BigNumber; sizeDelta: BigNumber }; - - export type ActiveMarketPositionStruct = { - marketKey: PromiseOrValue; - margin: PromiseOrValue; - size: PromiseOrValue; + export type NewPositionStructOutput = [string, BigNumber, BigNumber] & { + marketKey: string; + marginDelta: BigNumber; + sizeDelta: BigNumber; }; - - export type ActiveMarketPositionStructOutput = [ - string, - BigNumber, - BigNumber - ] & { marketKey: string; margin: BigNumber; size: BigNumber }; } export interface CrossMarginBaseInterface extends utils.Interface { functions: { "ETH()": FunctionFragment; "activeMarketKeys(uint256)": FunctionFragment; - "activeMarketPositions(bytes32)": FunctionFragment; "cancelOrder(uint256)": FunctionFragment; "checker(uint256)": FunctionFragment; "committedMargin()": FunctionFragment; "deposit(uint256)": FunctionFragment; + "depositAndDistribute(uint256,(bytes32,int256,int256)[])": FunctionFragment; "distributeMargin((bytes32,int256,int256)[])": FunctionFragment; "executeOrder(uint256)": FunctionFragment; "freeMargin()": FunctionFragment; "gelato()": FunctionFragment; - "getAllActiveMarketPositions()": FunctionFragment; - "getNumberOfActivePositions()": FunctionFragment; + "getNumberOfInternalPositions()": FunctionFragment; + "getPosition(bytes32)": FunctionFragment; "initialize()": FunctionFragment; "initialize(address,address,address,address)": FunctionFragment; "marginAsset()": FunctionFragment; "marginBaseSettings()": FunctionFragment; + "marketKeyIndex(bytes32)": FunctionFragment; "ops()": FunctionFragment; "orderId()": FunctionFragment; "orders(uint256)": FunctionFragment; "owner()": FunctionFragment; - "placeOrder(bytes32,int256,int256,uint256)": FunctionFragment; + "placeOrder(bytes32,int256,int256,uint256,uint8)": FunctionFragment; + "placeOrderWithFeeCap(bytes32,int256,int256,uint256,uint8,uint256)": FunctionFragment; "renounceOwnership()": FunctionFragment; + "rescueERC20(address,uint256)": FunctionFragment; "transferOwnership(address)": FunctionFragment; "validOrder(uint256)": FunctionFragment; "withdraw(uint256)": FunctionFragment; @@ -89,27 +80,30 @@ export interface CrossMarginBaseInterface extends utils.Interface { nameOrSignatureOrTopic: | "ETH" | "activeMarketKeys" - | "activeMarketPositions" | "cancelOrder" | "checker" | "committedMargin" | "deposit" + | "depositAndDistribute" | "distributeMargin" | "executeOrder" | "freeMargin" | "gelato" - | "getAllActiveMarketPositions" - | "getNumberOfActivePositions" + | "getNumberOfInternalPositions" + | "getPosition" | "initialize()" | "initialize(address,address,address,address)" | "marginAsset" | "marginBaseSettings" + | "marketKeyIndex" | "ops" | "orderId" | "orders" | "owner" | "placeOrder" + | "placeOrderWithFeeCap" | "renounceOwnership" + | "rescueERC20" | "transferOwnership" | "validOrder" | "withdraw" @@ -121,10 +115,6 @@ export interface CrossMarginBaseInterface extends utils.Interface { functionFragment: "activeMarketKeys", values: [PromiseOrValue] ): string; - encodeFunctionData( - functionFragment: "activeMarketPositions", - values: [PromiseOrValue] - ): string; encodeFunctionData( functionFragment: "cancelOrder", values: [PromiseOrValue] @@ -141,9 +131,13 @@ export interface CrossMarginBaseInterface extends utils.Interface { functionFragment: "deposit", values: [PromiseOrValue] ): string; + encodeFunctionData( + functionFragment: "depositAndDistribute", + values: [PromiseOrValue, IMarginBaseTypes.NewPositionStruct[]] + ): string; encodeFunctionData( functionFragment: "distributeMargin", - values: [IMarginBaseTypes.UpdateMarketPositionSpecStruct[]] + values: [IMarginBaseTypes.NewPositionStruct[]] ): string; encodeFunctionData( functionFragment: "executeOrder", @@ -155,12 +149,12 @@ export interface CrossMarginBaseInterface extends utils.Interface { ): string; encodeFunctionData(functionFragment: "gelato", values?: undefined): string; encodeFunctionData( - functionFragment: "getAllActiveMarketPositions", + functionFragment: "getNumberOfInternalPositions", values?: undefined ): string; encodeFunctionData( - functionFragment: "getNumberOfActivePositions", - values?: undefined + functionFragment: "getPosition", + values: [PromiseOrValue] ): string; encodeFunctionData( functionFragment: "initialize()", @@ -183,6 +177,10 @@ export interface CrossMarginBaseInterface extends utils.Interface { functionFragment: "marginBaseSettings", values?: undefined ): string; + encodeFunctionData( + functionFragment: "marketKeyIndex", + values: [PromiseOrValue] + ): string; encodeFunctionData(functionFragment: "ops", values?: undefined): string; encodeFunctionData(functionFragment: "orderId", values?: undefined): string; encodeFunctionData( @@ -196,6 +194,18 @@ export interface CrossMarginBaseInterface extends utils.Interface { PromiseOrValue, PromiseOrValue, PromiseOrValue, + PromiseOrValue, + PromiseOrValue + ] + ): string; + encodeFunctionData( + functionFragment: "placeOrderWithFeeCap", + values: [ + PromiseOrValue, + PromiseOrValue, + PromiseOrValue, + PromiseOrValue, + PromiseOrValue, PromiseOrValue ] ): string; @@ -203,6 +213,10 @@ export interface CrossMarginBaseInterface extends utils.Interface { functionFragment: "renounceOwnership", values?: undefined ): string; + encodeFunctionData( + functionFragment: "rescueERC20", + values: [PromiseOrValue, PromiseOrValue] + ): string; encodeFunctionData( functionFragment: "transferOwnership", values: [PromiseOrValue] @@ -225,10 +239,6 @@ export interface CrossMarginBaseInterface extends utils.Interface { functionFragment: "activeMarketKeys", data: BytesLike ): Result; - decodeFunctionResult( - functionFragment: "activeMarketPositions", - data: BytesLike - ): Result; decodeFunctionResult( functionFragment: "cancelOrder", data: BytesLike @@ -239,6 +249,10 @@ export interface CrossMarginBaseInterface extends utils.Interface { data: BytesLike ): Result; decodeFunctionResult(functionFragment: "deposit", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "depositAndDistribute", + data: BytesLike + ): Result; decodeFunctionResult( functionFragment: "distributeMargin", data: BytesLike @@ -250,11 +264,11 @@ export interface CrossMarginBaseInterface extends utils.Interface { decodeFunctionResult(functionFragment: "freeMargin", data: BytesLike): Result; decodeFunctionResult(functionFragment: "gelato", data: BytesLike): Result; decodeFunctionResult( - functionFragment: "getAllActiveMarketPositions", + functionFragment: "getNumberOfInternalPositions", data: BytesLike ): Result; decodeFunctionResult( - functionFragment: "getNumberOfActivePositions", + functionFragment: "getPosition", data: BytesLike ): Result; decodeFunctionResult( @@ -273,15 +287,27 @@ export interface CrossMarginBaseInterface extends utils.Interface { functionFragment: "marginBaseSettings", data: BytesLike ): Result; + decodeFunctionResult( + functionFragment: "marketKeyIndex", + data: BytesLike + ): Result; decodeFunctionResult(functionFragment: "ops", data: BytesLike): Result; decodeFunctionResult(functionFragment: "orderId", data: BytesLike): Result; decodeFunctionResult(functionFragment: "orders", data: BytesLike): Result; decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result; decodeFunctionResult(functionFragment: "placeOrder", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "placeOrderWithFeeCap", + data: BytesLike + ): Result; decodeFunctionResult( functionFragment: "renounceOwnership", data: BytesLike ): Result; + decodeFunctionResult( + functionFragment: "rescueERC20", + data: BytesLike + ): Result; decodeFunctionResult( functionFragment: "transferOwnership", data: BytesLike @@ -295,12 +321,22 @@ export interface CrossMarginBaseInterface extends utils.Interface { events: { "Deposit(address,uint256)": EventFragment; + "FeeImposed(address,uint256)": EventFragment; + "OrderCancelled(address,uint256)": EventFragment; + "OrderFilled(address,uint256,uint256,uint256)": EventFragment; + "OrderPlaced(address,uint256,bytes32,int256,int256,uint256,uint8)": EventFragment; "OwnershipTransferred(address,address)": EventFragment; + "Rescued(address,uint256)": EventFragment; "Withdraw(address,uint256)": EventFragment; }; getEvent(nameOrSignatureOrTopic: "Deposit"): EventFragment; + getEvent(nameOrSignatureOrTopic: "FeeImposed"): EventFragment; + getEvent(nameOrSignatureOrTopic: "OrderCancelled"): EventFragment; + getEvent(nameOrSignatureOrTopic: "OrderFilled"): EventFragment; + getEvent(nameOrSignatureOrTopic: "OrderPlaced"): EventFragment; getEvent(nameOrSignatureOrTopic: "OwnershipTransferred"): EventFragment; + getEvent(nameOrSignatureOrTopic: "Rescued"): EventFragment; getEvent(nameOrSignatureOrTopic: "Withdraw"): EventFragment; } @@ -312,6 +348,57 @@ export type DepositEvent = TypedEvent<[string, BigNumber], DepositEventObject>; export type DepositEventFilter = TypedEventFilter; +export interface FeeImposedEventObject { + account: string; + amount: BigNumber; +} +export type FeeImposedEvent = TypedEvent< + [string, BigNumber], + FeeImposedEventObject +>; + +export type FeeImposedEventFilter = TypedEventFilter; + +export interface OrderCancelledEventObject { + account: string; + orderId: BigNumber; +} +export type OrderCancelledEvent = TypedEvent< + [string, BigNumber], + OrderCancelledEventObject +>; + +export type OrderCancelledEventFilter = TypedEventFilter; + +export interface OrderFilledEventObject { + account: string; + orderId: BigNumber; + fillPrice: BigNumber; + keeperFee: BigNumber; +} +export type OrderFilledEvent = TypedEvent< + [string, BigNumber, BigNumber, BigNumber], + OrderFilledEventObject +>; + +export type OrderFilledEventFilter = TypedEventFilter; + +export interface OrderPlacedEventObject { + account: string; + orderId: BigNumber; + marketKey: string; + marginDelta: BigNumber; + sizeDelta: BigNumber; + targetPrice: BigNumber; + orderType: number; +} +export type OrderPlacedEvent = TypedEvent< + [string, BigNumber, string, BigNumber, BigNumber, BigNumber, number], + OrderPlacedEventObject +>; + +export type OrderPlacedEventFilter = TypedEventFilter; + export interface OwnershipTransferredEventObject { previousOwner: string; newOwner: string; @@ -324,6 +411,14 @@ export type OwnershipTransferredEvent = TypedEvent< export type OwnershipTransferredEventFilter = TypedEventFilter; +export interface RescuedEventObject { + token: string; + amount: BigNumber; +} +export type RescuedEvent = TypedEvent<[string, BigNumber], RescuedEventObject>; + +export type RescuedEventFilter = TypedEventFilter; + export interface WithdrawEventObject { user: string; amount: BigNumber; @@ -369,17 +464,6 @@ export interface CrossMarginBase extends BaseContract { overrides?: CallOverrides ): Promise<[string]>; - activeMarketPositions( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise< - [string, BigNumber, BigNumber] & { - marketKey: string; - margin: BigNumber; - size: BigNumber; - } - >; - cancelOrder( _orderId: PromiseOrValue, overrides?: Overrides & { from?: PromiseOrValue } @@ -397,8 +481,14 @@ export interface CrossMarginBase extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; + depositAndDistribute( + _amount: PromiseOrValue, + _newPositions: IMarginBaseTypes.NewPositionStruct[], + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + distributeMargin( - _newPositions: IMarginBaseTypes.UpdateMarketPositionSpecStruct[], + _newPositions: IMarginBaseTypes.NewPositionStruct[], overrides?: Overrides & { from?: PromiseOrValue } ): Promise; @@ -411,11 +501,22 @@ export interface CrossMarginBase extends BaseContract { gelato(overrides?: CallOverrides): Promise<[string]>; - getAllActiveMarketPositions( + getNumberOfInternalPositions( overrides?: CallOverrides - ): Promise<[IMarginBaseTypes.ActiveMarketPositionStructOutput[]]>; + ): Promise<[BigNumber]>; - getNumberOfActivePositions(overrides?: CallOverrides): Promise<[BigNumber]>; + getPosition( + _marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, BigNumber, BigNumber, BigNumber, BigNumber] & { + id: BigNumber; + fundingIndex: BigNumber; + margin: BigNumber; + lastPrice: BigNumber; + size: BigNumber; + } + >; "initialize()"( overrides?: Overrides & { from?: PromiseOrValue } @@ -433,6 +534,11 @@ export interface CrossMarginBase extends BaseContract { marginBaseSettings(overrides?: CallOverrides): Promise<[string]>; + marketKeyIndex( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber]>; + ops(overrides?: CallOverrides): Promise<[string]>; orderId(overrides?: CallOverrides): Promise<[BigNumber]>; @@ -441,12 +547,14 @@ export interface CrossMarginBase extends BaseContract { arg0: PromiseOrValue, overrides?: CallOverrides ): Promise< - [string, BigNumber, BigNumber, BigNumber, string] & { + [string, BigNumber, BigNumber, BigNumber, string, number, BigNumber] & { marketKey: string; marginDelta: BigNumber; sizeDelta: BigNumber; - desiredPrice: BigNumber; + targetPrice: BigNumber; gelatoTaskId: string; + orderType: number; + maxDynamicFee: BigNumber; } >; @@ -456,7 +564,18 @@ export interface CrossMarginBase extends BaseContract { _marketKey: PromiseOrValue, _marginDelta: PromiseOrValue, _sizeDelta: PromiseOrValue, - _limitPrice: PromiseOrValue, + _targetPrice: PromiseOrValue, + _orderType: PromiseOrValue, + overrides?: PayableOverrides & { from?: PromiseOrValue } + ): Promise; + + placeOrderWithFeeCap( + _marketKey: PromiseOrValue, + _marginDelta: PromiseOrValue, + _sizeDelta: PromiseOrValue, + _targetPrice: PromiseOrValue, + _orderType: PromiseOrValue, + _maxDynamicFee: PromiseOrValue, overrides?: PayableOverrides & { from?: PromiseOrValue } ): Promise; @@ -464,6 +583,12 @@ export interface CrossMarginBase extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; + rescueERC20( + tokenAddress: PromiseOrValue, + tokenAmount: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + transferOwnership( newOwner: PromiseOrValue, overrides?: Overrides & { from?: PromiseOrValue } @@ -472,7 +597,7 @@ export interface CrossMarginBase extends BaseContract { validOrder( _orderId: PromiseOrValue, overrides?: CallOverrides - ): Promise<[boolean]>; + ): Promise<[boolean, BigNumber]>; withdraw( _amount: PromiseOrValue, @@ -492,17 +617,6 @@ export interface CrossMarginBase extends BaseContract { overrides?: CallOverrides ): Promise; - activeMarketPositions( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise< - [string, BigNumber, BigNumber] & { - marketKey: string; - margin: BigNumber; - size: BigNumber; - } - >; - cancelOrder( _orderId: PromiseOrValue, overrides?: Overrides & { from?: PromiseOrValue } @@ -520,8 +634,14 @@ export interface CrossMarginBase extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; + depositAndDistribute( + _amount: PromiseOrValue, + _newPositions: IMarginBaseTypes.NewPositionStruct[], + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + distributeMargin( - _newPositions: IMarginBaseTypes.UpdateMarketPositionSpecStruct[], + _newPositions: IMarginBaseTypes.NewPositionStruct[], overrides?: Overrides & { from?: PromiseOrValue } ): Promise; @@ -534,11 +654,20 @@ export interface CrossMarginBase extends BaseContract { gelato(overrides?: CallOverrides): Promise; - getAllActiveMarketPositions( - overrides?: CallOverrides - ): Promise; + getNumberOfInternalPositions(overrides?: CallOverrides): Promise; - getNumberOfActivePositions(overrides?: CallOverrides): Promise; + getPosition( + _marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, BigNumber, BigNumber, BigNumber, BigNumber] & { + id: BigNumber; + fundingIndex: BigNumber; + margin: BigNumber; + lastPrice: BigNumber; + size: BigNumber; + } + >; "initialize()"( overrides?: Overrides & { from?: PromiseOrValue } @@ -556,6 +685,11 @@ export interface CrossMarginBase extends BaseContract { marginBaseSettings(overrides?: CallOverrides): Promise; + marketKeyIndex( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + ops(overrides?: CallOverrides): Promise; orderId(overrides?: CallOverrides): Promise; @@ -564,12 +698,14 @@ export interface CrossMarginBase extends BaseContract { arg0: PromiseOrValue, overrides?: CallOverrides ): Promise< - [string, BigNumber, BigNumber, BigNumber, string] & { + [string, BigNumber, BigNumber, BigNumber, string, number, BigNumber] & { marketKey: string; marginDelta: BigNumber; sizeDelta: BigNumber; - desiredPrice: BigNumber; + targetPrice: BigNumber; gelatoTaskId: string; + orderType: number; + maxDynamicFee: BigNumber; } >; @@ -579,7 +715,18 @@ export interface CrossMarginBase extends BaseContract { _marketKey: PromiseOrValue, _marginDelta: PromiseOrValue, _sizeDelta: PromiseOrValue, - _limitPrice: PromiseOrValue, + _targetPrice: PromiseOrValue, + _orderType: PromiseOrValue, + overrides?: PayableOverrides & { from?: PromiseOrValue } + ): Promise; + + placeOrderWithFeeCap( + _marketKey: PromiseOrValue, + _marginDelta: PromiseOrValue, + _sizeDelta: PromiseOrValue, + _targetPrice: PromiseOrValue, + _orderType: PromiseOrValue, + _maxDynamicFee: PromiseOrValue, overrides?: PayableOverrides & { from?: PromiseOrValue } ): Promise; @@ -587,6 +734,12 @@ export interface CrossMarginBase extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; + rescueERC20( + tokenAddress: PromiseOrValue, + tokenAmount: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + transferOwnership( newOwner: PromiseOrValue, overrides?: Overrides & { from?: PromiseOrValue } @@ -595,7 +748,7 @@ export interface CrossMarginBase extends BaseContract { validOrder( _orderId: PromiseOrValue, overrides?: CallOverrides - ): Promise; + ): Promise<[boolean, BigNumber]>; withdraw( _amount: PromiseOrValue, @@ -615,17 +768,6 @@ export interface CrossMarginBase extends BaseContract { overrides?: CallOverrides ): Promise; - activeMarketPositions( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise< - [string, BigNumber, BigNumber] & { - marketKey: string; - margin: BigNumber; - size: BigNumber; - } - >; - cancelOrder( _orderId: PromiseOrValue, overrides?: CallOverrides @@ -643,8 +785,14 @@ export interface CrossMarginBase extends BaseContract { overrides?: CallOverrides ): Promise; + depositAndDistribute( + _amount: PromiseOrValue, + _newPositions: IMarginBaseTypes.NewPositionStruct[], + overrides?: CallOverrides + ): Promise; + distributeMargin( - _newPositions: IMarginBaseTypes.UpdateMarketPositionSpecStruct[], + _newPositions: IMarginBaseTypes.NewPositionStruct[], overrides?: CallOverrides ): Promise; @@ -657,11 +805,20 @@ export interface CrossMarginBase extends BaseContract { gelato(overrides?: CallOverrides): Promise; - getAllActiveMarketPositions( - overrides?: CallOverrides - ): Promise; + getNumberOfInternalPositions(overrides?: CallOverrides): Promise; - getNumberOfActivePositions(overrides?: CallOverrides): Promise; + getPosition( + _marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, BigNumber, BigNumber, BigNumber, BigNumber] & { + id: BigNumber; + fundingIndex: BigNumber; + margin: BigNumber; + lastPrice: BigNumber; + size: BigNumber; + } + >; "initialize()"(overrides?: CallOverrides): Promise; @@ -677,6 +834,11 @@ export interface CrossMarginBase extends BaseContract { marginBaseSettings(overrides?: CallOverrides): Promise; + marketKeyIndex( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + ops(overrides?: CallOverrides): Promise; orderId(overrides?: CallOverrides): Promise; @@ -685,12 +847,14 @@ export interface CrossMarginBase extends BaseContract { arg0: PromiseOrValue, overrides?: CallOverrides ): Promise< - [string, BigNumber, BigNumber, BigNumber, string] & { + [string, BigNumber, BigNumber, BigNumber, string, number, BigNumber] & { marketKey: string; marginDelta: BigNumber; sizeDelta: BigNumber; - desiredPrice: BigNumber; + targetPrice: BigNumber; gelatoTaskId: string; + orderType: number; + maxDynamicFee: BigNumber; } >; @@ -700,12 +864,29 @@ export interface CrossMarginBase extends BaseContract { _marketKey: PromiseOrValue, _marginDelta: PromiseOrValue, _sizeDelta: PromiseOrValue, - _limitPrice: PromiseOrValue, + _targetPrice: PromiseOrValue, + _orderType: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + placeOrderWithFeeCap( + _marketKey: PromiseOrValue, + _marginDelta: PromiseOrValue, + _sizeDelta: PromiseOrValue, + _targetPrice: PromiseOrValue, + _orderType: PromiseOrValue, + _maxDynamicFee: PromiseOrValue, overrides?: CallOverrides ): Promise; renounceOwnership(overrides?: CallOverrides): Promise; + rescueERC20( + tokenAddress: PromiseOrValue, + tokenAmount: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + transferOwnership( newOwner: PromiseOrValue, overrides?: CallOverrides @@ -714,7 +895,7 @@ export interface CrossMarginBase extends BaseContract { validOrder( _orderId: PromiseOrValue, overrides?: CallOverrides - ): Promise; + ): Promise<[boolean, BigNumber]>; withdraw( _amount: PromiseOrValue, @@ -737,6 +918,56 @@ export interface CrossMarginBase extends BaseContract { amount?: null ): DepositEventFilter; + "FeeImposed(address,uint256)"( + account?: PromiseOrValue | null, + amount?: null + ): FeeImposedEventFilter; + FeeImposed( + account?: PromiseOrValue | null, + amount?: null + ): FeeImposedEventFilter; + + "OrderCancelled(address,uint256)"( + account?: PromiseOrValue | null, + orderId?: null + ): OrderCancelledEventFilter; + OrderCancelled( + account?: PromiseOrValue | null, + orderId?: null + ): OrderCancelledEventFilter; + + "OrderFilled(address,uint256,uint256,uint256)"( + account?: PromiseOrValue | null, + orderId?: null, + fillPrice?: null, + keeperFee?: null + ): OrderFilledEventFilter; + OrderFilled( + account?: PromiseOrValue | null, + orderId?: null, + fillPrice?: null, + keeperFee?: null + ): OrderFilledEventFilter; + + "OrderPlaced(address,uint256,bytes32,int256,int256,uint256,uint8)"( + account?: PromiseOrValue | null, + orderId?: null, + marketKey?: null, + marginDelta?: null, + sizeDelta?: null, + targetPrice?: null, + orderType?: null + ): OrderPlacedEventFilter; + OrderPlaced( + account?: PromiseOrValue | null, + orderId?: null, + marketKey?: null, + marginDelta?: null, + sizeDelta?: null, + targetPrice?: null, + orderType?: null + ): OrderPlacedEventFilter; + "OwnershipTransferred(address,address)"( previousOwner?: PromiseOrValue | null, newOwner?: PromiseOrValue | null @@ -746,6 +977,9 @@ export interface CrossMarginBase extends BaseContract { newOwner?: PromiseOrValue | null ): OwnershipTransferredEventFilter; + "Rescued(address,uint256)"(token?: null, amount?: null): RescuedEventFilter; + Rescued(token?: null, amount?: null): RescuedEventFilter; + "Withdraw(address,uint256)"( user?: PromiseOrValue | null, amount?: null @@ -764,11 +998,6 @@ export interface CrossMarginBase extends BaseContract { overrides?: CallOverrides ): Promise; - activeMarketPositions( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise; - cancelOrder( _orderId: PromiseOrValue, overrides?: Overrides & { from?: PromiseOrValue } @@ -786,8 +1015,14 @@ export interface CrossMarginBase extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; + depositAndDistribute( + _amount: PromiseOrValue, + _newPositions: IMarginBaseTypes.NewPositionStruct[], + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + distributeMargin( - _newPositions: IMarginBaseTypes.UpdateMarketPositionSpecStruct[], + _newPositions: IMarginBaseTypes.NewPositionStruct[], overrides?: Overrides & { from?: PromiseOrValue } ): Promise; @@ -800,9 +1035,12 @@ export interface CrossMarginBase extends BaseContract { gelato(overrides?: CallOverrides): Promise; - getAllActiveMarketPositions(overrides?: CallOverrides): Promise; + getNumberOfInternalPositions(overrides?: CallOverrides): Promise; - getNumberOfActivePositions(overrides?: CallOverrides): Promise; + getPosition( + _marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise; "initialize()"( overrides?: Overrides & { from?: PromiseOrValue } @@ -820,6 +1058,11 @@ export interface CrossMarginBase extends BaseContract { marginBaseSettings(overrides?: CallOverrides): Promise; + marketKeyIndex( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + ops(overrides?: CallOverrides): Promise; orderId(overrides?: CallOverrides): Promise; @@ -835,7 +1078,18 @@ export interface CrossMarginBase extends BaseContract { _marketKey: PromiseOrValue, _marginDelta: PromiseOrValue, _sizeDelta: PromiseOrValue, - _limitPrice: PromiseOrValue, + _targetPrice: PromiseOrValue, + _orderType: PromiseOrValue, + overrides?: PayableOverrides & { from?: PromiseOrValue } + ): Promise; + + placeOrderWithFeeCap( + _marketKey: PromiseOrValue, + _marginDelta: PromiseOrValue, + _sizeDelta: PromiseOrValue, + _targetPrice: PromiseOrValue, + _orderType: PromiseOrValue, + _maxDynamicFee: PromiseOrValue, overrides?: PayableOverrides & { from?: PromiseOrValue } ): Promise; @@ -843,6 +1097,12 @@ export interface CrossMarginBase extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; + rescueERC20( + tokenAddress: PromiseOrValue, + tokenAmount: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + transferOwnership( newOwner: PromiseOrValue, overrides?: Overrides & { from?: PromiseOrValue } @@ -872,11 +1132,6 @@ export interface CrossMarginBase extends BaseContract { overrides?: CallOverrides ): Promise; - activeMarketPositions( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise; - cancelOrder( _orderId: PromiseOrValue, overrides?: Overrides & { from?: PromiseOrValue } @@ -894,8 +1149,14 @@ export interface CrossMarginBase extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; + depositAndDistribute( + _amount: PromiseOrValue, + _newPositions: IMarginBaseTypes.NewPositionStruct[], + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + distributeMargin( - _newPositions: IMarginBaseTypes.UpdateMarketPositionSpecStruct[], + _newPositions: IMarginBaseTypes.NewPositionStruct[], overrides?: Overrides & { from?: PromiseOrValue } ): Promise; @@ -908,11 +1169,12 @@ export interface CrossMarginBase extends BaseContract { gelato(overrides?: CallOverrides): Promise; - getAllActiveMarketPositions( + getNumberOfInternalPositions( overrides?: CallOverrides ): Promise; - getNumberOfActivePositions( + getPosition( + _marketKey: PromiseOrValue, overrides?: CallOverrides ): Promise; @@ -934,6 +1196,11 @@ export interface CrossMarginBase extends BaseContract { overrides?: CallOverrides ): Promise; + marketKeyIndex( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + ops(overrides?: CallOverrides): Promise; orderId(overrides?: CallOverrides): Promise; @@ -949,7 +1216,18 @@ export interface CrossMarginBase extends BaseContract { _marketKey: PromiseOrValue, _marginDelta: PromiseOrValue, _sizeDelta: PromiseOrValue, - _limitPrice: PromiseOrValue, + _targetPrice: PromiseOrValue, + _orderType: PromiseOrValue, + overrides?: PayableOverrides & { from?: PromiseOrValue } + ): Promise; + + placeOrderWithFeeCap( + _marketKey: PromiseOrValue, + _marginDelta: PromiseOrValue, + _sizeDelta: PromiseOrValue, + _targetPrice: PromiseOrValue, + _orderType: PromiseOrValue, + _maxDynamicFee: PromiseOrValue, overrides?: PayableOverrides & { from?: PromiseOrValue } ): Promise; @@ -957,6 +1235,12 @@ export interface CrossMarginBase extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; + rescueERC20( + tokenAddress: PromiseOrValue, + tokenAmount: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + transferOwnership( newOwner: PromiseOrValue, overrides?: Overrides & { from?: PromiseOrValue } diff --git a/lib/abis/types/CrossMarginBaseSettings.ts b/lib/abis/types/CrossMarginBaseSettings.ts index 158e872cf7..ec5d2116b3 100644 --- a/lib/abis/types/CrossMarginBaseSettings.ts +++ b/lib/abis/types/CrossMarginBaseSettings.ts @@ -33,10 +33,10 @@ export interface CrossMarginBaseSettingsInterface extends utils.Interface { "owner()": FunctionFragment; "renounceOwnership()": FunctionFragment; "setLimitOrderFee(uint256)": FunctionFragment; - "setStopLossFee(uint256)": FunctionFragment; + "setStopOrderFee(uint256)": FunctionFragment; "setTradeFee(uint256)": FunctionFragment; "setTreasury(address)": FunctionFragment; - "stopLossFee()": FunctionFragment; + "stopOrderFee()": FunctionFragment; "tradeFee()": FunctionFragment; "transferOwnership(address)": FunctionFragment; "treasury()": FunctionFragment; @@ -48,10 +48,10 @@ export interface CrossMarginBaseSettingsInterface extends utils.Interface { | "owner" | "renounceOwnership" | "setLimitOrderFee" - | "setStopLossFee" + | "setStopOrderFee" | "setTradeFee" | "setTreasury" - | "stopLossFee" + | "stopOrderFee" | "tradeFee" | "transferOwnership" | "treasury" @@ -71,7 +71,7 @@ export interface CrossMarginBaseSettingsInterface extends utils.Interface { values: [PromiseOrValue] ): string; encodeFunctionData( - functionFragment: "setStopLossFee", + functionFragment: "setStopOrderFee", values: [PromiseOrValue] ): string; encodeFunctionData( @@ -83,7 +83,7 @@ export interface CrossMarginBaseSettingsInterface extends utils.Interface { values: [PromiseOrValue] ): string; encodeFunctionData( - functionFragment: "stopLossFee", + functionFragment: "stopOrderFee", values?: undefined ): string; encodeFunctionData(functionFragment: "tradeFee", values?: undefined): string; @@ -107,7 +107,7 @@ export interface CrossMarginBaseSettingsInterface extends utils.Interface { data: BytesLike ): Result; decodeFunctionResult( - functionFragment: "setStopLossFee", + functionFragment: "setStopOrderFee", data: BytesLike ): Result; decodeFunctionResult( @@ -119,7 +119,7 @@ export interface CrossMarginBaseSettingsInterface extends utils.Interface { data: BytesLike ): Result; decodeFunctionResult( - functionFragment: "stopLossFee", + functionFragment: "stopOrderFee", data: BytesLike ): Result; decodeFunctionResult(functionFragment: "tradeFee", data: BytesLike): Result; @@ -132,14 +132,14 @@ export interface CrossMarginBaseSettingsInterface extends utils.Interface { events: { "LimitOrderFeeChanged(uint256)": EventFragment; "OwnershipTransferred(address,address)": EventFragment; - "StopLossFeeChanged(uint256)": EventFragment; + "StopOrderFeeChanged(uint256)": EventFragment; "TradeFeeChanged(uint256)": EventFragment; "TreasuryAddressChanged(address)": EventFragment; }; getEvent(nameOrSignatureOrTopic: "LimitOrderFeeChanged"): EventFragment; getEvent(nameOrSignatureOrTopic: "OwnershipTransferred"): EventFragment; - getEvent(nameOrSignatureOrTopic: "StopLossFeeChanged"): EventFragment; + getEvent(nameOrSignatureOrTopic: "StopOrderFeeChanged"): EventFragment; getEvent(nameOrSignatureOrTopic: "TradeFeeChanged"): EventFragment; getEvent(nameOrSignatureOrTopic: "TreasuryAddressChanged"): EventFragment; } @@ -167,16 +167,16 @@ export type OwnershipTransferredEvent = TypedEvent< export type OwnershipTransferredEventFilter = TypedEventFilter; -export interface StopLossFeeChangedEventObject { +export interface StopOrderFeeChangedEventObject { fee: BigNumber; } -export type StopLossFeeChangedEvent = TypedEvent< +export type StopOrderFeeChangedEvent = TypedEvent< [BigNumber], - StopLossFeeChangedEventObject + StopOrderFeeChangedEventObject >; -export type StopLossFeeChangedEventFilter = - TypedEventFilter; +export type StopOrderFeeChangedEventFilter = + TypedEventFilter; export interface TradeFeeChangedEventObject { fee: BigNumber; @@ -239,7 +239,7 @@ export interface CrossMarginBaseSettings extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; - setStopLossFee( + setStopOrderFee( _fee: PromiseOrValue, overrides?: Overrides & { from?: PromiseOrValue } ): Promise; @@ -254,7 +254,7 @@ export interface CrossMarginBaseSettings extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; - stopLossFee(overrides?: CallOverrides): Promise<[BigNumber]>; + stopOrderFee(overrides?: CallOverrides): Promise<[BigNumber]>; tradeFee(overrides?: CallOverrides): Promise<[BigNumber]>; @@ -279,7 +279,7 @@ export interface CrossMarginBaseSettings extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; - setStopLossFee( + setStopOrderFee( _fee: PromiseOrValue, overrides?: Overrides & { from?: PromiseOrValue } ): Promise; @@ -294,7 +294,7 @@ export interface CrossMarginBaseSettings extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; - stopLossFee(overrides?: CallOverrides): Promise; + stopOrderFee(overrides?: CallOverrides): Promise; tradeFee(overrides?: CallOverrides): Promise; @@ -317,7 +317,7 @@ export interface CrossMarginBaseSettings extends BaseContract { overrides?: CallOverrides ): Promise; - setStopLossFee( + setStopOrderFee( _fee: PromiseOrValue, overrides?: CallOverrides ): Promise; @@ -332,7 +332,7 @@ export interface CrossMarginBaseSettings extends BaseContract { overrides?: CallOverrides ): Promise; - stopLossFee(overrides?: CallOverrides): Promise; + stopOrderFee(overrides?: CallOverrides): Promise; tradeFee(overrides?: CallOverrides): Promise; @@ -359,8 +359,8 @@ export interface CrossMarginBaseSettings extends BaseContract { newOwner?: PromiseOrValue | null ): OwnershipTransferredEventFilter; - "StopLossFeeChanged(uint256)"(fee?: null): StopLossFeeChangedEventFilter; - StopLossFeeChanged(fee?: null): StopLossFeeChangedEventFilter; + "StopOrderFeeChanged(uint256)"(fee?: null): StopOrderFeeChangedEventFilter; + StopOrderFeeChanged(fee?: null): StopOrderFeeChangedEventFilter; "TradeFeeChanged(uint256)"(fee?: null): TradeFeeChangedEventFilter; TradeFeeChanged(fee?: null): TradeFeeChangedEventFilter; @@ -385,7 +385,7 @@ export interface CrossMarginBaseSettings extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; - setStopLossFee( + setStopOrderFee( _fee: PromiseOrValue, overrides?: Overrides & { from?: PromiseOrValue } ): Promise; @@ -400,7 +400,7 @@ export interface CrossMarginBaseSettings extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; - stopLossFee(overrides?: CallOverrides): Promise; + stopOrderFee(overrides?: CallOverrides): Promise; tradeFee(overrides?: CallOverrides): Promise; @@ -426,7 +426,7 @@ export interface CrossMarginBaseSettings extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; - setStopLossFee( + setStopOrderFee( _fee: PromiseOrValue, overrides?: Overrides & { from?: PromiseOrValue } ): Promise; @@ -441,7 +441,7 @@ export interface CrossMarginBaseSettings extends BaseContract { overrides?: Overrides & { from?: PromiseOrValue } ): Promise; - stopLossFee(overrides?: CallOverrides): Promise; + stopOrderFee(overrides?: CallOverrides): Promise; tradeFee(overrides?: CallOverrides): Promise; diff --git a/lib/abis/types/FuturesMarket.ts b/lib/abis/types/FuturesMarket.ts new file mode 100644 index 0000000000..5fffffb6ee --- /dev/null +++ b/lib/abis/types/FuturesMarket.ts @@ -0,0 +1,1596 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { + BaseContract, + BigNumber, + BigNumberish, + BytesLike, + CallOverrides, + ContractTransaction, + Overrides, + PopulatedTransaction, + Signer, + utils, +} from "ethers"; +import type { + FunctionFragment, + Result, + EventFragment, +} from "@ethersproject/abi"; +import type { Listener, Provider } from "@ethersproject/providers"; +import type { + TypedEventFilter, + TypedEvent, + TypedListener, + OnEvent, + PromiseOrValue, +} from "./common"; + +export interface FuturesMarketInterface extends utils.Interface { + functions: { + "accessibleMargin(address)": FunctionFragment; + "accruedFunding(address)": FunctionFragment; + "assetPrice()": FunctionFragment; + "baseAsset()": FunctionFragment; + "canLiquidate(address)": FunctionFragment; + "cancelNextPriceOrder(address)": FunctionFragment; + "closePosition()": FunctionFragment; + "closePositionWithTracking(bytes32)": FunctionFragment; + "currentFundingRate()": FunctionFragment; + "executeNextPriceOrder(address)": FunctionFragment; + "fundingLastRecomputed()": FunctionFragment; + "fundingSequence(uint256)": FunctionFragment; + "fundingSequenceLength()": FunctionFragment; + "isResolverCached()": FunctionFragment; + "liquidatePosition(address)": FunctionFragment; + "liquidationFee(address)": FunctionFragment; + "liquidationPrice(address)": FunctionFragment; + "marketDebt()": FunctionFragment; + "marketKey()": FunctionFragment; + "marketSize()": FunctionFragment; + "marketSizes()": FunctionFragment; + "marketSkew()": FunctionFragment; + "modifyPosition(int256)": FunctionFragment; + "modifyPositionWithTracking(int256,bytes32)": FunctionFragment; + "nextPriceOrders(address)": FunctionFragment; + "notionalValue(address)": FunctionFragment; + "orderFee(int256)": FunctionFragment; + "positions(address)": FunctionFragment; + "postTradeDetails(int256,address)": FunctionFragment; + "profitLoss(address)": FunctionFragment; + "rebuildCache()": FunctionFragment; + "recomputeFunding()": FunctionFragment; + "remainingMargin(address)": FunctionFragment; + "resolver()": FunctionFragment; + "resolverAddressesRequired()": FunctionFragment; + "submitNextPriceOrder(int256)": FunctionFragment; + "submitNextPriceOrderWithTracking(int256,bytes32)": FunctionFragment; + "transferMargin(int256)": FunctionFragment; + "unrecordedFunding()": FunctionFragment; + "withdrawAllMargin()": FunctionFragment; + }; + + getFunction( + nameOrSignatureOrTopic: + | "accessibleMargin" + | "accruedFunding" + | "assetPrice" + | "baseAsset" + | "canLiquidate" + | "cancelNextPriceOrder" + | "closePosition" + | "closePositionWithTracking" + | "currentFundingRate" + | "executeNextPriceOrder" + | "fundingLastRecomputed" + | "fundingSequence" + | "fundingSequenceLength" + | "isResolverCached" + | "liquidatePosition" + | "liquidationFee" + | "liquidationPrice" + | "marketDebt" + | "marketKey" + | "marketSize" + | "marketSizes" + | "marketSkew" + | "modifyPosition" + | "modifyPositionWithTracking" + | "nextPriceOrders" + | "notionalValue" + | "orderFee" + | "positions" + | "postTradeDetails" + | "profitLoss" + | "rebuildCache" + | "recomputeFunding" + | "remainingMargin" + | "resolver" + | "resolverAddressesRequired" + | "submitNextPriceOrder" + | "submitNextPriceOrderWithTracking" + | "transferMargin" + | "unrecordedFunding" + | "withdrawAllMargin" + ): FunctionFragment; + + encodeFunctionData( + functionFragment: "accessibleMargin", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "accruedFunding", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "assetPrice", + values?: undefined + ): string; + encodeFunctionData(functionFragment: "baseAsset", values?: undefined): string; + encodeFunctionData( + functionFragment: "canLiquidate", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "cancelNextPriceOrder", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "closePosition", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "closePositionWithTracking", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "currentFundingRate", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "executeNextPriceOrder", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "fundingLastRecomputed", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "fundingSequence", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "fundingSequenceLength", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "isResolverCached", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "liquidatePosition", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "liquidationFee", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "liquidationPrice", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "marketDebt", + values?: undefined + ): string; + encodeFunctionData(functionFragment: "marketKey", values?: undefined): string; + encodeFunctionData( + functionFragment: "marketSize", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "marketSizes", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "marketSkew", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "modifyPosition", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "modifyPositionWithTracking", + values: [PromiseOrValue, PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "nextPriceOrders", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "notionalValue", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "orderFee", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "positions", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "postTradeDetails", + values: [PromiseOrValue, PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "profitLoss", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "rebuildCache", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "recomputeFunding", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "remainingMargin", + values: [PromiseOrValue] + ): string; + encodeFunctionData(functionFragment: "resolver", values?: undefined): string; + encodeFunctionData( + functionFragment: "resolverAddressesRequired", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "submitNextPriceOrder", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "submitNextPriceOrderWithTracking", + values: [PromiseOrValue, PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "transferMargin", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "unrecordedFunding", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "withdrawAllMargin", + values?: undefined + ): string; + + decodeFunctionResult( + functionFragment: "accessibleMargin", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "accruedFunding", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "assetPrice", data: BytesLike): Result; + decodeFunctionResult(functionFragment: "baseAsset", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "canLiquidate", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "cancelNextPriceOrder", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "closePosition", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "closePositionWithTracking", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "currentFundingRate", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "executeNextPriceOrder", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "fundingLastRecomputed", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "fundingSequence", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "fundingSequenceLength", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "isResolverCached", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "liquidatePosition", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "liquidationFee", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "liquidationPrice", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "marketDebt", data: BytesLike): Result; + decodeFunctionResult(functionFragment: "marketKey", data: BytesLike): Result; + decodeFunctionResult(functionFragment: "marketSize", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "marketSizes", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "marketSkew", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "modifyPosition", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "modifyPositionWithTracking", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "nextPriceOrders", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "notionalValue", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "orderFee", data: BytesLike): Result; + decodeFunctionResult(functionFragment: "positions", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "postTradeDetails", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "profitLoss", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "rebuildCache", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "recomputeFunding", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "remainingMargin", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "resolver", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "resolverAddressesRequired", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "submitNextPriceOrder", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "submitNextPriceOrderWithTracking", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "transferMargin", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "unrecordedFunding", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "withdrawAllMargin", + data: BytesLike + ): Result; + + events: { + "CacheUpdated(bytes32,address)": EventFragment; + "FundingRecomputed(int256,uint256,uint256)": EventFragment; + "FuturesTracking(bytes32,bytes32,bytes32,int256,uint256)": EventFragment; + "MarginTransferred(address,int256)": EventFragment; + "NextPriceOrderRemoved(address,uint256,int256,uint256,uint256,uint256,bytes32)": EventFragment; + "NextPriceOrderSubmitted(address,int256,uint256,uint256,uint256,bytes32)": EventFragment; + "PositionLiquidated(uint256,address,address,int256,uint256,uint256)": EventFragment; + "PositionModified(uint256,address,uint256,int256,int256,uint256,uint256,uint256)": EventFragment; + }; + + getEvent(nameOrSignatureOrTopic: "CacheUpdated"): EventFragment; + getEvent(nameOrSignatureOrTopic: "FundingRecomputed"): EventFragment; + getEvent(nameOrSignatureOrTopic: "FuturesTracking"): EventFragment; + getEvent(nameOrSignatureOrTopic: "MarginTransferred"): EventFragment; + getEvent(nameOrSignatureOrTopic: "NextPriceOrderRemoved"): EventFragment; + getEvent(nameOrSignatureOrTopic: "NextPriceOrderSubmitted"): EventFragment; + getEvent(nameOrSignatureOrTopic: "PositionLiquidated"): EventFragment; + getEvent(nameOrSignatureOrTopic: "PositionModified"): EventFragment; +} + +export interface CacheUpdatedEventObject { + name: string; + destination: string; +} +export type CacheUpdatedEvent = TypedEvent< + [string, string], + CacheUpdatedEventObject +>; + +export type CacheUpdatedEventFilter = TypedEventFilter; + +export interface FundingRecomputedEventObject { + funding: BigNumber; + index: BigNumber; + timestamp: BigNumber; +} +export type FundingRecomputedEvent = TypedEvent< + [BigNumber, BigNumber, BigNumber], + FundingRecomputedEventObject +>; + +export type FundingRecomputedEventFilter = + TypedEventFilter; + +export interface FuturesTrackingEventObject { + trackingCode: string; + baseAsset: string; + marketKey: string; + sizeDelta: BigNumber; + fee: BigNumber; +} +export type FuturesTrackingEvent = TypedEvent< + [string, string, string, BigNumber, BigNumber], + FuturesTrackingEventObject +>; + +export type FuturesTrackingEventFilter = TypedEventFilter; + +export interface MarginTransferredEventObject { + account: string; + marginDelta: BigNumber; +} +export type MarginTransferredEvent = TypedEvent< + [string, BigNumber], + MarginTransferredEventObject +>; + +export type MarginTransferredEventFilter = + TypedEventFilter; + +export interface NextPriceOrderRemovedEventObject { + account: string; + currentRoundId: BigNumber; + sizeDelta: BigNumber; + targetRoundId: BigNumber; + commitDeposit: BigNumber; + keeperDeposit: BigNumber; + trackingCode: string; +} +export type NextPriceOrderRemovedEvent = TypedEvent< + [string, BigNumber, BigNumber, BigNumber, BigNumber, BigNumber, string], + NextPriceOrderRemovedEventObject +>; + +export type NextPriceOrderRemovedEventFilter = + TypedEventFilter; + +export interface NextPriceOrderSubmittedEventObject { + account: string; + sizeDelta: BigNumber; + targetRoundId: BigNumber; + commitDeposit: BigNumber; + keeperDeposit: BigNumber; + trackingCode: string; +} +export type NextPriceOrderSubmittedEvent = TypedEvent< + [string, BigNumber, BigNumber, BigNumber, BigNumber, string], + NextPriceOrderSubmittedEventObject +>; + +export type NextPriceOrderSubmittedEventFilter = + TypedEventFilter; + +export interface PositionLiquidatedEventObject { + id: BigNumber; + account: string; + liquidator: string; + size: BigNumber; + price: BigNumber; + fee: BigNumber; +} +export type PositionLiquidatedEvent = TypedEvent< + [BigNumber, string, string, BigNumber, BigNumber, BigNumber], + PositionLiquidatedEventObject +>; + +export type PositionLiquidatedEventFilter = + TypedEventFilter; + +export interface PositionModifiedEventObject { + id: BigNumber; + account: string; + margin: BigNumber; + size: BigNumber; + tradeSize: BigNumber; + lastPrice: BigNumber; + fundingIndex: BigNumber; + fee: BigNumber; +} +export type PositionModifiedEvent = TypedEvent< + [ + BigNumber, + string, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber + ], + PositionModifiedEventObject +>; + +export type PositionModifiedEventFilter = + TypedEventFilter; + +export interface FuturesMarket extends BaseContract { + connect(signerOrProvider: Signer | Provider | string): this; + attach(addressOrName: string): this; + deployed(): Promise; + + interface: FuturesMarketInterface; + + queryFilter( + event: TypedEventFilter, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined + ): Promise>; + + listeners( + eventFilter?: TypedEventFilter + ): Array>; + listeners(eventName?: string): Array; + removeAllListeners( + eventFilter: TypedEventFilter + ): this; + removeAllListeners(eventName?: string): this; + off: OnEvent; + on: OnEvent; + once: OnEvent; + removeListener: OnEvent; + + functions: { + accessibleMargin( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, boolean] & { marginAccessible: BigNumber; invalid: boolean } + >; + + accruedFunding( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { funding: BigNumber; invalid: boolean }>; + + assetPrice( + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { price: BigNumber; invalid: boolean }>; + + baseAsset(overrides?: CallOverrides): Promise<[string]>; + + canLiquidate( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[boolean]>; + + cancelNextPriceOrder( + account: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + closePosition( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + closePositionWithTracking( + trackingCode: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + currentFundingRate(overrides?: CallOverrides): Promise<[BigNumber]>; + + executeNextPriceOrder( + account: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + fundingLastRecomputed(overrides?: CallOverrides): Promise<[number]>; + + fundingSequence( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber]>; + + fundingSequenceLength(overrides?: CallOverrides): Promise<[BigNumber]>; + + isResolverCached(overrides?: CallOverrides): Promise<[boolean]>; + + liquidatePosition( + account: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + liquidationFee( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber]>; + + liquidationPrice( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { price: BigNumber; invalid: boolean }>; + + marketDebt( + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { debt: BigNumber; invalid: boolean }>; + + marketKey(overrides?: CallOverrides): Promise<[string]>; + + marketSize(overrides?: CallOverrides): Promise<[BigNumber]>; + + marketSizes( + overrides?: CallOverrides + ): Promise<[BigNumber, BigNumber] & { long: BigNumber; short: BigNumber }>; + + marketSkew(overrides?: CallOverrides): Promise<[BigNumber]>; + + modifyPosition( + sizeDelta: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + modifyPositionWithTracking( + sizeDelta: PromiseOrValue, + trackingCode: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + nextPriceOrders( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, BigNumber, BigNumber, BigNumber, string] & { + sizeDelta: BigNumber; + targetRoundId: BigNumber; + commitDeposit: BigNumber; + keeperDeposit: BigNumber; + trackingCode: string; + } + >; + + notionalValue( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { value: BigNumber; invalid: boolean }>; + + orderFee( + sizeDelta: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { fee: BigNumber; invalid: boolean }>; + + positions( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, BigNumber, BigNumber, BigNumber, BigNumber] & { + id: BigNumber; + lastFundingIndex: BigNumber; + margin: BigNumber; + lastPrice: BigNumber; + size: BigNumber; + } + >; + + postTradeDetails( + sizeDelta: PromiseOrValue, + sender: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, BigNumber, BigNumber, BigNumber, BigNumber, number] & { + margin: BigNumber; + size: BigNumber; + price: BigNumber; + liqPrice: BigNumber; + fee: BigNumber; + status: number; + } + >; + + profitLoss( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { pnl: BigNumber; invalid: boolean }>; + + rebuildCache( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + recomputeFunding( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + remainingMargin( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, boolean] & { marginRemaining: BigNumber; invalid: boolean } + >; + + resolver(overrides?: CallOverrides): Promise<[string]>; + + resolverAddressesRequired( + overrides?: CallOverrides + ): Promise<[string[]] & { addresses: string[] }>; + + submitNextPriceOrder( + sizeDelta: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + submitNextPriceOrderWithTracking( + sizeDelta: PromiseOrValue, + trackingCode: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + transferMargin( + marginDelta: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + unrecordedFunding( + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { funding: BigNumber; invalid: boolean }>; + + withdrawAllMargin( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + }; + + accessibleMargin( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, boolean] & { marginAccessible: BigNumber; invalid: boolean } + >; + + accruedFunding( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { funding: BigNumber; invalid: boolean }>; + + assetPrice( + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { price: BigNumber; invalid: boolean }>; + + baseAsset(overrides?: CallOverrides): Promise; + + canLiquidate( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + cancelNextPriceOrder( + account: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + closePosition( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + closePositionWithTracking( + trackingCode: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + currentFundingRate(overrides?: CallOverrides): Promise; + + executeNextPriceOrder( + account: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + fundingLastRecomputed(overrides?: CallOverrides): Promise; + + fundingSequence( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + fundingSequenceLength(overrides?: CallOverrides): Promise; + + isResolverCached(overrides?: CallOverrides): Promise; + + liquidatePosition( + account: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + liquidationFee( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + liquidationPrice( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { price: BigNumber; invalid: boolean }>; + + marketDebt( + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { debt: BigNumber; invalid: boolean }>; + + marketKey(overrides?: CallOverrides): Promise; + + marketSize(overrides?: CallOverrides): Promise; + + marketSizes( + overrides?: CallOverrides + ): Promise<[BigNumber, BigNumber] & { long: BigNumber; short: BigNumber }>; + + marketSkew(overrides?: CallOverrides): Promise; + + modifyPosition( + sizeDelta: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + modifyPositionWithTracking( + sizeDelta: PromiseOrValue, + trackingCode: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + nextPriceOrders( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, BigNumber, BigNumber, BigNumber, string] & { + sizeDelta: BigNumber; + targetRoundId: BigNumber; + commitDeposit: BigNumber; + keeperDeposit: BigNumber; + trackingCode: string; + } + >; + + notionalValue( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { value: BigNumber; invalid: boolean }>; + + orderFee( + sizeDelta: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { fee: BigNumber; invalid: boolean }>; + + positions( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, BigNumber, BigNumber, BigNumber, BigNumber] & { + id: BigNumber; + lastFundingIndex: BigNumber; + margin: BigNumber; + lastPrice: BigNumber; + size: BigNumber; + } + >; + + postTradeDetails( + sizeDelta: PromiseOrValue, + sender: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, BigNumber, BigNumber, BigNumber, BigNumber, number] & { + margin: BigNumber; + size: BigNumber; + price: BigNumber; + liqPrice: BigNumber; + fee: BigNumber; + status: number; + } + >; + + profitLoss( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { pnl: BigNumber; invalid: boolean }>; + + rebuildCache( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + recomputeFunding( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + remainingMargin( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, boolean] & { marginRemaining: BigNumber; invalid: boolean } + >; + + resolver(overrides?: CallOverrides): Promise; + + resolverAddressesRequired(overrides?: CallOverrides): Promise; + + submitNextPriceOrder( + sizeDelta: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + submitNextPriceOrderWithTracking( + sizeDelta: PromiseOrValue, + trackingCode: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + transferMargin( + marginDelta: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + unrecordedFunding( + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { funding: BigNumber; invalid: boolean }>; + + withdrawAllMargin( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + callStatic: { + accessibleMargin( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, boolean] & { marginAccessible: BigNumber; invalid: boolean } + >; + + accruedFunding( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { funding: BigNumber; invalid: boolean }>; + + assetPrice( + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { price: BigNumber; invalid: boolean }>; + + baseAsset(overrides?: CallOverrides): Promise; + + canLiquidate( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + cancelNextPriceOrder( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + closePosition(overrides?: CallOverrides): Promise; + + closePositionWithTracking( + trackingCode: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + currentFundingRate(overrides?: CallOverrides): Promise; + + executeNextPriceOrder( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + fundingLastRecomputed(overrides?: CallOverrides): Promise; + + fundingSequence( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + fundingSequenceLength(overrides?: CallOverrides): Promise; + + isResolverCached(overrides?: CallOverrides): Promise; + + liquidatePosition( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + liquidationFee( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + liquidationPrice( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { price: BigNumber; invalid: boolean }>; + + marketDebt( + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { debt: BigNumber; invalid: boolean }>; + + marketKey(overrides?: CallOverrides): Promise; + + marketSize(overrides?: CallOverrides): Promise; + + marketSizes( + overrides?: CallOverrides + ): Promise<[BigNumber, BigNumber] & { long: BigNumber; short: BigNumber }>; + + marketSkew(overrides?: CallOverrides): Promise; + + modifyPosition( + sizeDelta: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + modifyPositionWithTracking( + sizeDelta: PromiseOrValue, + trackingCode: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + nextPriceOrders( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, BigNumber, BigNumber, BigNumber, string] & { + sizeDelta: BigNumber; + targetRoundId: BigNumber; + commitDeposit: BigNumber; + keeperDeposit: BigNumber; + trackingCode: string; + } + >; + + notionalValue( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { value: BigNumber; invalid: boolean }>; + + orderFee( + sizeDelta: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { fee: BigNumber; invalid: boolean }>; + + positions( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, BigNumber, BigNumber, BigNumber, BigNumber] & { + id: BigNumber; + lastFundingIndex: BigNumber; + margin: BigNumber; + lastPrice: BigNumber; + size: BigNumber; + } + >; + + postTradeDetails( + sizeDelta: PromiseOrValue, + sender: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, BigNumber, BigNumber, BigNumber, BigNumber, number] & { + margin: BigNumber; + size: BigNumber; + price: BigNumber; + liqPrice: BigNumber; + fee: BigNumber; + status: number; + } + >; + + profitLoss( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { pnl: BigNumber; invalid: boolean }>; + + rebuildCache(overrides?: CallOverrides): Promise; + + recomputeFunding(overrides?: CallOverrides): Promise; + + remainingMargin( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise< + [BigNumber, boolean] & { marginRemaining: BigNumber; invalid: boolean } + >; + + resolver(overrides?: CallOverrides): Promise; + + resolverAddressesRequired(overrides?: CallOverrides): Promise; + + submitNextPriceOrder( + sizeDelta: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + submitNextPriceOrderWithTracking( + sizeDelta: PromiseOrValue, + trackingCode: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + transferMargin( + marginDelta: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + unrecordedFunding( + overrides?: CallOverrides + ): Promise<[BigNumber, boolean] & { funding: BigNumber; invalid: boolean }>; + + withdrawAllMargin(overrides?: CallOverrides): Promise; + }; + + filters: { + "CacheUpdated(bytes32,address)"( + name?: null, + destination?: null + ): CacheUpdatedEventFilter; + CacheUpdated(name?: null, destination?: null): CacheUpdatedEventFilter; + + "FundingRecomputed(int256,uint256,uint256)"( + funding?: null, + index?: null, + timestamp?: null + ): FundingRecomputedEventFilter; + FundingRecomputed( + funding?: null, + index?: null, + timestamp?: null + ): FundingRecomputedEventFilter; + + "FuturesTracking(bytes32,bytes32,bytes32,int256,uint256)"( + trackingCode?: PromiseOrValue | null, + baseAsset?: null, + marketKey?: null, + sizeDelta?: null, + fee?: null + ): FuturesTrackingEventFilter; + FuturesTracking( + trackingCode?: PromiseOrValue | null, + baseAsset?: null, + marketKey?: null, + sizeDelta?: null, + fee?: null + ): FuturesTrackingEventFilter; + + "MarginTransferred(address,int256)"( + account?: PromiseOrValue | null, + marginDelta?: null + ): MarginTransferredEventFilter; + MarginTransferred( + account?: PromiseOrValue | null, + marginDelta?: null + ): MarginTransferredEventFilter; + + "NextPriceOrderRemoved(address,uint256,int256,uint256,uint256,uint256,bytes32)"( + account?: PromiseOrValue | null, + currentRoundId?: null, + sizeDelta?: null, + targetRoundId?: null, + commitDeposit?: null, + keeperDeposit?: null, + trackingCode?: null + ): NextPriceOrderRemovedEventFilter; + NextPriceOrderRemoved( + account?: PromiseOrValue | null, + currentRoundId?: null, + sizeDelta?: null, + targetRoundId?: null, + commitDeposit?: null, + keeperDeposit?: null, + trackingCode?: null + ): NextPriceOrderRemovedEventFilter; + + "NextPriceOrderSubmitted(address,int256,uint256,uint256,uint256,bytes32)"( + account?: PromiseOrValue | null, + sizeDelta?: null, + targetRoundId?: null, + commitDeposit?: null, + keeperDeposit?: null, + trackingCode?: null + ): NextPriceOrderSubmittedEventFilter; + NextPriceOrderSubmitted( + account?: PromiseOrValue | null, + sizeDelta?: null, + targetRoundId?: null, + commitDeposit?: null, + keeperDeposit?: null, + trackingCode?: null + ): NextPriceOrderSubmittedEventFilter; + + "PositionLiquidated(uint256,address,address,int256,uint256,uint256)"( + id?: PromiseOrValue | null, + account?: PromiseOrValue | null, + liquidator?: PromiseOrValue | null, + size?: null, + price?: null, + fee?: null + ): PositionLiquidatedEventFilter; + PositionLiquidated( + id?: PromiseOrValue | null, + account?: PromiseOrValue | null, + liquidator?: PromiseOrValue | null, + size?: null, + price?: null, + fee?: null + ): PositionLiquidatedEventFilter; + + "PositionModified(uint256,address,uint256,int256,int256,uint256,uint256,uint256)"( + id?: PromiseOrValue | null, + account?: PromiseOrValue | null, + margin?: null, + size?: null, + tradeSize?: null, + lastPrice?: null, + fundingIndex?: null, + fee?: null + ): PositionModifiedEventFilter; + PositionModified( + id?: PromiseOrValue | null, + account?: PromiseOrValue | null, + margin?: null, + size?: null, + tradeSize?: null, + lastPrice?: null, + fundingIndex?: null, + fee?: null + ): PositionModifiedEventFilter; + }; + + estimateGas: { + accessibleMargin( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + accruedFunding( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + assetPrice(overrides?: CallOverrides): Promise; + + baseAsset(overrides?: CallOverrides): Promise; + + canLiquidate( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + cancelNextPriceOrder( + account: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + closePosition( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + closePositionWithTracking( + trackingCode: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + currentFundingRate(overrides?: CallOverrides): Promise; + + executeNextPriceOrder( + account: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + fundingLastRecomputed(overrides?: CallOverrides): Promise; + + fundingSequence( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + fundingSequenceLength(overrides?: CallOverrides): Promise; + + isResolverCached(overrides?: CallOverrides): Promise; + + liquidatePosition( + account: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + liquidationFee( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + liquidationPrice( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + marketDebt(overrides?: CallOverrides): Promise; + + marketKey(overrides?: CallOverrides): Promise; + + marketSize(overrides?: CallOverrides): Promise; + + marketSizes(overrides?: CallOverrides): Promise; + + marketSkew(overrides?: CallOverrides): Promise; + + modifyPosition( + sizeDelta: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + modifyPositionWithTracking( + sizeDelta: PromiseOrValue, + trackingCode: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + nextPriceOrders( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + notionalValue( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + orderFee( + sizeDelta: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + positions( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + postTradeDetails( + sizeDelta: PromiseOrValue, + sender: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + profitLoss( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + rebuildCache( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + recomputeFunding( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + remainingMargin( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + resolver(overrides?: CallOverrides): Promise; + + resolverAddressesRequired(overrides?: CallOverrides): Promise; + + submitNextPriceOrder( + sizeDelta: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + submitNextPriceOrderWithTracking( + sizeDelta: PromiseOrValue, + trackingCode: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + transferMargin( + marginDelta: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + unrecordedFunding(overrides?: CallOverrides): Promise; + + withdrawAllMargin( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + }; + + populateTransaction: { + accessibleMargin( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + accruedFunding( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + assetPrice(overrides?: CallOverrides): Promise; + + baseAsset(overrides?: CallOverrides): Promise; + + canLiquidate( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + cancelNextPriceOrder( + account: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + closePosition( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + closePositionWithTracking( + trackingCode: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + currentFundingRate( + overrides?: CallOverrides + ): Promise; + + executeNextPriceOrder( + account: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + fundingLastRecomputed( + overrides?: CallOverrides + ): Promise; + + fundingSequence( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + fundingSequenceLength( + overrides?: CallOverrides + ): Promise; + + isResolverCached(overrides?: CallOverrides): Promise; + + liquidatePosition( + account: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + liquidationFee( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + liquidationPrice( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + marketDebt(overrides?: CallOverrides): Promise; + + marketKey(overrides?: CallOverrides): Promise; + + marketSize(overrides?: CallOverrides): Promise; + + marketSizes(overrides?: CallOverrides): Promise; + + marketSkew(overrides?: CallOverrides): Promise; + + modifyPosition( + sizeDelta: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + modifyPositionWithTracking( + sizeDelta: PromiseOrValue, + trackingCode: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + nextPriceOrders( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + notionalValue( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + orderFee( + sizeDelta: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + positions( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + postTradeDetails( + sizeDelta: PromiseOrValue, + sender: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + profitLoss( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + rebuildCache( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + recomputeFunding( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + remainingMargin( + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + resolver(overrides?: CallOverrides): Promise; + + resolverAddressesRequired( + overrides?: CallOverrides + ): Promise; + + submitNextPriceOrder( + sizeDelta: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + submitNextPriceOrderWithTracking( + sizeDelta: PromiseOrValue, + trackingCode: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + transferMargin( + marginDelta: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + unrecordedFunding(overrides?: CallOverrides): Promise; + + withdrawAllMargin( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + }; +} diff --git a/lib/abis/types/FuturesMarketData.ts b/lib/abis/types/FuturesMarketData.ts new file mode 100644 index 0000000000..5bbcc36f55 --- /dev/null +++ b/lib/abis/types/FuturesMarketData.ts @@ -0,0 +1,650 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { + BaseContract, + BigNumber, + BigNumberish, + BytesLike, + CallOverrides, + PopulatedTransaction, + Signer, + utils, +} from "ethers"; +import type { FunctionFragment, Result } from "@ethersproject/abi"; +import type { Listener, Provider } from "@ethersproject/providers"; +import type { + TypedEventFilter, + TypedEvent, + TypedListener, + OnEvent, + PromiseOrValue, +} from "./common"; + +export declare namespace FuturesMarketData { + export type FeeRatesStruct = { + takerFee: PromiseOrValue; + makerFee: PromiseOrValue; + takerFeeNextPrice: PromiseOrValue; + makerFeeNextPrice: PromiseOrValue; + }; + + export type FeeRatesStructOutput = [ + BigNumber, + BigNumber, + BigNumber, + BigNumber + ] & { + takerFee: BigNumber; + makerFee: BigNumber; + takerFeeNextPrice: BigNumber; + makerFeeNextPrice: BigNumber; + }; + + export type MarketSummaryStruct = { + market: PromiseOrValue; + asset: PromiseOrValue; + key: PromiseOrValue; + maxLeverage: PromiseOrValue; + price: PromiseOrValue; + marketSize: PromiseOrValue; + marketSkew: PromiseOrValue; + marketDebt: PromiseOrValue; + currentFundingRate: PromiseOrValue; + feeRates: FuturesMarketData.FeeRatesStruct; + }; + + export type MarketSummaryStructOutput = [ + string, + string, + string, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + FuturesMarketData.FeeRatesStructOutput + ] & { + market: string; + asset: string; + key: string; + maxLeverage: BigNumber; + price: BigNumber; + marketSize: BigNumber; + marketSkew: BigNumber; + marketDebt: BigNumber; + currentFundingRate: BigNumber; + feeRates: FuturesMarketData.FeeRatesStructOutput; + }; + + export type FuturesGlobalsStruct = { + minInitialMargin: PromiseOrValue; + liquidationFeeRatio: PromiseOrValue; + liquidationBufferRatio: PromiseOrValue; + minKeeperFee: PromiseOrValue; + }; + + export type FuturesGlobalsStructOutput = [ + BigNumber, + BigNumber, + BigNumber, + BigNumber + ] & { + minInitialMargin: BigNumber; + liquidationFeeRatio: BigNumber; + liquidationBufferRatio: BigNumber; + minKeeperFee: BigNumber; + }; + + export type MarketLimitsStruct = { + maxLeverage: PromiseOrValue; + maxMarketValueUSD: PromiseOrValue; + }; + + export type MarketLimitsStructOutput = [BigNumber, BigNumber] & { + maxLeverage: BigNumber; + maxMarketValueUSD: BigNumber; + }; + + export type FundingParametersStruct = { + maxFundingRate: PromiseOrValue; + skewScaleUSD: PromiseOrValue; + }; + + export type FundingParametersStructOutput = [BigNumber, BigNumber] & { + maxFundingRate: BigNumber; + skewScaleUSD: BigNumber; + }; + + export type SidesStruct = { + long: PromiseOrValue; + short: PromiseOrValue; + }; + + export type SidesStructOutput = [BigNumber, BigNumber] & { + long: BigNumber; + short: BigNumber; + }; + + export type MarketSizeDetailsStruct = { + marketSize: PromiseOrValue; + sides: FuturesMarketData.SidesStruct; + marketDebt: PromiseOrValue; + marketSkew: PromiseOrValue; + }; + + export type MarketSizeDetailsStructOutput = [ + BigNumber, + FuturesMarketData.SidesStructOutput, + BigNumber, + BigNumber + ] & { + marketSize: BigNumber; + sides: FuturesMarketData.SidesStructOutput; + marketDebt: BigNumber; + marketSkew: BigNumber; + }; + + export type PriceDetailsStruct = { + price: PromiseOrValue; + invalid: PromiseOrValue; + }; + + export type PriceDetailsStructOutput = [BigNumber, boolean] & { + price: BigNumber; + invalid: boolean; + }; + + export type MarketDataStruct = { + market: PromiseOrValue; + baseAsset: PromiseOrValue; + marketKey: PromiseOrValue; + feeRates: FuturesMarketData.FeeRatesStruct; + limits: FuturesMarketData.MarketLimitsStruct; + fundingParameters: FuturesMarketData.FundingParametersStruct; + marketSizeDetails: FuturesMarketData.MarketSizeDetailsStruct; + priceDetails: FuturesMarketData.PriceDetailsStruct; + }; + + export type MarketDataStructOutput = [ + string, + string, + string, + FuturesMarketData.FeeRatesStructOutput, + FuturesMarketData.MarketLimitsStructOutput, + FuturesMarketData.FundingParametersStructOutput, + FuturesMarketData.MarketSizeDetailsStructOutput, + FuturesMarketData.PriceDetailsStructOutput + ] & { + market: string; + baseAsset: string; + marketKey: string; + feeRates: FuturesMarketData.FeeRatesStructOutput; + limits: FuturesMarketData.MarketLimitsStructOutput; + fundingParameters: FuturesMarketData.FundingParametersStructOutput; + marketSizeDetails: FuturesMarketData.MarketSizeDetailsStructOutput; + priceDetails: FuturesMarketData.PriceDetailsStructOutput; + }; + + export type PositionDataStruct = { + position: IFuturesMarketBaseTypes.PositionStruct; + notionalValue: PromiseOrValue; + profitLoss: PromiseOrValue; + accruedFunding: PromiseOrValue; + remainingMargin: PromiseOrValue; + accessibleMargin: PromiseOrValue; + liquidationPrice: PromiseOrValue; + canLiquidatePosition: PromiseOrValue; + }; + + export type PositionDataStructOutput = [ + IFuturesMarketBaseTypes.PositionStructOutput, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + boolean + ] & { + position: IFuturesMarketBaseTypes.PositionStructOutput; + notionalValue: BigNumber; + profitLoss: BigNumber; + accruedFunding: BigNumber; + remainingMargin: BigNumber; + accessibleMargin: BigNumber; + liquidationPrice: BigNumber; + canLiquidatePosition: boolean; + }; +} + +export declare namespace IFuturesMarketSettings { + export type ParametersStruct = { + takerFee: PromiseOrValue; + makerFee: PromiseOrValue; + takerFeeNextPrice: PromiseOrValue; + makerFeeNextPrice: PromiseOrValue; + nextPriceConfirmWindow: PromiseOrValue; + maxLeverage: PromiseOrValue; + maxMarketValueUSD: PromiseOrValue; + maxFundingRate: PromiseOrValue; + skewScaleUSD: PromiseOrValue; + }; + + export type ParametersStructOutput = [ + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber + ] & { + takerFee: BigNumber; + makerFee: BigNumber; + takerFeeNextPrice: BigNumber; + makerFeeNextPrice: BigNumber; + nextPriceConfirmWindow: BigNumber; + maxLeverage: BigNumber; + maxMarketValueUSD: BigNumber; + maxFundingRate: BigNumber; + skewScaleUSD: BigNumber; + }; +} + +export declare namespace IFuturesMarketBaseTypes { + export type PositionStruct = { + id: PromiseOrValue; + lastFundingIndex: PromiseOrValue; + margin: PromiseOrValue; + lastPrice: PromiseOrValue; + size: PromiseOrValue; + }; + + export type PositionStructOutput = [ + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber + ] & { + id: BigNumber; + lastFundingIndex: BigNumber; + margin: BigNumber; + lastPrice: BigNumber; + size: BigNumber; + }; +} + +export interface FuturesMarketDataInterface extends utils.Interface { + functions: { + "allMarketSummaries()": FunctionFragment; + "globals()": FunctionFragment; + "marketDetails(address)": FunctionFragment; + "marketDetailsForKey(bytes32)": FunctionFragment; + "marketSummaries(address[])": FunctionFragment; + "marketSummariesForKeys(bytes32[])": FunctionFragment; + "parameters(bytes32)": FunctionFragment; + "positionDetails(address,address)": FunctionFragment; + "positionDetailsForMarketKey(bytes32,address)": FunctionFragment; + "resolverProxy()": FunctionFragment; + }; + + getFunction( + nameOrSignatureOrTopic: + | "allMarketSummaries" + | "globals" + | "marketDetails" + | "marketDetailsForKey" + | "marketSummaries" + | "marketSummariesForKeys" + | "parameters" + | "positionDetails" + | "positionDetailsForMarketKey" + | "resolverProxy" + ): FunctionFragment; + + encodeFunctionData( + functionFragment: "allMarketSummaries", + values?: undefined + ): string; + encodeFunctionData(functionFragment: "globals", values?: undefined): string; + encodeFunctionData( + functionFragment: "marketDetails", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "marketDetailsForKey", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "marketSummaries", + values: [PromiseOrValue[]] + ): string; + encodeFunctionData( + functionFragment: "marketSummariesForKeys", + values: [PromiseOrValue[]] + ): string; + encodeFunctionData( + functionFragment: "parameters", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "positionDetails", + values: [PromiseOrValue, PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "positionDetailsForMarketKey", + values: [PromiseOrValue, PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "resolverProxy", + values?: undefined + ): string; + + decodeFunctionResult( + functionFragment: "allMarketSummaries", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "globals", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "marketDetails", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "marketDetailsForKey", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "marketSummaries", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "marketSummariesForKeys", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "parameters", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "positionDetails", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "positionDetailsForMarketKey", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "resolverProxy", + data: BytesLike + ): Result; + + events: {}; +} + +export interface FuturesMarketData extends BaseContract { + connect(signerOrProvider: Signer | Provider | string): this; + attach(addressOrName: string): this; + deployed(): Promise; + + interface: FuturesMarketDataInterface; + + queryFilter( + event: TypedEventFilter, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined + ): Promise>; + + listeners( + eventFilter?: TypedEventFilter + ): Array>; + listeners(eventName?: string): Array; + removeAllListeners( + eventFilter: TypedEventFilter + ): this; + removeAllListeners(eventName?: string): this; + off: OnEvent; + on: OnEvent; + once: OnEvent; + removeListener: OnEvent; + + functions: { + allMarketSummaries( + overrides?: CallOverrides + ): Promise<[FuturesMarketData.MarketSummaryStructOutput[]]>; + + globals( + overrides?: CallOverrides + ): Promise<[FuturesMarketData.FuturesGlobalsStructOutput]>; + + marketDetails( + market: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[FuturesMarketData.MarketDataStructOutput]>; + + marketDetailsForKey( + marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[FuturesMarketData.MarketDataStructOutput]>; + + marketSummaries( + markets: PromiseOrValue[], + overrides?: CallOverrides + ): Promise<[FuturesMarketData.MarketSummaryStructOutput[]]>; + + marketSummariesForKeys( + marketKeys: PromiseOrValue[], + overrides?: CallOverrides + ): Promise<[FuturesMarketData.MarketSummaryStructOutput[]]>; + + parameters( + marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[IFuturesMarketSettings.ParametersStructOutput]>; + + positionDetails( + market: PromiseOrValue, + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[FuturesMarketData.PositionDataStructOutput]>; + + positionDetailsForMarketKey( + marketKey: PromiseOrValue, + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[FuturesMarketData.PositionDataStructOutput]>; + + resolverProxy(overrides?: CallOverrides): Promise<[string]>; + }; + + allMarketSummaries( + overrides?: CallOverrides + ): Promise; + + globals( + overrides?: CallOverrides + ): Promise; + + marketDetails( + market: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + marketDetailsForKey( + marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + marketSummaries( + markets: PromiseOrValue[], + overrides?: CallOverrides + ): Promise; + + marketSummariesForKeys( + marketKeys: PromiseOrValue[], + overrides?: CallOverrides + ): Promise; + + parameters( + marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + positionDetails( + market: PromiseOrValue, + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + positionDetailsForMarketKey( + marketKey: PromiseOrValue, + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + resolverProxy(overrides?: CallOverrides): Promise; + + callStatic: { + allMarketSummaries( + overrides?: CallOverrides + ): Promise; + + globals( + overrides?: CallOverrides + ): Promise; + + marketDetails( + market: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + marketDetailsForKey( + marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + marketSummaries( + markets: PromiseOrValue[], + overrides?: CallOverrides + ): Promise; + + marketSummariesForKeys( + marketKeys: PromiseOrValue[], + overrides?: CallOverrides + ): Promise; + + parameters( + marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + positionDetails( + market: PromiseOrValue, + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + positionDetailsForMarketKey( + marketKey: PromiseOrValue, + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + resolverProxy(overrides?: CallOverrides): Promise; + }; + + filters: {}; + + estimateGas: { + allMarketSummaries(overrides?: CallOverrides): Promise; + + globals(overrides?: CallOverrides): Promise; + + marketDetails( + market: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + marketDetailsForKey( + marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + marketSummaries( + markets: PromiseOrValue[], + overrides?: CallOverrides + ): Promise; + + marketSummariesForKeys( + marketKeys: PromiseOrValue[], + overrides?: CallOverrides + ): Promise; + + parameters( + marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + positionDetails( + market: PromiseOrValue, + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + positionDetailsForMarketKey( + marketKey: PromiseOrValue, + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + resolverProxy(overrides?: CallOverrides): Promise; + }; + + populateTransaction: { + allMarketSummaries( + overrides?: CallOverrides + ): Promise; + + globals(overrides?: CallOverrides): Promise; + + marketDetails( + market: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + marketDetailsForKey( + marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + marketSummaries( + markets: PromiseOrValue[], + overrides?: CallOverrides + ): Promise; + + marketSummariesForKeys( + marketKeys: PromiseOrValue[], + overrides?: CallOverrides + ): Promise; + + parameters( + marketKey: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + positionDetails( + market: PromiseOrValue, + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + positionDetailsForMarketKey( + marketKey: PromiseOrValue, + account: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + resolverProxy(overrides?: CallOverrides): Promise; + }; +} diff --git a/lib/abis/types/factories/CrossMarginBaseSettings__factory.ts b/lib/abis/types/factories/CrossMarginBaseSettings__factory.ts index 35fb710bbf..4dbe295172 100644 --- a/lib/abis/types/factories/CrossMarginBaseSettings__factory.ts +++ b/lib/abis/types/factories/CrossMarginBaseSettings__factory.ts @@ -29,7 +29,7 @@ const _abi = [ }, { internalType: "uint256", - name: "_stopLossFee", + name: "_stopOrderFee", type: "uint256", }, ], @@ -94,7 +94,7 @@ const _abi = [ type: "uint256", }, ], - name: "StopLossFeeChanged", + name: "StopOrderFeeChanged", type: "event", }, { @@ -177,7 +177,7 @@ const _abi = [ type: "uint256", }, ], - name: "setStopLossFee", + name: "setStopOrderFee", outputs: [], stateMutability: "nonpayable", type: "function", @@ -210,7 +210,7 @@ const _abi = [ }, { inputs: [], - name: "stopLossFee", + name: "stopOrderFee", outputs: [ { internalType: "uint256", diff --git a/lib/abis/types/factories/CrossMarginBase__factory.ts b/lib/abis/types/factories/CrossMarginBase__factory.ts index 7b542f1465..07a110da66 100644 --- a/lib/abis/types/factories/CrossMarginBase__factory.ts +++ b/lib/abis/types/factories/CrossMarginBase__factory.ts @@ -15,6 +15,16 @@ const _abi = [ stateMutability: "nonpayable", type: "constructor", }, + { + inputs: [], + name: "CannotPayFee", + type: "error", + }, + { + inputs: [], + name: "CannotRescueMarginAsset", + type: "error", + }, { inputs: [], name: "EthWithdrawalFailed", @@ -24,38 +34,48 @@ const _abi = [ inputs: [ { internalType: "uint256", - name: "available", + name: "balance", type: "uint256", }, { internalType: "uint256", - name: "required", + name: "minimum", type: "uint256", }, ], - name: "InsufficientFreeMargin", + name: "InsufficientEthBalance", type: "error", }, { inputs: [ { internalType: "uint256", - name: "numberOfNewPositions", + name: "available", + type: "uint256", + }, + { + internalType: "uint256", + name: "required", type: "uint256", }, ], - name: "MaxNewPositionsExceeded", + name: "InsufficientFreeMargin", + type: "error", + }, + { + inputs: [], + name: "InvalidPrice", type: "error", }, { inputs: [ { - internalType: "bytes32", - name: "marketKey", - type: "bytes32", + internalType: "uint256", + name: "numberOfNewPositions", + type: "uint256", }, ], - name: "MissingMarketKey", + name: "MaxNewPositionsExceeded", type: "error", }, { @@ -93,6 +113,124 @@ const _abi = [ name: "Deposit", type: "event", }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "FeeImposed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "orderId", + type: "uint256", + }, + ], + name: "OrderCancelled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "orderId", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "fillPrice", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "keeperFee", + type: "uint256", + }, + ], + name: "OrderFilled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "orderId", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes32", + name: "marketKey", + type: "bytes32", + }, + { + indexed: false, + internalType: "int256", + name: "marginDelta", + type: "int256", + }, + { + indexed: false, + internalType: "int256", + name: "sizeDelta", + type: "int256", + }, + { + indexed: false, + internalType: "uint256", + name: "targetPrice", + type: "uint256", + }, + { + indexed: false, + internalType: "enum IMarginBaseTypes.OrderTypes", + name: "orderType", + type: "uint8", + }, + ], + name: "OrderPlaced", + type: "event", + }, { anonymous: false, inputs: [ @@ -112,6 +250,25 @@ const _abi = [ name: "OwnershipTransferred", type: "event", }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "Rescued", + type: "event", + }, { anonymous: false, inputs: [ @@ -163,35 +320,6 @@ const _abi = [ stateMutability: "view", type: "function", }, - { - inputs: [ - { - internalType: "bytes32", - name: "", - type: "bytes32", - }, - ], - name: "activeMarketPositions", - outputs: [ - { - internalType: "bytes32", - name: "marketKey", - type: "bytes32", - }, - { - internalType: "uint128", - name: "margin", - type: "uint128", - }, - { - internalType: "int128", - name: "size", - type: "int128", - }, - ], - stateMutability: "view", - type: "function", - }, { inputs: [ { @@ -255,6 +383,41 @@ const _abi = [ stateMutability: "nonpayable", type: "function", }, + { + inputs: [ + { + internalType: "uint256", + name: "_amount", + type: "uint256", + }, + { + components: [ + { + internalType: "bytes32", + name: "marketKey", + type: "bytes32", + }, + { + internalType: "int256", + name: "marginDelta", + type: "int256", + }, + { + internalType: "int256", + name: "sizeDelta", + type: "int256", + }, + ], + internalType: "struct IMarginBaseTypes.NewPosition[]", + name: "_newPositions", + type: "tuple[]", + }, + ], + name: "depositAndDistribute", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, { inputs: [ { @@ -275,7 +438,7 @@ const _abi = [ type: "int256", }, ], - internalType: "struct IMarginBaseTypes.UpdateMarketPositionSpec[]", + internalType: "struct IMarginBaseTypes.NewPosition[]", name: "_newPositions", type: "tuple[]", }, @@ -326,42 +489,51 @@ const _abi = [ }, { inputs: [], - name: "getAllActiveMarketPositions", + name: "getNumberOfInternalPositions", outputs: [ { - components: [ - { - internalType: "bytes32", - name: "marketKey", - type: "bytes32", - }, - { - internalType: "uint128", - name: "margin", - type: "uint128", - }, - { - internalType: "int128", - name: "size", - type: "int128", - }, - ], - internalType: "struct IMarginBaseTypes.ActiveMarketPosition[]", + internalType: "uint256", name: "", - type: "tuple[]", + type: "uint256", }, ], stateMutability: "view", type: "function", }, { - inputs: [], - name: "getNumberOfActivePositions", + inputs: [ + { + internalType: "bytes32", + name: "_marketKey", + type: "bytes32", + }, + ], + name: "getPosition", outputs: [ { - internalType: "uint256", - name: "", - type: "uint256", + internalType: "uint64", + name: "id", + type: "uint64", + }, + { + internalType: "uint64", + name: "fundingIndex", + type: "uint64", + }, + { + internalType: "uint128", + name: "margin", + type: "uint128", + }, + { + internalType: "uint128", + name: "lastPrice", + type: "uint128", + }, + { + internalType: "int128", + name: "size", + type: "int128", }, ], stateMutability: "view", @@ -428,6 +600,25 @@ const _abi = [ stateMutability: "view", type: "function", }, + { + inputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + name: "marketKeyIndex", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, { inputs: [], name: "ops", @@ -481,7 +672,7 @@ const _abi = [ }, { internalType: "uint256", - name: "desiredPrice", + name: "targetPrice", type: "uint256", }, { @@ -489,6 +680,16 @@ const _abi = [ name: "gelatoTaskId", type: "bytes32", }, + { + internalType: "enum IMarginBaseTypes.OrderTypes", + name: "orderType", + type: "uint8", + }, + { + internalType: "uint256", + name: "maxDynamicFee", + type: "uint256", + }, ], stateMutability: "view", type: "function", @@ -525,9 +726,14 @@ const _abi = [ }, { internalType: "uint256", - name: "_limitPrice", + name: "_targetPrice", type: "uint256", }, + { + internalType: "enum IMarginBaseTypes.OrderTypes", + name: "_orderType", + type: "uint8", + }, ], name: "placeOrder", outputs: [ @@ -540,6 +746,50 @@ const _abi = [ stateMutability: "payable", type: "function", }, + { + inputs: [ + { + internalType: "bytes32", + name: "_marketKey", + type: "bytes32", + }, + { + internalType: "int256", + name: "_marginDelta", + type: "int256", + }, + { + internalType: "int256", + name: "_sizeDelta", + type: "int256", + }, + { + internalType: "uint256", + name: "_targetPrice", + type: "uint256", + }, + { + internalType: "enum IMarginBaseTypes.OrderTypes", + name: "_orderType", + type: "uint8", + }, + { + internalType: "uint256", + name: "_maxDynamicFee", + type: "uint256", + }, + ], + name: "placeOrderWithFeeCap", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "payable", + type: "function", + }, { inputs: [], name: "renounceOwnership", @@ -547,6 +797,24 @@ const _abi = [ stateMutability: "nonpayable", type: "function", }, + { + inputs: [ + { + internalType: "address", + name: "tokenAddress", + type: "address", + }, + { + internalType: "uint256", + name: "tokenAmount", + type: "uint256", + }, + ], + name: "rescueERC20", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, { inputs: [ { @@ -575,6 +843,11 @@ const _abi = [ name: "", type: "bool", }, + { + internalType: "uint256", + name: "", + type: "uint256", + }, ], stateMutability: "view", type: "function", diff --git a/lib/abis/types/factories/FuturesMarketData__factory.ts b/lib/abis/types/factories/FuturesMarketData__factory.ts new file mode 100644 index 0000000000..1db76577ef --- /dev/null +++ b/lib/abis/types/factories/FuturesMarketData__factory.ts @@ -0,0 +1,937 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import { Contract, Signer, utils } from "ethers"; +import type { Provider } from "@ethersproject/providers"; +import type { + FuturesMarketData, + FuturesMarketDataInterface, +} from "../FuturesMarketData"; + +const _abi = [ + { + inputs: [ + { + internalType: "contract IAddressResolver", + name: "_resolverProxy", + type: "address", + }, + ], + payable: false, + stateMutability: "nonpayable", + type: "constructor", + }, + { + constant: true, + inputs: [], + name: "allMarketSummaries", + outputs: [ + { + components: [ + { + internalType: "address", + name: "market", + type: "address", + }, + { + internalType: "bytes32", + name: "asset", + type: "bytes32", + }, + { + internalType: "bytes32", + name: "key", + type: "bytes32", + }, + { + internalType: "uint256", + name: "maxLeverage", + type: "uint256", + }, + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "uint256", + name: "marketSize", + type: "uint256", + }, + { + internalType: "int256", + name: "marketSkew", + type: "int256", + }, + { + internalType: "uint256", + name: "marketDebt", + type: "uint256", + }, + { + internalType: "int256", + name: "currentFundingRate", + type: "int256", + }, + { + components: [ + { + internalType: "uint256", + name: "takerFee", + type: "uint256", + }, + { + internalType: "uint256", + name: "makerFee", + type: "uint256", + }, + { + internalType: "uint256", + name: "takerFeeNextPrice", + type: "uint256", + }, + { + internalType: "uint256", + name: "makerFeeNextPrice", + type: "uint256", + }, + ], + internalType: "struct FuturesMarketData.FeeRates", + name: "feeRates", + type: "tuple", + }, + ], + internalType: "struct FuturesMarketData.MarketSummary[]", + name: "", + type: "tuple[]", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "globals", + outputs: [ + { + components: [ + { + internalType: "uint256", + name: "minInitialMargin", + type: "uint256", + }, + { + internalType: "uint256", + name: "liquidationFeeRatio", + type: "uint256", + }, + { + internalType: "uint256", + name: "liquidationBufferRatio", + type: "uint256", + }, + { + internalType: "uint256", + name: "minKeeperFee", + type: "uint256", + }, + ], + internalType: "struct FuturesMarketData.FuturesGlobals", + name: "", + type: "tuple", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "contract IFuturesMarket", + name: "market", + type: "address", + }, + ], + name: "marketDetails", + outputs: [ + { + components: [ + { + internalType: "address", + name: "market", + type: "address", + }, + { + internalType: "bytes32", + name: "baseAsset", + type: "bytes32", + }, + { + internalType: "bytes32", + name: "marketKey", + type: "bytes32", + }, + { + components: [ + { + internalType: "uint256", + name: "takerFee", + type: "uint256", + }, + { + internalType: "uint256", + name: "makerFee", + type: "uint256", + }, + { + internalType: "uint256", + name: "takerFeeNextPrice", + type: "uint256", + }, + { + internalType: "uint256", + name: "makerFeeNextPrice", + type: "uint256", + }, + ], + internalType: "struct FuturesMarketData.FeeRates", + name: "feeRates", + type: "tuple", + }, + { + components: [ + { + internalType: "uint256", + name: "maxLeverage", + type: "uint256", + }, + { + internalType: "uint256", + name: "maxMarketValueUSD", + type: "uint256", + }, + ], + internalType: "struct FuturesMarketData.MarketLimits", + name: "limits", + type: "tuple", + }, + { + components: [ + { + internalType: "uint256", + name: "maxFundingRate", + type: "uint256", + }, + { + internalType: "uint256", + name: "skewScaleUSD", + type: "uint256", + }, + ], + internalType: "struct FuturesMarketData.FundingParameters", + name: "fundingParameters", + type: "tuple", + }, + { + components: [ + { + internalType: "uint256", + name: "marketSize", + type: "uint256", + }, + { + components: [ + { + internalType: "uint256", + name: "long", + type: "uint256", + }, + { + internalType: "uint256", + name: "short", + type: "uint256", + }, + ], + internalType: "struct FuturesMarketData.Sides", + name: "sides", + type: "tuple", + }, + { + internalType: "uint256", + name: "marketDebt", + type: "uint256", + }, + { + internalType: "int256", + name: "marketSkew", + type: "int256", + }, + ], + internalType: "struct FuturesMarketData.MarketSizeDetails", + name: "marketSizeDetails", + type: "tuple", + }, + { + components: [ + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "bool", + name: "invalid", + type: "bool", + }, + ], + internalType: "struct FuturesMarketData.PriceDetails", + name: "priceDetails", + type: "tuple", + }, + ], + internalType: "struct FuturesMarketData.MarketData", + name: "", + type: "tuple", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "bytes32", + name: "marketKey", + type: "bytes32", + }, + ], + name: "marketDetailsForKey", + outputs: [ + { + components: [ + { + internalType: "address", + name: "market", + type: "address", + }, + { + internalType: "bytes32", + name: "baseAsset", + type: "bytes32", + }, + { + internalType: "bytes32", + name: "marketKey", + type: "bytes32", + }, + { + components: [ + { + internalType: "uint256", + name: "takerFee", + type: "uint256", + }, + { + internalType: "uint256", + name: "makerFee", + type: "uint256", + }, + { + internalType: "uint256", + name: "takerFeeNextPrice", + type: "uint256", + }, + { + internalType: "uint256", + name: "makerFeeNextPrice", + type: "uint256", + }, + ], + internalType: "struct FuturesMarketData.FeeRates", + name: "feeRates", + type: "tuple", + }, + { + components: [ + { + internalType: "uint256", + name: "maxLeverage", + type: "uint256", + }, + { + internalType: "uint256", + name: "maxMarketValueUSD", + type: "uint256", + }, + ], + internalType: "struct FuturesMarketData.MarketLimits", + name: "limits", + type: "tuple", + }, + { + components: [ + { + internalType: "uint256", + name: "maxFundingRate", + type: "uint256", + }, + { + internalType: "uint256", + name: "skewScaleUSD", + type: "uint256", + }, + ], + internalType: "struct FuturesMarketData.FundingParameters", + name: "fundingParameters", + type: "tuple", + }, + { + components: [ + { + internalType: "uint256", + name: "marketSize", + type: "uint256", + }, + { + components: [ + { + internalType: "uint256", + name: "long", + type: "uint256", + }, + { + internalType: "uint256", + name: "short", + type: "uint256", + }, + ], + internalType: "struct FuturesMarketData.Sides", + name: "sides", + type: "tuple", + }, + { + internalType: "uint256", + name: "marketDebt", + type: "uint256", + }, + { + internalType: "int256", + name: "marketSkew", + type: "int256", + }, + ], + internalType: "struct FuturesMarketData.MarketSizeDetails", + name: "marketSizeDetails", + type: "tuple", + }, + { + components: [ + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "bool", + name: "invalid", + type: "bool", + }, + ], + internalType: "struct FuturesMarketData.PriceDetails", + name: "priceDetails", + type: "tuple", + }, + ], + internalType: "struct FuturesMarketData.MarketData", + name: "", + type: "tuple", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "address[]", + name: "markets", + type: "address[]", + }, + ], + name: "marketSummaries", + outputs: [ + { + components: [ + { + internalType: "address", + name: "market", + type: "address", + }, + { + internalType: "bytes32", + name: "asset", + type: "bytes32", + }, + { + internalType: "bytes32", + name: "key", + type: "bytes32", + }, + { + internalType: "uint256", + name: "maxLeverage", + type: "uint256", + }, + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "uint256", + name: "marketSize", + type: "uint256", + }, + { + internalType: "int256", + name: "marketSkew", + type: "int256", + }, + { + internalType: "uint256", + name: "marketDebt", + type: "uint256", + }, + { + internalType: "int256", + name: "currentFundingRate", + type: "int256", + }, + { + components: [ + { + internalType: "uint256", + name: "takerFee", + type: "uint256", + }, + { + internalType: "uint256", + name: "makerFee", + type: "uint256", + }, + { + internalType: "uint256", + name: "takerFeeNextPrice", + type: "uint256", + }, + { + internalType: "uint256", + name: "makerFeeNextPrice", + type: "uint256", + }, + ], + internalType: "struct FuturesMarketData.FeeRates", + name: "feeRates", + type: "tuple", + }, + ], + internalType: "struct FuturesMarketData.MarketSummary[]", + name: "", + type: "tuple[]", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "bytes32[]", + name: "marketKeys", + type: "bytes32[]", + }, + ], + name: "marketSummariesForKeys", + outputs: [ + { + components: [ + { + internalType: "address", + name: "market", + type: "address", + }, + { + internalType: "bytes32", + name: "asset", + type: "bytes32", + }, + { + internalType: "bytes32", + name: "key", + type: "bytes32", + }, + { + internalType: "uint256", + name: "maxLeverage", + type: "uint256", + }, + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "uint256", + name: "marketSize", + type: "uint256", + }, + { + internalType: "int256", + name: "marketSkew", + type: "int256", + }, + { + internalType: "uint256", + name: "marketDebt", + type: "uint256", + }, + { + internalType: "int256", + name: "currentFundingRate", + type: "int256", + }, + { + components: [ + { + internalType: "uint256", + name: "takerFee", + type: "uint256", + }, + { + internalType: "uint256", + name: "makerFee", + type: "uint256", + }, + { + internalType: "uint256", + name: "takerFeeNextPrice", + type: "uint256", + }, + { + internalType: "uint256", + name: "makerFeeNextPrice", + type: "uint256", + }, + ], + internalType: "struct FuturesMarketData.FeeRates", + name: "feeRates", + type: "tuple", + }, + ], + internalType: "struct FuturesMarketData.MarketSummary[]", + name: "", + type: "tuple[]", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "bytes32", + name: "marketKey", + type: "bytes32", + }, + ], + name: "parameters", + outputs: [ + { + components: [ + { + internalType: "uint256", + name: "takerFee", + type: "uint256", + }, + { + internalType: "uint256", + name: "makerFee", + type: "uint256", + }, + { + internalType: "uint256", + name: "takerFeeNextPrice", + type: "uint256", + }, + { + internalType: "uint256", + name: "makerFeeNextPrice", + type: "uint256", + }, + { + internalType: "uint256", + name: "nextPriceConfirmWindow", + type: "uint256", + }, + { + internalType: "uint256", + name: "maxLeverage", + type: "uint256", + }, + { + internalType: "uint256", + name: "maxMarketValueUSD", + type: "uint256", + }, + { + internalType: "uint256", + name: "maxFundingRate", + type: "uint256", + }, + { + internalType: "uint256", + name: "skewScaleUSD", + type: "uint256", + }, + ], + internalType: "struct IFuturesMarketSettings.Parameters", + name: "", + type: "tuple", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "contract IFuturesMarket", + name: "market", + type: "address", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "positionDetails", + outputs: [ + { + components: [ + { + components: [ + { + internalType: "uint64", + name: "id", + type: "uint64", + }, + { + internalType: "uint64", + name: "lastFundingIndex", + type: "uint64", + }, + { + internalType: "uint128", + name: "margin", + type: "uint128", + }, + { + internalType: "uint128", + name: "lastPrice", + type: "uint128", + }, + { + internalType: "int128", + name: "size", + type: "int128", + }, + ], + internalType: "struct IFuturesMarketBaseTypes.Position", + name: "position", + type: "tuple", + }, + { + internalType: "int256", + name: "notionalValue", + type: "int256", + }, + { + internalType: "int256", + name: "profitLoss", + type: "int256", + }, + { + internalType: "int256", + name: "accruedFunding", + type: "int256", + }, + { + internalType: "uint256", + name: "remainingMargin", + type: "uint256", + }, + { + internalType: "uint256", + name: "accessibleMargin", + type: "uint256", + }, + { + internalType: "uint256", + name: "liquidationPrice", + type: "uint256", + }, + { + internalType: "bool", + name: "canLiquidatePosition", + type: "bool", + }, + ], + internalType: "struct FuturesMarketData.PositionData", + name: "", + type: "tuple", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "bytes32", + name: "marketKey", + type: "bytes32", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "positionDetailsForMarketKey", + outputs: [ + { + components: [ + { + components: [ + { + internalType: "uint64", + name: "id", + type: "uint64", + }, + { + internalType: "uint64", + name: "lastFundingIndex", + type: "uint64", + }, + { + internalType: "uint128", + name: "margin", + type: "uint128", + }, + { + internalType: "uint128", + name: "lastPrice", + type: "uint128", + }, + { + internalType: "int128", + name: "size", + type: "int128", + }, + ], + internalType: "struct IFuturesMarketBaseTypes.Position", + name: "position", + type: "tuple", + }, + { + internalType: "int256", + name: "notionalValue", + type: "int256", + }, + { + internalType: "int256", + name: "profitLoss", + type: "int256", + }, + { + internalType: "int256", + name: "accruedFunding", + type: "int256", + }, + { + internalType: "uint256", + name: "remainingMargin", + type: "uint256", + }, + { + internalType: "uint256", + name: "accessibleMargin", + type: "uint256", + }, + { + internalType: "uint256", + name: "liquidationPrice", + type: "uint256", + }, + { + internalType: "bool", + name: "canLiquidatePosition", + type: "bool", + }, + ], + internalType: "struct FuturesMarketData.PositionData", + name: "", + type: "tuple", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "resolverProxy", + outputs: [ + { + internalType: "contract IAddressResolver", + name: "", + type: "address", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, +]; + +export class FuturesMarketData__factory { + static readonly abi = _abi; + static createInterface(): FuturesMarketDataInterface { + return new utils.Interface(_abi) as FuturesMarketDataInterface; + } + static connect( + address: string, + signerOrProvider: Signer | Provider + ): FuturesMarketData { + return new Contract(address, _abi, signerOrProvider) as FuturesMarketData; + } +} diff --git a/lib/abis/types/factories/FuturesMarket__factory.ts b/lib/abis/types/factories/FuturesMarket__factory.ts new file mode 100644 index 0000000000..91594662c8 --- /dev/null +++ b/lib/abis/types/factories/FuturesMarket__factory.ts @@ -0,0 +1,1130 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import { Contract, Signer, utils } from "ethers"; +import type { Provider } from "@ethersproject/providers"; +import type { FuturesMarket, FuturesMarketInterface } from "../FuturesMarket"; + +const _abi = [ + { + inputs: [ + { + internalType: "address", + name: "_resolver", + type: "address", + }, + { + internalType: "bytes32", + name: "_baseAsset", + type: "bytes32", + }, + { + internalType: "bytes32", + name: "_marketKey", + type: "bytes32", + }, + ], + payable: false, + stateMutability: "nonpayable", + type: "constructor", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "bytes32", + name: "name", + type: "bytes32", + }, + { + indexed: false, + internalType: "address", + name: "destination", + type: "address", + }, + ], + name: "CacheUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "int256", + name: "funding", + type: "int256", + }, + { + indexed: false, + internalType: "uint256", + name: "index", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "timestamp", + type: "uint256", + }, + ], + name: "FundingRecomputed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "trackingCode", + type: "bytes32", + }, + { + indexed: false, + internalType: "bytes32", + name: "baseAsset", + type: "bytes32", + }, + { + indexed: false, + internalType: "bytes32", + name: "marketKey", + type: "bytes32", + }, + { + indexed: false, + internalType: "int256", + name: "sizeDelta", + type: "int256", + }, + { + indexed: false, + internalType: "uint256", + name: "fee", + type: "uint256", + }, + ], + name: "FuturesTracking", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "int256", + name: "marginDelta", + type: "int256", + }, + ], + name: "MarginTransferred", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "currentRoundId", + type: "uint256", + }, + { + indexed: false, + internalType: "int256", + name: "sizeDelta", + type: "int256", + }, + { + indexed: false, + internalType: "uint256", + name: "targetRoundId", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "commitDeposit", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "keeperDeposit", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes32", + name: "trackingCode", + type: "bytes32", + }, + ], + name: "NextPriceOrderRemoved", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "int256", + name: "sizeDelta", + type: "int256", + }, + { + indexed: false, + internalType: "uint256", + name: "targetRoundId", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "commitDeposit", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "keeperDeposit", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes32", + name: "trackingCode", + type: "bytes32", + }, + ], + name: "NextPriceOrderSubmitted", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "id", + type: "uint256", + }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "liquidator", + type: "address", + }, + { + indexed: false, + internalType: "int256", + name: "size", + type: "int256", + }, + { + indexed: false, + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "fee", + type: "uint256", + }, + ], + name: "PositionLiquidated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "id", + type: "uint256", + }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "margin", + type: "uint256", + }, + { + indexed: false, + internalType: "int256", + name: "size", + type: "int256", + }, + { + indexed: false, + internalType: "int256", + name: "tradeSize", + type: "int256", + }, + { + indexed: false, + internalType: "uint256", + name: "lastPrice", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "fundingIndex", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "fee", + type: "uint256", + }, + ], + name: "PositionModified", + type: "event", + }, + { + constant: true, + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "accessibleMargin", + outputs: [ + { + internalType: "uint256", + name: "marginAccessible", + type: "uint256", + }, + { + internalType: "bool", + name: "invalid", + type: "bool", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "accruedFunding", + outputs: [ + { + internalType: "int256", + name: "funding", + type: "int256", + }, + { + internalType: "bool", + name: "invalid", + type: "bool", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "assetPrice", + outputs: [ + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "bool", + name: "invalid", + type: "bool", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "baseAsset", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "canLiquidate", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: false, + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "cancelNextPriceOrder", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: false, + inputs: [], + name: "closePosition", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: false, + inputs: [ + { + internalType: "bytes32", + name: "trackingCode", + type: "bytes32", + }, + ], + name: "closePositionWithTracking", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: true, + inputs: [], + name: "currentFundingRate", + outputs: [ + { + internalType: "int256", + name: "", + type: "int256", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: false, + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "executeNextPriceOrder", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: true, + inputs: [], + name: "fundingLastRecomputed", + outputs: [ + { + internalType: "uint32", + name: "", + type: "uint32", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + name: "fundingSequence", + outputs: [ + { + internalType: "int128", + name: "", + type: "int128", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "fundingSequenceLength", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "isResolverCached", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: false, + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "liquidatePosition", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "liquidationFee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "liquidationPrice", + outputs: [ + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "bool", + name: "invalid", + type: "bool", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "marketDebt", + outputs: [ + { + internalType: "uint256", + name: "debt", + type: "uint256", + }, + { + internalType: "bool", + name: "invalid", + type: "bool", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "marketKey", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "marketSize", + outputs: [ + { + internalType: "uint128", + name: "", + type: "uint128", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "marketSizes", + outputs: [ + { + internalType: "uint256", + name: "long", + type: "uint256", + }, + { + internalType: "uint256", + name: "short", + type: "uint256", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "marketSkew", + outputs: [ + { + internalType: "int128", + name: "", + type: "int128", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: false, + inputs: [ + { + internalType: "int256", + name: "sizeDelta", + type: "int256", + }, + ], + name: "modifyPosition", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: false, + inputs: [ + { + internalType: "int256", + name: "sizeDelta", + type: "int256", + }, + { + internalType: "bytes32", + name: "trackingCode", + type: "bytes32", + }, + ], + name: "modifyPositionWithTracking", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + name: "nextPriceOrders", + outputs: [ + { + internalType: "int128", + name: "sizeDelta", + type: "int128", + }, + { + internalType: "uint128", + name: "targetRoundId", + type: "uint128", + }, + { + internalType: "uint128", + name: "commitDeposit", + type: "uint128", + }, + { + internalType: "uint128", + name: "keeperDeposit", + type: "uint128", + }, + { + internalType: "bytes32", + name: "trackingCode", + type: "bytes32", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "notionalValue", + outputs: [ + { + internalType: "int256", + name: "value", + type: "int256", + }, + { + internalType: "bool", + name: "invalid", + type: "bool", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "int256", + name: "sizeDelta", + type: "int256", + }, + ], + name: "orderFee", + outputs: [ + { + internalType: "uint256", + name: "fee", + type: "uint256", + }, + { + internalType: "bool", + name: "invalid", + type: "bool", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + name: "positions", + outputs: [ + { + internalType: "uint64", + name: "id", + type: "uint64", + }, + { + internalType: "uint64", + name: "lastFundingIndex", + type: "uint64", + }, + { + internalType: "uint128", + name: "margin", + type: "uint128", + }, + { + internalType: "uint128", + name: "lastPrice", + type: "uint128", + }, + { + internalType: "int128", + name: "size", + type: "int128", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "int256", + name: "sizeDelta", + type: "int256", + }, + { + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "postTradeDetails", + outputs: [ + { + internalType: "uint256", + name: "margin", + type: "uint256", + }, + { + internalType: "int256", + name: "size", + type: "int256", + }, + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "uint256", + name: "liqPrice", + type: "uint256", + }, + { + internalType: "uint256", + name: "fee", + type: "uint256", + }, + { + internalType: "enum IFuturesMarketBaseTypes.Status", + name: "status", + type: "uint8", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "profitLoss", + outputs: [ + { + internalType: "int256", + name: "pnl", + type: "int256", + }, + { + internalType: "bool", + name: "invalid", + type: "bool", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: false, + inputs: [], + name: "rebuildCache", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: false, + inputs: [], + name: "recomputeFunding", + outputs: [ + { + internalType: "uint256", + name: "lastIndex", + type: "uint256", + }, + ], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: true, + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "remainingMargin", + outputs: [ + { + internalType: "uint256", + name: "marginRemaining", + type: "uint256", + }, + { + internalType: "bool", + name: "invalid", + type: "bool", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "resolver", + outputs: [ + { + internalType: "contract AddressResolver", + name: "", + type: "address", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "resolverAddressesRequired", + outputs: [ + { + internalType: "bytes32[]", + name: "addresses", + type: "bytes32[]", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: false, + inputs: [ + { + internalType: "int256", + name: "sizeDelta", + type: "int256", + }, + ], + name: "submitNextPriceOrder", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: false, + inputs: [ + { + internalType: "int256", + name: "sizeDelta", + type: "int256", + }, + { + internalType: "bytes32", + name: "trackingCode", + type: "bytes32", + }, + ], + name: "submitNextPriceOrderWithTracking", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: false, + inputs: [ + { + internalType: "int256", + name: "marginDelta", + type: "int256", + }, + ], + name: "transferMargin", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: true, + inputs: [], + name: "unrecordedFunding", + outputs: [ + { + internalType: "int256", + name: "funding", + type: "int256", + }, + { + internalType: "bool", + name: "invalid", + type: "bool", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: false, + inputs: [], + name: "withdrawAllMargin", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, +]; + +export class FuturesMarket__factory { + static readonly abi = _abi; + static createInterface(): FuturesMarketInterface { + return new utils.Interface(_abi) as FuturesMarketInterface; + } + static connect( + address: string, + signerOrProvider: Signer | Provider + ): FuturesMarket { + return new Contract(address, _abi, signerOrProvider) as FuturesMarket; + } +} diff --git a/lib/abis/types/factories/index.ts b/lib/abis/types/factories/index.ts index c7957a6c1d..fb59eb11dc 100644 --- a/lib/abis/types/factories/index.ts +++ b/lib/abis/types/factories/index.ts @@ -5,5 +5,7 @@ export { CrossMarginAccountFactory__factory } from "./CrossMarginAccountFactory_ export { CrossMarginBase__factory } from "./CrossMarginBase__factory"; export { CrossMarginBaseSettings__factory } from "./CrossMarginBaseSettings__factory"; export { ERC20__factory } from "./ERC20__factory"; +export { FuturesMarket__factory } from "./FuturesMarket__factory"; +export { FuturesMarketData__factory } from "./FuturesMarketData__factory"; export { ReverseRecords__factory } from "./ReverseRecords__factory"; export { SynthSwap__factory } from "./SynthSwap__factory"; diff --git a/lib/abis/types/index.ts b/lib/abis/types/index.ts index 3001a844be..6355f6a156 100644 --- a/lib/abis/types/index.ts +++ b/lib/abis/types/index.ts @@ -5,6 +5,8 @@ export type { CrossMarginAccountFactory } from "./CrossMarginAccountFactory"; export type { CrossMarginBase } from "./CrossMarginBase"; export type { CrossMarginBaseSettings } from "./CrossMarginBaseSettings"; export type { ERC20 } from "./ERC20"; +export type { FuturesMarket } from "./FuturesMarket"; +export type { FuturesMarketData } from "./FuturesMarketData"; export type { ReverseRecords } from "./ReverseRecords"; export type { SynthSwap } from "./SynthSwap"; export * as factories from "./factories"; @@ -12,5 +14,7 @@ export { CrossMarginAccountFactory__factory } from "./factories/CrossMarginAccou export { CrossMarginBase__factory } from "./factories/CrossMarginBase__factory"; export { CrossMarginBaseSettings__factory } from "./factories/CrossMarginBaseSettings__factory"; export { ERC20__factory } from "./factories/ERC20__factory"; +export { FuturesMarket__factory } from "./factories/FuturesMarket__factory"; +export { FuturesMarketData__factory } from "./factories/FuturesMarketData__factory"; export { ReverseRecords__factory } from "./factories/ReverseRecords__factory"; export { SynthSwap__factory } from "./factories/SynthSwap__factory"; diff --git a/package-lock.json b/package-lock.json index b370ede319..624097d81a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "kwenta", - "version": "3.2.4", + "version": "3.2.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "kwenta", - "version": "3.2.4", + "version": "3.2.5", "hasInstallScript": true, "dependencies": { "@artsy/fresnel": "1.7.0", @@ -57,7 +57,6 @@ "react-slick": "0.29.0", "react-table": "7.7.0", "react-toastify": "9.0.4", - "recharts": "2.0.9", "recoil": "0.6.1", "slick-carousel": "1.8.1", "styled-components": "5.1.1", @@ -111,7 +110,7 @@ "eslint-plugin-testing-library": "^5.1.0", "eslint-plugin-ui-testing": "^2.0.0", "html-to-image": "^1.9.0", - "husky": "5.0.9", + "husky": "8.0.1", "jest": "27.3.1", "lint-staged": "^13.0.3", "pinst": "3.0.0", @@ -11868,32 +11867,6 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, - "node_modules/@types/d3-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.2.tgz", - "integrity": "sha512-3YHpvDw9LzONaJzejXLOwZ3LqwwkoXb9LI2YN7Hbd6pkGo5nIlJ09ul4bQhBN4hQZJKmUpX8HkVqbzgUKY48cg==" - }, - "node_modules/@types/d3-scale": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.3.2.tgz", - "integrity": "sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==", - "dependencies": { - "@types/d3-time": "^2" - } - }, - "node_modules/@types/d3-shape": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.1.3.tgz", - "integrity": "sha512-HAhCel3wP93kh4/rq+7atLdybcESZ5bRHDEZUojClyZWsRuEMo3A52NGYJSh48SxfxEU6RZIVbZL2YFZ2OAlzQ==", - "dependencies": { - "@types/d3-path": "^2" - } - }, - "node_modules/@types/d3-time": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz", - "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==" - }, "node_modules/@types/date-fns": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@types/date-fns/-/date-fns-2.6.0.tgz", @@ -12281,11 +12254,6 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" }, - "node_modules/@types/resize-observer-browser": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.7.tgz", - "integrity": "sha512-G9eN0Sn0ii9PWQ3Vl72jDPgeJwRWhv2Qk/nQkJuWmRmOB4HX3/BhD5SE1dZs/hzPZL/WKnvF0RHdTSG54QJFyg==" - }, "node_modules/@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -20295,11 +20263,6 @@ "node": ">=8.0.0" } }, - "node_modules/css-unit-converter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", - "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" - }, "node_modules/css-vendor": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", @@ -20626,73 +20589,6 @@ "type": "^1.0.1" } }, - "node_modules/d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "dependencies": { - "internmap": "^1.0.0" - } - }, - "node_modules/d3-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", - "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==" - }, - "node_modules/d3-format": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", - "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==" - }, - "node_modules/d3-interpolate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz", - "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==", - "dependencies": { - "d3-color": "1 - 2" - } - }, - "node_modules/d3-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz", - "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA==" - }, - "node_modules/d3-scale": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz", - "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==", - "dependencies": { - "d3-array": "^2.3.0", - "d3-format": "1 - 2", - "d3-interpolate": "1.2.0 - 2", - "d3-time": "^2.1.1", - "d3-time-format": "2 - 3" - } - }, - "node_modules/d3-shape": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.1.0.tgz", - "integrity": "sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==", - "dependencies": { - "d3-path": "1 - 2" - } - }, - "node_modules/d3-time": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", - "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", - "dependencies": { - "d3-array": "2" - } - }, - "node_modules/d3-time-format": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", - "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", - "dependencies": { - "d3-time": "1 - 2" - } - }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -20787,11 +20683,6 @@ "integrity": "sha512-Nv6ENEzyPQ6AItkGwLE2PGKinZZ9g59vSh2BeH6NqPu0OTKZ5ruJsVqh/orbAnqXc9pBbgXAIrc2EyaCj8NpGg==", "dev": true }, - "node_modules/decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" - }, "node_modules/decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -24682,11 +24573,6 @@ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", "dev": true }, - "node_modules/fast-equals": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-2.0.4.tgz", - "integrity": "sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==" - }, "node_modules/fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -27001,25 +26887,18 @@ } }, "node_modules/husky": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/husky/-/husky-5.0.9.tgz", - "integrity": "sha512-0SjcaY21a+IRdx7p7r/X33Vc09UR2m8SbP8yfkhUX2/jAmwcz+GR7i9jXkp2pP3GfX23JhMkVP6SWwXB18uXtg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.1.tgz", + "integrity": "sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/typicode" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/husky" - } - ], "bin": { "husky": "lib/bin.js" }, "engines": { - "node": ">= 10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" } }, "node_modules/hyphenate-style-name": { @@ -28052,11 +27931,6 @@ "node": ">= 0.4" } }, - "node_modules/internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" - }, "node_modules/interpret": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", @@ -38153,11 +38027,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, "node_modules/react-native-url-polyfill": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-1.3.0.tgz", @@ -38248,20 +38117,6 @@ } } }, - "node_modules/react-resize-detector": { - "version": "6.7.8", - "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-6.7.8.tgz", - "integrity": "sha512-0FaEcUBAbn+pq3PT5a9hHRebUfuS1SRLGLpIw8LydU7zX429I6XJgKerKAMPsJH0qWAl6o5bVKNqFJqr6tGPYw==", - "dependencies": { - "@types/resize-observer-browser": "^0.1.6", - "lodash": "^4.17.21", - "resize-observer-polyfill": "^1.5.1" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0", - "react-dom": "^16.0.0 || ^17.0.0" - } - }, "node_modules/react-rnd": { "version": "10.3.7", "resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.3.7.tgz", @@ -38328,43 +38183,6 @@ "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/react-smooth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.1.tgz", - "integrity": "sha512-Own9TA0GPPf3as4vSwFhDouVfXP15ie/wIHklhyKBH5AN6NFtdk0UpHBnonV11BtqDkAWlt40MOUc+5srmW7NA==", - "dependencies": { - "fast-equals": "^2.0.0", - "react-transition-group": "2.9.0" - }, - "peerDependencies": { - "prop-types": "^15.6.0", - "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-smooth/node_modules/dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", - "dependencies": { - "@babel/runtime": "^7.1.2" - } - }, - "node_modules/react-smooth/node_modules/react-transition-group": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", - "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", - "dependencies": { - "dom-helpers": "^3.4.0", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" - }, - "peerDependencies": { - "react": ">=15.0.0", - "react-dom": ">=15.0.0" - } - }, "node_modules/react-style-singleton": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", @@ -38602,43 +38420,6 @@ "node": ">=8.10.0" } }, - "node_modules/recharts": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.0.9.tgz", - "integrity": "sha512-JNsXE80PuF3hugUCE7JqDOMSvu5xQLxtjOaqFKKZI2pCJ1PVJzhwDv4TWk0nO4AvADbeWzYEHbg8C5Hcrh42UA==", - "dependencies": { - "@types/d3-scale": "^3.0.0", - "@types/d3-shape": "^2.0.0", - "classnames": "^2.2.5", - "d3-interpolate": "^2.0.1", - "d3-scale": "^3.2.3", - "d3-shape": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.19", - "react-is": "16.10.2", - "react-resize-detector": "^6.6.3", - "react-smooth": "^2.0.0", - "recharts-scale": "^0.4.4", - "reduce-css-calc": "^2.1.8" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0", - "react-dom": "^16.0.0 || ^17.0.0" - } - }, - "node_modules/recharts-scale": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", - "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", - "dependencies": { - "decimal.js-light": "^2.4.1" - } - }, - "node_modules/recharts/node_modules/react-is": { - "version": "16.10.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.10.2.tgz", - "integrity": "sha512-INBT1QEgtcCCgvccr5/86CfD71fw9EPmDxgiJX4I2Ddr6ZsV6iFXsuby+qWJPtmNuMY0zByTsG4468P7nHuNWA==" - }, "node_modules/recoil": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.6.1.tgz", @@ -38671,20 +38452,6 @@ "node": ">=8" } }, - "node_modules/reduce-css-calc": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", - "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", - "dependencies": { - "css-unit-converter": "^1.1.1", - "postcss-value-parser": "^3.3.0" - } - }, - "node_modules/reduce-css-calc/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - }, "node_modules/reduce-flatten": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", @@ -56734,32 +56501,6 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, - "@types/d3-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.2.tgz", - "integrity": "sha512-3YHpvDw9LzONaJzejXLOwZ3LqwwkoXb9LI2YN7Hbd6pkGo5nIlJ09ul4bQhBN4hQZJKmUpX8HkVqbzgUKY48cg==" - }, - "@types/d3-scale": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.3.2.tgz", - "integrity": "sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==", - "requires": { - "@types/d3-time": "^2" - } - }, - "@types/d3-shape": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.1.3.tgz", - "integrity": "sha512-HAhCel3wP93kh4/rq+7atLdybcESZ5bRHDEZUojClyZWsRuEMo3A52NGYJSh48SxfxEU6RZIVbZL2YFZ2OAlzQ==", - "requires": { - "@types/d3-path": "^2" - } - }, - "@types/d3-time": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz", - "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==" - }, "@types/date-fns": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@types/date-fns/-/date-fns-2.6.0.tgz", @@ -57149,11 +56890,6 @@ "@types/react": "*" } }, - "@types/resize-observer-browser": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.7.tgz", - "integrity": "sha512-G9eN0Sn0ii9PWQ3Vl72jDPgeJwRWhv2Qk/nQkJuWmRmOB4HX3/BhD5SE1dZs/hzPZL/WKnvF0RHdTSG54QJFyg==" - }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -63667,11 +63403,6 @@ "source-map": "^0.6.1" } }, - "css-unit-converter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", - "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" - }, "css-vendor": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", @@ -63919,73 +63650,6 @@ "type": "^1.0.1" } }, - "d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "requires": { - "internmap": "^1.0.0" - } - }, - "d3-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", - "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==" - }, - "d3-format": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", - "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==" - }, - "d3-interpolate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz", - "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==", - "requires": { - "d3-color": "1 - 2" - } - }, - "d3-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz", - "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA==" - }, - "d3-scale": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz", - "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==", - "requires": { - "d3-array": "^2.3.0", - "d3-format": "1 - 2", - "d3-interpolate": "1.2.0 - 2", - "d3-time": "^2.1.1", - "d3-time-format": "2 - 3" - } - }, - "d3-shape": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.1.0.tgz", - "integrity": "sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==", - "requires": { - "d3-path": "1 - 2" - } - }, - "d3-time": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", - "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", - "requires": { - "d3-array": "2" - } - }, - "d3-time-format": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", - "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", - "requires": { - "d3-time": "1 - 2" - } - }, "damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -64053,11 +63717,6 @@ "integrity": "sha512-Nv6ENEzyPQ6AItkGwLE2PGKinZZ9g59vSh2BeH6NqPu0OTKZ5ruJsVqh/orbAnqXc9pBbgXAIrc2EyaCj8NpGg==", "dev": true }, - "decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" - }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -67246,11 +66905,6 @@ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", "dev": true }, - "fast-equals": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-2.0.4.tgz", - "integrity": "sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==" - }, "fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -69061,9 +68715,9 @@ } }, "husky": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/husky/-/husky-5.0.9.tgz", - "integrity": "sha512-0SjcaY21a+IRdx7p7r/X33Vc09UR2m8SbP8yfkhUX2/jAmwcz+GR7i9jXkp2pP3GfX23JhMkVP6SWwXB18uXtg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.1.tgz", + "integrity": "sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==", "dev": true }, "hyphenate-style-name": { @@ -69836,11 +69490,6 @@ "side-channel": "^1.0.4" } }, - "internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" - }, "interpret": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", @@ -77616,11 +77265,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, - "react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, "react-native-url-polyfill": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-1.3.0.tgz", @@ -77666,16 +77310,6 @@ "tslib": "^2.0.0" } }, - "react-resize-detector": { - "version": "6.7.8", - "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-6.7.8.tgz", - "integrity": "sha512-0FaEcUBAbn+pq3PT5a9hHRebUfuS1SRLGLpIw8LydU7zX429I6XJgKerKAMPsJH0qWAl6o5bVKNqFJqr6tGPYw==", - "requires": { - "@types/resize-observer-browser": "^0.1.6", - "lodash": "^4.17.21", - "resize-observer-polyfill": "^1.5.1" - } - }, "react-rnd": { "version": "10.3.7", "resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.3.7.tgz", @@ -77729,36 +77363,6 @@ "resize-observer-polyfill": "^1.5.0" } }, - "react-smooth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.1.tgz", - "integrity": "sha512-Own9TA0GPPf3as4vSwFhDouVfXP15ie/wIHklhyKBH5AN6NFtdk0UpHBnonV11BtqDkAWlt40MOUc+5srmW7NA==", - "requires": { - "fast-equals": "^2.0.0", - "react-transition-group": "2.9.0" - }, - "dependencies": { - "dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", - "requires": { - "@babel/runtime": "^7.1.2" - } - }, - "react-transition-group": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", - "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", - "requires": { - "dom-helpers": "^3.4.0", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" - } - } - } - }, "react-style-singleton": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", @@ -77941,41 +77545,6 @@ "picomatch": "^2.2.1" } }, - "recharts": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.0.9.tgz", - "integrity": "sha512-JNsXE80PuF3hugUCE7JqDOMSvu5xQLxtjOaqFKKZI2pCJ1PVJzhwDv4TWk0nO4AvADbeWzYEHbg8C5Hcrh42UA==", - "requires": { - "@types/d3-scale": "^3.0.0", - "@types/d3-shape": "^2.0.0", - "classnames": "^2.2.5", - "d3-interpolate": "^2.0.1", - "d3-scale": "^3.2.3", - "d3-shape": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.19", - "react-is": "16.10.2", - "react-resize-detector": "^6.6.3", - "react-smooth": "^2.0.0", - "recharts-scale": "^0.4.4", - "reduce-css-calc": "^2.1.8" - }, - "dependencies": { - "react-is": { - "version": "16.10.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.10.2.tgz", - "integrity": "sha512-INBT1QEgtcCCgvccr5/86CfD71fw9EPmDxgiJX4I2Ddr6ZsV6iFXsuby+qWJPtmNuMY0zByTsG4468P7nHuNWA==" - } - } - }, - "recharts-scale": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", - "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", - "requires": { - "decimal.js-light": "^2.4.1" - } - }, "recoil": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.6.1.tgz", @@ -77994,22 +77563,6 @@ "strip-indent": "^3.0.0" } }, - "reduce-css-calc": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", - "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", - "requires": { - "css-unit-converter": "^1.1.1", - "postcss-value-parser": "^3.3.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, "reduce-flatten": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", diff --git a/package.json b/package.json index 5785fb0f03..d629bfd24a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kwenta", - "version": "3.2.4", + "version": "3.2.5", "scripts": { "dev": "next", "build": "next build", @@ -72,7 +72,6 @@ "react-slick": "0.29.0", "react-table": "7.7.0", "react-toastify": "9.0.4", - "recharts": "2.0.9", "recoil": "0.6.1", "slick-carousel": "1.8.1", "styled-components": "5.1.1", @@ -126,7 +125,7 @@ "eslint-plugin-testing-library": "^5.1.0", "eslint-plugin-ui-testing": "^2.0.0", "html-to-image": "^1.9.0", - "husky": "5.0.9", + "husky": "8.0.1", "jest": "27.3.1", "lint-staged": "^13.0.3", "pinst": "3.0.0", diff --git a/pages/market.tsx b/pages/market.tsx index f810dfde16..1b0662857b 100644 --- a/pages/market.tsx +++ b/pages/market.tsx @@ -1,25 +1,35 @@ import Head from 'next/head'; import { useRouter } from 'next/router'; -import { useEffect, FC } from 'react'; +import { useEffect, FC, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import styled from 'styled-components'; +import Error from 'components/Error'; import Loader from 'components/Loader'; import { DesktopOnlyView, MobileOrTabletView } from 'components/Media'; +import { CROSS_MARGIN_ENABLED, DEFAULT_FUTURES_MARGIN_TYPE } from 'constants/defaults'; import Connector from 'containers/Connector'; import { FuturesContext } from 'contexts/FuturesContext'; +import { useRefetchContext } from 'contexts/RefetchContext'; import useFuturesData from 'hooks/useFuturesData'; -import useIsL1 from 'hooks/useIsL1'; +import useIsL2 from 'hooks/useIsL2'; +import { FuturesAccountState, FuturesAccountType } from 'queries/futures/types'; +import CrossMarginOnboard from 'sections/futures/CrossMarginOnboard'; import LeftSidebar from 'sections/futures/LeftSidebar/LeftSidebar'; import MarketInfo from 'sections/futures/MarketInfo'; import MobileTrade from 'sections/futures/MobileTrade/MobileTrade'; -import FuturesUnsupported from 'sections/futures/Trade/FuturesUnsupported'; +import FuturesUnsupportedNetwork from 'sections/futures/Trade/FuturesUnsupported'; import TradeIsolatedMargin from 'sections/futures/Trade/TradeIsolatedMargin'; import TradeCrossMargin from 'sections/futures/TradeCrossMargin'; import AppLayout from 'sections/shared/Layout/AppLayout'; import GitHashID from 'sections/shared/Layout/AppLayout/GitHashID'; -import { currentMarketState, futuresAccountState, futuresAccountTypeState } from 'store/futures'; +import { + currentMarketState, + futuresAccountState, + futuresAccountTypeState, + showCrossMarginOnboardState, +} from 'store/futures'; import { PageContent, FullHeightContainer, RightSideContent } from 'styles/common'; import { FuturesMarketAsset } from 'utils/futures'; @@ -28,16 +38,14 @@ type MarketComponent = FC & { getLayout: (page: HTMLElement) => JSX.Element }; const Market: MarketComponent = () => { const { t } = useTranslation(); const router = useRouter(); - const { walletAddress, isWalletConnected, unsupportedNetwork } = Connector.useContainer(); - const isL1 = useIsL1(); + const { walletAddress } = Connector.useContainer(); + const futuresData = useFuturesData(); const marketAsset = router.query.asset as FuturesMarketAsset; const setCurrentMarket = useSetRecoilState(currentMarketState); - const selectedAccountType = useRecoilValue(futuresAccountTypeState); - const { ready } = useRecoilValue(futuresAccountState); - - const futuresData = useFuturesData(); + const account = useRecoilValue(futuresAccountState); + const [showOnboard, setShowOnboard] = useRecoilState(showCrossMarginOnboardState); useEffect(() => { if (marketAsset) setCurrentMarket(marketAsset); @@ -48,34 +56,84 @@ const Market: MarketComponent = () => { {t('futures.market.page-title', { pair: router.query.market })} + setShowOnboard(false)} isOpen={showOnboard} /> - {!isWalletConnected || unsupportedNetwork || isL1 ? ( - - ) : walletAddress && !ready ? ( - - ) : selectedAccountType === 'cross_margin' ? ( - - ) : ( - - )} + - {walletAddress && !ready ? : } + {walletAddress && account.status === 'initial-fetch' ? : } ); }; +type TradePanelProps = { + walletAddress: string | null; + account: FuturesAccountState; +}; + +function TradePanelDesktop({ walletAddress, account }: TradePanelProps) { + const { t } = useTranslation(); + const { handleRefetch } = useRefetchContext(); + const router = useRouter(); + const isL2 = useIsL2(); + const setSelectedAccountType = useSetRecoilState(futuresAccountTypeState); + + const accountType = useMemo(() => { + if (!CROSS_MARGIN_ENABLED) return DEFAULT_FUTURES_MARGIN_TYPE; + const routerType = + typeof router.query.accountType === 'string' + ? (router.query.accountType as FuturesAccountType) + : DEFAULT_FUTURES_MARGIN_TYPE; + return ['cross_margin', 'isolated_margin'].includes(routerType) + ? routerType + : DEFAULT_FUTURES_MARGIN_TYPE; + }, [router.query.accountType]); + + useEffect(() => { + setSelectedAccountType(accountType); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [accountType]); + + if (!isL2) { + return ; + } + + if ( + !router.isReady || + (accountType === 'cross_margin' && walletAddress && account.status === 'initial-fetch') + ) { + return ; + } + + if (accountType === 'cross_margin') { + return account.status === 'error' && !account.crossMarginAddress ? ( +
+ handleRefetch('cross-margin-account-change', 5), + label: 'Retry', + }} + /> +
+ ) : ( + + ); + } + return ; +} + Market.getLayout = (page) => {page}; export default Market; diff --git a/queries/futures/types.ts b/queries/futures/types.ts index 42a71f8ca8..ec770f7a8f 100644 --- a/queries/futures/types.ts +++ b/queries/futures/types.ts @@ -266,6 +266,7 @@ export type FundingRates = { export type FuturesPotentialTradeDetails = { size: Wei; + sizeDelta: Wei; liqPrice: Wei; margin: Wei; price: Wei; @@ -287,12 +288,16 @@ export type FuturesPotentialTradeDetailsQuery = { export type FuturesAccountType = 'cross_margin' | 'isolated_margin'; +type Wallet = string; +type CrossMarginAccount = string; +type FactoryAddress = string; +export type CrossMarginAccounts = Record>; + export type FuturesAccountState = { walletAddress: string | null; - selectedFuturesAddress: string | null; crossMarginAddress: string | null; crossMarginAvailable: boolean; - ready: boolean; + status: 'initial-fetch' | 'complete' | 'error' | 'refetching' | 'idle'; }; export type SynthBalances = Balances & { @@ -303,6 +308,8 @@ export type TradeFees = { staticFee: Wei; dynamicFeeRate: Wei; crossMarginFee: Wei; + keeperEthDeposit: Wei; + limitStopOrderFee: Wei; total: Wei; }; @@ -312,4 +319,7 @@ export type FuturesTradeInputs = { leverage: string; nativeSizeDelta: Wei; susdSizeDelta: Wei; + orderPrice?: Wei | undefined; }; + +export type FuturesOrderType = 'market' | 'next-price' | 'stop' | 'limit'; diff --git a/queries/futures/useGetCrossMarginAccountOverview.ts b/queries/futures/useGetCrossMarginAccountOverview.ts index d9b8298cc1..18a6553fc5 100644 --- a/queries/futures/useGetCrossMarginAccountOverview.ts +++ b/queries/futures/useGetCrossMarginAccountOverview.ts @@ -7,17 +7,19 @@ import QUERY_KEYS from 'constants/queryKeys'; import Connector from 'containers/Connector'; import useCrossMarginAccountContracts from 'hooks/useCrossMarginContracts'; import { - crossMarginAvailableMarginState, + crossMarginAccountOverviewState, crossMarginSettingsState, futuresAccountState, } from 'store/futures'; import { zeroBN } from 'utils/formatters/number'; +const BPS_CONVERSION = 10000; + export default function useGetCrossMarginAccountOverview() { - const { network } = Connector.useContainer(); + const { network, provider } = Connector.useContainer(); const { crossMarginAddress } = useRecoilValue(futuresAccountState); - const setFreeMargin = useSetRecoilState(crossMarginAvailableMarginState); const setCrossMarginSettings = useSetRecoilState(crossMarginSettingsState); + const setAccountOverview = useSetRecoilState(crossMarginAccountOverviewState); const { crossMarginAccountContract, crossMarginBaseSettings } = useCrossMarginAccountContracts(); @@ -29,25 +31,32 @@ export default function useGetCrossMarginAccountOverview() { ), async () => { if (!crossMarginAddress || !crossMarginAccountContract) { - setFreeMargin(zeroBN); - return { freeMargin: zeroBN }; + setAccountOverview({ + freeMargin: zeroBN, + keeperEthBal: zeroBN, + }); + return { freeMargin: zeroBN, keeperEthBal: zeroBN }; } const freeMargin = await crossMarginAccountContract.freeMargin(); const tradeFee = await crossMarginBaseSettings?.tradeFee(); const limitOrderFee = await crossMarginBaseSettings?.limitOrderFee(); - const stopLossFee = await crossMarginBaseSettings?.stopLossFee(); + const stopOrderFee = await crossMarginBaseSettings?.stopOrderFee(); + const keeperEthBal = await provider.getBalance(crossMarginAddress); const settings = { - tradeFee: tradeFee ? wei(tradeFee.toNumber() / 10000) : zeroBN, - limitOrderFee: limitOrderFee ? wei(limitOrderFee.toNumber() / 10000) : zeroBN, - stopLossFee: stopLossFee ? wei(stopLossFee.toNumber() / 1000) : zeroBN, + tradeFee: tradeFee ? wei(tradeFee.toNumber() / BPS_CONVERSION) : zeroBN, + limitOrderFee: limitOrderFee ? wei(limitOrderFee.toNumber() / BPS_CONVERSION) : zeroBN, + stopOrderFee: stopOrderFee ? wei(stopOrderFee.toNumber() / BPS_CONVERSION) : zeroBN, }; - setFreeMargin(wei(freeMargin)); + setAccountOverview({ + freeMargin: wei(freeMargin), + keeperEthBal: wei(keeperEthBal), + }); setCrossMarginSettings(settings); - return { freeMargin: wei(freeMargin), settings: settings }; + return { freeMargin: wei(freeMargin), settings: settings, keeperEthBal }; }, { enabled: !!crossMarginAddress, diff --git a/queries/futures/useGetCrossMarginTradePreview.ts b/queries/futures/useGetCrossMarginTradePreview.ts index 9f775eb38a..d50e6575fd 100644 --- a/queries/futures/useGetCrossMarginTradePreview.ts +++ b/queries/futures/useGetCrossMarginTradePreview.ts @@ -1,5 +1,5 @@ import { SynthetixJS } from '@synthetixio/contracts-interface'; -import { wei, WeiSource } from '@synthetixio/wei'; +import { wei } from '@synthetixio/wei'; import BN from 'bn.js'; import { BigNumber, Contract, ethers } from 'ethers'; import { formatBytes32String } from 'ethers/lib/utils'; @@ -65,11 +65,11 @@ export default function useGetCrossMarginTradePreview( }, [synthetixjs, provider, address, isL2, marketAsset]); const getPreview = useCallback( - async (sizeDelta: WeiSource | null | undefined, marginDelta: WeiSource | null | undefined) => { + async (sizeDelta: BigNumber, marginDelta: BigNumber, orderPrice?: BigNumber) => { if (contractInstance) { const sizeBN = wei(sizeDelta || 0).toBN(); const marginBN = wei(marginDelta || 0).toBN(); - const res = await contractInstance.getTradePreview(sizeBN, marginBN); + const res = await contractInstance.getTradePreview(sizeBN, marginBN, orderPrice); return res; } }, @@ -105,16 +105,20 @@ class FuturesMarketInternal { this._cache = {}; } - getTradePreview = async (sizeDelta: BigNumber, marginDelta: BigNumber) => { + getTradePreview = async ( + sizeDelta: BigNumber, + marginDelta: BigNumber, + limitStopPrice?: BigNumber + ) => { const position = await this._futuresMarketContract.positions(this._account); - const price = await this._futuresMarketContract.assetPrice(); + const price = limitStopPrice || (await this._futuresMarketContract.assetPrice()).price; const takerFee = await this._getSetting('takerFee', [this._marketKeyBytes]); const makerFee = await this._getSetting('makerFee', [this._marketKeyBytes]); const tradeParams = { sizeDelta, - price: price.price, + price: price, takerFee, makerFee, trackingCode: KWENTA_TRACKING_CODE, @@ -145,11 +149,13 @@ class FuturesMarketInternal { } const fee = await this._orderFee(tradeParams, dynamicFee.feeRate); + const { margin, status } = await this._recomputeMarginWithDelta( oldPos, tradeParams.price, marginDelta.add(fee.mul(-1)) ); + if (status !== PotentialTradeStatus.OK) { return { newPos: oldPos, fee: ZERO_BIG_NUM, status }; } diff --git a/queries/futures/useGetFuturesDailyTradeStats.ts b/queries/futures/useGetFuturesDailyTradeStats.ts deleted file mode 100644 index f0a13804a1..0000000000 --- a/queries/futures/useGetFuturesDailyTradeStats.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { NetworkId } from '@synthetixio/contracts-interface'; -import { useQuery, UseQueryOptions } from 'react-query'; - -import QUERY_KEYS from 'constants/queryKeys'; -import ROUTES from 'constants/routes'; -import Connector from 'containers/Connector'; -import useIsL2 from 'hooks/useIsL2'; -import { calculateTimestampForPeriod } from 'utils/formatters/date'; -import logError from 'utils/logError'; - -import { DAY_PERIOD, FUTURES_ENDPOINT_OP_MAINNET } from './constants'; -import { getFuturesOneMinStats } from './subgraph'; -import { FuturesDailyTradeStats, FuturesOneMinuteStat } from './types'; -import { getFuturesEndpoint, calculateDailyTradeStats } from './utils'; - -const useGetFuturesDailyTradeStats = (options?: UseQueryOptions) => { - const { network } = Connector.useContainer(); - const isL2 = useIsL2(); - const homepage = window.location.pathname === ROUTES.Home.Root; - const futuresEndpoint = homepage - ? FUTURES_ENDPOINT_OP_MAINNET - : getFuturesEndpoint(network?.id as NetworkId); - - const queryTrades = async ( - _skip: number, - _existing: FuturesOneMinuteStat[] - ): Promise => { - try { - const minTimestamp = Math.floor(calculateTimestampForPeriod(DAY_PERIOD) / 1000); - const response = await getFuturesOneMinStats( - futuresEndpoint, - { - first: 999999, - where: { - timestamp_gte: `${minTimestamp}`, - }, - }, - { - trades: true, - volume: true, - } - ); - - return response; - } catch (e) { - logError(e); - return []; - } - }; - - return useQuery( - QUERY_KEYS.Futures.DayTradeStats(network?.id as NetworkId, null), - async () => { - const trades = await queryTrades(0, []); - - return calculateDailyTradeStats(trades); - }, - { enabled: homepage || isL2, ...options } - ); -}; - -export default useGetFuturesDailyTradeStats; diff --git a/queries/futures/useGetFuturesMarginTransfers.ts b/queries/futures/useGetFuturesMarginTransfers.ts index 7a61161f75..2c6b8f954c 100644 --- a/queries/futures/useGetFuturesMarginTransfers.ts +++ b/queries/futures/useGetFuturesMarginTransfers.ts @@ -6,7 +6,7 @@ import { useRecoilValue } from 'recoil'; import QUERY_KEYS from 'constants/queryKeys'; import Connector from 'containers/Connector'; import useIsL2 from 'hooks/useIsL2'; -import { futuresAccountState } from 'store/futures'; +import { selectedFuturesAddressState } from 'store/futures'; import { getDisplayAsset } from 'utils/futures'; import logError from 'utils/logError'; @@ -17,7 +17,7 @@ const useGetFuturesMarginTransfers = ( currencyKey: string | null, options?: UseQueryOptions ) => { - const { selectedFuturesAddress } = useRecoilValue(futuresAccountState); + const selectedFuturesAddress = useRecoilValue(selectedFuturesAddressState); const { defaultSynthetixjs: synthetixjs, network, isWalletConnected } = Connector.useContainer(); const futuresEndpoint = getFuturesEndpoint(network?.id as NetworkId); const isL2 = useIsL2(); diff --git a/queries/futures/useGetFuturesOpenOrders.ts b/queries/futures/useGetFuturesOpenOrders.ts index 101f5a00a4..5cebe875bc 100644 --- a/queries/futures/useGetFuturesOpenOrders.ts +++ b/queries/futures/useGetFuturesOpenOrders.ts @@ -7,17 +7,22 @@ import { useSetRecoilState, useRecoilValue } from 'recoil'; import QUERY_KEYS from 'constants/queryKeys'; import Connector from 'containers/Connector'; -import { futuresAccountState, openOrdersState, marketInfoState } from 'store/futures'; +import { openOrdersState, marketInfoState, selectedFuturesAddressState } from 'store/futures'; import { formatCurrency, formatDollars, weiFromWei } from 'utils/formatters/number'; -import { FuturesMarketAsset, getMarketName, MarketKeyByAsset } from 'utils/futures'; +import { + FuturesMarketAsset, + getDisplayAsset, + getMarketName, + MarketKeyByAsset, +} from 'utils/futures'; import logError from 'utils/logError'; import { PositionSide, FuturesOrder } from './types'; import { getFuturesEndpoint } from './utils'; const useGetFuturesOpenOrders = (options?: UseQueryOptions) => { - const { selectedFuturesAddress } = useRecoilValue(futuresAccountState); - const { network, synthsMap } = Connector.useContainer(); + const selectedFuturesAddress = useRecoilValue(selectedFuturesAddressState); + const { network } = Connector.useContainer(); const futuresEndpoint = getFuturesEndpoint(network?.id as NetworkId); const marketInfo = useRecoilValue(marketInfoState); @@ -71,7 +76,7 @@ const useGetFuturesOpenOrders = (options?: UseQueryOptions) => { marketKey: MarketKeyByAsset[asset], orderType: o.orderType === 'NextPrice' ? 'Next-Price' : o.orderType, sizeTxt: formatCurrency(asset, size.abs(), { - sign: asset ? synthsMap[asset]?.sign : '', + currencyKey: getDisplayAsset(asset) ?? '', minDecimals: size.abs().lt(0.01) ? 4 : 2, }), targetPriceTxt: formatDollars(targetPrice), diff --git a/queries/futures/useGetFuturesPositionForAccount.ts b/queries/futures/useGetFuturesPositionForAccount.ts index 2e026b03e3..a9a18918ee 100644 --- a/queries/futures/useGetFuturesPositionForAccount.ts +++ b/queries/futures/useGetFuturesPositionForAccount.ts @@ -5,8 +5,7 @@ import { useRecoilValue } from 'recoil'; import QUERY_KEYS from 'constants/queryKeys'; import Connector from 'containers/Connector'; -import { futuresAccountTypeState } from 'store/futures'; -import { futuresAccountState } from 'store/futures'; +import { futuresAccountTypeState, selectedFuturesAddressState } from 'store/futures'; import logError from 'utils/logError'; import { FUTURES_POSITION_FRAGMENT } from './constants'; @@ -17,7 +16,7 @@ const useGetFuturesPositionForAccount = (options?: UseQueryOptions) => { const { network, walletAddress } = Connector.useContainer(); const selectedAccountType = useRecoilValue(futuresAccountTypeState); - const { selectedFuturesAddress } = useRecoilValue(futuresAccountState); + const selectedFuturesAddress = useRecoilValue(selectedFuturesAddressState); const futuresEndpoint = getFuturesEndpoint(network?.id as NetworkId); return useQuery( diff --git a/queries/futures/useGetFuturesPositionForMarket.ts b/queries/futures/useGetFuturesPositionForMarket.ts index 0eeb618ae3..956000d991 100644 --- a/queries/futures/useGetFuturesPositionForMarket.ts +++ b/queries/futures/useGetFuturesPositionForMarket.ts @@ -5,8 +5,7 @@ import { useRecoilValue, useSetRecoilState } from 'recoil'; import QUERY_KEYS from 'constants/queryKeys'; import Connector from 'containers/Connector'; -import useIsL2 from 'hooks/useIsL2'; -import { marketKeyState, futuresAccountState, positionState } from 'store/futures'; +import { marketKeyState, positionState, selectedFuturesAddressState } from 'store/futures'; import { MarketAssetByKey } from 'utils/futures'; import { FuturesPosition } from './types'; @@ -14,8 +13,7 @@ import { mapFuturesPosition, getFuturesMarketContract } from './utils'; const useGetFuturesPositionForMarket = (options?: UseQueryOptions) => { const { defaultSynthetixjs: synthetixjs, network } = Connector.useContainer(); - const isL2 = useIsL2(); - const { selectedFuturesAddress } = useRecoilValue(futuresAccountState); + const selectedFuturesAddress = useRecoilValue(selectedFuturesAddressState); const market = useRecoilValue(marketKeyState); const setPosition = useSetRecoilState(positionState); @@ -26,14 +24,13 @@ const useGetFuturesPositionForMarket = (options?: UseQueryOptions { - const { - contracts: { FuturesMarketData }, - } = synthetixjs!; - - if (!market || !selectedFuturesAddress) { + if (!market || !selectedFuturesAddress || !synthetixjs) { setPosition(null); return null; } + const { + contracts: { FuturesMarketData }, + } = synthetixjs; const [futuresPosition, canLiquidatePosition] = await Promise.all([ FuturesMarketData.positionDetailsForMarketKey( @@ -56,7 +53,6 @@ const useGetFuturesPositionForMarket = (options?: UseQueryOptions asset); @@ -88,7 +85,6 @@ const useGetFuturesPositionForMarkets = (options?: UseQueryOptions { const selectedAccountType = useRecoilValue(futuresAccountTypeState); - const { selectedFuturesAddress } = useRecoilValue(futuresAccountState); + const selectedFuturesAddress = useRecoilValue(selectedFuturesAddressState); const { defaultSynthetixjs: synthetixjs } = Connector.useContainer(); const isL2 = useIsL2(); const leverageSide = useRecoilValue(leverageSideState); const marketAsset = useRecoilValue(currentMarketState); + const orderType = useRecoilValue(orderTypeState); const getPreview = useGetCrossMarginPotentialTrade(marketAsset, selectedFuturesAddress); @@ -38,13 +40,15 @@ const useGetFuturesPotentialTradeDetails = () => { async ( nativeSizeDelta: Wei, positionMarginDelta: Wei, - leverage: number + leverage: number, + orderPrice?: Wei ): Promise => { if ( !synthetixjs || !marketAsset || (!nativeSizeDelta && selectedAccountType === 'isolated_margin') || (!nativeSizeDelta && (!positionMarginDelta || positionMarginDelta.eq(0))) || + ((orderType === 'limit' || orderType === 'stop') && orderPrice?.eq(0)) || !isL2 || !selectedFuturesAddress ) { @@ -60,7 +64,11 @@ const useGetFuturesPotentialTradeDetails = () => { const globals = await FuturesMarketData.globals(); const preview = selectedAccountType === 'cross_margin' - ? await getPreview(nativeSizeDelta, wei(positionMarginDelta).toBN()) + ? await getPreview( + nativeSizeDelta.toBN(), + wei(positionMarginDelta).toBN(), + orderPrice ? wei(orderPrice).toBN() : undefined + ) : await FuturesMarketContract.postTradeDetails( wei(nativeSizeDelta).toBN(), selectedFuturesAddress @@ -83,6 +91,7 @@ const useGetFuturesPotentialTradeDetails = () => { margin: wei(margin), price: wei(price), size: wei(size), + sizeDelta: nativeSizeDelta, side: leverageSide, leverage: wei(leverage ? leverage : 1), notionalValue: wei(size).mul(wei(price)), @@ -101,19 +110,20 @@ const useGetFuturesPotentialTradeDetails = () => { isL2, leverageSide, synthetixjs, + orderType, getPreview, ] ); const getTradeDetails = useCallback( - async (nativeSize: Wei, positionMarginDelta: Wei, leverage: number) => { + async (nativeSize: Wei, positionMarginDelta: Wei, leverage: number, orderPrice?: Wei) => { try { setPotentialTradeDetails({ data: null, status: 'fetching', error: null, }); - const data = await generatePreview(nativeSize, positionMarginDelta, leverage); + const data = await generatePreview(nativeSize, positionMarginDelta, leverage, orderPrice); setPotentialTradeDetails({ data, status: 'complete', error: null }); } catch (err) { logError(err); diff --git a/queries/futures/useQueryCrossMarginAccount.ts b/queries/futures/useQueryCrossMarginAccount.ts index 45d8afac87..33feeb0f01 100644 --- a/queries/futures/useQueryCrossMarginAccount.ts +++ b/queries/futures/useQueryCrossMarginAccount.ts @@ -1,76 +1,127 @@ -import { useCallback } from 'react'; +import { NetworkId } from '@synthetixio/contracts-interface'; +import request, { gql } from 'graphql-request'; import { useQuery } from 'react-query'; import { useRecoilState } from 'recoil'; import { CROSS_MARGIN_ACCOUNT_FACTORY } from 'constants/address'; import QUERY_KEYS from 'constants/queryKeys'; import Connector from 'containers/Connector'; -import { futuresAccountState, futuresAccountTypeState } from 'store/futures'; +import usePersistedRecoilState from 'hooks/usePersistedRecoilState'; +import { crossMarginAccountsState, futuresAccountState } from 'store/futures'; +import logError from 'utils/logError'; import useCrossMarginAccountContracts from '../../hooks/useCrossMarginContracts'; +import { FuturesAccountState } from './types'; +import { getFuturesEndpoint } from './utils'; const SUPPORTED_NETWORKS = Object.keys(CROSS_MARGIN_ACCOUNT_FACTORY); export default function useQueryCrossMarginAccount() { const { crossMarginContractFactory } = useCrossMarginAccountContracts(); const { network, walletAddress } = Connector.useContainer(); + const futuresEndpoint = getFuturesEndpoint(network?.id as NetworkId); + const [futuresAccount, setFuturesAccount] = useRecoilState(futuresAccountState); - const [selectedAccountType, setSelectedAccountType] = useRecoilState(futuresAccountTypeState); - - const queryAccountLogs = useCallback(async () => { - if (!walletAddress || !crossMarginContractFactory) return null; - const accountFilter = crossMarginContractFactory.filters.NewAccount(walletAddress); - if (accountFilter) { - const logs = await crossMarginContractFactory.queryFilter(accountFilter); - if (logs.length) { - return logs[0].args?.[1] || null; - } - } - return null; - }, [walletAddress, crossMarginContractFactory]); + const [storedCrossMarginAccounts, setStoredCrossMarginAccount] = usePersistedRecoilState( + crossMarginAccountsState + ); - return useQuery( + return useQuery( QUERY_KEYS.Futures.CrossMarginAccount( - crossMarginContractFactory?.address || '', walletAddress || '', - selectedAccountType + crossMarginContractFactory?.address || '' ), async () => { + // TODO: Improve Cross margin loading states + if (!SUPPORTED_NETWORKS.includes(String(network.id))) { - const accountState = { - ready: true, + const accountState: FuturesAccountState = { crossMarginAvailable: false, crossMarginAddress: null, walletAddress, - selectedFuturesAddress: walletAddress, + status: 'complete', }; - setSelectedAccountType('isolated_margin'); setFuturesAccount(accountState); - return accountState; + return null; + } + + if (!crossMarginContractFactory?.address || !walletAddress) { + setFuturesAccount({ + ...futuresAccount, + status: 'idle', + crossMarginAddress: null, + crossMarginAvailable: true, + walletAddress, + }); + return null; + } + + const existing = crossMarginContractFactory?.address + ? storedCrossMarginAccounts[crossMarginContractFactory?.address]?.[walletAddress] + : null; + + if (existing) { + setFuturesAccount({ + ...futuresAccount, + status: 'complete', + crossMarginAddress: existing, + crossMarginAvailable: true, + walletAddress, + }); + return existing; } setFuturesAccount({ ...futuresAccount, - crossMarginAddress: - walletAddress === futuresAccount.walletAddress ? futuresAccount.crossMarginAddress : null, + status: futuresAccount.status === 'initial-fetch' ? 'initial-fetch' : 'refetching', + crossMarginAddress: null, crossMarginAvailable: true, walletAddress, - selectedFuturesAddress: futuresAccount?.selectedFuturesAddress, }); - const crossMarginAccount = await queryAccountLogs(); + try { + const response = await request( + futuresEndpoint, + gql` + query crossMarginAccounts($owner: String!) { + crossMarginAccounts(where: { owner: $owner }) { + id + owner + } + } + `, + { owner: walletAddress } + ); - const accountState = { - ready: true, - crossMarginAvailable: true, - crossMarginAddress: crossMarginAccount, - walletAddress, - selectedFuturesAddress: - selectedAccountType === 'cross_margin' ? crossMarginAccount : walletAddress, - }; - setFuturesAccount(accountState); - return accountState; - }, - { enabled: !!walletAddress } + const crossMarginAccount = response?.crossMarginAccounts[0]?.id || null; + + const existingAccounts = crossMarginContractFactory + ? storedCrossMarginAccounts[crossMarginContractFactory.address] + : {}; + + setStoredCrossMarginAccount({ + ...storedCrossMarginAccounts, + [crossMarginContractFactory!.address]: { + ...existingAccounts, + [walletAddress]: crossMarginAccount, + }, + }); + + const accountState: FuturesAccountState = { + status: 'complete', + crossMarginAvailable: true, + crossMarginAddress: crossMarginAccount, + walletAddress, + }; + setFuturesAccount(accountState); + return crossMarginAccount; + } catch (err) { + logError(err); + setFuturesAccount({ + ...futuresAccount, + status: 'error', + }); + } + } ); } diff --git a/queries/synths/useSynthBalances.ts b/queries/synths/useSynthBalances.ts index c80069916b..2bd67b7021 100644 --- a/queries/synths/useSynthBalances.ts +++ b/queries/synths/useSynthBalances.ts @@ -1,5 +1,5 @@ import { CurrencyKey, NetworkId } from '@synthetixio/contracts-interface'; -import { Balances, SynthBalancesMap } from '@synthetixio/queries'; +import { SynthBalancesMap } from '@synthetixio/queries'; import { wei } from '@synthetixio/wei'; import { ethers } from 'ethers'; import orderBy from 'lodash/orderBy'; @@ -8,6 +8,7 @@ import { useRecoilState } from 'recoil'; import QUERY_KEYS from 'constants/queryKeys'; import Connector from 'containers/Connector'; +import { SynthBalances } from 'queries/futures/types'; import { balancesState } from 'store/futures'; import { zeroBN } from 'utils/formatters/number'; @@ -15,12 +16,12 @@ import { notNill } from './utils'; type SynthBalancesTuple = [string[], ethers.BigNumber[], ethers.BigNumber[]]; -const useSynthBalances = (options?: UseQueryOptions) => { +const useSynthBalances = (options?: UseQueryOptions) => { const { network, defaultSynthetixjs: synthetixjs, walletAddress } = Connector.useContainer(); const [, setBalances] = useRecoilState(balancesState); - return useQuery( + return useQuery( QUERY_KEYS.Synths.Balances(network?.id as NetworkId, walletAddress), async () => { if (!synthetixjs) { diff --git a/sections/dashboard/FuturesHistoryTable/FuturesHistoryTable.tsx b/sections/dashboard/FuturesHistoryTable/FuturesHistoryTable.tsx index 88ab454ee7..f2a81a515b 100644 --- a/sections/dashboard/FuturesHistoryTable/FuturesHistoryTable.tsx +++ b/sections/dashboard/FuturesHistoryTable/FuturesHistoryTable.tsx @@ -21,14 +21,14 @@ import useSelectedPriceCurrency from 'hooks/useSelectedPriceCurrency'; import { FuturesTrade } from 'queries/futures/types'; import useGetAllFuturesTradesForAccount from 'queries/futures/useGetAllFuturesTradesForAccount'; import { TradeStatus } from 'sections/futures/types'; -import { futuresAccountState, futuresAccountTypeState } from 'store/futures'; +import { futuresAccountTypeState, selectedFuturesAddressState } from 'store/futures'; import { formatCryptoCurrency, formatDollars } from 'utils/formatters/number'; import { FuturesMarketAsset, getMarketName, isDecimalFour, MarketKeyByAsset } from 'utils/futures'; import TimeDisplay from '../../futures/Trades/TimeDisplay'; const FuturesHistoryTable: FC = () => { - const { selectedFuturesAddress } = useRecoilValue(futuresAccountState); + const selectedFuturesAddress = useRecoilValue(selectedFuturesAddressState); const accountType = useRecoilValue(futuresAccountTypeState); const { t } = useTranslation(); diff --git a/sections/dashboard/MobileDashboard/FuturesMarkets.tsx b/sections/dashboard/MobileDashboard/FuturesMarkets.tsx index e9b23a7540..c0b8f86813 100644 --- a/sections/dashboard/MobileDashboard/FuturesMarkets.tsx +++ b/sections/dashboard/MobileDashboard/FuturesMarkets.tsx @@ -1,10 +1,10 @@ +import { wei } from '@synthetixio/wei'; import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useRecoilValue } from 'recoil'; -import useGetFuturesDailyTradeStats from 'queries/futures/useGetFuturesDailyTradeStats'; import { SectionHeader, SectionTitle } from 'sections/futures/MobileTrade/common'; -import { futuresMarketsState } from 'store/futures'; +import { futuresMarketsState, futuresVolumesState } from 'store/futures'; import { formatDollars, formatNumber, zeroBN } from 'utils/formatters/number'; import FuturesMarketsTable from '../FuturesMarketsTable'; @@ -14,17 +14,32 @@ const FuturesMarkets = () => { const { t } = useTranslation(); const futuresMarkets = useRecoilValue(futuresMarketsState); - - const dailyTradeStats = useGetFuturesDailyTradeStats(); + const futuresVolumes = useRecoilValue(futuresVolumesState); const openInterest = useMemo(() => { return ( - futuresMarkets - .map((market) => market.marketSize.mul(market.price).toNumber()) - .reduce((total, openInterest) => total + openInterest, 0) ?? null + futuresMarkets.reduce( + (total, { openInterest }) => + total.add(openInterest?.shortUSD ?? wei(0)).add(openInterest?.longUSD ?? wei(0)), + wei(0) + ) ?? null ); }, [futuresMarkets]); + const [trades, volume] = useMemo(() => { + const { totalTrades, totalVolume } = Object.values(futuresVolumes).reduce( + ({ totalTrades, totalVolume }, { trades, volume }) => ({ + totalTrades: totalTrades.add(trades), + totalVolume: totalVolume.add(volume), + }), + { + totalTrades: wei(0), + totalVolume: wei(0), + } + ); + return [totalTrades, totalVolume]; + }, [futuresVolumes]); + return (
@@ -37,7 +52,7 @@ const FuturesMarkets = () => { {t('dashboard.overview.futures-markets-table.daily-volume')}
- {formatDollars(dailyTradeStats.data?.totalVolume ?? zeroBN, { + {formatDollars(volume ?? zeroBN, { minDecimals: 0, })}
@@ -56,9 +71,7 @@ const FuturesMarkets = () => {
{t('dashboard.overview.futures-markets-table.daily-trades')}
-
- {formatNumber(dailyTradeStats.data?.totalTrades ?? 0, { minDecimals: 0 })} -
+
{formatNumber(trades ?? 0, { minDecimals: 0 })}
diff --git a/sections/dashboard/MobileDashboard/SynthMarkets.tsx b/sections/dashboard/MobileDashboard/SynthMarkets.tsx deleted file mode 100644 index 45b5b8897d..0000000000 --- a/sections/dashboard/MobileDashboard/SynthMarkets.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { useRecoilValue } from 'recoil'; - -import useGetFuturesDailyTradeStats from 'queries/futures/useGetFuturesDailyTradeStats'; -import { SectionHeader, SectionTitle } from 'sections/futures/MobileTrade/common'; -import { futuresMarketsState, ratesState } from 'store/futures'; -import { formatDollars, formatNumber, zeroBN } from 'utils/formatters/number'; - -import SpotMarketsTable from '../SpotMarketsTable'; -import { HeaderContainer, MarketStatsContainer, MarketStat } from './common'; - -const SynthMarkets: React.FC = () => { - const { t } = useTranslation(); - const futuresMarkets = useRecoilValue(futuresMarketsState); - const exchangeRates = useRecoilValue(ratesState); - - const dailyTradeStats = useGetFuturesDailyTradeStats(); - - const openInterest = React.useMemo(() => { - return futuresMarkets - .map((market) => market.marketSize.mul(market.price).toNumber()) - .reduce((total, openInterest) => total + openInterest, 0); - }, [futuresMarkets]); - - return ( -
- - - {t('dashboard.overview.markets-tabs.spot')} - - - -
{t('dashboard.overview.spot-markets-table.24h-vol')}
-
- {formatDollars(dailyTradeStats.data?.totalVolume || zeroBN, { - minDecimals: 0, - })} -
-
- -
{t('dashboard.overview.spot-markets-table.open-interest')}
-
- {formatDollars(openInterest ?? 0, { - minDecimals: 0, - })} -
-
- -
{t('dashboard.overview.spot-markets-table.total-trades')}
-
- {formatNumber(dailyTradeStats.data?.totalTrades ?? 0, { minDecimals: 0 })} -
-
-
-
- - -
- ); -}; - -export default SynthMarkets; diff --git a/sections/futures/CrossMarginOnboard/CrossMarginOnboard.tsx b/sections/futures/CrossMarginOnboard/CrossMarginOnboard.tsx index aa7cb4db22..e9bda2d61c 100644 --- a/sections/futures/CrossMarginOnboard/CrossMarginOnboard.tsx +++ b/sections/futures/CrossMarginOnboard/CrossMarginOnboard.tsx @@ -32,6 +32,8 @@ type Props = { onClose: () => any; }; +const MAX_REFETCH_COUNT = 20; + export default function CrossMarginOnboard({ onClose, isOpen }: Props) { const { t } = useTranslation(); const { monitorTransaction } = TransactionNotifier.useContainer(); @@ -41,7 +43,7 @@ export default function CrossMarginOnboard({ onClose, isOpen }: Props) { crossMarginContractFactory, } = useCrossMarginAccountContracts(); const susdContract = useSUSDContract(); - const { handleRefetch } = useRefetchContext(); + const { handleRefetch, refetchUntilUpdate } = useRefetchContext(); const futuresAccount = useRecoilValue(futuresAccountState); const balances = useRecoilValue(balancesState); @@ -56,11 +58,13 @@ export default function CrossMarginOnboard({ onClose, isOpen }: Props) { const fetchAllowance = useCallback(async () => { if (!crossMarginAccountContract || !susdContract || !walletAddress) return; try { - const allowance = await susdContract.allowance( + const allowanceBN = await susdContract.allowance( walletAddress, crossMarginAccountContract.address ); - setAllowance(wei(allowance)); + const allowanceWei = wei(allowanceBN); + setAllowance(allowanceWei); + return allowanceWei; } catch (err) { logError(err); } @@ -86,7 +90,7 @@ export default function CrossMarginOnboard({ onClose, isOpen }: Props) { txHash: tx.hash, onTxConfirmed: async () => { try { - handleRefetch('cross-margin-account-change', 1000); + await refetchUntilUpdate('cross-margin-account-change'); } catch (err) { logError(err); } finally { @@ -105,9 +109,9 @@ export default function CrossMarginOnboard({ onClose, isOpen }: Props) { synthetixjs, crossMarginContractFactory, network, - handleRefetch, setSubmitting, monitorTransaction, + refetchUntilUpdate, ]); const submitDeposit = useCallback( @@ -132,6 +136,26 @@ export default function CrossMarginOnboard({ onClose, isOpen }: Props) { [crossMarginAccountContract, monitorTransaction, handleRefetch] ); + const fetchUntilAllowance = useCallback(async () => { + let count = 0; + return new Promise(async (res, rej) => { + const refetchAllowance = async () => { + const fetchedAllowance = await fetchAllowance(); + if (fetchedAllowance?.eq(0) && count < MAX_REFETCH_COUNT) { + setTimeout(() => { + count++; + refetchAllowance(); + }, 1000); + } else if (count === MAX_REFETCH_COUNT) { + rej(new Error('Timeout fetching allowance')); + } else { + res(fetchedAllowance); + } + }; + refetchAllowance(); + }); + }, [fetchAllowance]); + const onClickApprove = useCallback(async () => { try { if (!crossMarginAccountContract || !susdContract) @@ -143,16 +167,21 @@ export default function CrossMarginOnboard({ onClose, isOpen }: Props) { ); monitorTransaction({ txHash: tx.hash, - onTxConfirmed: () => { - setSubmitting(null); - fetchAllowance(); + onTxConfirmed: async () => { + try { + await fetchUntilAllowance(); + setSubmitting(null); + } catch (err) { + setSubmitting(null); + logError(err); + } }, }); } catch (err) { setSubmitting(null); logError(err); } - }, [crossMarginAccountContract, susdContract, monitorTransaction, fetchAllowance]); + }, [crossMarginAccountContract, susdContract, monitorTransaction, fetchUntilAllowance]); const depositToAccount = useCallback(async () => { try { @@ -182,8 +211,12 @@ export default function CrossMarginOnboard({ onClose, isOpen }: Props) { if (futuresAccount && !futuresAccount.crossMarginAvailable) { return ; } - if (!futuresAccount || !futuresAccount.ready) { - return ; + if (!futuresAccount || futuresAccount.status === 'initial-fetch') { + return ( + + + + ); } if (depositComplete) { @@ -300,3 +333,7 @@ const Complete = styled.div` padding: 40px; text-align: center; `; + +const LoaderContainer = styled.div` + height: 120px; +`; diff --git a/sections/futures/FeeInfoBox/FeeInfoBox.tsx b/sections/futures/FeeInfoBox/FeeInfoBox.tsx index 15ff49eb85..db6ee3a7e4 100644 --- a/sections/futures/FeeInfoBox/FeeInfoBox.tsx +++ b/sections/futures/FeeInfoBox/FeeInfoBox.tsx @@ -1,13 +1,12 @@ -import React, { FC } from 'react'; +import React, { FC, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useRecoilValue } from 'recoil'; import styled from 'styled-components'; import TimerIcon from 'assets/svg/app/timer.svg'; -import InfoBox from 'components/InfoBox'; +import InfoBox, { DetailedInfo } from 'components/InfoBox/InfoBox'; import StyledTooltip from 'components/Tooltip/StyledTooltip'; import { NO_VALUE } from 'constants/placeholder'; -import useSelectedPriceCurrency from 'hooks/useSelectedPriceCurrency'; import { tradeFeesState, marketInfoState, @@ -17,138 +16,147 @@ import { crossMarginSettingsState, } from 'store/futures'; import { computeNPFee, computeMarketFee } from 'utils/costCalculations'; -import { formatCurrency, formatPercent, zeroBN } from 'utils/formatters/number'; +import { formatCurrency, formatDollars, formatPercent, zeroBN } from 'utils/formatters/number'; const FeeInfoBox: React.FC = () => { - const { selectedPriceCurrency } = useSelectedPriceCurrency(); const orderType = useRecoilValue(orderTypeState); const fees = useRecoilValue(tradeFeesState); const sizeDelta = useRecoilValue(sizeDeltaState); const marketInfo = useRecoilValue(marketInfoState); const accountType = useRecoilValue(futuresAccountTypeState); - const { tradeFee: crossMarginTradeFee } = useRecoilValue(crossMarginSettingsState); + const { tradeFee: crossMarginTradeFee, limitOrderFee, stopOrderFee } = useRecoilValue( + crossMarginSettingsState + ); - const { commitDeposit, nextPriceFee } = React.useMemo(() => computeNPFee(marketInfo, sizeDelta), [ + const { commitDeposit, nextPriceFee } = useMemo(() => computeNPFee(marketInfo, sizeDelta), [ marketInfo, sizeDelta, ]); - const totalDeposit = React.useMemo(() => { + const totalDeposit = useMemo(() => { return (commitDeposit ?? zeroBN).add(marketInfo?.keeperDeposit ?? zeroBN); }, [commitDeposit, marketInfo?.keeperDeposit]); - const nextPriceDiscount = React.useMemo(() => { + const nextPriceDiscount = useMemo(() => { return (nextPriceFee ?? zeroBN).sub(commitDeposit ?? zeroBN); }, [commitDeposit, nextPriceFee]); - const staticRate = React.useMemo(() => computeMarketFee(marketInfo, sizeDelta), [ + const staticRate = useMemo(() => computeMarketFee(marketInfo, sizeDelta), [ marketInfo, sizeDelta, ]); - const marketCostTooltip = ( - <> - {formatPercent(staticRate ?? zeroBN)} - {fees.dynamicFeeRate?.gt(0) && ( - <> - {' + '} - - {formatPercent(fees.dynamicFeeRate)} - - - )} - + const orderFeeRate = useMemo( + () => (orderType === 'limit' ? limitOrderFee : orderType === 'stop' ? stopOrderFee : null), + [orderType, stopOrderFee, limitOrderFee] ); - return ( - : null, - }, - } - : accountType === 'isolated_margin' + const marketCostTooltip = useMemo( + () => ( + <> + {formatPercent(staticRate ?? zeroBN)} + {fees.dynamicFeeRate?.gt(0) && ( + <> + {' + '} + + {formatPercent(fees.dynamicFeeRate)} + + + )} + + ), + [staticRate, fees.dynamicFeeRate] + ); + + const feesInfo = useMemo>(() => { + const crossMarginFeeInfo = { + 'Protocol Fee': { + value: formatDollars(fees.staticFee, { + minDecimals: fees.staticFee.lt(0.01) ? 4 : 2, + }), + keyNode: marketCostTooltip, + }, + 'Limit / Stop Fee': + fees.limitStopOrderFee.gt(0) && orderFeeRate ? { - Fee: { - value: formatCurrency(selectedPriceCurrency.name, fees.total, { - sign: selectedPriceCurrency.sign, - minDecimals: fees.total.lt(0.01) ? 4 : 2, - }), - keyNode: marketCostTooltip, - }, + value: formatDollars(fees.limitStopOrderFee, { + minDecimals: fees.limitStopOrderFee.lt(0.01) ? 4 : 2, + }), + keyNode: formatPercent(orderFeeRate), } - : { - 'Protocol Fee': { - value: formatCurrency(selectedPriceCurrency.name, fees.staticFee, { - sign: selectedPriceCurrency.sign, - minDecimals: fees.total.lt(0.01) ? 4 : 2, - }), - keyNode: marketCostTooltip, - }, - 'Cross Margin Fee': { - value: formatCurrency(selectedPriceCurrency.name, fees.crossMarginFee, { - sign: selectedPriceCurrency.sign, - minDecimals: fees.total.lt(0.01) ? 4 : 2, - }), - spaceBeneath: true, - keyNode: formatPercent(crossMarginTradeFee), - }, - 'Total Fee': { - value: formatCurrency(selectedPriceCurrency.name, fees.total, { - sign: selectedPriceCurrency.sign, - minDecimals: fees.total.lt(0.01) ? 4 : 2, - }), - }, - }), - }} - /> - ); + : null, + 'Cross Margin Fee': { + value: formatDollars(fees.crossMarginFee, { + minDecimals: fees.crossMarginFee.lt(0.01) ? 4 : 2, + }), + spaceBeneath: true, + keyNode: formatPercent(crossMarginTradeFee), + }, + + 'Total Fee': { + value: formatDollars(fees.total, { + minDecimals: fees.total.lt(0.01) ? 4 : 2, + }), + }, + }; + if (orderType === 'limit' || orderType === 'stop') { + return { + ...crossMarginFeeInfo, + 'Keeper Deposit': { + value: !!marketInfo?.keeperDeposit + ? formatCurrency('ETH', fees.keeperEthDeposit, { currencyKey: 'ETH' }) + : NO_VALUE, + }, + }; + } + if (orderType === 'next-price') { + return { + 'Keeper Deposit': { + value: !!marketInfo?.keeperDeposit ? formatDollars(marketInfo.keeperDeposit) : NO_VALUE, + }, + 'Commit Deposit': { + value: !!commitDeposit + ? formatDollars(commitDeposit, { minDecimals: commitDeposit.lt(0.01) ? 4 : 2 }) + : NO_VALUE, + }, + 'Total Deposit': { + value: formatDollars(totalDeposit), + spaceBeneath: true, + }, + 'Next-Price Discount': { + value: !!nextPriceDiscount ? formatDollars(nextPriceDiscount) : NO_VALUE, + color: nextPriceDiscount.lt(0) ? 'green' : nextPriceDiscount.gt(0) ? 'red' : undefined, + }, + 'Estimated Fees': { + value: formatDollars(totalDeposit.add(nextPriceDiscount ?? zeroBN)), + keyNode: fees.dynamicFeeRate?.gt(0) ? : null, + }, + }; + } + return accountType === 'isolated_margin' + ? { + Fee: { + value: formatDollars(fees.total, { + minDecimals: fees.total.lt(0.01) ? 4 : 2, + }), + keyNode: marketCostTooltip, + }, + } + : crossMarginFeeInfo; + }, [ + orderType, + crossMarginTradeFee, + fees, + orderFeeRate, + commitDeposit, + accountType, + marketInfo?.keeperDeposit, + nextPriceDiscount, + marketCostTooltip, + totalDeposit, + ]); + + return ; }; const ToolTip: FC = (props) => { diff --git a/sections/futures/LeverageInput/LeverageInput.tsx b/sections/futures/LeverageInput/LeverageInput.tsx index 185c3d20f5..07beae94c4 100644 --- a/sections/futures/LeverageInput/LeverageInput.tsx +++ b/sections/futures/LeverageInput/LeverageInput.tsx @@ -65,7 +65,7 @@ const LeverageInput: FC = () => { {modeButton} - {orderType === 1 && isDisclaimerDisplayed && ( + {orderType === 'next-price' && isDisclaimerDisplayed && ( {t('futures.market.trade.input.leverage.disclaimer')} diff --git a/sections/futures/MarketDetails/MarketDetail.tsx b/sections/futures/MarketDetails/MarketDetail.tsx new file mode 100644 index 0000000000..695d5ed52f --- /dev/null +++ b/sections/futures/MarketDetails/MarketDetail.tsx @@ -0,0 +1,83 @@ +import { ReactElement, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useRecoilValue } from 'recoil'; +import styled from 'styled-components'; + +import StyledTooltip from 'components/Tooltip/StyledTooltip'; +import TimerTooltip from 'components/Tooltip/TimerTooltip'; +import useRateUpdateQuery from 'queries/rates/useRateUpdateQuery'; +import { currentMarketState, marketInfoState } from 'store/futures'; + +import { isMarketDataKey, marketDataKeyMap } from './utils'; + +type MarketDetailProps = { + mobile: boolean; + marketKey: string; + color?: string; + value: string | ReactElement; +}; + +const MarketDetail: React.FC = ({ mobile, marketKey, color, value }) => { + const { t } = useTranslation(); + const marketInfo = useRecoilValue(marketInfoState); + const marketAsset = useRecoilValue(currentMarketState); + + const pausedClass = marketInfo?.isSuspended ? 'paused' : ''; + const lastOracleUpdateTimeQuery = useRateUpdateQuery({ + baseCurrencyKey: marketAsset, + }); + + const lastOracleUpdateTime: Date = useMemo(() => lastOracleUpdateTimeQuery?.data ?? new Date(), [ + lastOracleUpdateTimeQuery, + ]); + const children = ( + +
+

{marketKey}

+ {value} +
+
+ ); + + if (marketKey === marketInfo?.marketName) { + return ( + + {children} + + ); + } + + if (isMarketDataKey(marketKey)) { + return ( + + {children} + + ); + } + + return children; +}; + +export default MarketDetail; + +// Extend type of cursor to accept different style of cursor. Currently accept only 'help' +const WithCursor = styled.div<{ cursor: 'help' }>` + cursor: ${(props) => props.cursor}; +`; + +const MarketDetailsTooltip = styled(StyledTooltip)<{ mobile?: boolean }>` + z-index: 2; + padding: 10px; + right: ${(props) => props.mobile && '1px'}; +`; diff --git a/sections/futures/MarketDetails/MarketDetails.tsx b/sections/futures/MarketDetails/MarketDetails.tsx index b221b5bee1..feb4e60f84 100644 --- a/sections/futures/MarketDetails/MarketDetails.tsx +++ b/sections/futures/MarketDetails/MarketDetails.tsx @@ -1,15 +1,10 @@ -import React, { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useRecoilValue } from 'recoil'; +import React from 'react'; import styled, { css } from 'styled-components'; -import StyledTooltip from 'components/Tooltip/StyledTooltip'; -import TimerTooltip from 'components/Tooltip/TimerTooltip'; -import useRateUpdateQuery from 'queries/rates/useRateUpdateQuery'; -import { currentMarketState, marketInfoState } from 'store/futures'; import media from 'styles/media'; -import { formatDollars, formatPercent } from 'utils/formatters/number'; +import MarketDetail from './MarketDetail'; +import MobileMarketDetail from './MobileMarketDetail'; import useGetMarketData from './useGetMarketData'; type MarketDetailsProps = { @@ -17,184 +12,19 @@ type MarketDetailsProps = { }; const MarketDetails: React.FC = ({ mobile }) => { - const { t } = useTranslation(); - - const marketInfo = useRecoilValue(marketInfoState); - const marketAsset = useRecoilValue(currentMarketState); - - const pausedClass = marketInfo?.isSuspended ? 'paused' : ''; - - const data = useGetMarketData(mobile); - - const lastOracleUpdateTimeQuery = useRateUpdateQuery({ - baseCurrencyKey: marketAsset, - }); - - const lastOracleUpdateTime: Date = useMemo(() => lastOracleUpdateTimeQuery?.data ?? new Date(), [ - lastOracleUpdateTimeQuery, - ]); - - // skew text - const longText = useMemo(() => { - return ( - marketInfo?.openInterest && - formatDollars(marketInfo.openInterest.longUSD, { - maxDecimals: 2, - ...(marketInfo?.openInterest?.longUSD.gt(1e6) - ? { truncation: { divisor: 1e6, unit: 'M' } } - : {}), - }) - ); - }, [marketInfo]); - - const shortText = useMemo(() => { - return ( - marketInfo?.openInterest && - formatDollars(marketInfo.openInterest.shortUSD, { - maxDecimals: 2, - ...(marketInfo?.openInterest?.shortUSD.gt(1e6) - ? { truncation: { divisor: 1e6, unit: 'M' } } - : {}), - }) - ); - }, [marketInfo]); - - const enableTooltip = (key: string, children: React.ReactElement) => { - switch (key) { - case 'External Price': - return ( - - {children} - - ); - case '24H Change': - return ( - - {children} - - ); - case '24H Volume': - return ( - - {children} - - ); - case '24H Trades': - return ( - - {children} - - ); - case 'Open Interest': - return ( - - {children} - - ); - case marketInfo?.marketName: - return ( - - {children} - - ); - case 'Inst. Funding Rate': - case '1H Funding Rate': - return ( - - {children} - - ); - default: - return children; - } - }; + const marketData = useGetMarketData(mobile); return ( - {Object.entries(data).map(([key, { value, color }]) => { - const colorClass = color || ''; - - return enableTooltip( - key, - -
-

{key}

- {value} -
-
- ); - })} + {Object.entries(marketData).map(([marketKey, data]) => ( + + ))} - {mobile && ( -
-

Skew

- -
- {marketInfo?.openInterest && - formatPercent(marketInfo.openInterest.longPct ?? 0, { minDecimals: 0 })}{' '} - ({longText}) -
-
- {marketInfo?.openInterest && - formatPercent(marketInfo.openInterest.shortPct ?? 0, { minDecimals: 0 })}{' '} - ({shortText}) -
-
-
- )} + {mobile && }
); }; -// Extend type of cursor to accept different style of cursor. Currently accept only 'help' -const WithCursor = styled.div<{ cursor: 'help' }>` - cursor: ${(props) => props.cursor}; -`; - -const SkewDataContainer = styled.div` - grid-row: 1; -`; - -const MarketDetailsTooltip = styled(StyledTooltip)` - z-index: 2; - padding: 10px; -`; - const MarketDetailsContainer = styled.div<{ mobile?: boolean }>` width: 100%; height: 55px; diff --git a/sections/futures/MarketDetails/MobileMarketDetail.tsx b/sections/futures/MarketDetails/MobileMarketDetail.tsx new file mode 100644 index 0000000000..1b5db01057 --- /dev/null +++ b/sections/futures/MarketDetails/MobileMarketDetail.tsx @@ -0,0 +1,59 @@ +import { useMemo } from 'react'; +import { useRecoilValue } from 'recoil'; +import styled from 'styled-components'; + +import { marketInfoState } from 'store/futures'; +import { formatDollars, formatPercent } from 'utils/formatters/number'; + +const MobileMarketDetail: React.FC = () => { + const marketInfo = useRecoilValue(marketInfoState); + const pausedClass = marketInfo?.isSuspended ? 'paused' : ''; + + const longSkewText = useMemo(() => { + return ( + marketInfo?.openInterest && + formatDollars(marketInfo.openInterest.longUSD, { + maxDecimals: 2, + ...(marketInfo?.openInterest?.longUSD.gt(1e6) + ? { truncation: { divisor: 1e6, unit: 'M' } } + : {}), + }) + ); + }, [marketInfo]); + + const shortSkewText = useMemo(() => { + return ( + marketInfo?.openInterest && + formatDollars(marketInfo.openInterest.shortUSD, { + maxDecimals: 2, + ...(marketInfo?.openInterest?.shortUSD.gt(1e6) + ? { truncation: { divisor: 1e6, unit: 'M' } } + : {}), + }) + ); + }, [marketInfo]); + + return ( +
+

Skew

+ +
+ {marketInfo?.openInterest && + formatPercent(marketInfo.openInterest.longPct ?? 0, { minDecimals: 0 })}{' '} + ({longSkewText}) +
+
+ {marketInfo?.openInterest && + formatPercent(marketInfo.openInterest.shortPct ?? 0, { minDecimals: 0 })}{' '} + ({shortSkewText}) +
+
+
+ ); +}; + +export default MobileMarketDetail; + +const SkewDataContainer = styled.div` + grid-row: 1; +`; diff --git a/sections/futures/MarketDetails/useGetMarketData.ts b/sections/futures/MarketDetails/useGetMarketData.ts index 94b6dd9cdb..f568ae9be3 100644 --- a/sections/futures/MarketDetails/useGetMarketData.ts +++ b/sections/futures/MarketDetails/useGetMarketData.ts @@ -19,6 +19,8 @@ import { isFiatCurrency } from 'utils/currencies'; import { formatCurrency, formatPercent, zeroBN } from 'utils/formatters/number'; import { isDecimalFour } from 'utils/futures'; +import { MarketDataKey } from './utils'; + type MarketData = Record; const useGetMarketData = (mobile?: boolean) => { @@ -74,10 +76,10 @@ const useGetMarketData = (mobile?: boolean) => { minDecimals, }), }, - '24H Trades': { + [MarketDataKey.dailyTrades]: { value: `${futuresTradeCount}`, }, - 'Open Interest': { + [MarketDataKey.openInterest]: { value: marketInfo?.marketSize?.mul(marketPrice) ? formatCurrency( selectedPriceCurrency.name, @@ -86,7 +88,7 @@ const useGetMarketData = (mobile?: boolean) => { ) : NO_VALUE, }, - '24H Volume': { + [MarketDataKey.dailyVolume]: { value: formatCurrency(selectedPriceCurrency.name, futuresTradingVolume ?? zeroBN, { sign: '$', }), @@ -97,7 +99,7 @@ const useGetMarketData = (mobile?: boolean) => { : NO_VALUE, color: fundingValue?.gt(zeroBN) ? 'green' : fundingValue?.lt(zeroBN) ? 'red' : undefined, }, - '24H Change': { + [MarketDataKey.dailyChange]: { value: marketPrice && marketPrice.gt(0) && pastPrice?.price ? `${formatCurrency( @@ -124,7 +126,7 @@ const useGetMarketData = (mobile?: boolean) => { minDecimals, }), }, - 'External Price': { + [MarketDataKey.externalPrice]: { value: externalPrice === 0 ? NO_VALUE @@ -133,7 +135,7 @@ const useGetMarketData = (mobile?: boolean) => { minDecimals, }), }, - '24H Change': { + [MarketDataKey.dailyChange]: { value: marketPrice && marketPrice.gt(0) && pastPrice?.price ? `${formatCurrency( @@ -151,15 +153,15 @@ const useGetMarketData = (mobile?: boolean) => { : '' : undefined, }, - '24H Volume': { + [MarketDataKey.dailyVolume]: { value: formatCurrency(selectedPriceCurrency.name, futuresTradingVolume ?? zeroBN, { sign: '$', }), }, - '24H Trades': { + [MarketDataKey.dailyTrades]: { value: `${futuresTradeCount}`, }, - 'Open Interest': { + [MarketDataKey.openInterest]: { value: marketInfo?.marketSize?.mul(marketPrice) ? formatCurrency(selectedPriceCurrency.name, marketInfo?.marketSize?.mul(marketPrice), { sign: '$', diff --git a/sections/futures/MarketDetails/utils.ts b/sections/futures/MarketDetails/utils.ts index 3255031a00..fa823ecaf3 100644 --- a/sections/futures/MarketDetails/utils.ts +++ b/sections/futures/MarketDetails/utils.ts @@ -32,6 +32,30 @@ const map: Record = { sAPE: 'apecoin', }; +export enum MarketDataKey { + externalPrice = 'External Price', + dailyChange = '24H Change', + dailyVolume = '24H Volume', + dailyTrades = '24H Trades', + openInterest = 'Open Interest', + instFundingRate = 'Inst. Funding Rate', + hourlyFundingRate = '1H Funding Rate', +} + +export const marketDataKeyMap: Record = { + [MarketDataKey.externalPrice]: 'external-price', + [MarketDataKey.dailyChange]: '24h-change', + [MarketDataKey.dailyVolume]: '24h-vol', + [MarketDataKey.dailyTrades]: '24h-trades', + [MarketDataKey.openInterest]: 'open-interest', + [MarketDataKey.instFundingRate]: '1h-funding-rate', + [MarketDataKey.hourlyFundingRate]: '1h-funding-rate', +}; + +export const isMarketDataKey = (key: string): key is MarketDataKey => { + return Object.values(MarketDataKey).includes(key); +}; + export const synthToCoingeckoPriceId = (synth: any) => { if (markets.includes(synth)) { return map[synth as typeof markets[number]]; diff --git a/sections/futures/MarketInfoBox/MarketInfoBox.tsx b/sections/futures/MarketInfoBox/MarketInfoBox.tsx index 003f389e42..31697c89ef 100644 --- a/sections/futures/MarketInfoBox/MarketInfoBox.tsx +++ b/sections/futures/MarketInfoBox/MarketInfoBox.tsx @@ -7,8 +7,6 @@ import InfoBox from 'components/InfoBox'; import PreviewArrow from 'components/PreviewArrow'; import { FuturesPotentialTradeDetails } from 'queries/futures/types'; import { - crossMarginAvailableMarginState, - futuresAccountTypeState, leverageSideState, marketInfoState, maxLeverageState, @@ -30,18 +28,9 @@ const MarketInfoBox: React.FC = () => { const leverageSide = useRecoilValue(leverageSideState); const { nativeSize } = useRecoilValue(futuresTradeInputsState); const potentialTrade = useRecoilValue(potentialTradeDetailsState); - const crossMarginFreeMargin = useRecoilValue(crossMarginAvailableMarginState); - const selectedAccountType = useRecoilValue(futuresAccountTypeState); - const totalMargin = - selectedAccountType === 'cross_margin' - ? position?.remainingMargin.add(crossMarginFreeMargin) ?? zeroBN - : position?.remainingMargin ?? zeroBN; - - const availableMargin = - selectedAccountType === 'cross_margin' - ? position?.accessibleMargin.add(crossMarginFreeMargin) ?? zeroBN - : position?.accessibleMargin ?? zeroBN; + const totalMargin = position?.remainingMargin ?? zeroBN; + const availableMargin = position?.accessibleMargin ?? zeroBN; const buyingPower = totalMargin.gt(zeroBN) ? totalMargin.mul(maxLeverage ?? zeroBN) : zeroBN; @@ -49,7 +38,7 @@ const MarketInfoBox: React.FC = () => { ? totalMargin.sub(availableMargin).div(totalMargin) : zeroBN; - const isNextPriceOrder = orderType === 1; + const isNextPriceOrder = orderType === 'next-price'; const positionSize = position?.position?.size ? wei(position?.position?.size) : zeroBN; const orderDetails = useMemo(() => { diff --git a/sections/futures/MobileTrade/UserTabs/TradesTab.tsx b/sections/futures/MobileTrade/UserTabs/TradesTab.tsx index c964322be6..6120fc5d91 100644 --- a/sections/futures/MobileTrade/UserTabs/TradesTab.tsx +++ b/sections/futures/MobileTrade/UserTabs/TradesTab.tsx @@ -10,7 +10,7 @@ import { FuturesTrade } from 'queries/futures/types'; import useGetFuturesTradesForAccount from 'queries/futures/useGetFuturesTradesForAccount'; import TimeDisplay from 'sections/futures/Trades/TimeDisplay'; import { PositionSide, TradeStatus } from 'sections/futures/types'; -import { currentMarketState, futuresAccountState } from 'store/futures'; +import { currentMarketState, selectedFuturesAddressState } from 'store/futures'; import { GridDivCenteredRow } from 'styles/common'; import { formatCryptoCurrency } from 'utils/formatters/number'; @@ -19,7 +19,7 @@ import TradeDrawer from '../drawers/TradeDrawer'; const TradesTab: React.FC = () => { const { t } = useTranslation(); - const { selectedFuturesAddress } = useRecoilValue(futuresAccountState); + const selectedFuturesAddress = useRecoilValue(selectedFuturesAddressState); const marketAsset = useRecoilValue(currentMarketState); const [selectedTrade, setSelectedTrade] = React.useState(); diff --git a/sections/futures/MobileTrade/drawers/BaseDrawer.tsx b/sections/futures/MobileTrade/drawers/BaseDrawer.tsx index 9cd0e57312..ec1017d3c2 100644 --- a/sections/futures/MobileTrade/drawers/BaseDrawer.tsx +++ b/sections/futures/MobileTrade/drawers/BaseDrawer.tsx @@ -8,7 +8,7 @@ import { resetButtonCSS } from 'styles/common'; type DrawerItem = { label: string; value: React.ReactNode; -}; +} | null; type BaseDrawerProps = { items: DrawerItem[]; @@ -26,12 +26,15 @@ const BaseDrawer: React.FC = ({ open, closeDrawer, items, butto - {items.map(({ label, value }) => ( - -
{label}
-
{value}
-
- ))} + {items.map((row) => { + if (!row) return null; + return ( + +
{row.label}
+
{row.value}
+
+ ); + })} {buttons && {buttons}} diff --git a/sections/futures/MobileTrade/drawers/OrderDrawer.tsx b/sections/futures/MobileTrade/drawers/OrderDrawer.tsx index baaa4a1cd7..c0aa293275 100644 --- a/sections/futures/MobileTrade/drawers/OrderDrawer.tsx +++ b/sections/futures/MobileTrade/drawers/OrderDrawer.tsx @@ -12,10 +12,17 @@ type OrderDrawerProps = { open: boolean; order: FuturesOrder | undefined; closeDrawer(): void; - setAction(action: 'execute' | 'cancel'): void; + onExecute(): void; + onCancel(order: FuturesOrder | undefined): void; }; -const OrderDrawer: React.FC = ({ open, order, closeDrawer, setAction }) => { +const OrderDrawer: React.FC = ({ + open, + order, + closeDrawer, + onCancel, + onExecute, +}) => { const { t } = useTranslation(); const items = React.useMemo(() => { @@ -59,9 +66,9 @@ const OrderDrawer: React.FC = ({ open, order, closeDrawer, set buttons={ <> {order?.isExecutable && ( - setAction('execute')}>Execute + onExecute()}>Execute )} - setAction('cancel')}>Cancel + onCancel(order)}>Cancel } /> diff --git a/sections/futures/OrderPriceInput/OrderPriceInput.tsx b/sections/futures/OrderPriceInput/OrderPriceInput.tsx new file mode 100644 index 0000000000..6552a2c60b --- /dev/null +++ b/sections/futures/OrderPriceInput/OrderPriceInput.tsx @@ -0,0 +1,116 @@ +import { wei } from '@synthetixio/wei'; +import { capitalize } from 'lodash'; +import { ChangeEvent, useEffect, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useRecoilState, useRecoilValue } from 'recoil'; +import styled from 'styled-components'; + +import CustomInput from 'components/Input/CustomInput'; +import InputTitle from 'components/Input/InputTitle'; +import SegmentedControl from 'components/SegmentedControl'; +import { FuturesOrderType } from 'queries/futures/types'; +import { leverageSideState, marketAssetRateState, orderFeeCapState } from 'store/futures'; +import { ceilNumber, floorNumber, weiToString, zeroBN } from 'utils/formatters/number'; +import { orderPriceInvalidLabel } from 'utils/futures'; + +type Props = { + value: string; + isDisabled?: boolean; + orderType: FuturesOrderType; + onChangeOrderPrice: (value: string) => void; +}; + +const FEE_CAP_OPTIONS = ['none', '0.5%', '1%', '2%', '5%']; + +export default function OrderPriceInput({ + isDisabled, + value, + orderType, + onChangeOrderPrice, +}: Props) { + const { t } = useTranslation(); + const marketAssetRate = useRecoilValue(marketAssetRateState); + const leverageSide = useRecoilValue(leverageSideState); + const [selectedFeeCap, setSelectedFeeCap] = useRecoilState(orderFeeCapState); + + useEffect(() => { + if (!value) { + const priceNum = + orderType === 'limit' ? floorNumber(marketAssetRate) : ceilNumber(marketAssetRate); + onChangeOrderPrice(String(priceNum)); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const minMaxLabelString = useMemo( + () => orderPriceInvalidLabel(value, leverageSide, marketAssetRate, orderType), + [value, orderType, leverageSide, marketAssetRate] + ); + + const handleOnChange = (_: ChangeEvent, v: string) => { + onChangeOrderPrice(v); + }; + + const onChangeFeeCap = (index: number) => { + const val = FEE_CAP_OPTIONS[index]; + if (val === 'none') { + setSelectedFeeCap(zeroBN); + } else { + setSelectedFeeCap(wei(val.replace('%', ''))); + } + }; + + const selectedFeeCapLabel = selectedFeeCap.eq(0) ? 'none' : weiToString(selectedFeeCap) + '%'; + + return ( + <> + + {capitalize(orderType)} Price{' '} + {minMaxLabelString && ( + <> +   —  {minMaxLabelString} + + )} + + + + + {t('futures.market.trade.orders.fee-rejection-label')}: + + + + + ); +} + +const StyledInputTitle = styled(InputTitle)` + span { + color: ${(props) => props.theme.colors.selectedTheme.red}; + } +`; + +const FeeCapContainer = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin: 10px 0; +`; + +const FeeRejectionLabel = styled.div` + min-width: 100px; + font-size: 12px; + color: ${(props) => props.theme.colors.selectedTheme.text.label}; +`; diff --git a/sections/futures/OrderPriceInput/index.tsx b/sections/futures/OrderPriceInput/index.tsx new file mode 100644 index 0000000000..6af374a827 --- /dev/null +++ b/sections/futures/OrderPriceInput/index.tsx @@ -0,0 +1 @@ +export { default } from './OrderPriceInput'; diff --git a/sections/futures/OrderSizing/OrderSizing.tsx b/sections/futures/OrderSizing/OrderSizing.tsx index 359dfb9335..ad75dfe45c 100644 --- a/sections/futures/OrderSizing/OrderSizing.tsx +++ b/sections/futures/OrderSizing/OrderSizing.tsx @@ -3,18 +3,23 @@ import React, { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'r import { useRecoilValue } from 'recoil'; import styled from 'styled-components'; +import SwitchAssetArrows from 'assets/svg/futures/switch-arrows.svg'; import CustomInput from 'components/Input/CustomInput'; +import InputTitle from 'components/Input/InputTitle'; import { useFuturesContext } from 'contexts/FuturesContext'; import { - crossMarginAvailableMarginState, - currentMarketState, futuresAccountTypeState, simulatedTradeState, positionState, futuresTradeInputsState, + orderTypeState, + marketAssetRateState, + futuresOrderPriceState, + marketKeyState, + crossMarginAccountOverviewState, } from 'store/futures'; import { FlexDivRow } from 'styles/common'; -import { zeroBN } from 'utils/formatters/number'; +import { floorNumber, isZero, zeroBN } from 'utils/formatters/number'; import { getDisplayAsset } from 'utils/futures'; type OrderSizingProps = { @@ -22,16 +27,28 @@ type OrderSizingProps = { }; const OrderSizing: React.FC = ({ disabled }) => { + const { onTradeAmountChange, maxUsdInputAmount } = useFuturesContext(); + const { nativeSize, susdSize } = useRecoilValue(futuresTradeInputsState); const simulatedTrade = useRecoilValue(simulatedTradeState); - const marketAsset = useRecoilValue(currentMarketState); - const freeCrossMargin = useRecoilValue(crossMarginAvailableMarginState); + const { freeMargin: freeCrossMargin } = useRecoilValue(crossMarginAccountOverviewState); const position = useRecoilValue(positionState); const selectedAccountType = useRecoilValue(futuresAccountTypeState); + const orderType = useRecoilValue(orderTypeState); + const assetPrice = useRecoilValue(marketAssetRateState); + const orderPrice = useRecoilValue(futuresOrderPriceState); + const marketKey = useRecoilValue(marketKeyState); const [usdValue, setUsdValue] = useState(susdSize); const [assetValue, setAssetValue] = useState(nativeSize); + const [assetInputType, setAssetInputType] = useState<'usd' | 'native'>('usd'); + + const tradePrice = useMemo(() => orderPrice || assetPrice, [orderPrice, assetPrice]); + const maxNativeValue = useMemo( + () => (!isZero(tradePrice) ? maxUsdInputAmount.div(tradePrice) : zeroBN), + [tradePrice, maxUsdInputAmount] + ); useEffect( () => { @@ -59,40 +76,33 @@ const OrderSizing: React.FC = ({ disabled }) => { ] ); - const { onTradeAmountChange, onTradeAmountSUSDChange, maxUsdInputAmount } = useFuturesContext(); - const handleSetMax = () => { - onTradeAmountSUSDChange(Number(maxUsdInputAmount).toFixed(0)); + if (assetInputType === 'usd') { + onTradeAmountChange(String(floorNumber(maxUsdInputAmount)), 'usd'); + } else { + onTradeAmountChange(String(floorNumber(maxNativeValue)), 'native'); + } }; - // eslint-disable-next-line - const debounceOnChangeUsd = useCallback( - debounce((value) => { - onTradeAmountSUSDChange(value); - }, 500), - [debounce, onTradeAmountSUSDChange] - ); - - useEffect(() => { - return () => debounceOnChangeUsd?.cancel(); - }, [debounceOnChangeUsd]); + const handleSetPositionSize = () => { + onTradeAmountChange(position?.position?.size.toString() ?? '0', 'native'); + }; // eslint-disable-next-line - const debounceOnChangeAssetValue = useCallback( - debounce((value) => { - onTradeAmountChange(value); + const debounceOnChangeValue = useCallback( + debounce((value, assetType) => { + onTradeAmountChange(value, assetType); }, 500), [debounce, onTradeAmountChange] ); - const onChangeUsdValue = (_: ChangeEvent, v: string) => { - setUsdValue(v); - debounceOnChangeUsd(v); - }; + useEffect(() => { + return () => debounceOnChangeValue?.cancel(); + }, [debounceOnChangeValue]); - const onChangeAssetValue = (_: ChangeEvent, v: string) => { - setAssetValue(v); - debounceOnChangeAssetValue(v); + const onChangeValue = (_: ChangeEvent, v: string) => { + setUsdValue(v); + debounceOnChangeValue(v, assetInputType); }; const isDisabled = useMemo(() => { @@ -103,40 +113,42 @@ const OrderSizing: React.FC = ({ disabled }) => { return remaining.lte(0) || disabled; }, [position?.remainingMargin, disabled, selectedAccountType, freeCrossMargin]); + const showPosSizeHelper = + position?.position?.size && (orderType === 'limit' || orderType === 'stop'); + + const invalid = + (assetInputType === 'usd' && usdValue !== '' && maxUsdInputAmount.lte(usdValue || 0)) || + (assetInputType === 'native' && assetValue !== '' && maxNativeValue.lte(assetValue || 0)); + return ( - + Amount  —  Set order size - - Max + + + Max + {showPosSizeHelper && ( + Position Size + )} + - - setAssetInputType(assetInputType === 'usd' ? 'native' : 'usd')} + > + {assetInputType === 'usd' ? 'sUSD' : getDisplayAsset(marketKey)}{' '} + {} + + } + value={assetInputType === 'usd' ? usdValue : assetValue} placeholder="0.0" - onChange={onChangeUsdValue} - style={{ - borderTopRightRadius: '0px', - borderTopLeftRadius: '0px', - }} + onChange={onChangeValue} /> ); @@ -147,15 +159,6 @@ const OrderSizingContainer = styled.div` margin-bottom: 16px; `; -const OrderSizingTitle = styled.div` - color: ${(props) => props.theme.colors.selectedTheme.button.text.primary}; - font-size: 13px; - - span { - color: ${(props) => props.theme.colors.selectedTheme.gray}; - } -`; - const OrderSizingRow = styled(FlexDivRow)` width: 100%; align-items: center; @@ -172,4 +175,19 @@ const MaxButton = styled.button` cursor: pointer; `; +const InputButton = styled.button` + height: 22px; + padding: 4px 10px; + border: none; + background: transparent; + font-size: 16px; + line-height: 16px; + color: ${(props) => props.theme.colors.selectedTheme.text.label}; + cursor: pointer; +`; + +const InputHelpers = styled.div` + display: flex; +`; + export default OrderSizing; diff --git a/sections/futures/PositionCard/PositionCard.tsx b/sections/futures/PositionCard/PositionCard.tsx index eb91727dd5..a29be80b3c 100644 --- a/sections/futures/PositionCard/PositionCard.tsx +++ b/sections/futures/PositionCard/PositionCard.tsx @@ -282,7 +282,7 @@ const PositionCard: React.FC = () => { @@ -294,7 +294,7 @@ const PositionCard: React.FC = () => { @@ -309,7 +309,7 @@ const PositionCard: React.FC = () => { @@ -331,7 +331,7 @@ const PositionCard: React.FC = () => { @@ -349,7 +349,7 @@ const PositionCard: React.FC = () => { @@ -373,20 +373,20 @@ const PositionCard: React.FC = () => { - {t('futures.market.position-card.leverage')} - + {data.leverage} @@ -397,15 +397,15 @@ const PositionCard: React.FC = () => { {data.liquidationPrice} - {t('futures.market.position-card.avg-entry-price')} - + {data.avgEntryPrice} @@ -484,13 +484,7 @@ const StyledSubtitleWithCursor = styled.p` const PositionCardTooltip = styled(StyledTooltip)` z-index: 2; -`; - -const LeftMarginTooltip = styled(StyledTooltip)` - ${media.greaterThan('sm')` - left: -60px; - z-index: 2; - `} + padding: 0px 10px 0px 10px; `; const StyledValue = styled.p` diff --git a/sections/futures/Trade/DepositMarginModal.tsx b/sections/futures/Trade/DepositMarginModal.tsx index 75bc3f8392..b0c4808e21 100644 --- a/sections/futures/Trade/DepositMarginModal.tsx +++ b/sections/futures/Trade/DepositMarginModal.tsx @@ -1,6 +1,6 @@ import useSynthetixQueries from '@synthetixio/queries'; import Wei, { wei } from '@synthetixio/wei'; -import React, { useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useRecoilValue } from 'recoil'; import styled from 'styled-components'; @@ -13,10 +13,10 @@ import { NO_VALUE } from 'constants/placeholder'; import TransactionNotifier from 'containers/TransactionNotifier'; import { useRefetchContext } from 'contexts/RefetchContext'; import useEstimateGasCost from 'hooks/useEstimateGasCost'; -import { currentMarketState } from 'store/futures'; +import { currentMarketState, positionState } from 'store/futures'; import { gasSpeedState } from 'store/wallet'; import { FlexDivRowCentered } from 'styles/common'; -import { formatDollars } from 'utils/formatters/number'; +import { formatDollars, zeroBN } from 'utils/formatters/number'; import { getDisplayAsset } from 'utils/futures'; type DepositMarginModalProps = { @@ -25,23 +25,41 @@ type DepositMarginModalProps = { }; const PLACEHOLDER = '$0.00'; -const MIN_DEPOSIT_AMOUNT = wei('50'); +const MIN_MARGIN_AMOUNT = wei('50'); const DepositMarginModal: React.FC = ({ onDismiss, sUSDBalance }) => { const { t } = useTranslation(); const { monitorTransaction } = TransactionNotifier.useContainer(); - const gasSpeed = useRecoilValue(gasSpeedState); - const market = useRecoilValue(currentMarketState); const { useEthGasPriceQuery, useSynthetixTxn } = useSynthetixQueries(); const { estimateSnxTxGasCost } = useEstimateGasCost(); + const gasSpeed = useRecoilValue(gasSpeedState); + const position = useRecoilValue(positionState); + const market = useRecoilValue(currentMarketState); + + const minDeposit = useMemo(() => { + const accessibleMargin = position?.accessibleMargin ?? zeroBN; + const min = MIN_MARGIN_AMOUNT.sub(accessibleMargin); + return min.lt(zeroBN) ? zeroBN : min; + }, [position?.accessibleMargin]); + const [amount, setAmount] = useState(''); - const [isDisabled, setDisabled] = useState(true); const ethGasPriceQuery = useEthGasPriceQuery(); const { handleRefetch } = useRefetchContext(); const gasPrice = ethGasPriceQuery.data != null ? ethGasPriceQuery.data[gasSpeed] : null; + const isDisabled = useMemo(() => { + if (!amount) { + return true; + } + const amtWei = wei(amount); + if (amtWei.eq(0) || amtWei.gt(sUSDBalance) || amtWei.lt(minDeposit)) { + return true; + } + return false; + }, [amount, sUSDBalance, minDeposit]); + const depositTxn = useSynthetixTxn( `FuturesMarket${getDisplayAsset(market)}`, 'transferMargin', @@ -52,22 +70,7 @@ const DepositMarginModal: React.FC = ({ onDismiss, sUSD const transactionFee = estimateSnxTxGasCost(depositTxn); - React.useEffect(() => { - if (!amount) { - setDisabled(true); - return; - } - - const amtWei = wei(amount); - - if (amtWei.gte(MIN_DEPOSIT_AMOUNT) && amtWei.lte(sUSDBalance)) { - setDisabled(false); - } else { - setDisabled(true); - } - }, [amount, isDisabled, sUSDBalance, setDisabled]); - - React.useEffect(() => { + useEffect(() => { if (depositTxn.hash) { monitorTransaction({ txHash: depositTxn.hash, @@ -81,7 +84,7 @@ const DepositMarginModal: React.FC = ({ onDismiss, sUSD // eslint-disable-next-line react-hooks/exhaustive-deps }, [depositTxn.hash]); - const handleSetMax = React.useCallback(() => { + const handleSetMax = useCallback(() => { setAmount(sUSDBalance.toString()); }, [sUSDBalance]); @@ -138,9 +141,6 @@ export const StyledBaseModal = styled(BaseModal)` [data-reach-dialog-content] { width: 400px; } - .card-body { - padding: 28px; - } `; export const BalanceContainer = styled(FlexDivRowCentered)` diff --git a/sections/futures/Trade/FuturesUnsupported.tsx b/sections/futures/Trade/FuturesUnsupported.tsx index ae1c49378d..538271d1f1 100644 --- a/sections/futures/Trade/FuturesUnsupported.tsx +++ b/sections/futures/Trade/FuturesUnsupported.tsx @@ -1,36 +1,21 @@ -import { useConnectModal } from '@rainbow-me/rainbowkit'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; import useNetworkSwitcher from 'hooks/useNetworkSwitcher'; import { BorderedPanel } from 'styles/common'; -type FuturesUnsupportedProps = { - isWalletConnected: boolean; -}; - -const FuturesUnsupported: React.FC = ({ isWalletConnected }) => { +const FuturesUnsupportedNetwork = () => { const { t } = useTranslation(); const { switchToL2 } = useNetworkSwitcher(); - const { openConnectModal: connectWallet } = useConnectModal(); return ( {t('futures.page-title')} - {isWalletConnected ? ( - <> - {t('common.l2-cta')} - -
{t('homepage.l2.cta-buttons.switch-l2')}
-
- - ) : ( - <> - {t('common.perp-cta')} - -
{t('common.wallet.connect-wallet')}
-
- - )} + <> + {t('common.l2-cta')} + +
{t('homepage.l2.cta-buttons.switch-l2')}
+
+
); }; @@ -60,4 +45,4 @@ const MessageContainer = styled(BorderedPanel)` color: ${(props) => props.theme.colors.selectedTheme.gray}; `; -export default FuturesUnsupported; +export default FuturesUnsupportedNetwork; diff --git a/sections/futures/Trade/ManagePosition.tsx b/sections/futures/Trade/ManagePosition.tsx index dbae0d8cf3..5f9ff6ea3d 100644 --- a/sections/futures/Trade/ManagePosition.tsx +++ b/sections/futures/Trade/ManagePosition.tsx @@ -1,6 +1,6 @@ import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { useSetRecoilState, useRecoilValue, useRecoilState } from 'recoil'; +import { useRecoilValue, useRecoilState } from 'recoil'; import styled from 'styled-components'; import Button from 'components/Button'; @@ -21,8 +21,11 @@ import { futuresTradeInputsState, futuresAccountTypeState, crossMarginMarginDeltaState, + futuresOrderPriceState, + marketAssetRateState, } from 'store/futures'; -import { zeroBN } from 'utils/formatters/number'; +import { isZero } from 'utils/formatters/number'; +import { orderPriceInvalidLabel } from 'utils/futures'; import ClosePositionModalCrossMargin from '../PositionCard/ClosePositionModalCrossMargin'; import ClosePositionModalIsolatedMargin from '../PositionCard/ClosePositionModalIsolatedMargin'; @@ -36,6 +39,8 @@ type OrderTxnError = { const ManagePosition: React.FC = () => { const { t } = useTranslation(); + const { error, orderTxn, onTradeAmountChange, maxUsdInputAmount } = useFuturesContext(); + const sizeDelta = useRecoilValue(sizeDeltaState); const marginDelta = useRecoilValue(crossMarginMarginDeltaState); const position = useRecoilValue(positionState); @@ -44,15 +49,19 @@ const ManagePosition: React.FC = () => { const selectedAccountType = useRecoilValue(futuresAccountTypeState); const { data: previewTrade, error: previewError } = useRecoilValue(potentialTradeDetailsState); const orderType = useRecoilValue(orderTypeState); - const setLeverageSide = useSetRecoilState(leverageSideState); + const [leverageSide, setLeverageSide] = useRecoilState(leverageSideState); const { leverage } = useRecoilValue(futuresTradeInputsState); - const [isCancelModalOpen, setCancelModalOpen] = React.useState(false); const [isConfirmationModalOpen, setConfirmationModalOpen] = useRecoilState( confirmationModalOpenState ); - const { error, orderTxn, onTradeAmountChange } = useFuturesContext(); const isMarketCapReached = useRecoilValue(isMarketCapReachedState); const placeOrderTranslationKey = useRecoilValue(placeOrderTranslationKeyState); + const potentialTradeDetails = useRecoilValue(potentialTradeDetailsState); + const orderPrice = useRecoilValue(futuresOrderPriceState); + const marketAssetRate = useRecoilValue(marketAssetRateState); + const tradeInputs = useRecoilValue(futuresTradeInputsState); + + const [isCancelModalOpen, setCancelModalOpen] = React.useState(false); const positionDetails = position?.position; @@ -78,6 +87,45 @@ const ManagePosition: React.FC = () => { return leverageNum > 0 && leverageNum < maxLeverageValue.toNumber(); }, [leverage, selectedAccountType, maxLeverageValue]); + const placeOrderDisabled = useMemo(() => { + const invalidReason = orderPriceInvalidLabel( + orderPrice, + leverageSide, + marketAssetRate, + orderType + ); + + if (!leverageValid || !!error || marketInfo?.isSuspended || isMarketCapReached) return true; + if ((orderType === 'limit' || orderType === 'stop') && !!invalidReason) return true; + if (tradeInputs.susdSizeDelta.abs().gt(maxUsdInputAmount)) return true; + if (placeOrderTranslationKey === 'futures.market.trade.button.deposit-margin-minimum') + return true; + if (selectedAccountType === 'cross_margin') { + if ((isZero(marginDelta) && isZero(sizeDelta)) || potentialTradeDetails.status !== 'complete') + return true; + if (orderType !== 'market' && isZero(orderPrice)) return true; + } else if (isZero(sizeDelta)) { + return true; + } + return false; + }, [ + leverageValid, + error, + sizeDelta, + marginDelta, + orderType, + orderPrice, + leverageSide, + marketAssetRate, + marketInfo?.isSuspended, + placeOrderTranslationKey, + tradeInputs.susdSizeDelta, + maxUsdInputAmount, + selectedAccountType, + isMarketCapReached, + potentialTradeDetails, + ]); + return ( <>
@@ -90,17 +138,7 @@ const ManagePosition: React.FC = () => { data-testid="trade-open-position-button" noOutline fullWidth - disabled={ - !leverageValid || - (selectedAccountType === 'isolated_margin' && sizeDelta.eq(zeroBN)) || - (selectedAccountType === 'cross_margin' && - marginDelta.eq(zeroBN) && - sizeDelta.eq(zeroBN)) || - !!error || - placeOrderTranslationKey === 'futures.market.trade.button.deposit-margin-minimum' || - marketInfo?.isSuspended || - isMarketCapReached - } + disabled={placeOrderDisabled} onClick={() => setConfirmationModalOpen(true)} > {t(placeOrderTranslationKey)} @@ -112,14 +150,14 @@ const ManagePosition: React.FC = () => { noOutline variant="danger" onClick={() => { - if (orderType === 1 && position?.position?.size) { + if (orderType === 'next-price' && position?.position?.size) { const newTradeSize = position.position.size; const newLeverageSide = position.position.side === PositionSide.LONG ? PositionSide.SHORT : PositionSide.LONG; setLeverageSide(newLeverageSide); - onTradeAmountChange(newTradeSize.toString(), true); + onTradeAmountChange(newTradeSize.toString(), 'native'); setConfirmationModalOpen(true); } else { setCancelModalOpen(true); @@ -144,14 +182,13 @@ const ManagePosition: React.FC = () => { ))} {isConfirmationModalOpen && - orderType === 0 && (selectedAccountType === 'cross_margin' ? ( ) : ( ))} - {isConfirmationModalOpen && orderType === 1 && } + {isConfirmationModalOpen && orderType === 'next-price' && } ); }; diff --git a/sections/futures/Trade/MarketActions.tsx b/sections/futures/Trade/MarketActions.tsx index 9422e58aee..87f9675111 100644 --- a/sections/futures/Trade/MarketActions.tsx +++ b/sections/futures/Trade/MarketActions.tsx @@ -6,8 +6,7 @@ import styled from 'styled-components'; import Button from 'components/Button'; import Connector from 'containers/Connector'; import useIsL2 from 'hooks/useIsL2'; -import useSUSDBalance from 'hooks/useSUSDBalance'; -import { marketInfoState, positionState } from 'store/futures'; +import { balancesState, marketInfoState, positionState } from 'store/futures'; import { zeroBN } from 'utils/formatters/number'; import DepositMarginModal from './DepositMarginModal'; @@ -16,7 +15,7 @@ import WithdrawMarginModal from './WithdrawMarginModal'; const MarketActions: React.FC = () => { const { t } = useTranslation(); const { walletAddress } = Connector.useContainer(); - const sUSDBalance = useSUSDBalance(); + const { susdWalletBalance } = useRecoilValue(balancesState); const position = useRecoilValue(positionState); const marketInfo = useRecoilValue(marketInfoState); @@ -49,7 +48,7 @@ const MarketActions: React.FC = () => { {openModal === 'deposit' && ( - setOpenModal(null)} /> + setOpenModal(null)} /> )} {openModal === 'withdraw' && setOpenModal(null)} />} diff --git a/sections/futures/Trade/TradeConfirmationModal.tsx b/sections/futures/Trade/TradeConfirmationModal.tsx index 073267cd4e..f1f503d88c 100644 --- a/sections/futures/Trade/TradeConfirmationModal.tsx +++ b/sections/futures/Trade/TradeConfirmationModal.tsx @@ -1,4 +1,5 @@ import Wei, { wei } from '@synthetixio/wei'; +import { capitalize } from 'lodash'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useRecoilValue } from 'recoil'; @@ -8,12 +9,16 @@ import BaseModal from 'components/BaseModal'; import Button from 'components/Button'; import ErrorView from 'components/Error'; import { DesktopOnlyView, MobileOrTabletView } from 'components/Media'; -import { CurrencyKey } from 'constants/currency'; -import Connector from 'containers/Connector'; -import useSelectedPriceCurrency from 'hooks/useSelectedPriceCurrency'; -import { currentMarketState, potentialTradeDetailsState } from 'store/futures'; +import { + currentMarketState, + futuresOrderPriceState, + orderTypeState, + positionState, + potentialTradeDetailsState, +} from 'store/futures'; import { FlexDivCentered } from 'styles/common'; import { zeroBN, formatCurrency, formatDollars, formatNumber } from 'utils/formatters/number'; +import { getDisplayAsset } from 'utils/futures'; import BaseDrawer from '../MobileTrade/drawers/BaseDrawer'; import { PositionSide } from '../types'; @@ -21,6 +26,7 @@ import { PositionSide } from '../types'; type Props = { gasFee: Wei; tradeFee: Wei; + keeperFee?: Wei | null; errorMessage?: string | null | undefined; onConfirmOrder: () => any; onDismiss: () => void; @@ -29,23 +35,33 @@ type Props = { export default function TradeConfirmationModal({ tradeFee, gasFee, + keeperFee, errorMessage, onConfirmOrder, onDismiss, }: Props) { const { t } = useTranslation(); - const { synthsMap } = Connector.useContainer(); - const { selectedPriceCurrency } = useSelectedPriceCurrency(); const market = useRecoilValue(currentMarketState); const { data: potentialTradeDetails } = useRecoilValue(potentialTradeDetailsState); + const orderType = useRecoilValue(orderTypeState); + const orderPrice = useRecoilValue(futuresOrderPriceState); + const position = useRecoilValue(positionState); + + const positionSide = useMemo(() => { + if (potentialTradeDetails?.size.eq(zeroBN)) { + return position?.position?.side === PositionSide.LONG + ? PositionSide.SHORT + : PositionSide.LONG; + } + return potentialTradeDetails?.size.gte(zeroBN) ? PositionSide.LONG : PositionSide.SHORT; + }, [potentialTradeDetails, position?.position?.side]); const positionDetails = useMemo(() => { return potentialTradeDetails ? { ...potentialTradeDetails, - size: potentialTradeDetails.size.abs(), - side: potentialTradeDetails.size.gte(zeroBN) ? PositionSide.LONG : PositionSide.SHORT, + side: positionSide, leverage: potentialTradeDetails.margin.eq(zeroBN) ? zeroBN : potentialTradeDetails.size @@ -54,22 +70,36 @@ export default function TradeConfirmationModal({ .abs(), } : null; - }, [potentialTradeDetails]); + }, [potentialTradeDetails, positionSide]); const dataRows = useMemo( () => [ { label: 'side', value: (positionDetails?.side ?? PositionSide.LONG).toUpperCase() }, + { label: 'order Type', value: capitalize(orderType) }, { label: 'size', - value: formatCurrency(market || '', positionDetails?.size ?? zeroBN, { - sign: market ? synthsMap[market]?.sign : '', - }), + value: formatCurrency( + getDisplayAsset(market) || '', + positionDetails?.sizeDelta.abs() ?? zeroBN, + { + currencyKey: getDisplayAsset(market) ?? '', + } + ), }, - { label: 'leverage', value: `${formatNumber(positionDetails?.leverage ?? zeroBN)}x` }, { - label: 'current price', - value: formatDollars(positionDetails?.price ?? zeroBN), + label: 'resulting leverage', + value: `${formatNumber(positionDetails?.leverage ?? zeroBN)}x`, }, + + orderType === 'limit' || orderType === 'stop' + ? { + label: orderType + ' order price', + value: formatDollars(orderPrice), + } + : { + label: 'current price', + value: formatDollars(positionDetails?.price ?? zeroBN), + }, { label: 'liquidation price', value: formatDollars(positionDetails?.liqPrice ?? zeroBN), @@ -82,15 +112,18 @@ export default function TradeConfirmationModal({ label: 'protocol fee', value: formatDollars(tradeFee), }, + keeperFee + ? { + label: 'Keeper ETH deposit', + value: formatCurrency('ETH', keeperFee, { currencyKey: 'ETH' }), + } + : null, { label: 'network gas fee', - value: formatCurrency(selectedPriceCurrency.name as CurrencyKey, gasFee ?? zeroBN, { - sign: '$', - minDecimals: 2, - }), + value: formatDollars(gasFee ?? zeroBN), }, ], - [positionDetails, market, synthsMap, gasFee, selectedPriceCurrency, tradeFee] + [positionDetails, market, keeperFee, gasFee, tradeFee, orderType, orderPrice] ); const disabledReason = useMemo(() => { @@ -106,12 +139,15 @@ export default function TradeConfirmationModal({ isOpen title={t('futures.market.trade.confirmation.modal.confirm-order')} > - {dataRows.map(({ label, value }, i) => ( - - - {value} - - ))} + {dataRows.map((row, i) => { + if (!row) return null; + return ( + + + {row.value} + + ); + })} { setConfirmationModalOpen(false); }, [setConfirmationModalOpen]); @@ -112,7 +110,8 @@ export default function TradeConfirmationModalCrossMargin() { diff --git a/sections/futures/Trade/TradeIsolatedMargin.tsx b/sections/futures/Trade/TradeIsolatedMargin.tsx index df0b72b0b1..c914442e9b 100644 --- a/sections/futures/Trade/TradeIsolatedMargin.tsx +++ b/sections/futures/Trade/TradeIsolatedMargin.tsx @@ -1,12 +1,20 @@ -import React from 'react'; +import { useConnectModal } from '@rainbow-me/rainbowkit'; +import { useMemo, useState } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import styled from 'styled-components'; import DepositArrow from 'assets/svg/futures/deposit-arrow.svg'; import WithdrawArrow from 'assets/svg/futures/withdraw-arrow.svg'; import SegmentedControl from 'components/SegmentedControl'; -import useSUSDBalance from 'hooks/useSUSDBalance'; -import { leverageSideState, marketInfoState, orderTypeState, positionState } from 'store/futures'; +import { ISOLATED_MARGIN_ORDER_TYPES } from 'constants/futures'; +import Connector from 'containers/Connector'; +import { + balancesState, + leverageSideState, + marketInfoState, + orderTypeState, + positionState, +} from 'store/futures'; import { zeroBN } from 'utils/formatters/number'; import FeeInfoBox from '../FeeInfoBox'; @@ -26,48 +34,64 @@ type Props = { }; const TradeIsolatedMargin = ({ isMobile }: Props) => { - const sUSDBalance = useSUSDBalance(); + const { openConnectModal: connectWallet } = useConnectModal(); + const { walletAddress } = Connector.useContainer(); const [leverageSide, setLeverageSide] = useRecoilState(leverageSideState); const position = useRecoilValue(positionState); const marketInfo = useRecoilValue(marketInfoState); + const { susdWalletBalance } = useRecoilValue(balancesState); const [orderType, setOrderType] = useRecoilState(orderTypeState); - const [openModal, setOpenModal] = React.useState<'deposit' | 'withdraw' | null>(null); + const [openModal, setOpenModal] = useState<'deposit' | 'withdraw' | null>(null); - const transferButtons = !marketInfo?.isSuspended - ? [ + const headerButtons = useMemo(() => { + if (!walletAddress) { + return [ { - i18nTitle: 'futures.market.trade.button.deposit', - Icon: DepositArrow, - onClick: () => setOpenModal('deposit'), + i18nTitle: 'futures.market.trade.button.connect-wallet', + onClick: connectWallet, }, - ] - : []; - - if (position?.remainingMargin?.gt(zeroBN) && !marketInfo?.isSuspended) { - transferButtons.push({ - i18nTitle: 'futures.market.trade.button.withdraw', - Icon: WithdrawArrow, - onClick: () => setOpenModal('withdraw'), - }); - } + ]; + } + const transferButtons = !marketInfo?.isSuspended + ? [ + { + i18nTitle: 'futures.market.trade.button.deposit', + Icon: DepositArrow, + onClick: () => setOpenModal('deposit'), + }, + ] + : []; + + if (position?.remainingMargin?.gt(zeroBN) && !marketInfo?.isSuspended) { + transferButtons.push({ + i18nTitle: 'futures.market.trade.button.withdraw', + Icon: WithdrawArrow, + onClick: () => setOpenModal('withdraw'), + }); + } + return transferButtons; + }, [walletAddress, position?.remainingMargin, marketInfo?.isSuspended, connectWallet]); return (
{!isMobile && } - + {!isMobile && } { + setOrderType(oType === 0 ? 'market' : 'next-price'); + }} /> - {orderType === 1 && } + {orderType === 'next-price' && } @@ -79,7 +103,7 @@ const TradeIsolatedMargin = ({ isMobile }: Props) => { {openModal === 'deposit' && ( - setOpenModal(null)} /> + setOpenModal(null)} /> )} {openModal === 'withdraw' && setOpenModal(null)} />} diff --git a/sections/futures/Trade/TradePanelHeader.tsx b/sections/futures/Trade/TradePanelHeader.tsx index 5e6c3fc785..53d590b484 100644 --- a/sections/futures/Trade/TradePanelHeader.tsx +++ b/sections/futures/Trade/TradePanelHeader.tsx @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; import Button from 'components/Button'; +import { ButtonVariant } from 'components/Button/Button'; import { FuturesAccountType } from 'queries/futures/subgraph'; import { BorderedPanel } from 'styles/common'; import media from 'styles/media'; @@ -10,7 +11,8 @@ import media from 'styles/media'; type Props = { accountType: FuturesAccountType; buttons?: { - onClick: () => any; + onClick: (() => void) | undefined; + variant?: ButtonVariant; i18nTitle: string; Icon?: FunctionComponent; }[]; @@ -29,10 +31,10 @@ export default function TradePanelHeader({ accountType, buttons }: Props) { {buttons && - buttons.map(({ Icon, i18nTitle, onClick }) => ( + buttons.map(({ Icon, i18nTitle, variant, onClick }) => ( (null); - - const { selectedLeverage } = useFuturesContext(); + const [openModal, setOpenModal] = useState<'leverage' | 'keeper-deposit' | null>(null); const totalMargin = position?.remainingMargin.add(crossMarginFreeMargin) ?? zeroBN; const availableMargin = position?.accessibleMargin.add(crossMarginFreeMargin) ?? zeroBN; @@ -97,7 +105,10 @@ function MarginInfoBox({ editingLeverage }: Props) { const size = wei(nativeSize || zeroBN); return { - showPreview: !size.eq(0) || !marginDelta.eq(0), + showPreview: + ((orderType === 'market' || orderType === 'next-price') && + (!size.eq(0) || !marginDelta.eq(0))) || + ((orderType === 'limit' || orderType === 'stop') && !!orderPrice && !size.eq(0)), totalMargin: potentialTrade.data?.margin.sub(crossMarginFee) || zeroBN, freeAccountMargin: crossMarginFreeMargin.sub(marginDelta), availableMargin: previewAvailableMargin.gt(0) ? previewAvailableMargin : zeroBN, @@ -111,6 +122,8 @@ function MarginInfoBox({ editingLeverage }: Props) { nativeSize, marginDelta, crossMarginFee, + orderType, + orderPrice, potentialTrade.data?.margin, previewAvailableMargin, potentialTrade.data?.notionalValue, @@ -164,12 +177,27 @@ function MarginInfoBox({ editingLeverage }: Props) { ), }, + 'Keeper ETH Balance': + orderType === 'limit' || orderType === 'stop' + ? { + value: formatCurrency('ETH', keeperEthBal, { currencyKey: 'ETH' }), + valueNode: ( + <> + {keeperEthBal.gt(0) && ( + setOpenModal('keeper-deposit')}> + + + )} + + ), + } + : null, Leverage: { value: ( <> {formatNumber(selectedLeverage, { maxDecimals: 2 })}x {!editingLeverage && ( - setOpenModal('leverage')}>Edit + setOpenModal('leverage')}>Edit )} ), @@ -202,6 +230,9 @@ function MarginInfoBox({ editingLeverage }: Props) { /> {openModal === 'leverage' && setOpenModal(null)} />} + {openModal === 'keeper-deposit' && ( + setOpenModal(null)} /> + )} ); } @@ -225,10 +256,16 @@ const Button = styled.span` } `; -const EditButton = styled(Button)` +const ActionButton = styled(Button)<{ hideBorder?: boolean }>` margin-left: 8px; cursor: pointer; + font-size: 10px; + font-family: ${(props) => props.theme.fonts.bold}; + border: 1px solid + ${(props) => (!props.hideBorder ? props.theme.colors.selectedTheme.yellow : 'none')}; color: ${(props) => props.theme.colors.selectedTheme.yellow}; + border-radius: 10px; + padding: ${(props) => (props.hideBorder ? '3px 2px 3px 0px' : '3px 5px')}; `; export default React.memo(MarginInfoBox); diff --git a/sections/futures/TradeCrossMargin/DepositWithdrawCrossMargin.tsx b/sections/futures/TradeCrossMargin/DepositWithdrawCrossMargin.tsx index 077061003e..225cb8a20f 100644 --- a/sections/futures/TradeCrossMargin/DepositWithdrawCrossMargin.tsx +++ b/sections/futures/TradeCrossMargin/DepositWithdrawCrossMargin.tsx @@ -11,14 +11,12 @@ import ErrorView from 'components/Error'; import CustomInput from 'components/Input/CustomInput'; import Loader from 'components/Loader'; import SegmentedControl from 'components/SegmentedControl'; -import { NO_VALUE } from 'constants/placeholder'; import Connector from 'containers/Connector'; import TransactionNotifier from 'containers/TransactionNotifier'; import { useRefetchContext } from 'contexts/RefetchContext'; import useCrossMarginAccountContracts from 'hooks/useCrossMarginContracts'; -import useEstimateGasCost from 'hooks/useEstimateGasCost'; import useSUSDContract from 'hooks/useSUSDContract'; -import { balancesState, crossMarginAvailableMarginState } from 'store/futures'; +import { balancesState, crossMarginAccountOverviewState } from 'store/futures'; import { FlexDivRowCentered } from 'styles/common'; import { formatDollars, zeroBN } from 'utils/formatters/number'; import logError from 'utils/logError'; @@ -41,38 +39,22 @@ export default function DepositWithdrawCrossMargin({ const { signer } = Connector.useContainer(); const { monitorTransaction } = TransactionNotifier.useContainer(); const { crossMarginAccountContract } = useCrossMarginAccountContracts(); + const { handleRefetch, refetchUntilUpdate } = useRefetchContext(); const susdContract = useSUSDContract(); const balances = useRecoilValue(balancesState); - const freeMargin = useRecoilValue(crossMarginAvailableMarginState); + const { freeMargin } = useRecoilValue(crossMarginAccountOverviewState); const [amount, setAmount] = useState(''); const [transferType, setTransferType] = useState(0); const [txState, setTxState] = useState<'none' | 'approving' | 'submitting' | 'complete'>('none'); const [error, setError] = useState(null); - const [transactionFee, setTransactionFee] = useState(zeroBN); - - const { handleRefetch } = useRefetchContext(); useEffect(() => { setTransferType(defaultTab === 'deposit' ? 0 : 1); }, [defaultTab]); const susdBal = transferType === 0 ? balances?.susdWalletBalance || zeroBN : freeMargin; - const { estimateEthersContractTxCost } = useEstimateGasCost(); - - useEffect(() => { - if (!crossMarginAccountContract) return; - const estimateGas = async () => { - if (!amount) return; - const method = transferType === 0 ? 'deposit' : 'withdraw'; - const fee = await estimateEthersContractTxCost(crossMarginAccountContract, method, [ - wei(amount).toBN(), - ]); - setTransactionFee(fee); - }; - estimateGas(); - }, [crossMarginAccountContract, amount, transferType, estimateEthersContractTxCost]); const submitDeposit = useCallback(async () => { try { @@ -82,11 +64,17 @@ export default function DepositWithdrawCrossMargin({ const tx = await crossMarginAccountContract.deposit(wei(amount || 0).toBN()); monitorTransaction({ txHash: tx.hash, - onTxConfirmed: () => { - setTxState('complete'); - handleRefetch('account-margin-change'); - onComplete?.(); - onDismiss(); + onTxConfirmed: async () => { + try { + await refetchUntilUpdate('wallet-balance-change'); + handleRefetch('account-margin-change'); + } catch (err) { + logError(err); + } finally { + setTxState('complete'); + onComplete?.(); + onDismiss(); + } }, }); } catch (err) { @@ -97,6 +85,7 @@ export default function DepositWithdrawCrossMargin({ }, [ crossMarginAccountContract, amount, + refetchUntilUpdate, monitorTransaction, handleRefetch, onComplete, @@ -215,13 +204,6 @@ export default function DepositWithdrawCrossMargin({ } /> - - {t('futures.market.trade.margin.modal.gas-fee')}: - - {transactionFee ? formatDollars(transactionFee) : NO_VALUE} - - - (null); - const maxLeverage = Number((market?.maxLeverage || wei(10)).toString(2)); + const maxLeverage = Number((market?.maxLeverage || wei(DEFAULT_LEVERAGE)).toString(2)); const maxPositionUsd = useMemo(() => { return totalMargin.mul(leverage); @@ -85,10 +87,11 @@ export default function EditLeverageModal({ onDismiss }: DepositMarginModalProps ); const onConfirm = useCallback(async () => { + setError(null); if (position?.position) { try { setSubmitting(true); - const tx = await submitCrossMarginOrder(); + const tx = await submitCrossMarginOrder(true); if (tx?.hash) { monitorTransaction({ txHash: tx.hash, @@ -103,6 +106,7 @@ export default function EditLeverageModal({ onDismiss }: DepositMarginModalProps }); } } catch (err) { + setError(t('common.transaction.transaction-failed')); logError(err); } finally { setSubmitting(false); @@ -202,7 +206,12 @@ export default function EditLeverageModal({ onDismiss }: DepositMarginModalProps {submitting ? : t('futures.market.trade.leverage.modal.confirm')} - {error && } + {error && ( + <> + + + + )} ); } diff --git a/sections/futures/TradeCrossMargin/ManageKeeperBalanceModal.tsx b/sections/futures/TradeCrossMargin/ManageKeeperBalanceModal.tsx new file mode 100644 index 0000000000..d20f0c7c4e --- /dev/null +++ b/sections/futures/TradeCrossMargin/ManageKeeperBalanceModal.tsx @@ -0,0 +1,213 @@ +import { wei } from '@synthetixio/wei'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; + +import Error from 'components/Error'; +import CustomInput from 'components/Input/CustomInput'; +import Loader from 'components/Loader'; +import SegmentedControl from 'components/SegmentedControl'; +import Spacer from 'components/Spacer'; +import Connector from 'containers/Connector'; +import TransactionNotifier from 'containers/TransactionNotifier'; +import { useRefetchContext } from 'contexts/RefetchContext'; +import useCrossMarginAccountContracts from 'hooks/useCrossMarginContracts'; +import useCrossMarginKeeperDeposit from 'hooks/useCrossMarginKeeperEthBal'; +import { isUserDeniedError } from 'utils/formatters/error'; +import { formatCurrency, zeroBN } from 'utils/formatters/number'; +import logError from 'utils/logError'; + +import { + StyledBaseModal, + BalanceContainer, + BalanceText, + MaxButton, + MarginActionButton, +} from '../Trade/DepositMarginModal'; + +type TransferType = 'deposit' | 'withdraw'; + +type Props = { + onDismiss(): void; + defaultType: TransferType; +}; + +const DEPOSIT_ENABLED = false; + +export default function ManageKeeperBalanceModal({ onDismiss, defaultType }: Props) { + const { t } = useTranslation(); + const { keeperEthBal, getKeeperEthBal } = useCrossMarginKeeperDeposit(); + const { crossMarginAccountContract } = useCrossMarginAccountContracts(); + const { monitorTransaction } = TransactionNotifier.useContainer(); + const { provider, walletAddress } = Connector.useContainer(); + const { handleRefetch } = useRefetchContext(); + + const [amount, setAmount] = useState(''); + const [isMax, setMax] = useState(false); + const [userEthBal, setUserEthBal] = useState(zeroBN); + const [error, setError] = useState(null); + const [transacting, setTransacting] = useState(false); + const [transferType, setTransferType] = useState(defaultType === 'deposit' ? 0 : 1); + + const getUserEthBal = useCallback(async () => { + if (!walletAddress) return; + try { + const bal = await provider.getBalance(walletAddress); + setUserEthBal(wei(bal)); + } catch (err) { + logError(err); + } + }, [walletAddress, provider]); + + useEffect(() => { + getUserEthBal(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [walletAddress]); + + const onWithdrawKeeperDeposit = useCallback(async () => { + try { + if (keeperEthBal.eq(0)) return; + setTransacting(true); + setError(null); + const tx = await crossMarginAccountContract?.withdrawEth(wei(amount).toBN()); + if (tx?.hash) { + monitorTransaction({ + txHash: tx.hash, + onTxConfirmed: () => { + setTimeout(() => { + handleRefetch('account-margin-change'); + setTransacting(false); + getKeeperEthBal(); + onDismiss(); + }, 2000); + }, + }); + } + } catch (err) { + setTransacting(false); + if (!isUserDeniedError(err.message)) { + setError(t('common.transaction.transaction-failed')); + } + logError(err); + } + }, [ + keeperEthBal, + crossMarginAccountContract, + amount, + t, + handleRefetch, + onDismiss, + getKeeperEthBal, + monitorTransaction, + ]); + + const onDepositKeeperDeposit = useCallback(async () => { + // if (!crossMarginAccountContract || !signer) return; + // try { + // setTransacting(true); + // setError(null); + // // TODO: Waiting for the function to be added to the smart contract + // const tx = await crossMarginAccountContract?.depositEth(wei(amount).toBN()); + // if (tx?.hash) { + // monitorTransaction({ + // txHash: tx.hash, + // onTxConfirmed: () => { + // setTransacting(false); + // getUserEthBal(); + // onDismiss(); + // }, + // }); + // } + // } catch (err) { + // setTransacting(false); + // if (!isUserDeniedError(err.message)) { + // setError(t('common.transaction.transaction-failed')); + // } + // logError(err); + // } + }, []); + + const exceedsLimit = useMemo(() => { + const amtWei = wei(amount || 0); + return transferType === 0 && amtWei.gt(transferType === 0 ? userEthBal : keeperEthBal); + }, [transferType, amount, userEthBal, keeperEthBal]); + + const isDisabled = useMemo(() => { + if (!amount || transacting || exceedsLimit) return true; + return false; + }, [amount, transacting, exceedsLimit]); + + const handleSetMax = React.useCallback(() => { + setMax(true); + setAmount(keeperEthBal.toString()); + }, [keeperEthBal]); + + const onChangeTab = (selection: number) => { + setTransferType(selection); + setAmount(''); + }; + + return ( + + + {DEPOSIT_ENABLED && ( + + )} + + + {t('futures.market.trade.margin.modal.balance')}: + + {formatCurrency('ETH', transferType === 0 ? userEthBal : keeperEthBal)} ETH + + + + { + if (isMax) setMax(false); + setAmount(v); + }} + right={ + + {t('futures.market.trade.margin.modal.max')} + + } + /> + + + + {transacting ? ( + + ) : ( + t( + `futures.market.trade.orders.manage-keeper-deposit.${ + transferType === 0 ? 'deposit' : 'withdraw' + }` + ) + )} + + + {error && } + + ); +} + +const StyledSegmentedControl = styled(SegmentedControl)` + margin-bottom: 16px; +`; diff --git a/sections/futures/TradeCrossMargin/TradeCrossMargin.tsx b/sections/futures/TradeCrossMargin/TradeCrossMargin.tsx index a51070de65..c33d621620 100644 --- a/sections/futures/TradeCrossMargin/TradeCrossMargin.tsx +++ b/sections/futures/TradeCrossMargin/TradeCrossMargin.tsx @@ -1,24 +1,36 @@ +import { useConnectModal } from '@rainbow-me/rainbowkit'; import { wei } from '@synthetixio/wei'; import { useCallback, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; import { useRecoilState, useRecoilValue } from 'recoil'; import styled from 'styled-components'; import DepositArrow from 'assets/svg/futures/deposit-arrow.svg'; import WithdrawArrow from 'assets/svg/futures/withdraw-arrow.svg'; +import Loader from 'components/Loader'; +import SegmentedControl from 'components/SegmentedControl'; import StyledSlider from 'components/Slider/StyledSlider'; +import Spacer from 'components/Spacer'; +import { CROSS_MARGIN_ORDER_TYPES } from 'constants/futures'; import Connector from 'containers/Connector'; import { useFuturesContext } from 'contexts/FuturesContext'; +import { FuturesOrderType } from 'queries/futures/types'; import { futuresAccountState, futuresAccountTypeState, leverageSideState, futuresTradeInputsState, + orderTypeState, + futuresOrderPriceState, + marketAssetRateState, + showCrossMarginOnboardState, + crossMarginAccountOverviewState, } from 'store/futures'; -import { BorderedPanel, FlexDivRow } from 'styles/common'; +import { FlexDivRow } from 'styles/common'; +import { ceilNumber, floorNumber } from 'utils/formatters/number'; +import { orderPriceInvalidLabel } from 'utils/futures'; -import CrossMarginOnboard from '../CrossMarginOnboard'; import FeeInfoBox from '../FeeInfoBox'; +import OrderPriceInput from '../OrderPriceInput/OrderPriceInput'; import OrderSizing from '../OrderSizing'; import PositionButtons from '../PositionButtons'; import ManagePosition from '../Trade/ManagePosition'; @@ -34,19 +46,24 @@ type Props = { }; export default function TradeCrossMargin({ isMobile }: Props) { - const { t } = useTranslation(); const { walletAddress } = Connector.useContainer(); const [leverageSide, setLeverageSide] = useRecoilState(leverageSideState); - const { crossMarginAddress, crossMarginAvailable } = useRecoilValue(futuresAccountState); + const { crossMarginAddress, crossMarginAvailable, status } = useRecoilValue(futuresAccountState); const selectedAccountType = useRecoilValue(futuresAccountTypeState); + const { freeMargin } = useRecoilValue(crossMarginAccountOverviewState); + const { susdSize } = useRecoilValue(futuresTradeInputsState); + const marketAssetRate = useRecoilValue(marketAssetRateState); + const [orderType, setOrderType] = useRecoilState(orderTypeState); + const [orderPrice, setOrderPrice] = useRecoilState(futuresOrderPriceState); - const { onTradeAmountSUSDChange, maxUsdInputAmount } = useFuturesContext(); + const { onTradeAmountChange, maxUsdInputAmount, onTradeOrderPriceChange } = useFuturesContext(); + const { openConnectModal: connectWallet } = useConnectModal(); const [percent, setPercent] = useState(0); const [usdAmount, setUsdAmount] = useState(susdSize); - const [showOnboard, setShowOnboard] = useState(false); + const [showOnboard, setShowOnboard] = useRecoilState(showCrossMarginOnboardState); const [openTransferModal, setOpenTransferModal] = useState<'deposit' | 'withdraw' | null>(null); // eslint-disable-next-line @@ -57,9 +74,20 @@ export default function TradeCrossMargin({ isMobile }: Props) { const usdAmount = maxUsdInputAmount.mul(fraction).toString(); const usdValue = Number(usdAmount).toFixed(0); setUsdAmount(usdValue); - onTradeAmountSUSDChange(usdValue, commit); + onTradeAmountChange(usdValue, 'usd', { simulateChange: !commit }); }, - [onTradeAmountSUSDChange, maxUsdInputAmount] + [onTradeAmountChange, maxUsdInputAmount] + ); + + const onChangeOrderPrice = useCallback( + (price: string) => { + const invalidLabel = orderPriceInvalidLabel(price, leverageSide, marketAssetRate, orderType); + if (!invalidLabel || !price) { + onTradeOrderPriceChange(price); + } + setOrderPrice(price); + }, + [onTradeOrderPriceChange, setOrderPrice, leverageSide, marketAssetRate, orderType] ); useEffect(() => { @@ -75,43 +103,64 @@ export default function TradeCrossMargin({ isMobile }: Props) { // eslint-disable-next-line }, [susdSize]); + const headerButtons = walletAddress + ? [ + { + i18nTitle: 'futures.market.trade.button.deposit', + Icon: DepositArrow, + onClick: () => setOpenTransferModal('deposit'), + }, + { + i18nTitle: 'futures.market.trade.button.withdraw', + Icon: WithdrawArrow, + onClick: () => setOpenTransferModal('withdraw'), + }, + ] + : [ + { + i18nTitle: 'futures.market.trade.button.connect-wallet', + onClick: connectWallet, + }, + ]; + + if (!showOnboard && (status === 'refetching' || status === 'initial-fetch')) return ; + return ( <> - setShowOnboard(false)} isOpen={showOnboard} /> - - {!walletAddress ? ( - {t('futures.market.trade.cross-margin.connect-wallet')} - ) : !crossMarginAvailable ? ( + {walletAddress && !crossMarginAvailable ? ( - ) : !crossMarginAddress ? ( + ) : (walletAddress && !crossMarginAddress && status !== 'idle') || showOnboard ? ( setShowOnboard(true)} /> ) : ( <> {!isMobile && } - setOpenTransferModal('deposit'), - }, - { - i18nTitle: 'futures.market.trade.button.withdraw', - Icon: WithdrawArrow, - onClick: () => setOpenTransferModal('withdraw'), - }, - ]} - /> + {} + { + const type = CROSS_MARGIN_ORDER_TYPES[index]; + setOrderType(type as FuturesOrderType); + const price = + type === 'limit' + ? floorNumber(marketAssetRate) + : type === 'stop' + ? ceilNumber(marketAssetRate) + : ''; + onChangeOrderPrice(String(price)); + }} + /> onChangeMarginPercent(value, false)} @@ -125,6 +174,17 @@ export default function TradeCrossMargin({ isMobile }: Props) { $currentMark={percent} /> + {orderType !== 'market' && ( + <> + + + + )} @@ -145,9 +205,3 @@ const SliderRow = styled(FlexDivRow)` margin-bottom: 32px; position: relative; `; - -const MessageContainer = styled(BorderedPanel)` - text-align: center; - padding: 20px; - color: ${(props) => props.theme.colors.selectedTheme.gray}; -`; diff --git a/sections/futures/Trades/Trades.tsx b/sections/futures/Trades/Trades.tsx index 4ad0cb75c7..ba34143430 100644 --- a/sections/futures/Trades/Trades.tsx +++ b/sections/futures/Trades/Trades.tsx @@ -56,6 +56,7 @@ const Trades: React.FC = ({ history, isLoading, isLoaded, marketAss return ( = ({ marginTransfers, isLoading, isLoaded }: ) } showPagination + pageSize={5} /> ); }; diff --git a/sections/futures/UserInfo/OpenOrdersTable.tsx b/sections/futures/UserInfo/OpenOrdersTable.tsx index 5607eae9bb..166dfe2d9f 100644 --- a/sections/futures/UserInfo/OpenOrdersTable.tsx +++ b/sections/futures/UserInfo/OpenOrdersTable.tsx @@ -1,5 +1,5 @@ import useSynthetixQueries from '@synthetixio/queries'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { CellProps } from 'react-table'; import { useRecoilValue } from 'recoil'; @@ -12,12 +12,14 @@ import Table, { TableNoResults } from 'components/Table'; import PositionType from 'components/Text/PositionType'; import TransactionNotifier from 'containers/TransactionNotifier'; import { useRefetchContext } from 'contexts/RefetchContext'; +import useCrossMarginContracts from 'hooks/useCrossMarginContracts'; import useIsL2 from 'hooks/useIsL2'; import useNetworkSwitcher from 'hooks/useNetworkSwitcher'; import { FuturesOrder, PositionSide } from 'queries/futures/types'; -import { currentMarketState, futuresAccountState, openOrdersState } from 'store/futures'; +import { currentMarketState, openOrdersState, selectedFuturesAddressState } from 'store/futures'; import { gasSpeedState } from 'store/wallet'; import { getDisplayAsset } from 'utils/futures'; +import logError from 'utils/logError'; import OrderDrawer from '../MobileTrade/drawers/OrderDrawer'; @@ -25,61 +27,103 @@ const OpenOrdersTable: React.FC = () => { const { t } = useTranslation(); const { monitorTransaction } = TransactionNotifier.useContainer(); const { useSynthetixTxn, useEthGasPriceQuery } = useSynthetixQueries(); + const { crossMarginAccountContract } = useCrossMarginContracts(); + const { handleRefetch } = useRefetchContext(); + const ethGasPriceQuery = useEthGasPriceQuery(); const { switchToL2 } = useNetworkSwitcher(); const isL2 = useIsL2(); const gasSpeed = useRecoilValue(gasSpeedState); const currencyKey = useRecoilValue(currentMarketState); const openOrders = useRecoilValue(openOrdersState); - const { selectedFuturesAddress } = useRecoilValue(futuresAccountState); - - const { handleRefetch } = useRefetchContext(); + const selectedFuturesAddress = useRecoilValue(selectedFuturesAddressState); - const [action, setAction] = useState<'' | 'cancel' | 'execute'>(''); + const [cancelling, setCancelling] = useState(null); const [selectedOrder, setSelectedOrder] = useState(); - const ethGasPriceQuery = useEthGasPriceQuery(); - const gasPrice = ethGasPriceQuery.data?.[gasSpeed]; - const cancelOrExecuteOrderTxn = useSynthetixTxn( + const synthetixTxCb = { + enabled: !!selectedFuturesAddress, + onError: () => { + setCancelling(null); + }, + onSettled: () => { + setCancelling(null); + }, + }; + + const cancelNextPriceOrder = useSynthetixTxn( `FuturesMarket${getDisplayAsset(currencyKey)}`, - `${action}NextPriceOrder`, + `cancelNextPriceOrder`, [selectedFuturesAddress], gasPrice, - { - enabled: !!action, - onSettled: () => { - setAction(''); - }, - } + synthetixTxCb ); - useEffect(() => { - if (!!action) { - cancelOrExecuteOrderTxn.mutate(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [action]); + const executeNextPriceOrder = useSynthetixTxn( + `FuturesMarket${getDisplayAsset(currencyKey)}`, + `executeNextPriceOrder`, + [selectedFuturesAddress], + gasPrice, + synthetixTxCb + ); - useEffect(() => { - if (cancelOrExecuteOrderTxn.hash) { + const handleTx = useCallback( + (txHash: string) => { monitorTransaction({ - txHash: cancelOrExecuteOrderTxn.hash, + txHash: txHash, onTxConfirmed: () => { handleRefetch('new-order'); }, }); - } + }, + [monitorTransaction, handleRefetch] + ); + const onCancel = useCallback( + async (order: FuturesOrder | undefined) => { + if (!order) return; + setCancelling(order.id); + if (order.orderType === 'Limit' || order.orderType === 'Stop') { + try { + const id = order.id.split('-')[2]; + const tx = await crossMarginAccountContract?.cancelOrder(id); + if (tx?.hash) handleTx(tx.hash); + setCancelling(null); + } catch (err) { + setCancelling(null); + logError(err); + } + } else { + cancelNextPriceOrder.mutate(); + } + }, + [crossMarginAccountContract, cancelNextPriceOrder, handleTx] + ); + + useEffect(() => { + if (cancelNextPriceOrder.hash) { + handleTx(cancelNextPriceOrder.hash); + } else if (executeNextPriceOrder.hash) { + handleTx(executeNextPriceOrder.hash); + } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [cancelOrExecuteOrderTxn.hash]); + }, [cancelNextPriceOrder.hash, executeNextPriceOrder.hash]); + + const rowsData = useMemo(() => { + if (!cancelling) return openOrders; + const copyOrders = [...openOrders]; + const cancellingIndex = copyOrders.findIndex((o) => o.id === cancelling); + copyOrders[cancellingIndex] = { ...copyOrders[cancellingIndex], isCancelling: true }; + return copyOrders; + }, [openOrders, cancelling]); return ( <> { ), accessor: 'actions', Cell: (cellProps: CellProps) => { + const cancellingRow = cellProps.row.original.isCancelling; return (
{ - setAction('cancel'); - }} + disabled={cancellingRow} + onClick={() => onCancel(cellProps.row.original)} > {t('futures.market.user.open-orders.actions.cancel')} {cellProps.row.original.isExecutable && ( - { - setAction('execute'); - }} - > + executeNextPriceOrder.mutate()}> {t('futures.market.user.open-orders.actions.execute')} )} @@ -263,7 +303,8 @@ const OpenOrdersTable: React.FC = () => { open={!!selectedOrder} order={selectedOrder} closeDrawer={() => setSelectedOrder(undefined)} - setAction={setAction} + onCancel={onCancel} + onExecute={executeNextPriceOrder.mutate} /> @@ -328,6 +369,7 @@ const EditButton = styled.button` `; const CancelButton = styled(EditButton)` + opacity: ${(props) => (props.disabled ? 0.4 : 1)}; border: 1px solid ${(props) => props.theme.colors.selectedTheme.red}; color: ${(props) => props.theme.colors.selectedTheme.red}; margin-right: 8px; diff --git a/sections/homepage/Features/Features.tsx b/sections/homepage/Features/Features.tsx index ce0a1b2d78..a7318a2ff1 100644 --- a/sections/homepage/Features/Features.tsx +++ b/sections/homepage/Features/Features.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; -import PermissionlessIcon from 'assets/png/features/permissionless.png'; +import OpenSourceIcon from 'assets/png/features/opensource.png'; import BlazingFastIcon from 'assets/svg/features/blazing-fast.svg'; import EasyRampingIcon from 'assets/svg/features/easy-ramping.svg'; import LowGasFeeIcon from 'assets/svg/features/low-gas-fee.svg'; @@ -51,10 +51,10 @@ const FEATURES = [ image: , }, { - key: 'permissionless', - title: 'homepage.features.permissionless.title', - copy: 'homepage.features.permissionless.copy', - image: , + key: 'open-source', + title: 'homepage.features.open-source.title', + copy: 'homepage.features.open-source.copy', + image: , }, { key: 'mobile', diff --git a/sections/homepage/ShortList/ShortList.tsx b/sections/homepage/ShortList/ShortList.tsx index 69205ed8b0..5203fc90f0 100644 --- a/sections/homepage/ShortList/ShortList.tsx +++ b/sections/homepage/ShortList/ShortList.tsx @@ -96,7 +96,6 @@ const ShortList = () => { const sectionTitle = ( <> {t('homepage.shortlist.stats.title')} - {t('homepage.shortlist.stats.copy')} ); diff --git a/sections/shared/Layout/AppLayout/Header/BalanceActions.tsx b/sections/shared/Layout/AppLayout/Header/BalanceActions.tsx index 1351e51edf..2c5e2e635a 100644 --- a/sections/shared/Layout/AppLayout/Header/BalanceActions.tsx +++ b/sections/shared/Layout/AppLayout/Header/BalanceActions.tsx @@ -8,8 +8,7 @@ import styled, { useTheme } from 'styled-components'; import Button from 'components/Button'; import CurrencyIcon from 'components/Currency/CurrencyIcon'; import Select from 'components/Select'; -import useSUSDBalance from 'hooks/useSUSDBalance'; -import { positionsState } from 'store/futures'; +import { balancesState, positionsState } from 'store/futures'; import { FlexDivRow, FlexDivRowCentered } from 'styles/common'; import { zeroBN, formatDollars } from 'utils/formatters/number'; import { FuturesMarketAsset, getMarketName, MarketKeyByAsset } from 'utils/futures'; @@ -26,9 +25,9 @@ const BalanceActions: FC = () => { const { t } = useTranslation(); const theme = useTheme(); const router = useRouter(); - const sUSDBalance = useSUSDBalance(); const futuresPositions = useRecoilValue(positionsState); + const { susdWalletBalance } = useRecoilValue(balancesState); const accessiblePositions = useMemo( () => futuresPositions?.filter((position) => position.remainingMargin.gt(zeroBN)) ?? [], @@ -116,8 +115,8 @@ const BalanceActions: FC = () => { }; useEffect(() => { - setBalanceLabel(formatDollars(sUSDBalance, { sign: '$' })); - }, [balanceLabel, sUSDBalance]); + setBalanceLabel(formatDollars(susdWalletBalance, { sign: '$' })); + }, [balanceLabel, susdWalletBalance]); if (!balanceLabel) { return null; @@ -125,7 +124,7 @@ const BalanceActions: FC = () => { return ( - {sUSDBalance.eq(zeroBN) && futuresPositions?.length === 0 ? ( + {susdWalletBalance.eq(zeroBN) && futuresPositions?.length === 0 ? ( router.push(`/exchange/?quote=sUSD`)} diff --git a/sections/shared/Layout/AppLayout/Header/Nav/Nav.tsx b/sections/shared/Layout/AppLayout/Header/Nav/Nav.tsx index 6da1df4346..256715d3a0 100644 --- a/sections/shared/Layout/AppLayout/Header/Nav/Nav.tsx +++ b/sections/shared/Layout/AppLayout/Header/Nav/Nav.tsx @@ -120,8 +120,8 @@ const MenuInside = styled.div<{ isActive: boolean; isDropDown?: boolean }>` const DropDownSelect = styled(Select)` .react-select__control { - padding: 0; - width: 92px; + padding: 0 6px; + width: 98px; } .react-select__group { @@ -138,6 +138,7 @@ const DropDownSelect = styled(Select)` .react-select__dropdown-indicator { margin-right: 5px; + margin-top: 2px; padding: 0; } diff --git a/sections/shared/modals/TxConfirmationModal/TxConfirmationModal.tsx b/sections/shared/modals/TxConfirmationModal/TxConfirmationModal.tsx index a277c57770..4dafe21dbc 100644 --- a/sections/shared/modals/TxConfirmationModal/TxConfirmationModal.tsx +++ b/sections/shared/modals/TxConfirmationModal/TxConfirmationModal.tsx @@ -242,7 +242,7 @@ export const TxConfirmationModal: FC = ({ {txProvider === '1inch' && ( - {t('common.powered-by')} + {t('common.powered-by')} )} @@ -302,7 +302,7 @@ const Summary = styled.div``; const SummaryItem = styled(FlexDivRowCentered)` margin-bottom: 8px; padding-bottom: 8px; - border-bottom: 1px solid ${(props) => props.theme.colors.navy}; + border-bottom: ${(props) => props.theme.colors.selectedTheme.border}; `; const SummaryItemLabel = styled.div` @@ -318,13 +318,17 @@ const Actions = styled(FlexDivColCentered)` margin: 8px 0px; `; -const TxProviderContainer = styled.div` +const Text = styled.span` + color: ${(props) => props.theme.colors.selectedTheme.text.label}; +`; + +const TxProviderContainer = styled(FlexDivRowCentered)` padding-top: 32px; - text-align: center; img { vertical-align: middle; margin-left: 10px; } + justify-content: center; `; const CustomStyledTooltip = styled(StyledTooltip)` diff --git a/store/futures/index.ts b/store/futures/index.ts index 1d7b9e4cf4..3ee0beea7a 100644 --- a/store/futures/index.ts +++ b/store/futures/index.ts @@ -1,7 +1,8 @@ -import Wei, { wei } from '@synthetixio/wei'; +import { wei } from '@synthetixio/wei'; import { atom, selector } from 'recoil'; import { DEFAULT_FUTURES_MARGIN_TYPE, DEFAULT_NP_LEVERAGE_ADJUSTMENT } from 'constants/defaults'; +import { DEFAULT_MAX_LEVERAGE } from 'constants/futures'; import { FuturesAccountState, FuturesAccountType, @@ -11,7 +12,9 @@ import { SynthBalances, TradeFees, FuturesTradeInputs, + FuturesOrderType, FuturesVolumes, + CrossMarginAccounts, } from 'queries/futures/types'; import { FundingRateResponse } from 'queries/futures/useGetAverageFundingRateForMarkets'; import { Price, Rates } from 'queries/rates/types'; @@ -22,8 +25,6 @@ import { newGetExchangeRatesForCurrencies } from 'utils/currencies'; import { zeroBN } from 'utils/formatters/number'; import { FuturesMarketAsset, MarketAssetByKey, MarketKeyByAsset } from 'utils/futures'; -const DEFAULT_MAX_LEVERAGE = wei(10); - export const currentMarketState = atom({ key: getFuturesKey('currentMarket'), default: FuturesMarketAsset.sETH, @@ -57,9 +58,14 @@ export const marketAssetsState = selector({ }, }); -export const balancesState = atom({ +export const balancesState = atom({ key: getSynthsKey('balances'), - default: null, + default: { + balancesMap: {}, + balances: [], + totalUSDBalance: zeroBN, + susdWalletBalance: zeroBN, + }, }); export const activeTabState = atom({ @@ -131,7 +137,15 @@ export const crossMarginSettingsState = atom({ default: { tradeFee: zeroBN, limitOrderFee: zeroBN, - stopLossFee: zeroBN, + stopOrderFee: zeroBN, + }, +}); + +export const crossMarginAccountOverviewState = atom({ + key: getFuturesKey('crossMarginAccountOverview'), + default: { + freeMargin: zeroBN, + keeperEthBal: zeroBN, }, }); @@ -167,9 +181,27 @@ export const pastRatesState = atom({ default: [], }); -export const orderTypeState = atom({ +export const orderTypeState = atom({ key: getFuturesKey('orderType'), - default: 0, + default: 'market', +}); + +export const isAdvancedOrderState = selector({ + key: getFuturesKey('isAdvancedOrder'), + get: ({ get }) => { + const orderType = get(orderTypeState); + return orderType === 'limit' || orderType === 'stop'; + }, +}); + +export const orderFeeCapState = atom({ + key: getFuturesKey('orderFeeCapState'), + default: zeroBN, +}); + +export const futuresOrderPriceState = atom({ + key: getFuturesKey('futuresOrderPrice'), + default: '', }); export const tradeFeesState = atom({ @@ -178,6 +210,8 @@ export const tradeFeesState = atom({ staticFee: zeroBN, dynamicFeeRate: zeroBN, crossMarginFee: zeroBN, + keeperEthDeposit: zeroBN, + limitStopOrderFee: zeroBN, total: zeroBN, }, }); @@ -229,7 +263,9 @@ export const maxLeverageState = selector({ const positionSide = position?.position?.side; const marketMaxLeverage = market?.maxLeverage ?? DEFAULT_MAX_LEVERAGE; const adjustedMaxLeverage = - orderType === 1 ? marketMaxLeverage.mul(DEFAULT_NP_LEVERAGE_ADJUSTMENT) : marketMaxLeverage; + orderType === 'next-price' + ? marketMaxLeverage.mul(DEFAULT_NP_LEVERAGE_ADJUSTMENT) + : marketMaxLeverage; if (!positionLeverage || positionLeverage.eq(wei(0))) return adjustedMaxLeverage; if (positionSide === leverageSide) { @@ -264,27 +300,40 @@ export const futuresAccountState = atom({ default: { crossMarginAddress: null, walletAddress: null, - selectedFuturesAddress: null, crossMarginAvailable: false, - ready: false, + status: 'initial-fetch', + }, +}); + +export const selectedFuturesAddressState = selector({ + key: getFuturesKey('selectedFuturesAddress'), + get: ({ get }) => { + const futuresType = get(futuresAccountTypeState); + const account = get(futuresAccountState); + return futuresType === 'cross_margin' ? account.crossMarginAddress : account.walletAddress; }, }); +export const crossMarginAccountsState = atom({ + key: getFuturesKey('crossMarginAccounts'), + default: {}, +}); + export const futuresAccountTypeState = atom({ key: getFuturesKey('futuresAccountType'), default: DEFAULT_FUTURES_MARGIN_TYPE, }); -export const crossMarginAvailableMarginState = atom({ - key: getFuturesKey('crossMarginAvailableMargin'), - default: zeroBN, +export const showCrossMarginOnboardState = atom({ + key: getFuturesKey('showCrossMarginOnboard'), + default: false, }); export const crossMarginTotalMarginState = selector({ key: getFuturesKey('crossMarginTotalMargin'), get: ({ get }) => { const position = get(positionState); - const freeMargin = get(crossMarginAvailableMarginState); + const { freeMargin } = get(crossMarginAccountOverviewState); return position?.remainingMargin.add(freeMargin) ?? zeroBN; }, }); @@ -328,7 +377,7 @@ export const placeOrderTranslationKeyState = selector({ const isMarketCapReached = get(isMarketCapReachedState); const orderType = get(orderTypeState); const selectedAccountType = get(futuresAccountTypeState); - const freeMargin = get(crossMarginAvailableMarginState); + const { freeMargin } = get(crossMarginAccountOverviewState); let remainingMargin; if (selectedAccountType === 'isolated_margin') { @@ -338,7 +387,9 @@ export const placeOrderTranslationKeyState = selector({ remainingMargin = positionMargin.add(freeMargin); } - if (orderType === 1) return 'futures.market.trade.button.place-next-price-order'; + if (orderType === 'next-price') return 'futures.market.trade.button.place-next-price-order'; + if (orderType === 'limit') return 'futures.market.trade.button.place-limit-order'; + if (orderType === 'stop') return 'futures.market.trade.button.place-stop-order'; if (!!position?.position) return 'futures.market.trade.button.modify-position'; return remainingMargin.lt('50') ? 'futures.market.trade.button.deposit-margin-minimum' diff --git a/styles/theme/colors/dark.ts b/styles/theme/colors/dark.ts index 5d277b2527..649df9ded2 100644 --- a/styles/theme/colors/dark.ts +++ b/styles/theme/colors/dark.ts @@ -3,6 +3,7 @@ import common from './common'; const darkTheme = { background: '#131212', border: '1px solid rgba(255, 255, 255, 0.12)', + outlineBorder: '1px solid rgba(255, 255, 255, 0.12)', red: '#EF6868', green: '#7FD482', black: '#171002', diff --git a/styles/theme/colors/light.ts b/styles/theme/colors/light.ts index cb02f8d056..1bfebb7aca 100644 --- a/styles/theme/colors/light.ts +++ b/styles/theme/colors/light.ts @@ -3,6 +3,7 @@ import common from './common'; const lightTheme = { background: '#F2F2F2', border: '1px solid rgba(0,0,0,0.17)', + outlineBorder: '1px solid rgba(0,0,0,0.17)', red: '#A80300', green: '#1D5D1F', black: '#171002', diff --git a/translations/en.json b/translations/en.json index 236616419d..9a41b84f67 100644 --- a/translations/en.json +++ b/translations/en.json @@ -58,8 +58,8 @@ "learn-more": "Learn more" }, "hero": { - "title": "UNSTOPPABLE SYNTHETIC LEVERAGE", - "copy": "Trade commodities, forex, crypto, and more with up to <0>25x leverage and <0>deep liquidity without the risk of your assets being frozen." + "title": "TRADE SYNTHETIC PERPETUALS", + "copy": "Gain exposure to a variety of asset with up to <0>25x leverage and <0>deep liquidity." }, "assets": { "title": "UNIQUE MARKETS", @@ -76,7 +76,6 @@ "description": "Follow Top Traders", "stats": { "title": "Fostering a growing community of traders", - "copy": "Zero withdrawals frozen", "volume": "Total Volume", "traders": "Total Traders", "trades": "Total Trades", @@ -93,7 +92,7 @@ }, "stake-and-earn": { "title": "stake & earn", - "copy": "$KWENTA stakers earn protocol fees and inflationary rewards" + "copy": "$KWENTA stakers earn inflationary rewards" }, "trade-and-earn": { "title": "trade & earn", @@ -144,9 +143,9 @@ "title": "Unique Assets", "copy": "Trade a variety of digital and traditional assets with low trading fees" }, - "permissionless": { - "title": "Permissionless", - "copy": "A full on-chain DeFi futures trading experience" + "open-source": { + "title": "Open Source", + "copy": " Don't trust, verify" }, "mobile": { "title": "Mobile", @@ -643,7 +642,10 @@ "modify-position": "modify", "deposit-margin-minimum": "Deposit Margin", "oi-caps-reached": "Open Interest Cap Reached", - "place-next-price-order": "Place Next-Price Order" + "place-next-price-order": "Place Next-Price Order", + "place-limit-order": "Place Limit Order", + "place-stop-order": "Place Stop Order", + "connect-wallet": "Connect Wallet" }, "margin": { "deposit-susd": "Deposit your sUSD to start trading", @@ -701,14 +703,23 @@ }, "cross-margin": { "connect-wallet": "Connect your wallet to start trading", - "title": "Cross-Margin", - "faq-title": "Cross-Margin FAQ's", + "title": "Cross Margin", + "faq-title": "Cross Margin FAQ's", "create-account": "Create Account", - "unsupported": "Cross-margin is not supported on this network", - "switch-type": "Switch to isolated margin" + "unsupported": "Cross margin is not supported on this network", + "switch-type": "Switch to isolated margin", + "account-query-failed": "Failed to fetch account" }, "preview": { "error": "Error generating preview" + }, + "orders": { + "manage-keeper-deposit": { + "title": "Manage Keeper Balance", + "withdraw": "Withdraw", + "deposit": "Deposit" + }, + "fee-rejection-label": "Fee Rejection" } }, "history": { @@ -843,15 +854,15 @@ } }, "onboard": { - "title": "Cross-Margin", - "step1-intro": "To Get started you must first create your cross-margin account", + "title": "Cross Margin", + "step1-intro": "To Get started you must first create your cross margin account", "step2-intro": "Now, approve the contract to spend sUSD", "step3-intro": "Lastly, deposit your sUSD into the contact", - "step3-complete": "Your cross-margin account has been successfully created!", - "faq1": "What is cross-margin?", - "faq2": "Is cross-margin better than isolated margin", - "faq3": "How will a cross-margin account affect my trading", - "unsupported-network": "Cross-margin is not supported on this network" + "step3-complete": "Your cross margin account has been successfully created!", + "faq1": "What is cross margin?", + "faq2": "Is cross margin better than isolated margin", + "faq3": "How will a cross margin account affect my trading", + "unsupported-network": "Cross margin is not supported on this network" } } }, diff --git a/utils/formatters/number.ts b/utils/formatters/number.ts index 7b5081ef6a..7415199023 100644 --- a/utils/formatters/number.ts +++ b/utils/formatters/number.ts @@ -219,3 +219,17 @@ export const floorNumber = (num: WeiSource, decimals?: number) => { const precision = 10 ** (decimals ?? suggestedDecimals(num)); return Math.floor(Number(num) * precision) / precision; }; + +export const ceilNumber = (num: WeiSource, decimals?: number) => { + const precision = 10 ** (decimals ?? suggestedDecimals(num)); + return Math.ceil(Number(num) * precision) / precision; +}; + +// Converts to string but strips trailing zeros +export const weiToString = (weiVal: Wei) => { + return String(parseFloat(weiVal.toString())); +}; + +export const isZero = (num: WeiSource) => { + return wei(num || 0).eq(0); +}; diff --git a/utils/futures.ts b/utils/futures.ts index 61febe7fbc..e537048418 100644 --- a/utils/futures.ts +++ b/utils/futures.ts @@ -1,9 +1,14 @@ import { NetworkId, NetworkNameById, Synth } from '@synthetixio/contracts-interface'; +import Wei, { wei } from '@synthetixio/wei'; import { TFunction } from 'i18next'; import { Dictionary } from 'lodash'; +import { FuturesOrderType } from 'queries/futures/types'; +import { PositionSide } from 'sections/futures/types'; import logError from 'utils/logError'; +import { formatNumber } from './formatters/number'; + export const getMarketAsset = (marketKey: FuturesMarketKey) => { return markets[marketKey].asset; }; @@ -267,3 +272,24 @@ export const marketsForNetwork = (networkId: NetworkId) => { return []; } }; + +export const orderPriceInvalidLabel = ( + orderPrice: string, + leverageSide: PositionSide, + currentPrice: Wei, + orderType: FuturesOrderType +): string | null => { + if (!orderPrice || Number(orderPrice) <= 0) return null; + const isLong = leverageSide === 'long'; + if ( + ((isLong && orderType === 'limit') || (!isLong && orderType === 'stop')) && + wei(orderPrice).gt(currentPrice) + ) + return 'max ' + formatNumber(currentPrice); + if ( + ((!isLong && orderType === 'limit') || (isLong && orderType === 'stop')) && + wei(orderPrice).lt(currentPrice) + ) + return 'min ' + formatNumber(currentPrice); + return null; +};