Skip to content

Commit

Permalink
Merge pull request #6 from orbs-network/twap-integration
Browse files Browse the repository at this point in the history
Twap integration
  • Loading branch information
denis-orbs authored Oct 16, 2024
2 parents 0014d2e + 4d101a1 commit 730a528
Show file tree
Hide file tree
Showing 44 changed files with 8,152 additions and 200 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
"dependencies": {
"@ethersproject/hash": "^5.7.0",
"@orbs-network/liquidity-hub-sdk": "^1.0.40",
"@orbs-network/swap-ui": "^0.0.10",
"@orbs-network/swap-ui": "^0.0.14",
"@orbs-network/twap-sdk": "^2.0.38",
"@paraswap/sdk": "^6.10.0",
"@radix-ui/react-accordion": "^1.2.1",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
Expand All @@ -23,15 +25,18 @@
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.3",
"@rainbow-me/rainbowkit": "^2.1.5",
"@tanstack/react-query": "^5.54.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"lucide-react": "^0.438.0",
"moment": "^2.30.1",
"next-themes": "^0.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-number-format": "^5.4.2",
"react-virtuoso": "^4.12.0",
"sonner": "^1.5.0",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
Expand Down
136 changes: 136 additions & 0 deletions src/components/order-details.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { format, makeElipsisAddress } from "@/lib";
import { useExplorer, useToExactAmount } from "@/trade/hooks";
import { fillDelayText } from "@/trade/twap/utils";
import { Token } from "@/types";
import { TimeDuration } from "@orbs-network/twap-sdk";
import moment from "moment";
import { ReactNode } from "react";
import { useAccount } from "wagmi";
import { Card } from "./ui/card";
import { QuestionHelperTooltip } from "./ui/tooltip";

export const OrderDetails = ({ children }: { children: ReactNode }) => {
return (
<Card className="w-full bg-slate-50 dark:bg-slate-900 p-3 flex flex-col gap-2 mt-5">
{children}
</Card>
);
};

const OrderDetail = ({
title,
children,
tooltip,
}: {
title: string;
children: ReactNode;
tooltip?: ReactNode;
}) => {
return (
<div className="flex flex-row gap-2 w-full justify-between flex-wrap">
<div className="flex gap-2 justify-start items-center">
<p style={{ fontSize: 14 }}> {title}</p>{" "}
{tooltip && <QuestionHelperTooltip content={tooltip} />}
</div>
<div style={{ fontSize: 14 }}>{children}</div>
</div>
);
};

const Deadline = ({ deadline }: { deadline?: number }) => {
return (
<OrderDetails.Detail
title="Deadline"
tooltip="This is the date and time marking the end of the period which you have selected for your order to be executed."
>
<div>{moment(deadline).format("DD/MM/YY HH:mm")} UTC</div>
</OrderDetails.Detail>
);
};

const TradeSize = ({
srcChunkAmount,
inToken,
}: {
srcChunkAmount?: string;
inToken?: Token | null;
}) => {
const amount = useToExactAmount(srcChunkAmount, inToken?.decimals);

return (
<OrderDetails.Detail
title="Individual trade size"
tooltip="The number of input tokens that will be removed from your balance and swapped for the output token in each individual trade."
>
<div>
{format.crypto(Number(amount))} {inToken?.symbol}
</div>
</OrderDetails.Detail>
);
};

const Chunks = ({ chunks }: { chunks?: number }) => {
return (
<OrderDetails.Detail
title="No. of trades"
tooltip="The total number of individual trades that will be scheduled as part of your order."
>
<div>{chunks}</div>
</OrderDetails.Detail>
);
};

const FillDelay = ({ fillDelay }: { fillDelay?: TimeDuration }) => {
return (
<OrderDetails.Detail
title="Every"
tooltip="The estimated minimum amount of time that will elapse between each trade in your order."
>
<div>{fillDelayText(fillDelay)}</div>
</OrderDetails.Detail>
);
};

const MinReceived = ({
destTokenMinAmount,
outToken,
}: {
destTokenMinAmount?: string;
outToken?: Token | null;
}) => {
const amount = useToExactAmount(destTokenMinAmount, outToken?.decimals);

return (
<OrderDetails.Detail
title="Minimum received"
tooltip="This is the minimum number of tokens that may be received. NOTE: This minimum only refers to executed trades. Some trades may not be executed if the limit price is higher than the available market prices and your order may only be partially filled."
>
<div>
{format.crypto(Number(amount))} {outToken?.symbol}
</div>
</OrderDetails.Detail>
);
};

const Recepient = () => {
const { address } = useAccount();
const explorer = useExplorer();

return (
<OrderDetails.Detail title="Recipient">
<a target="_blank" href={`${explorer}/address/${address}`}>
{makeElipsisAddress(address)}
</a>
</OrderDetails.Detail>
);
};



OrderDetails.Detail = OrderDetail;
OrderDetails.Deadline = Deadline;
OrderDetails.TradeSize = TradeSize;
OrderDetails.Chunks = Chunks;
OrderDetails.FillDelay = FillDelay;
OrderDetails.MinReceived = MinReceived;
OrderDetails.Recepient = Recepient;
30 changes: 13 additions & 17 deletions src/components/tokens/token-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { NumericFormat } from 'react-number-format'
import {
format,
cn,
ErrorCodes,
fromBigNumber,
fromBigNumberToStr,
ErrorCodes,
} from '@/lib'
import { Skeleton } from '../ui/skeleton'
import { Button } from '../ui/button'

import { useToExactAmount } from '@/trade/hooks'
import BN from 'bignumber.js'
function getTextSize(amountLength: number) {
if (amountLength > 16) {
return 'text-xl'
Expand All @@ -29,7 +29,7 @@ export type TokenCardProps = {
label: string
amount: string
amountUsd?: string
balance: bigint
balance: any
selectedToken: Token
tokens: TokensWithBalances
onSelectToken: (token: Token) => void
Expand All @@ -52,22 +52,20 @@ export function TokenCard({
amountLoading,
inputError,
}: TokenCardProps) {

const balanceError = inputError === ErrorCodes.InsufficientBalance
const balanceDisplay = selectedToken
? format.crypto(fromBigNumber(balance, selectedToken.decimals))
: '0'
const halfBalance =
balance !== 0n && selectedToken
? fromBigNumberToStr(balance / 2n, selectedToken.decimals)
: '0'
const maxBalance = selectedToken
? fromBigNumberToStr(balance, selectedToken.decimals)
: '0'

const maxBalance = useToExactAmount(balance, selectedToken?.decimals)
const halfBalance = useToExactAmount(BN(balance || 0).dividedBy(2).toString(), selectedToken?.decimals)

return (
<Card
className={cn(
'bg-slate-50 dark:bg-slate-900 p-4 flex flex-col gap-4',
inputError &&
balanceError &&
'mix-blend-multiply bg-red-50 dark:mix-blend-screen dark:bg-red-950'
)}
>
Expand Down Expand Up @@ -99,7 +97,7 @@ export function TokenCard({
{amountLoading ? (
<Skeleton className="h-10 w-[250px]" />
) : (
<div className={cn(getTextSize(amount.length), 'w-full')}>
<div className={cn(getTextSize(amount?.length), 'w-full')}>
<NumericFormat
className="bg-transparent w-full min-w-0 outline-none"
value={amount}
Expand All @@ -122,11 +120,9 @@ export function TokenCard({
</div>
</div>
<div className="flex justify-between items-center">
{inputError ? (
{balanceError ? (
<div className="text-red-700 dark:text-red-600 text-lg">
{inputError === ErrorCodes.InsufficientBalance
? 'Exceeds balance'
: inputError}
Exceeds balance
</div>
) : (
<div className="text-gray-500 dark:text-gray-400 text-lg">
Expand Down
46 changes: 23 additions & 23 deletions src/components/tokens/token-select.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
import { ChevronDownIcon } from 'lucide-react'
import { Button } from '../ui/button'
import { ChevronDownIcon } from "lucide-react";
import { Button } from "../ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '../ui/dialog'
import { Input } from '../ui/input'
import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar'
import { Token, TokensWithBalances } from '@/types'
import { Card } from '../ui/card'
import { useMemo, useState } from 'react'
import { fromBigNumber } from '@/lib'
} from "../ui/dialog";
import { Input } from "../ui/input";
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
import { Token, TokensWithBalances } from "@/types";
import { Card } from "../ui/card";
import { useMemo, useState } from "react";
import { fromBigNumber } from "@/lib";

type TokenSelectProps = {
selectedToken: Token | undefined
tokens: TokensWithBalances
onSelectToken: (token: Token) => void
}
selectedToken: Token | undefined;
tokens: TokensWithBalances;
onSelectToken: (token: Token) => void;
};

export function TokenSelect({
selectedToken,
tokens,
onSelectToken,
}: TokenSelectProps) {
const [open, setOpen] = useState(false)
const [filterInput, setFilterInput] = useState('')
const [open, setOpen] = useState(false);
const [filterInput, setFilterInput] = useState("");

const SortedTokens = useMemo(() => {
return Object.values(tokens)
.filter((t) => {
return (
t.token.symbol.toLowerCase().includes(filterInput.toLowerCase()) ||
t.token.address.toLowerCase().includes(filterInput.toLowerCase())
)
);
})
.sort(
(a, b) =>
Expand All @@ -47,8 +47,8 @@ export function TokenSelect({
key={t.token.address}
className="cursor-pointer p-4 flex items-center justify-between gap-3"
onClick={() => {
onSelectToken(t.token)
setOpen(false)
onSelectToken(t.token);
setOpen(false);
}}
>
<div className="flex items-center gap-3">
Expand All @@ -65,8 +65,8 @@ export function TokenSelect({
</div>
<div>{fromBigNumber(t.balance, t.token.decimals).toFixed(5)}</div>
</Card>
))
}, [filterInput, onSelectToken, tokens])
));
}, [filterInput, onSelectToken, tokens]);

return (
<Dialog modal={true} open={open} onOpenChange={(o) => setOpen(o)}>
Expand All @@ -84,11 +84,11 @@ export function TokenSelect({
/>
)}
<AvatarFallback className="bg-slate-200 dark:bg-slate-700">
{selectedToken ? selectedToken.symbol.charAt(0) : '-'}
{selectedToken ? selectedToken.symbol.charAt(0) : "-"}
</AvatarFallback>
</Avatar>
<div className="text-xl">
{selectedToken ? selectedToken.symbol : '-'}
{selectedToken ? selectedToken.symbol : "-"}
</div>
<ChevronDownIcon className="h-6 w-6" />
</Button>
Expand All @@ -110,5 +110,5 @@ export function TokenSelect({
</div>
</DialogContent>
</Dialog>
)
);
}
Loading

0 comments on commit 730a528

Please sign in to comment.