Skip to content
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

[MS-323][wallet] feat: PnL UI and API integration #605

Merged
merged 31 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1c1e004
PnL with mock data
ltminhthu May 19, 2024
857cbb4
adjust some details of value pnl
ltminhthu May 20, 2024
04fcf34
fix some details in token item pnl
ltminhthu May 25, 2024
a190a22
Adding query P&L token
kayx86 May 26, 2024
7fa2d18
Add field to TokenMetadata
kayx86 May 26, 2024
577057f
Remove pnl24h in TokenMetadata
kayx86 May 26, 2024
2cde3c9
Update schema
kayx86 May 26, 2024
ff45dd1
add type pnl24h to TokenInfo
kayx86 May 31, 2024
94aaa52
Add pnl24h
kayx86 Jun 3, 2024
861bb69
Add pnl to Token struct
kayx86 Jun 3, 2024
94873a0
Merge pull request #570 from cocrafts/dev
tanlethanh Jun 15, 2024
fb40042
Merge pull request #578 from cocrafts/dev
tanlethanh Jun 29, 2024
1177d57
Merge pull request #586 from cocrafts/dev
tanlethanh Jul 7, 2024
50fed57
Merge pull request #589 from cocrafts/dev
tanlethanh Jul 7, 2024
e042679
Merge pull request #590 from cocrafts/dev
tanlethanh Jul 7, 2024
0dd6110
Merge pull request #591 from cocrafts/staging
tanlethanh Jul 7, 2024
42d48c6
Merge pull request #597 from cocrafts/dev
tanlethanh Jul 15, 2024
194b4b5
Merge pull request #598 from cocrafts/staging
tanlethanh Jul 15, 2024
3822701
Merge branch 'dev' into staging
tanlethanh Jul 15, 2024
c5198d8
Merge branch 'staging'
tanlethanh Jul 15, 2024
2418488
Merge pull request #599 from cocrafts/dev
tanlethanh Jul 15, 2024
60ccffd
Merge pull request #600 from cocrafts/staging
tanlethanh Jul 15, 2024
62f2e27
Merge branch 'main' into ltminhthu/profit-and-lost
hnimtadd Jul 31, 2024
3806c6e
feat: sync pnl schema from server
hnimtadd Jul 31, 2024
d53d984
feat: sync changes from types
hnimtadd Jul 31, 2024
5519006
feat-wip: working with PnL UI
hnimtadd Jul 31, 2024
942f658
chore: make tokenPnL render with 4 precision
hnimtadd Aug 1, 2024
f7aa19d
fix: fix render wrong pnl percentage container
hnimtadd Aug 8, 2024
f295234
feat: calc tokenPnL
hnimtadd Aug 1, 2024
66398b1
chore: remove hardcoded address
hnimtadd Aug 8, 2024
7a34a91
chore: Merge branch 'dev' into check_old_pnl-1
hnimtadd Aug 10, 2024
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,5 @@ cli/tokens/**/cache.json
cli/tokens/**/config.json
cli/tokens/**/sugar.log

playground
playground
.nvmrc
28 changes: 26 additions & 2 deletions apps/wallet/src/components/TokenList/Item.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { FC } from 'react';
import type { StyleProp, ViewStyle } from 'react-native';
import { Image, StyleSheet } from 'react-native';
import type { TokenPnL } from '@walless/core';
import { Hoverable, Text, View } from '@walless/gui';
import type { TokenDocument } from '@walless/store';
import assets from 'utils/assets';
Expand All @@ -10,22 +11,31 @@ interface Props {
style?: StyleProp<ViewStyle>;
token: TokenDocument;
onPress?: () => void;
tokenPnL?: TokenPnL;
}

export const TokenItem: FC<Props> = ({ style, token, onPress }) => {
export const TokenItem: FC<Props> = ({ style, token, onPress, tokenPnL }) => {
const pnl = tokenPnL?.priceChangePercentage24H || 0;
const { symbol, image, quotes, balance } = token;
const unitQuote = quotes?.usd;
const totalQuote = unitQuote && unitQuote * balance;
const iconSource = image ? { uri: image } : assets.misc.unknownToken;

const itemName = symbol || 'Unknown';
const isLost = pnl && pnl < 0;

return (
<Hoverable style={[styles.container, style]} onPress={onPress}>
<Image style={styles.iconImg} source={iconSource} resizeMode="cover" />
<View style={styles.infoContainer}>
<Text style={styles.primaryText}>{itemName}</Text>
<Text style={styles.secondaryText}>{formatQuote(unitQuote)}</Text>
<View style={styles.unitQuoteContainer}>
<Text style={styles.secondaryText}>{formatQuote(unitQuote)}</Text>
<Text style={isLost ? styles.lostText : styles.profitText}>
{isLost ? '-' : '+'}{' '}
{Math.abs(Math.round(pnl * 10000) / 10000 ?? 0)}%
</Text>
</View>
</View>
<View style={styles.balanceContainer}>
<Text style={styles.primaryText}>{balance}</Text>
Expand Down Expand Up @@ -75,4 +85,18 @@ const styles = StyleSheet.create({
color: '#566674',
fontSize: 13,
},
unitQuoteContainer: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
},
profitText: {
color: '#60C591',
fontSize: 10,
alignSelf: 'flex-end',
},
lostText: {
color: '#AE3939',
fontSize: 10,
},
});
1 change: 1 addition & 0 deletions apps/wallet/src/components/TokenList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const TokenList = <T extends Token>({
index === tokens.length - 1 && styles.lastItem,
]}
onPress={handlePressItem}
tokenPnL={item.pnl}
/>
);
};
Expand Down
113 changes: 113 additions & 0 deletions apps/wallet/src/components/TotalPnL.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import type { FC } from 'react';
import { StyleSheet } from 'react-native';
import { Text, View } from '@walless/gui';

