-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Connect wallet with ledger live app #36
Changes from all commits
65ab5ab
0b6c887
2325e13
404d3c9
10715fd
647f359
89a597e
fd9a657
35acd4c
086bd7d
30c5530
d866e8f
2a7c10f
83b2799
c1d1bb8
5a7a29c
168f26a
6a89cc7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
VITE_USE_TESTNET=true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import React from "react" | ||
import { Box, Button, Image, Text } from "@chakra-ui/react" | ||
import { Account } from "@ledgerhq/wallet-api-client" | ||
import BitcoinIcon from "../../assets/bitcoin.svg" | ||
import EthereumIcon from "../../assets/ethereum.svg" | ||
import InfoIcon from "../../assets/info.svg" | ||
import { BITCOIN } from "../../constants" | ||
import { | ||
useRequestBitcoinAccount, | ||
useRequestEthereumAccount, | ||
useWalletContext, | ||
} from "../../hooks" | ||
import { formatSatoshiAmount, truncateAddress } from "../../utils" | ||
|
||
export type ConnectButtonsProps = { | ||
leftIcon: string | ||
rightIcon: string | ||
account: Account | undefined | ||
requestAccount: () => Promise<void> | ||
} | ||
|
||
function ConnectButton({ | ||
leftIcon, | ||
rightIcon, | ||
account, | ||
requestAccount, | ||
}: ConnectButtonsProps) { | ||
const styles = !account ? { color: "error", borderColor: "error" } : undefined | ||
return ( | ||
<Button | ||
variant="outline" | ||
sx={styles} | ||
leftIcon={<Image src={leftIcon} />} | ||
// TODO: Add a tooltip here | ||
rightIcon={!account ? <Image src={rightIcon} /> : undefined} | ||
onClick={requestAccount} | ||
> | ||
{account ? truncateAddress(account.address) : "Not connected"} | ||
</Button> | ||
) | ||
} | ||
|
||
export default function ConnectWallet() { | ||
const { requestAccount: requestBitcoinAccount } = useRequestBitcoinAccount() | ||
const { requestAccount: requestEthereumAccount } = useRequestEthereumAccount() | ||
const { btcAccount, ethAccount } = useWalletContext() | ||
|
||
return ( | ||
<Box display="flex" alignItems="center" gap="4"> | ||
<Box display="flex" gap="2"> | ||
<Text>Balance</Text> | ||
<Text as="b"> | ||
{!btcAccount || btcAccount?.balance.isZero() | ||
? "0.00" | ||
: formatSatoshiAmount(btcAccount.balance.toString())} | ||
</Text> | ||
<Text>{BITCOIN.symbol}</Text> | ||
</Box> | ||
<ConnectButton | ||
leftIcon={BitcoinIcon} | ||
rightIcon={InfoIcon} | ||
account={btcAccount} | ||
requestAccount={async () => { | ||
await requestBitcoinAccount() | ||
}} | ||
/> | ||
<ConnectButton | ||
leftIcon={EthereumIcon} | ||
rightIcon={InfoIcon} | ||
account={ethAccount} | ||
requestAccount={async () => { | ||
await requestEthereumAccount() | ||
}} | ||
/> | ||
</Box> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from "react" | ||
import { Box } from "@chakra-ui/react" | ||
import ConnectWallet from "./ConnectWallet" | ||
|
||
export default function Navbar() { | ||
return ( | ||
<Box p={4} display="flex" justifyContent="end"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just flagging, we can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the next PR I use here |
||
<ConnectWallet /> | ||
</Box> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Currency } from "../types" | ||
|
||
export const BITCOIN: Currency = { | ||
name: "Bitcoin", | ||
symbol: "BTC", | ||
decimals: 8, | ||
} | ||
|
||
export const ETHEREUM: Currency = { | ||
name: "Ethereum", | ||
symbol: "ETH", | ||
decimals: 18, | ||
} | ||
|
||
export const CURRENCY_ID_BITCOIN = | ||
import.meta.env.VITE_USE_TESTNET === "true" ? "bitcoin_testnet" : "bitcoin" | ||
|
||
export const CURRENCY_ID_ETHEREUM = | ||
import.meta.env.VITE_USE_TESTNET === "true" ? "ethereum_goerli" : "ethereum" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./currency" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { Account } from "@ledgerhq/wallet-api-client" | ||
import React, { createContext, useMemo, useState } from "react" | ||
|
||
type WalletContextValue = { | ||
btcAccount: Account | undefined | ||
setBtcAccount: React.Dispatch<React.SetStateAction<Account | undefined>> | ||
ethAccount: Account | undefined | ||
setEthAccount: React.Dispatch<React.SetStateAction<Account | undefined>> | ||
} | ||
|
||
export const WalletContext = createContext<WalletContextValue | undefined>( | ||
undefined, | ||
) | ||
|
||
export function WalletContextProvider({ | ||
children, | ||
}: { | ||
children: React.ReactNode | ||
}): React.ReactElement { | ||
const [btcAccount, setBtcAccount] = useState<Account | undefined>(undefined) | ||
const [ethAccount, setEthAccount] = useState<Account | undefined>(undefined) | ||
|
||
const contextValue: WalletContextValue = useMemo<WalletContextValue>( | ||
() => ({ | ||
btcAccount, | ||
setBtcAccount, | ||
ethAccount, | ||
setEthAccount, | ||
}), | ||
[btcAccount, setBtcAccount, ethAccount, setEthAccount], | ||
) | ||
|
||
return ( | ||
<WalletContext.Provider value={contextValue}> | ||
{children} | ||
</WalletContext.Provider> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./WalletContext" | ||
export * from "./LedgerWalletAPIProvider" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
export * from "./useDetectThemeMode" | ||
export * from "./useRequestBitcoinAccount" | ||
export * from "./useRequestEthereumAccount" | ||
export * from "./useWalletContext" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { useRequestAccount } from "@ledgerhq/wallet-api-client-react" | ||
import { useCallback, useContext, useEffect } from "react" | ||
import { CURRENCY_ID_BITCOIN } from "../constants" | ||
import { UseRequestAccountReturn } from "../types" | ||
import { WalletContext } from "../contexts" | ||
|
||
export function useRequestBitcoinAccount(): UseRequestAccountReturn { | ||
const walletContext = useContext(WalletContext) | ||
const { account, requestAccount } = useRequestAccount() | ||
|
||
useEffect(() => { | ||
walletContext?.setBtcAccount(account || undefined) | ||
}, [account, walletContext]) | ||
|
||
const requestBitcoinAccount = useCallback(async () => { | ||
await requestAccount({ currencyIds: [CURRENCY_ID_BITCOIN] }) | ||
}, [requestAccount]) | ||
|
||
return { requestAccount: requestBitcoinAccount } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { useRequestAccount } from "@ledgerhq/wallet-api-client-react" | ||
import { useCallback, useContext, useEffect } from "react" | ||
import { CURRENCY_ID_ETHEREUM } from "../constants" | ||
import { UseRequestAccountReturn } from "../types" | ||
import { WalletContext } from "../contexts" | ||
|
||
export function useRequestEthereumAccount(): UseRequestAccountReturn { | ||
const walletContext = useContext(WalletContext) | ||
const { account, requestAccount } = useRequestAccount() | ||
|
||
useEffect(() => { | ||
walletContext?.setEthAccount(account || undefined) | ||
}, [account, walletContext]) | ||
|
||
const requestEthereumAccount = useCallback(async () => { | ||
await requestAccount({ currencyIds: [CURRENCY_ID_ETHEREUM] }) | ||
}, [requestAccount]) | ||
|
||
return { requestAccount: requestEthereumAccount } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { useContext } from "react" | ||
import { WalletContext } from "../contexts" | ||
|
||
export function useWalletContext() { | ||
const context = useContext(WalletContext) | ||
|
||
if (!context) { | ||
throw new Error("WalletContext used outside of WalletContext component") | ||
} | ||
|
||
return context | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export type Currency = { | ||
name: string | ||
symbol: string | ||
decimals: number | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./ledger-live-app" | ||
export * from "./currency" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think would be great if we could create a reusable component where we just pass value and currency and all this stuff will be handled in a component. Otherwise, we always duplicate logic when displaying the amount. Ofc we can address it in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you're right. Let's do it in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's fix it in #56.