Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,17 @@
"storybook": "^7.5.3",
"typescript": "^5.2.2",
"vite": "^5.0.0",
"vite-plugin-dts": "^3.0.0"
"vite-plugin-dts": "^3.0.0",
"vitest": "^1.0.4"
},
"peerDependencies": {
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"starknetkit": "^1.0.20"
},
"dependencies": {
"bignumber.js": "^9.1.2"
}
}
24 changes: 24 additions & 0 deletions packages/ui/src/components/Connect/ConnectButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { FC } from "react"

interface ConnectButtonProps {
connect: () => Promise<void>
connecting: boolean
}

const ConnectButton: FC<ConnectButtonProps> = ({ connect, connecting }) => {
return (
<div className="inline-block">
<button
onClick={connect}
className={`flex items-center shadow-list-item font-barlow font-semibold text-base leading-4 h-10 px-4 gap-2.5 rounded-lg
text-black bg-white
${connecting ? "cursor-not-allowed text-neutral-400" : ""}`}
disabled={connecting}
>
<span>Connect wallet</span>
</button>
</div>
)
}

export { ConnectButton }
Original file line number Diff line number Diff line change
@@ -1,39 +1,38 @@
import { FC, ReactNode, useEffect, useRef, useState } from "react"
import { truncateAddress } from "../helpers/address"
import { FC, useEffect, useRef, useState } from "react"
import { ProviderInterface, uint256 } from "starknet"
import { truncateAddress } from "../../helpers/address"
import { formatUnits } from "../../helpers/formatUnits"
import { prettifyTokenNumber } from "../../helpers/prettifyNumber"
import { ChevronDown } from "../../icons/ChevronDown"
import { ProfileIcon } from "../../icons/ProfileIcon"
import { hexSchema } from "../../schemas/hexSchema"
import { DropdownElement } from "../../types/DropdownElement"
import { ConnectedMenu } from "./ConnectedMenu"
import { ChevronDown } from "../icons/ChevronDown"
import { ProfileIcon } from "../icons/ProfileIcon"
import { ProviderInterface } from "starknet"
import { Hex, hexSchema } from "../schemas/hexSchema"

export interface DropdownElement {
icon: string | ReactNode
label: string
onClick: () => void
}

const { uint256ToBN } = uint256

interface ConnectedButtonProps {
address: string
showBalance?: boolean
dropdownElements?: DropdownElement[]
provider: ProviderInterface
symbol?: string
webWalletUrl?: string
}

const FEE_TOKEN_ADDRESS = // ETH on starknet
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"

export const uint256ToBigInt = (low: Hex, high: Hex): bigint => {
return BigInt(low) + (BigInt(high) << BigInt(128))
}

