Skip to content

Commit 014a2ff

Browse files
authored
Merge pull request #55 from argentlabs/feat/starknetid-info
Feat/starknetid info
2 parents b549951 + a808ef0 commit 014a2ff

File tree

9 files changed

+302
-69
lines changed

9 files changed

+302
-69
lines changed

packages/ui/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070
"starknetkit": "^1.0.20"
7171
},
7272
"dependencies": {
73-
"bignumber.js": "^9.1.2"
73+
"@argent/x-multicall": "^7.0.9",
74+
"bignumber.js": "^9.1.2",
75+
"swr": "^2.2.4"
7476
}
7577
}

packages/ui/src/components/Connect/ConnectedButton.tsx

Lines changed: 73 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
1-
import { FC, useEffect, useRef, useState, CSSProperties } from "react"
2-
import { ProviderInterface, uint256 } from "starknet"
1+
import BigDecimalNumber from "bignumber.js"
2+
import { CSSProperties, FC, useEffect, useRef, useState } from "react"
3+
import { ProviderInterface, constants, uint256 } from "starknet"
34
import { truncateAddress } from "../../helpers/address"
45
import { formatUnits } from "../../helpers/formatUnits"
56
import { prettifyTokenNumber } from "../../helpers/prettifyNumber"
7+
import { useStarknetId } from "../../hooks/useStarknetId"
68
import { ChevronDown } from "../../icons/ChevronDown"
79
import { ProfileIcon } from "../../icons/ProfileIcon"
810
import { hexSchema } from "../../schemas/hexSchema"
911
import { DropdownElement } from "../../types/DropdownElement"
12+
import { SkeletonLoading } from "../Loading/SkeletonLoading"
1013
import { ConnectedMenu } from "./ConnectedMenu"
14+
import { AccountInfo } from "./types"
1115

1216
const { uint256ToBN } = uint256
1317

