Skip to content

Commit

Permalink
feat: integrate solana token extensions into LLM
Browse files Browse the repository at this point in the history
  • Loading branch information
mikhd committed May 27, 2024
1 parent 1393e3a commit bb837a4
Show file tree
Hide file tree
Showing 8 changed files with 645 additions and 26 deletions.
15 changes: 12 additions & 3 deletions apps/ledger-live-mobile/src/families/solana/AccountSubHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import React from "react";
import { Trans } from "react-i18next";
import { SubAccount } from "@ledgerhq/types-live";
import { Box, Alert, Text } from "@ledgerhq/native-ui";
import { isTokenAccountFrozen } from "@ledgerhq/live-common/families/solana/logic";
import { isTokenAccountFrozen } from "@ledgerhq/live-common/families/solana/token";
import { SolanaAccount, SolanaTokenAccount } from "@ledgerhq/live-common/families/solana/types";
import AccountSubHeader from "~/components/AccountSubHeader";
import TokenExtensionsInfoBox from "./Token2022/TokenExtensionsInfoBox";

type Account = SolanaAccount | SolanaTokenAccount | SubAccount;

Expand All @@ -13,18 +14,26 @@ type Props = {
};

function SolanaAccountSubHeader({ account }: Props) {
const tokenExtensions =
account.type === "TokenAccount" ? (account as SolanaTokenAccount)?.extensions : undefined;
return (
<>
<AccountSubHeader family="Solana" team="Solana Labs" />
{isTokenAccountFrozen(account) && (
<Box mt={6}>
<Box mb={6}>
<Alert type="warning">
<Text variant="body">
<Trans i18nKey="solana.token.frozenStateWarning" />
</Text>
</Alert>
</Box>
)}
<AccountSubHeader family="Solana" team="Solana Labs" />
{!!tokenExtensions && (
<TokenExtensionsInfoBox
tokenAccount={account as SolanaTokenAccount}
extensions={tokenExtensions}
/>
)}
</>
);
}
Expand Down
66 changes: 44 additions & 22 deletions apps/ledger-live-mobile/src/families/solana/SendRowsFee.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { getAccountCurrency } from "@ledgerhq/live-common/account/index";
import { Transaction, TransactionStatus } from "@ledgerhq/live-common/generated/types";
import { TransactionStatus as SolanaTransactionStatus } from "@ledgerhq/live-common/families/solana/types";
import {
TransactionStatus as SolanaTransactionStatus,
Transaction as SolanaTransaction,
SolanaAccount,
} from "@ledgerhq/live-common/families/solana/types";
import { Account, AccountLike } from "@ledgerhq/types-live";
import { Text } from "@ledgerhq/native-ui";
import { CompositeScreenProps, useTheme } from "@react-navigation/native";
Expand All @@ -19,11 +23,13 @@ import { ScreenName } from "~/const";
import { SignTransactionNavigatorParamList } from "~/components/RootNavigator/types/SignTransactionNavigator";
import { SwapNavigatorParamList } from "~/components/RootNavigator/types/SwapNavigator";
import { useAccountUnit } from "~/hooks/useAccountUnit";
import TokenTransferFeesWarning from "./Token2022/TokenTransferFeesWarning";

