From 889aac2d739edb853e94d6d2a7fa42b3b3547618 Mon Sep 17 00:00:00 2001 From: jordankzf Date: Mon, 20 Jan 2025 15:22:36 +0800 Subject: [PATCH 1/8] Prevent broken ME swaps --- src/app/screens/swap/quoteSummary/index.tsx | 2 +- src/locales/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/screens/swap/quoteSummary/index.tsx b/src/app/screens/swap/quoteSummary/index.tsx index 48191544..54676122 100644 --- a/src/app/screens/swap/quoteSummary/index.tsx +++ b/src/app/screens/swap/quoteSummary/index.tsx @@ -329,7 +329,7 @@ export default function QuoteSummary({ Date: Mon, 20 Jan 2025 15:27:41 +0800 Subject: [PATCH 2/8] Potential fix for double Runes swap bug --- src/app/screens/swap/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/screens/swap/index.tsx b/src/app/screens/swap/index.tsx index 6eacb4f1..d570e2fd 100644 --- a/src/app/screens/swap/index.tsx +++ b/src/app/screens/swap/index.tsx @@ -216,9 +216,10 @@ export default function SwapScreen() { setInputError(''); setAmount(''); setHasQuoteError(false); + const newFrom = toToken; const newTo = fromToken; - setFromToken(newFrom); + setToToken(newTo); if (newFrom?.principal !== 'BTC') { @@ -228,6 +229,8 @@ export default function SwapScreen() { if (matchingToken) { setFromToken(matchingToken); + } else { + setFromToken(newFrom); } } }; From 127c40d682556ca485913606d5a7432d9238455e Mon Sep 17 00:00:00 2001 From: jordankzf Date: Tue, 21 Jan 2025 15:54:01 +0800 Subject: [PATCH 3/8] Fix STX wild fromTokenUsdValue numbers --- src/app/screens/swap/mixpanel.ts | 56 ++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/app/screens/swap/mixpanel.ts b/src/app/screens/swap/mixpanel.ts index 093efb6b..475973d5 100644 --- a/src/app/screens/swap/mixpanel.ts +++ b/src/app/screens/swap/mixpanel.ts @@ -8,14 +8,21 @@ import { } from '@secretkeylabs/xverse-core'; import BigNumber from 'bignumber.js'; -const getFiatEquivalent = ( - token: FungibleToken | undefined, - amount: BigNumber, - btcUsdRate: BigNumber, - runeFloorPrice?: BigNumber, - stxBtcRate?: BigNumber, - stxTokenFiatValue?: BigNumber, -) => { +const getFiatEquivalent = ({ + token, + amount, + btcUsdRate, + runeFloorPrice, + stxBtcRate, + stxTokenFiatValue, +}: { + token: FungibleToken | undefined; + amount: BigNumber; + btcUsdRate: BigNumber; + runeFloorPrice?: BigNumber; + stxBtcRate?: BigNumber; + stxTokenFiatValue?: BigNumber; +}) => { if (!token || amount.isZero()) { return undefined; } @@ -65,17 +72,29 @@ const getSwapsMixpanelProperties = ({ const to = toToken?.principal === 'BTC' ? 'BTC' : toToken?.name ?? toToken?.ticker; const fromTokenAmount = amount; - - const fromTokenUsdValue = getFiatEquivalent( - fromToken, - new BigNumber(amount), - btcUsdRate, - fromRuneFloorPrice, - stxBtcRate, - fromStxTokenFiatValue, - ); - const toTokenAmount = quote?.receiveAmount; + let rawFromTokenUsdValue; + + let fromTokenUsdValue = getFiatEquivalent({ + token: fromToken, + amount: new BigNumber(amount), + btcUsdRate, + runeFloorPrice: fromRuneFloorPrice, + stxBtcRate, + stxTokenFiatValue: fromStxTokenFiatValue, + }); + + // Derive fromTokenUsdValue from received STX amount + if (toToken?.principal === 'STX' && quote?.receiveAmount && stxBtcRate) { + rawFromTokenUsdValue = fromTokenUsdValue; + + fromTokenUsdValue = getFiatEquivalent({ + token: toToken, + amount: new BigNumber(quote.receiveAmount), + btcUsdRate, + stxBtcRate, + }); + } const fromPrincipal = fromToken?.protocol === 'stacks' ? fromToken?.principal : undefined; const toPrincipal = toToken?.protocol === 'stacks' ? toToken?.principal : undefined; @@ -89,6 +108,7 @@ const getSwapsMixpanelProperties = ({ ...(fromTokenAmount ? { fromTokenAmount } : {}), ...{ fromTokenUsdValue: fromTokenUsdValue ?? 0 }, ...(toTokenAmount ? { toTokenAmount } : {}), + ...(rawFromTokenUsdValue ? { rawFromTokenUsdValue } : {}), }; }; From 4d2eaf1c3b9dd91fe24a2e501eca3bae9168eb3f Mon Sep 17 00:00:00 2001 From: jordankzf Date: Tue, 21 Jan 2025 15:54:20 +0800 Subject: [PATCH 4/8] Revert changes --- src/app/screens/swap/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/screens/swap/index.tsx b/src/app/screens/swap/index.tsx index d570e2fd..63853c56 100644 --- a/src/app/screens/swap/index.tsx +++ b/src/app/screens/swap/index.tsx @@ -221,6 +221,7 @@ export default function SwapScreen() { const newTo = fromToken; setToToken(newTo); + setFromToken(newFrom); if (newFrom?.principal !== 'BTC') { const matchingToken = fromTokens.find( @@ -229,8 +230,6 @@ export default function SwapScreen() { if (matchingToken) { setFromToken(matchingToken); - } else { - setFromToken(newFrom); } } }; From 691f1cb3b06355a9da06e4ae168e846efe8893db Mon Sep 17 00:00:00 2001 From: jordankzf Date: Tue, 21 Jan 2025 17:04:27 +0800 Subject: [PATCH 5/8] Fix DOG <-> DOG duplicate swap --- src/app/screens/swap/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/screens/swap/index.tsx b/src/app/screens/swap/index.tsx index 63853c56..e6937414 100644 --- a/src/app/screens/swap/index.tsx +++ b/src/app/screens/swap/index.tsx @@ -146,11 +146,11 @@ export default function SwapScreen() { const { data: fromRuneFloorPrice } = useRuneFloorPriceQuery(fromToken?.name ?? ''); useEffect(() => { - if (defaultFrom) { + if (defaultFrom && !fromToken) { const token = coinsMasterList.find((coin) => coin.principal === defaultFrom); setFromToken(token); } - if (defaultTo) { + if (defaultTo && !toToken) { const token = coinsMasterList.find((coin) => coin.principal === defaultTo); setToToken(token); } From f16ace2f6ea81e3a6d22d304d36a622650ef262e Mon Sep 17 00:00:00 2001 From: jordankzf Date: Tue, 21 Jan 2025 17:09:59 +0800 Subject: [PATCH 6/8] Undo inversion --- src/app/screens/swap/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/screens/swap/index.tsx b/src/app/screens/swap/index.tsx index e6937414..4c80ab37 100644 --- a/src/app/screens/swap/index.tsx +++ b/src/app/screens/swap/index.tsx @@ -220,8 +220,8 @@ export default function SwapScreen() { const newFrom = toToken; const newTo = fromToken; - setToToken(newTo); setFromToken(newFrom); + setToToken(newTo); if (newFrom?.principal !== 'BTC') { const matchingToken = fromTokens.find( From 52461e784f1df6f82b0b0434d1b0013c3caeca0d Mon Sep 17 00:00:00 2001 From: Edu Date: Tue, 28 Jan 2025 17:27:03 +0100 Subject: [PATCH 7/8] Fix connection screen (#899) --- .../connect/connectionRequest/dappLogo.tsx | 23 ++++++++---- .../connect/connectionRequest/host.tsx | 30 +++++++++++++--- .../connectionRequest/index.styles.tsx | 12 +++++++ .../connect/connectionRequest/index.tsx | 35 ++++++++++++++----- 4 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/app/screens/connect/connectionRequest/dappLogo.tsx b/src/app/screens/connect/connectionRequest/dappLogo.tsx index 7499a1f9..4f79d696 100644 --- a/src/app/screens/connect/connectionRequest/dappLogo.tsx +++ b/src/app/screens/connect/connectionRequest/dappLogo.tsx @@ -1,24 +1,33 @@ /* eslint-disable import/prefer-default-export */ import styled from 'styled-components'; -// eslint-disable-next-line import/prefer-default-export -const Logo = styled('img')((props) => ({ - maxHeight: 48, - maxWidth: 48, - paddingBlockStart: props.theme.space.xxl, +const ImgContainer = styled('div')({ + display: 'flex', + justifyContent: 'center', +}); + +const Img = styled('img')((props) => ({ + height: 48, + width: 48, alignSelf: 'center', + objectFit: 'contain', + borderRadius: props.theme.radius(1), })); type Props = { /** * Any source that can be used as an image source. */ - src?: string; + src?: string | null; }; export function DappLogo({ src }: Props) { if (!src) { return null; } - return ; + return ( + + Dapp Logo + + ); } diff --git a/src/app/screens/connect/connectionRequest/host.tsx b/src/app/screens/connect/connectionRequest/host.tsx index 83e0f40c..d6e4ae83 100644 --- a/src/app/screens/connect/connectionRequest/host.tsx +++ b/src/app/screens/connect/connectionRequest/host.tsx @@ -1,3 +1,6 @@ +import { permissions } from '@secretkeylabs/xverse-core'; +import { useTranslation } from 'react-i18next'; + import styled from 'styled-components'; /* eslint-disable import/prefer-default-export */ @@ -8,12 +11,29 @@ const Container = styled('div')((props) => ({ })); type Props = { - url: ConstructorParameters[0]; + origin: string; }; -export function Host({ url }: Props) { - const parsedUrl = new URL(url); - const { host } = parsedUrl; +export function Host({ origin }: Props) { + const { t } = useTranslation('translation', { keyPrefix: 'AUTH_REQUEST_SCREEN' }); - return {host}; + const { nameFromOrigin } = permissions.utils.originName; + const name = nameFromOrigin(origin); + + const dappName = (() => { + // This means there was no name found for the origin, and the host is used + // as the name instead. + if (name === origin) { + const parsedUrl = new URL(origin); + return parsedUrl.host; + } + + return name; + })(); + + return ( + + {t('REQUEST_TOOLTIP')} {dappName} + + ); } diff --git a/src/app/screens/connect/connectionRequest/index.styles.tsx b/src/app/screens/connect/connectionRequest/index.styles.tsx index acad298d..a0789ae6 100644 --- a/src/app/screens/connect/connectionRequest/index.styles.tsx +++ b/src/app/screens/connect/connectionRequest/index.styles.tsx @@ -56,3 +56,15 @@ export const RequestMessage = styled.p((props) => ({ wordWrap: 'break-word', marginTop: props.theme.space.l, })); + +export const DappInfoContainer = styled('div')((props) => ({ + display: 'flex', + flexDirection: 'column', + rowGap: props.theme.space.m, +})); + +export const DappInfoTextContainer = styled('div')((props) => ({ + display: 'flex', + flexDirection: 'column', + rowGap: props.theme.space.xxs, +})); diff --git a/src/app/screens/connect/connectionRequest/index.tsx b/src/app/screens/connect/connectionRequest/index.tsx index 2a58171e..51389685 100644 --- a/src/app/screens/connect/connectionRequest/index.tsx +++ b/src/app/screens/connect/connectionRequest/index.tsx @@ -23,11 +23,15 @@ import { AddressBoxContainer, Container, ContentContainer, + DappInfoContainer, + DappInfoTextContainer, PermissionDescriptionsContainer, RequestMessage, } from './index.styles'; import useSelectedAccount from '@hooks/useSelectedAccount'; +import { getAppIconFromWebManifest, safePromise } from '@secretkeylabs/xverse-core'; +import { useQuery } from '@tanstack/react-query'; import AddressPurposeBox from '../addressPurposeBox'; import * as Permissions from './permissions'; import { SelectAccountPrompt } from './selectAccount'; @@ -45,6 +49,17 @@ function ConnectionRequestInner({ data, context }: ConnectionRequestInnerProps) }, []); const handleAccept = useMakeHandleAccept({ context, data }); + const { data: appIconSrc } = useQuery({ + queryKey: ['appIcon', context.origin], + queryFn: async () => { + const [error, icon] = await safePromise(getAppIconFromWebManifest(context.origin)); + + if (error) return null; + + return icon; + }, + }); + const AddressPurposeRow = useCallback( (purpose: AddressPurpose) => { if (purpose === AddressPurpose.Payment) { @@ -88,14 +103,18 @@ function ConnectionRequestInner({ data, context }: ConnectionRequestInnerProps) return ( - - - <Host url={context.origin} /> - {(data as ConnectRequestMessage).params?.message && ( - <RequestMessage> - {(data as ConnectRequestMessage).params?.message?.substring(0, 80)} - </RequestMessage> - )} + <DappInfoContainer> + <DappLogo src={appIconSrc} /> + <DappInfoTextContainer> + <Title /> + <Host origin={context.origin} /> + {(data as ConnectRequestMessage).params?.message && ( + <RequestMessage> + {(data as ConnectRequestMessage).params?.message?.substring(0, 80)} + </RequestMessage> + )} + </DappInfoTextContainer> + </DappInfoContainer> <AccountSwitcherContainer> <SelectAccountPrompt /> </AccountSwitcherContainer> From 25bead5c968c42d5c84d028ec0e0f87a5dae9f18 Mon Sep 17 00:00:00 2001 From: Tim Man <tim@secretkeylabs.com> Date: Fri, 31 Jan 2025 18:47:13 +0800 Subject: [PATCH 8/8] chore: update pr template (#859) * chore: update pr template * chore: update pull request template after comments * chore: update PULL_REQUEST_TEMPLATE.md * chore: update PULL_REQUEST_TEMPLATE.md --------- Co-authored-by: Den <36603049+dhriaznov@users.noreply.github.com> --- .github/PULL_REQUEST_TEMPLATE.md | 100 +++++++++++++------------------ 1 file changed, 43 insertions(+), 57 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 504fed18..19fbcff5 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,63 +1,49 @@ -# πŸ”˜ PR Type - -What kind of change does this PR introduce? - -<!-- Please check the one that applies to this PR using "x". --> - -- [ ] Bugfix -- [ ] Enhancement -- [ ] Code style update (formatting, local variables) -- [ ] Refactoring (no functional changes, no API changes) -- [ ] Build related changes -- [ ] CI related changes -- [ ] Documentation content changes -- [ ] Other... Please describe: - -# πŸ“œ Background - -Provide a brief explanation of why this pull request is needed. Include the problem you are solving or the functionality you are adding. Reference any related issues. - -Issue Link: #[issue_number] -Context Link (if applicable): - -# πŸ”„ Changes - -Enumerate the changes made in this pull request, detailing what has been modified, added, or removed. Include technical details and implications if necessary. - -Impact: - -- Explain the broader impact of these changes. -- How it improves performance, fixes bugs, adds functionality, etc. - -# πŸ§ͺ E2E Test Result - -Include a screenshot of the e2e test result. - -`Run E2E Tests` -Our End-to-end (E2E) test suite is build with Playwright. To run the whole E2E test suite, run: -`npm run e2etest` - -If you only want to run the smoke test suite, run -`npm run e2etest:smoketest` - -If you want to run the e2e test in UI Mode: -`npm run e2etest:ui` - -To generate test report, run: -`npm run e2etest:report` +# Summary +Link to Linear issue (if not linked by bot): +- [ ] PR Title was updated to match: `<branch_type>: <descriptive, short message of what work was done> [<parent issue ID1>][<parent issue ID2>]` +[See Branch types here](https://www.notion.so/xverseapp/Xverse-Contributing-1215520b9dee80cb9497f6866a99e2f4?pvs=4#1365520b9dee80f78f9ecd9f0bb23a8f) # πŸ–Ό Screenshot / πŸ“Ή Video - Include screenshots or a video demonstrating the changes. This is especially helpful for UI changes. +| Before | After | +| :---: | :---: | +|<img width="50%" alt="" src="" />|<img width="50%" alt="" src="" />| -# βœ… Review checklist +# πŸ”„ Changes +- What has been added, modified removed? +- Where was the fix? +- Include technical details and implications if necessary. +- What xverse-core changes were included? -Please ensure the following are true before merging: +Impact: +- Explain the broader impact of these changes. +- Which areas of the application have been touched? +- What should the tester be aware of when testing? +- What can help the reviewer to be aware of when reading the code? +- What are the security implications? -- [ ] Code Style is consistent with the project guidelines. -- [ ] Code is readable and well-commented. -- [ ] No unnecessary or debugging code has been added. -- [ ] Security considerations have been taken into account. -- [ ] The change has been manually tested and works as expected. -- [ ] Breaking changes and their impacts have been considered and documented. -- [ ] Code does not introduce new technical debt or issues. +# πŸ” Testing Steps +1. How to set up testing of the changes +2. What steps to test +3. What is expected + +- [ ] Does this need QA? (If so, add the label `Ready for test`) [See considerations here](https://www.notion.so/xverseapp/Xverse-Contributing-1215520b9dee80cb9497f6866a99e2f4?pvs=4#13e5520b9dee80938816fa66214541db) + +# βœ… Author checklist (can be draft if not all ready) +Please ensure the following are true before submitting for review: +- [ ] The PR template has been filled. +- [ ] E2E tests or unit tests have been added/updated. +- [ ] The changes have been self-reviewed, and has been tidied up for an easy peer review. + - [ ] No debugging code + - [ ] No out of scope changes + - [ ] Should the PR be split up for easier review? +- [ ] The change has been manually tested and works as expected. +- [ ] The PR has been labeled/flagged for QA if necessary. + +# βœ… Reviewer checklist +- [ ] The changes follow the intended scope. +- [ ] Code is clean and readable. No anti-patterns, no code smells. +- [ ] Code structure maintains separation of concerns. +- [ ] Check that code does not belong in xverse-core, or also on mobile. +- [ ] E2E tests or unit tests have been added/updated. +- [ ] (If no QA) The PR has been manually tested and works as expected.