1418
interface ConnectedButtonProps {
1519
address: string
16-
accountInfo?: {
17-
showBalance?: boolean
18-
starknetId?: string
19-
starknetIdAvatar?: string
20-
}
20+
chainId: constants.StarknetChainId
21+
accountInfo?: AccountInfo
2122
dropdownElements?: DropdownElement[]
2223
provider: ProviderInterface
2324
symbol?: string
@@ -30,17 +31,28 @@ const FEE_TOKEN_ADDRESS = // ETH on starknet
3031

3132
const ConnectedButton: FC<ConnectedButtonProps> = ({
3233
address,
34+
chainId,
3335
accountInfo,
3436
dropdownElements,
3537
provider,
36-
symbol,
38+
symbol = "ETH",
3739
webWalletUrl,
3840
style,
3941
}) => {
4042
const [isOpen, setIsOpen] = useState(false)
4143
const [balance, setBalance] = useState("")
44+
const [isFetchingBalance, setIsFetchingBalance] = useState(false)
45+
46+
const { showBalance, displayStarknetId, displayStarknetIdAvatar } =
47+
accountInfo ?? {}
4248

43-
const { showBalance, starknetId, starknetIdAvatar } = accountInfo ?? {}
49+
const { data, isLoading } = useStarknetId({
50+
address,
51+
chainId,
52+
displayStarknetId,
53+
displayStarknetIdAvatar,
54+
provider,
55+
})
4456

4557
const toggleMenu = () => {
4658
setIsOpen((prev) => !prev)
@@ -56,6 +68,7 @@ const ConnectedButton: FC<ConnectedButtonProps> = ({
5668
}
5769

5870
const balanceOf = async (address: string) => {
71+
setIsFetchingBalance(true)
5972
try {
6073
const values = await provider.callContract({
6174
contractAddress: FEE_TOKEN_ADDRESS,
@@ -77,10 +90,17 @@ const ConnectedButton: FC<ConnectedButtonProps> = ({
7790
decimals: 18,
7891
})
7992

80-
setBalance(prettifyTokenNumber(formatted) ?? "-")
93+
const formattedBigDecimalValue = new BigDecimalNumber(formatted)
94+
setBalance(
95+
formattedBigDecimalValue.lt(0.001)
96+
? "< 0.001"
97+
: prettifyTokenNumber(formatted, { maxDecimalPlaces: 3 }) ?? "-",
98+
)
8199
} catch (e) {
82100
console.error(e)
83101
setBalance("-")
102+
} finally {
103+
setIsFetchingBalance(false)
84104
}
85105
}
86106

@@ -105,27 +125,52 @@ const ConnectedButton: FC<ConnectedButtonProps> = ({
105125
>
106126
{showBalance && (
107127
<>
108-
<div className="flex items-center">
109-
<span>
110-
{balance} {symbol}
111-
</span>
112-
</div>
128+
{isFetchingBalance ? (
129+
<div className="flex items-center justify-center w-8 h-2">
130+
<SkeletonLoading />
131+
</div>
132+
) : (
133+
<div className="flex items-center">
134+
<span>
135+
{balance} {symbol}
136+
</span>
137+
</div>
138+
)}
113139
<div className="h-10 border border-solid border-neutrals.200" />
114140
</>
115141
)}
116-
<div className="flex items-center gap-2">
117-
{starknetIdAvatar ? (
118-
<img className="w-4 h-4" src={starknetIdAvatar} />
119-
) : (
120-
<div className="flex items-center justify-center bg-[#F0F0F0] rounded-full w-6 h-6">
121-
<ProfileIcon className="w-4 h-4" />
122-
</div>
123-
)}
124-
{starknetId ? (
125-
<div>{starknetId}</div>
126-
) : (
127-
<div>{truncateAddress(address)}</div>
128-
)}
142+
<div className="flex flex-1 items-center justify-between">
143+
<div className="flex items-center gap-2 relative">
144+
{isLoading ? (
145+
<div className="flex items-center justify-center w-4 h-2">
146+
<SkeletonLoading />
147+
</div>
148+
) : (
149+
<>
150+
{displayStarknetIdAvatar && data?.starknetIdAvatar ? (
151+
<img className="w-6 h-6" src={data.starknetIdAvatar} />
152+
) : (
153+
<div className="flex items-center justify-center bg-[#F0F0F0] rounded-full w-6 h-6">
154+
<ProfileIcon className="w-4 h-4" />
155+
</div>
156+
)}
157+
</>
158+
)}
159+
160+
{isLoading ? (
161+
<div className="flex items-center justify-center w-4 h-2">
162+
<SkeletonLoading />
163+
</div>
164+
) : (
165+
<>
166+
{displayStarknetId && data?.starknetId ? (
167+
<div>{data.starknetId}</div>
168+
) : (
169+
<div>{truncateAddress(address)}</div>
170+
)}
171+
</>
172+
)}
173+
</div>
129174
</div>
130175
<ChevronDown />
131176
</button>

packages/ui/src/components/Connect/StarknetkitButton.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import { FC, useContext, useEffect, useState, CSSProperties } from "react"
2-
import { ProviderInterface } from "starknet"
3-
import type { Connector } from "starknetkit"
4-
import { DEFAULT_WEBWALLET_URL, connect, type ModalMode } from "starknetkit"
2+
import { ProviderInterface, RpcProvider, constants } from "starknet"
3+
import type { ModalMode, Connector } from "starknetkit"
4+
import { connect } from "starknetkit"
55
import type { ArgentMobileConnectorOptions } from "starknetkit/argentMobile"
66
import { ConnectButton } from "./ConnectButton"
77
import { ConnectedButton } from "./ConnectedButton"
88
import { WalletContext } from "../WalletContext"
99
import { DropdownElement } from "../../types/DropdownElement"
10+
import { AccountInfo } from "./types"
11+
12+
const DEFAULT_WEBWALLET_URL = "https://web.argent.xyz"
1013

1114
interface StarknetkitButtonProps {
12-
accountInfo?: {
13-
showBalance?: boolean
14-
starknetId?: string
15-
starknetIdAvatar?: string
16-
}
15+
accountInfo?: AccountInfo
1716
argentMobileOptions?: ArgentMobileConnectorOptions
1817
connectors?: Connector[]
1918
dropdownElements?: DropdownElement[]
@@ -29,9 +28,11 @@ const StarknetkitButton: FC<StarknetkitButtonProps> = ({
2928
connectors,
3029
dropdownElements,
3130
enableReconnect,
32-
provider,
33-
style,
31+
provider = new RpcProvider({
32+
nodeUrl: "https://starknet-testnet.public.blastapi.io/rpc/v0.5",
33+
}),
3434
webWalletUrl = DEFAULT_WEBWALLET_URL,
35+
style,
3536
}) => {
3637
const walletContext = useContext(WalletContext)
3738
const [connecting, setConnecting] = useState(false)
@@ -45,7 +46,7 @@ const StarknetkitButton: FC<StarknetkitButtonProps> = ({
4546
webWalletUrl,
4647
argentMobileOptions,
4748
connectors,
48-
// provider
49+
// provider, // TODO: remove comment after merging develop
4950
})
5051

5152
walletContext.setWallet(wallet)
@@ -74,6 +75,7 @@ const StarknetkitButton: FC<StarknetkitButtonProps> = ({
7475
{wallet?.isConnected && (
7576
<ConnectedButton
7677
address={wallet.selectedAddress}
78+
chainId={wallet.chainId as constants.StarknetChainId} // TODO: update with getChainId after merging develop
7779
accountInfo={accountInfo}
7880
dropdownElements={dropdownElements}
7981
provider={provider}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface AccountInfo {
2+
showBalance?: boolean
3+
displayStarknetId?: boolean
4+
displayStarknetIdAvatar?: boolean
5+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { FC } from "react"
2+
3+
const SkeletonLoading: FC = () => (
4+
<>
5+
<style>
6+
{`.skeleton {
7+
animation: skeleton-loading 1s linear infinite alternate;
8+
}
9+
10+
@keyframes skeleton-loading {
11+
0% {
12+
background-color: hsl(200, 20%, 80%);
13+
}
14+
100% {
15+
background-color: hsl(200, 20%, 95%);
16+
}
17+
}
18+
`}
19+
</style>
20+
<div className={`skeleton flex flex-1 w-full h-full`} />
21+
</>
22+
)
23+
24+
export { SkeletonLoading }
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { getBatchProvider } from "@argent/x-multicall"
2+
import { Call, ProviderInterface, constants, starknetId } from "starknet"
3+
import useSWR from "swr"
4+
5+
type UseStarknetId = {
6+
address?: string
7+
chainId: constants.StarknetChainId
8+
displayStarknetId?: boolean
9+
displayStarknetIdAvatar?: boolean
10+
provider: ProviderInterface
11+
}
12+
13+
export const useStarknetId = ({
14+
address,
15+
chainId,
16+
displayStarknetId,
17+
displayStarknetIdAvatar,
18+
provider,
19+
}: UseStarknetId) => {
20+
return useSWR(
21+
[address && [`${chainId}::${address}`], "starknetId"],
22+
() => {
23+
if (!address || (!displayStarknetId && !displayStarknetIdAvatar)) {
24+
return {
25+
starknetId: undefined,
26+
starknetIdAvatar: undefined,
27+
}
28+
}
29+
30+
return getStarknetId(address, chainId, provider, displayStarknetIdAvatar)
31+
},
32+
{
33+
revalidateOnMount: true,
34+
dedupingInterval: 1000 * 60 * 60 * 1,
35+
},
36+
)
37+
}
38+
39+
// TODO: remove after starknetkit version is updated
40+
const defaultMulticallAddress =
41+
"0x05754af3760f3356da99aea5c3ec39ccac7783d925a19666ebbeca58ff0087f4"
42+
43+
const getStarknetId = async (
44+
address: string,
45+
chainId: constants.StarknetChainId,
46+
provider: ProviderInterface,
47+
displayStarknetIdAvatar?: boolean,
48+
) => {
49+
try {
50+
const starknetIdContractAddress = starknetId.getStarknetIdContract(chainId)
51+
52+
// TODO: remove after starknetkit is updated
53+
const multiCall = getBatchProvider(
54+
provider,
55+
undefined,
56+
defaultMulticallAddress,
57+
)
58+
59+
const callAddressToDomain: Call = {
60+
contractAddress: starknetIdContractAddress,
61+
entrypoint: "address_to_domain",
62+
calldata: [address],
63+
}
64+
65+
const domainResponse = await multiCall.callContract(callAddressToDomain)
66+
67+
const decimalDomain = domainResponse.result
68+
.map((element) => BigInt(element))
69+
.slice(1)
70+
71+
const starknetIdName = starknetId.useDecoded(decimalDomain)
72+
let starknetIdAvatar: string | undefined
73+
74+
if (displayStarknetIdAvatar) {
75+
const callDomainToId: Call = {
76+
contractAddress: starknetIdContractAddress,
77+
entrypoint: "domain_to_id",
78+
calldata: [decimalDomain],
79+
}
80+
81+
const domainToIdResponse = await multiCall.callContract(callDomainToId)
82+
starknetIdAvatar = `https://starknet.id/api/identicons/${domainToIdResponse.result[0].toString()}`
83+
}
84+
85+
return {
86+
starknetId: starknetIdName,
87+
starknetIdAvatar: starknetIdAvatar,
88+
}
89+
} catch (e) {
90+
console.error(e)
91+
}
92+
93+
return {
94+
starknetId: undefined,
95+
starknetIdAvatar: undefined,
96+
}
97+
}

0 commit comments

Comments
 (0)