type Props = {
account: AccountLike;
parentAccount?: Account | null;
transaction: Transaction;
setTransaction: (..._: Array<Transaction>) => void;
status?: TransactionStatus;
} & CompositeScreenProps<
| StackNavigatorProps<SendFundsNavigatorStackParamList, ScreenName.SendSummary>
Expand All @@ -32,38 +38,54 @@ type Props = {
StackNavigatorProps<BaseNavigatorStackParamList>
>;

export default function SolanaFeeRow({ account, parentAccount, status }: Props) {
export default function SolanaFeeRow({
account,
parentAccount,
status,
transaction,
setTransaction,
}: Props) {
const { colors } = useTheme();
const extraInfoFees = useCallback(() => {
Linking.openURL(urls.solana.supportPage);
}, []);

const fees = (status as SolanaTransactionStatus).estimatedFees;

const unit = useAccountUnit(account.type === "TokenAccount" ? parentAccount || account : account);
const isTokenAccount = account.type === "TokenAccount";
const unit = useAccountUnit(isTokenAccount ? parentAccount || account : account);
const currency = getAccountCurrency(account);

return (
<SummaryRow
onPress={extraInfoFees}
title={<Trans i18nKey="send.fees.title" />}
additionalInfo={
<View>
<ExternalLink size={12} color={colors.grey} />
</View>
}
>
<View style={{ alignItems: "flex-end" }}>
<View style={styles.accountContainer}>
<Text style={styles.valueText}>
<CurrencyUnitValue unit={unit} value={fees} />
<>
<SummaryRow
onPress={extraInfoFees}
title={<Trans i18nKey="send.fees.title" />}
additionalInfo={
<View>
<ExternalLink size={12} color={colors.grey} />
</View>
}
>
<View style={{ alignItems: "flex-end" }}>
<View style={styles.accountContainer}>
<Text style={styles.valueText}>
<CurrencyUnitValue unit={unit} value={fees} />
</Text>
</View>
<Text style={styles.countervalue} color="grey">
<CounterValue before="≈ " value={fees} currency={currency} />
</Text>
</View>
<Text style={styles.countervalue} color="grey">
<CounterValue before="≈ " value={fees} currency={currency} />
</Text>
</View>
</SummaryRow>
</SummaryRow>
{isTokenAccount && (
<TokenTransferFeesWarning
account={parentAccount as SolanaAccount}
tokenAccount={account}
transaction={transaction as SolanaTransaction}
setTransaction={setTransaction}
/>
)}
</>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import React from "react";
import { View } from "react-native";
import BigNumber from "bignumber.js";
import { Trans } from "react-i18next";
import { Flex, Text, Alert, Box } from "@ledgerhq/native-ui";
import {
SolanaTokenAccount,
SolanaTokenAccountExtensions,
} from "@ledgerhq/live-common/families/solana/types";
import { bpsToPercent } from "@ledgerhq/live-common/families/solana/token";
import TooltipLabel from "~/components/TooltipLabel";
import TokenExtensionsInfoDrawer from "./TokenExtensionsInfoDrawer";
import { InfoMedium } from "@ledgerhq/native-ui/assets/icons";
import CopyButton from "../shared/CopyButton";
import Button from "~/components/Button";

export default function TokenExtensionsInfoBox({
tokenAccount,
extensions,
}: {
tokenAccount: SolanaTokenAccount;
extensions: SolanaTokenAccountExtensions;
}) {
const [isDrawerOpen, setIsDrawerOpen] = React.useState(false);

const extensionsSize = Object.values(extensions);
if (!extensionsSize.length) return null;

function openDrawer() {
setIsDrawerOpen(true);
}

function closeDrawer() {
setIsDrawerOpen(false);
}

return (
<View>
<Alert showIcon={extensionsSize.length === 1} type="info">
<Flex width="100%" flexDirection="column" columnGap={4} alignItems="flex-start">
{!!extensions.nonTransferable && (
<Text>
<Trans i18nKey="solana.token.nonTransferable.notice" />
</Text>
)}

{!!extensions.interestRate && (
<TooltipLabel
label={
<Text>
<Trans
i18nKey="solana.token.interestRate.notice"
values={{
rate: BigNumber(extensions.interestRate.rateBps).div(100).toNumber(),
}}
/>
</Text>
}
tooltip={<Trans i18nKey="solana.token.interestRate.tooltipHint" />}
/>
)}

{extensions.permanentDelegate ? (
extensions.permanentDelegate.delegateAddress ? (
<TooltipLabel
label={
<Text>
<Trans i18nKey="solana.token.permanentDelegate.notice" />
</Text>
}
tooltip={
<Trans
i18nKey="solana.token.permanentDelegate.tooltipHint"
values={{ delegateAddress: extensions.permanentDelegate.delegateAddress }}
components={[
<CopyButton
key="SolanaCopyDelegateAddress"
copyString={extensions.permanentDelegate.delegateAddress}
/>,
]}
/>
}
/>
) : (
<TooltipLabel
label={
<Text>
<Trans i18nKey="solana.token.permanentDelegate.initializationNotice" />
</Text>
}
tooltip={
<Trans i18nKey="solana.token.permanentDelegate.initializationNoticeTooltipHint" />
}
/>
)
) : null}

{!!extensions.transferFee && (
<TooltipLabel
label={
<Text>
<Trans
i18nKey="solana.token.transferFees.notice"
values={{ fee: bpsToPercent(extensions.transferFee.feeBps) }}
/>
</Text>
}
tooltip={<Trans i18nKey="solana.token.transferFees.tooltipHint" />}
/>
)}

{extensions.transferHook ? (
extensions.transferHook.programAddress ? (
<TooltipLabel
label={
<Text>
<Trans i18nKey="solana.token.transferHook.notice" />
</Text>
}
tooltip={
<Trans
i18nKey="solana.token.transferHook.tooltipHint"
values={{ programAddress: extensions.transferHook.programAddress }}
components={[
<CopyButton
key="SolanaCopyHookAddress"
copyString={extensions.transferHook.programAddress}
/>,
]}
/>
}
/>
) : (
<Text>
<Trans i18nKey="solana.token.transferHook.initializationNotice" />
</Text>
)
) : null}

{!!extensions.requiredMemoOnTransfer && (
<TooltipLabel
label={
<Text>
<Trans i18nKey="solana.token.requiredMemoOnTransfer.notice" />
</Text>
}
tooltip={<Trans i18nKey="solana.token.requiredMemoOnTransfer.tooltipHint" />}
/>
)}
<Box width="100%" mt={4}>
{/* <Link type="color" size="medium" Icon={InfoMedium} onPress={openDrawer}>
<Trans i18nKey="common.moreInfo" />
</Link> */}
<Button
type="color"
size="small"
maxWidth={150}
alignSelf="center"
outline
Icon={InfoMedium}
onPress={openDrawer}
>
<Trans i18nKey="common.moreInfo" />
</Button>
</Box>
</Flex>
</Alert>
<TokenExtensionsInfoDrawer
extensions={extensions}
tokenAccount={tokenAccount}
isOpen={isDrawerOpen}
closeDrawer={closeDrawer}
/>
</View>
);
}
Loading

0 comments on commit bb837a4

Please sign in to comment.