interface Props {
value: number;
percentage: number;
isDarkTheme: boolean;
}

const TotalPnL: FC<Props> = ({ value, percentage, isDarkTheme = false }) => {
const isLost = value < 0;

return (
<View style={styles.container}>
<Text
style={
isDarkTheme
? isLost
? styles.darkThemeLostValue
: styles.darkThemeProfitValue
: isLost
? styles.lightThemeLostValue
: styles.lightThemeProfitValue
}
>
{isLost ? `-$${-value}` : `+$${value}`}
</Text>
<View
style={[
styles.percentageContainer,
isDarkTheme
? isLost
? styles.darkThemeLostPercentageContainer
: styles.darkThemeProfitPercentageContainer
: isLost
? styles.lightThemeLostPercentageContainer
: styles.lightThemeProfitPercentageContainer,
]}
>
<Text
style={
isDarkTheme
? isLost
? styles.darkThemeLostPercentage
: styles.darkThemeProfitPercentage
: isLost
? styles.lightThemeLostPercentage
: styles.lightThemeProfitPercentage
}
>
{!isLost && '+'}
{percentage}%
</Text>
</View>
</View>
);
};

export default TotalPnL;

const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
},
lightThemeProfitValue: {
color: '#60C591',
fontSize: 20,
},
darkThemeProfitValue: {
color: '#ffffff',
fontSize: 20,
},
lightThemeLostValue: {
color: '#AE3939',
fontSize: 20,
},
darkThemeLostValue: {
color: '#ffffff',
fontSize: 20,
},
lightThemeProfitPercentage: {
color: '#60C591',
},
lightThemeLostPercentage: {
color: '#AE3939',
},
darkThemeProfitPercentage: {
color: '#ffffff',
},
darkThemeLostPercentage: {
color: '#ffffff',
},
percentageContainer: {
borderRadius: 4,
paddingVertical: 4,
paddingHorizontal: 8,
},
lightThemeProfitPercentageContainer: {
backgroundColor: '#AE393933',
},
darkThemeProfitPercentageContainer: {
backgroundColor: '#2A9960',
},
lightThemeLostPercentageContainer: {
backgroundColor: '#AE393933',
},
darkThemeLostPercentageContainer: {
backgroundColor: '#941200',
},
});
2 changes: 2 additions & 0 deletions apps/wallet/src/engine/runners/solana/subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ const handleInitAccountOnLogsChange = async (
const quotes = await getTokenQuotes([
{ address: token.mint, network: token.network },
]);
token.pnl =
quotes[makeHashId({ address: token.mint, network: token.network })].pnl;
token.quotes =
quotes[
makeHashId({ address: token.mint, network: token.network })
Expand Down
12 changes: 7 additions & 5 deletions apps/wallet/src/engine/runners/solana/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { LAMPORTS_PER_SOL } from '@solana/web3.js';
import type { NetworkCluster, SolanaToken } from '@walless/core';
import { Networks } from '@walless/core';
import type { TokenDocument } from '@walless/store';
import { getTokenQuote } from 'utils/api';
import { getTokenQuote as getTokenInfo } from 'utils/api';
import { solMint, wrappedSolMint } from 'utils/constants';
import { addTokenToStorage } from 'utils/storage';

Expand All @@ -23,11 +23,12 @@ export const queryTokens = async (
cluster,
wallet,
).then(async (doc) => {
const quotes = await getTokenQuote({
const tokenInfo = await getTokenInfo({
address: wrappedSolMint,
network: doc.network,
});
doc.quotes = quotes?.quotes;
doc.quotes = tokenInfo?.quotes;
doc.pnl = tokenInfo?.pnl;

await addTokenToStorage(doc);
return doc;
Expand All @@ -42,11 +43,12 @@ export const queryTokens = async (
account,
);

const quotes = await getTokenQuote({
const tokenInfo = await getTokenInfo({
address: doc.mint,
network: doc.network,
});
doc.quotes = quotes?.quotes;
doc.quotes = tokenInfo?.quotes;
doc.pnl = tokenInfo?.pnl;

await addTokenToStorage(doc);
return doc;
Expand Down
43 changes: 24 additions & 19 deletions apps/wallet/src/features/Home/TokenValue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,43 @@ import type { FC } from 'react';
import { StyleSheet, View } from 'react-native';
import { Hoverable, Text } from '@walless/gui';
import { Eye, EyeOff } from '@walless/icons';
import { useSettings } from 'utils/hooks';
import TotalPnL from 'components/TotalPnL';
import { useSettings, useTokens } from 'utils/hooks';

interface Props {
value: number;
}

const TokenValue: FC<Props> = ({ value }) => {
const { setting, setPrivacy } = useSettings();
const balanceTextStyle = [
styles.balanceText,
setting.hideBalance && styles.protectedBalance,
];
const { valuation, pnl } = useTokens();

const handleToggleTokenValue = async () => {
setPrivacy(!setting.hideBalance);
};
const pnlRates = (pnl / (valuation != 0 ? valuation : 1)) * 100;

return (
<View style={styles.container}>
<Text style={styles.headingText}>Token value</Text>
<View style={styles.balanceContainer}>
<Text style={balanceTextStyle}>
{setting.hideBalance ? '****' : '$' + value.toFixed(2)}
</Text>
<Hoverable onPress={handleToggleTokenValue}>
{setting.hideBalance ? (
<Eye size={20} color="#566674" />
) : (
<EyeOff size={20} color="#566674" />
)}
</Hoverable>
<View style={styles.balanceAndPercentageContainer}>
<View style={styles.balanceContainer}>
<Text style={styles.balanceText}>
{setting.hideBalance ? '****' : '$' + value.toFixed(2)}
</Text>
<Hoverable onPress={handleToggleTokenValue}>
{setting.hideBalance ? (
<Eye size={20} color="#566674" />
) : (
<EyeOff size={20} color="#566674" />
)}
</Hoverable>
</View>
<TotalPnL
value={Math.round(pnl * 100) / 100}
percentage={Math.round(pnlRates * 100) / 100}
isDarkTheme={true}
/>
</View>
</View>
);
Expand All @@ -53,14 +59,13 @@ const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
gap: 12,
minHeight: 84,
},
balanceText: {
color: '#FFFFFF',
fontSize: 40,
fontWeight: '500',
},
protectedBalance: {
paddingTop: 16,
balanceAndPercentageContainer: {
alignItems: 'center',
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@ import type { FC } from 'react';
import { StyleSheet } from 'react-native';
import { Hoverable, Text, View } from '@walless/gui';
import { Eye, EyeOff } from '@walless/icons';
import TotalPnL from 'components/TotalPnL';
import { getValuationDisplay } from 'utils/helper';

interface Props {
onHide: (next: boolean) => void;
hideBalance: boolean;
valuation?: number;
pnl?: number;
}

export const WalletBalance: FC<Props> = ({
onHide,
hideBalance,
valuation = 0,
pnl = 0,
}) => {
const balanceTextStyle = [
styles.balanceText,
hideBalance && styles.protectedBalance,
];
const pnlRates = (pnl / (valuation != 0 ? valuation : 1)) * 100;

return (
<View style={styles.container}>
Expand All @@ -30,6 +34,13 @@ export const WalletBalance: FC<Props> = ({
{getValuationDisplay(valuation, hideBalance)}
</Text>
</View>
<View style={styles.pnLContainer}>
<TotalPnL
value={Math.round(pnl * 100) / 100}
percentage={Math.round(pnlRates * 100) / 100}
isDarkTheme={false}
/>
</View>
</View>
);
};
Expand All @@ -44,7 +55,6 @@ const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
paddingLeft: 5,
paddingBottom: 20,
gap: 10,
},
balanceText: {
Expand All @@ -60,4 +70,7 @@ const styles = StyleSheet.create({
opacity: 0.6,
marginLeft: 34,
},
pnLContainer: {
paddingLeft: 10,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface Props {
item: PublicKeyDocument;
skin: CardSkin;
valuation?: number;
pnl?: number;
hideBalance: boolean;
onCopyAddress?: (value: string) => void;
onChangePrivateSetting?: (value: boolean) => void;
Expand All @@ -26,6 +27,7 @@ export const WalletCard: FC<Props> = ({
item,
skin,
valuation = 0,
pnl = 0,
hideBalance,
onCopyAddress,
onChangePrivateSetting,
Expand Down Expand Up @@ -58,6 +60,7 @@ export const WalletCard: FC<Props> = ({
hideBalance={hideBalance}
valuation={valuation}
onHide={handleHide}
pnl={pnl}
/>
{skin.largeIconSrc && (
<View pointerEvents="none" style={styles.markContainer}>
Expand Down
Loading
Loading