const ConnectedButton: FC<ConnectedButtonProps> = ({
address,
showBalance,
dropdownElements,
provider,
symbol,
webWalletUrl,
}) => {
const [isOpen, setIsOpen] = useState(false)
const [balance, setBalance] = useState(0n)
const [balance, setBalance] = useState("")

const toggleMenu = () => {
setIsOpen((prev) => !prev)
Expand All @@ -49,16 +48,32 @@ const ConnectedButton: FC<ConnectedButtonProps> = ({
}

const balanceOf = async (address: string) => {
const values = await provider.callContract({
contractAddress: FEE_TOKEN_ADDRESS,
entrypoint: "balanceOf",
calldata: [address],
})

const [uint256Low, uint256High] = values.result.map((value) =>
hexSchema.parse(value),
)
setBalance(uint256ToBigInt(uint256Low, uint256High))
try {
const values = await provider.callContract({
contractAddress: FEE_TOKEN_ADDRESS,
entrypoint: "balanceOf",
calldata: [address],
})

const [uint256Low, uint256High] = values.result.map((value) =>
hexSchema.parse(value),
)

const amountUint256: uint256.Uint256 = {
low: uint256Low,
high: uint256High,
}

const formatted = formatUnits({
value: uint256ToBN(amountUint256),
decimals: 18,
})

setBalance(prettifyTokenNumber(formatted) ?? "-")
} catch (e) {
console.error(e)
setBalance("-")
}
}

document.addEventListener("mousedown", handleClickOutside)
Expand All @@ -74,15 +89,17 @@ const ConnectedButton: FC<ConnectedButtonProps> = ({

return (
<>
<div className="relative" ref={ref}>
<div className="relative inline-block" ref={ref}>
<button
className="flex items-center shadow-list-item font-barlow font-semibold text-base leading-4 h-10 px-4 gap-2.5 rounded-lg"
className="flex items-center shadow-list-item font-barlow font-semibold text-base leading-4 h-10 px-4 gap-2.5 rounded-lg text-black bg-white"
onClick={toggleMenu}
>
{showBalance && (
<>
<div className="flex items-center">
<span>{balance.toString()} ETH</span>
<span>
{balance} {symbol}
</span>
</div>
<div className="h-10 border border-solid border-neutrals.200" />
</>
Expand All @@ -99,6 +116,7 @@ const ConnectedButton: FC<ConnectedButtonProps> = ({
address={address}
open={isOpen}
dropdownElements={dropdownElements}
webWalletUrl={webWalletUrl}
/>
</div>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { FC, ReactNode, useContext } from "react"
import { CopyIcon } from "../icons/CopyIcon"
import { DisconnectIcon } from "../icons/DisconnectIcon"
import { WalletIcon } from "../icons/WalletIcon"
import { DropdownElement } from "./ConnectedButton"
import { WalletContext } from "./WalletContext"
import { CopyIcon } from "../../icons/CopyIcon"
import { DisconnectIcon } from "../../icons/DisconnectIcon"
import { WalletIcon } from "../../icons/WalletIcon"
import { WalletContext } from "../WalletContext"
import { disconnect } from "starknetkit"
import { DropdownElement } from "../../types/DropdownElement"

interface ConnectedMenuProps {
address: string
open: boolean
dropdownElements?: DropdownElement[]
webWalletUrl?: string
}

interface MenuButtonProps {
Expand All @@ -21,7 +22,7 @@ interface MenuButtonProps {
const MenuButton: FC<MenuButtonProps> = ({ onClick, label, icon }) => (
<button
onClick={onClick}
className="flex items-center w-full text-left px-2 py-2 text-base font-semibold hover:bg-[#F0F0F0] rounded-lg"
className="flex items-center w-full text-left px-2 py-2 text-base font-semibold rounded-lg hover:bg-[#F0F0F0]"
>
{typeof icon === "string" ? (
<img src={icon} className="flex items-center w-5 h-5 mr-2"></img>
Expand All @@ -37,6 +38,7 @@ const ConnectedMenu: FC<ConnectedMenuProps> = ({
address,
open,
dropdownElements,
webWalletUrl,
}) => {
const walletContext = useContext(WalletContext)
const { wallet } = walletContext
Expand All @@ -47,14 +49,14 @@ const ConnectedMenu: FC<ConnectedMenuProps> = ({
}
return (
<div
className={`absolute left-0 mt-1 w-50 rounded-lg shadow-lg z-10 ${
className={`absolute left-0 mt-0.5 w-50 rounded-lg shadow-lg z-10 text-black bg-white ${
!open ? "hidden" : ""
}`}
>
<div className="p-2">
{wallet?.id === "argentWebWallet" && (
<MenuButton
onClick={() => window.open("TODO", "_blank")}
onClick={() => window.open(webWalletUrl, "_blank")}
label="View wallet"
icon={<WalletIcon />}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import { FC, ReactNode, useContext, useEffect, useState } from "react"
import { FC, useContext, useEffect, useState } from "react"
import { ProviderInterface } from "starknet"
import { ConnectButton } from "./ConnectButton"
import type { Connector } from "starknetkit"
import { DEFAULT_WEBWALLET_URL, connect, type ModalMode } from "starknetkit"
import type { ArgentMobileConnectorOptions } from "starknetkit/argentMobile"
import { connect, DEFAULT_WEBWALLET_URL, type ModalMode } from "starknetkit"
import { ConnectButton } from "./ConnectButton"
import { ConnectedButton } from "./ConnectedButton"
import { WalletContext } from "./WalletContext"

export interface DropdownElement {
icon: string | ReactNode
label: string
onClick: () => void
}
import { WalletContext } from "../WalletContext"
import { DropdownElement } from "../../types/DropdownElement"

interface StarknetkitButtonProps {
provider: ProviderInterface
Expand All @@ -33,23 +28,24 @@ const StarknetkitButton: FC<StarknetkitButtonProps> = ({
provider,
}) => {
const walletContext = useContext(WalletContext)
const [loading, setLoading] = useState(false)
const [connecting, setConnecting] = useState(false)
const { wallet } = walletContext

const handleConnect = async (modalMode: ModalMode = "alwaysAsk") => {
setLoading(true)
setConnecting(true)
try {
const wallet = await connect({
modalMode,
webWalletUrl,
argentMobileOptions,
connectors,
})

walletContext.setWallet(wallet)
} catch (e) {
console.log(e)
} finally {
setLoading(false)
setConnecting(false)
}
}

Expand All @@ -59,20 +55,18 @@ const StarknetkitButton: FC<StarknetkitButtonProps> = ({
}
}, [])

if (loading) {
// TODOs
return <></>
}

return (
<>
{!wallet && <ConnectButton connect={handleConnect} />}
{!wallet && (
<ConnectButton connect={handleConnect} connecting={connecting} />
)}
{wallet?.isConnected && (
<ConnectedButton
address={wallet.selectedAddress}
showBalance={showBalance}
dropdownElements={dropdownElements}
provider={provider}
webWalletUrl={webWalletUrl}
/>
)}
</>
Expand Down
18 changes: 0 additions & 18 deletions packages/ui/src/components/ConnectButton.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion packages/ui/src/components/NotificationButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const NotificationButton: FC<NotificationButtonProps> = ({
return (
<button
onClick={() => console.log("asd")}
className="flex items-center justify-center shadow-list-item rounded-lg h-10 w-10"
className="flex items-center justify-center shadow-list-item rounded-lg h-10 w-10 bg-white"
>
<div className="relative">
<BellIcon className="w-6 h-6" />
Expand Down
18 changes: 18 additions & 0 deletions packages/ui/src/helpers/address.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, test } from "vitest"

import { formatAddress, truncateAddress } from "./address"

describe("address", () => {
test("should format address", async () => {
const address = formatAddress("0x1")
expect(address).toBe(
"0x0000000000000000000000000000000000000000000000000000000000000001",
)
})

test("should truncateAddress address", async () => {
const address = formatAddress("0x1")
const truncated = truncateAddress(address)
expect(truncated).toBe("0x0000…0001")
})
})
33 changes: 33 additions & 0 deletions packages/ui/src/helpers/formatUnits.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { describe, expect, test } from "vitest"

import { formatUnits } from "./formatUnits"

describe("formatUnits function", () => {
test("Should return string version of a value when decimals are 0", () => {
expect(formatUnits({ value: 123456789n, decimals: 0 })).toBe("123456789")
expect(formatUnits({ value: -123456789n, decimals: 0 })).toBe("-123456789")
})

test("Should handle negative values correctly", () => {
expect(formatUnits({ value: -123456789n, decimals: 2 })).toBe("-1234567.89")
expect(formatUnits({ value: -100n, decimals: 2 })).toBe("-1")
})

test("Should format value correctly when decimals are not 0", () => {
expect(formatUnits({ value: 123456789n, decimals: 2 })).toBe("1234567.89")
expect(formatUnits({ value: 123456789n, decimals: 3 })).toBe("123456.789")
expect(formatUnits({ value: 123456789n, decimals: 4 })).toBe("12345.6789")
})

test("Should correctly pad and format the value", () => {
expect(formatUnits({ value: 5n, decimals: 4 })).toBe("0.0005")
expect(formatUnits({ value: 50n, decimals: 4 })).toBe("0.005")
expect(formatUnits({ value: 500n, decimals: 4 })).toBe("0.05")
expect(formatUnits({ value: 5000n, decimals: 4 })).toBe("0.5")
})

test("Should correctly handle values with trailing zeros", () => {
expect(formatUnits({ value: 100n, decimals: 2 })).toBe("1")
expect(formatUnits({ value: 10000n, decimals: 4 })).toBe("1")
})
})
Loading