From 20106ab0af90c5a68026e96adae05695195ad7b0 Mon Sep 17 00:00:00 2001 From: Fara Woolf Date: Wed, 11 Dec 2024 13:39:42 -0500 Subject: [PATCH] refactor: more cleanup --- config/wallet-config.json | 2 +- package.json | 2 +- pnpm-lock.yaml | 10 +- .../sbtc-deposit-status-item.tsx | 1 - .../sip10-token-asset-item.tsx | 1 + src/app/pages/home/home.tsx | 5 +- src/app/pages/swap/bitflow-swap-container.tsx | 5 +- .../components/use-swap-asset-list.tsx | 11 +- src/app/pages/swap/hooks/use-bitflow-swap.tsx | 14 +-- .../hooks/use-sbtc-deposit-transaction.tsx | 29 ++++- .../test-deposit-sbtc/test-deposit-sbtc.tsx | 109 ------------------ .../remote-config/remote-config.query.ts | 4 +- src/app/query/sbtc/sbtc-deposits.query.ts | 10 +- src/app/routes/app-routes.tsx | 3 - tests/page-object-models/swap.page.ts | 8 +- tests/specs/swap/swap.spec.ts | 13 ++- 16 files changed, 62 insertions(+), 165 deletions(-) delete mode 100644 src/app/pages/test-deposit-sbtc/test-deposit-sbtc.tsx diff --git a/config/wallet-config.json b/config/wallet-config.json index 4578a39f18..205746e275 100644 --- a/config/wallet-config.json +++ b/config/wallet-config.json @@ -97,7 +97,7 @@ "enabled": true, "contracts": { "mainnet": { - "address": "currently-unknown" + "address": "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token::sbtc-token" }, "testnet": { "address": "SNGWPN3XDAQE673MXYXF81016M50NHF5X5PWWM70.sbtc-token::sbtc-token" diff --git a/package.json b/package.json index 7c7473b6cc..f9f3b610aa 100644 --- a/package.json +++ b/package.json @@ -248,7 +248,7 @@ "redux-persist": "6.0.0", "remark-gfm": "4.0.0", "rxjs": "7.8.1", - "sbtc": "0.2.4", + "sbtc": "0.2.5", "style-loader": "3.3.4", "ts-debounce": "4.0.0", "url": "0.11.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8f83c1a70b..818f14456f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -354,8 +354,8 @@ importers: specifier: 7.8.1 version: 7.8.1 sbtc: - specifier: 0.2.4 - version: 0.2.4(encoding@0.1.13) + specifier: 0.2.5 + version: 0.2.5(encoding@0.1.13) style-loader: specifier: 3.3.4 version: 3.3.4(webpack@5.94.0(@swc/core@1.9.3)(esbuild@0.24.0)(webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@4.15.1)(webpack@5.94.0))) @@ -13239,8 +13239,8 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} - sbtc@0.2.4: - resolution: {integrity: sha512-mLc5jZT3m4MrHqpc3H7L4DpJAGaXIS9/2XOD7toZZzj4YWkbTWKcz8Yy5Xp12YI1s0AC+K/WJNzDdyI4tpsM2A==} + sbtc@0.2.5: + resolution: {integrity: sha512-CXdgh4xq/86VqmvtHtFGQlL0R2ZjKdnuVgUh3mzya1blBNm0qlKYrC/woUgA0Gxth8uBUQh1cn+dduihxNoFvg==} sc-errors@3.0.0: resolution: {integrity: sha512-rIqv2HTPb9DVreZwK/DV0ytRUqyw2DbDcoB9XTKjEQL7oMEQKsfPA8V8dGGr7p8ZYfmvaRIGZ4Wu5qwvs/hGDA==} @@ -31394,7 +31394,7 @@ snapshots: dependencies: xmlchars: 2.2.0 - sbtc@0.2.4(encoding@0.1.13): + sbtc@0.2.5(encoding@0.1.13): dependencies: '@btc-helpers/rpc': 2.0.0(encoding@0.1.13) '@noble/secp256k1': 2.1.0 diff --git a/src/app/components/sbtc-deposit-status-item/sbtc-deposit-status-item.tsx b/src/app/components/sbtc-deposit-status-item/sbtc-deposit-status-item.tsx index 2b0aba29e5..92729567a7 100644 --- a/src/app/components/sbtc-deposit-status-item/sbtc-deposit-status-item.tsx +++ b/src/app/components/sbtc-deposit-status-item/sbtc-deposit-status-item.tsx @@ -26,7 +26,6 @@ export function SbtcDepositTransactionItem({ deposit }: SbtcDepositTransactionIt openTxLink={openTxLink} txCaption={truncateMiddle(deposit.bitcoinTxid, 4)} txIcon={ - // Replace with sBTC avatar icon diff --git a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.tsx b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.tsx index eb0ff4c5f6..989037344a 100644 --- a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.tsx +++ b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.tsx @@ -37,6 +37,7 @@ export function Sip10TokenAssetItem({ const { isTokenEnabled } = useManageTokens(); const { contractId, imageCanonicalUri, name, symbol } = info; + // This can be removed once an img is available directly from the sip10 token const isSbtc = symbol === 'sBTC'; const icon = ( diff --git a/src/app/pages/home/home.tsx b/src/app/pages/home/home.tsx index ad0ef10a8f..303ad935f1 100644 --- a/src/app/pages/home/home.tsx +++ b/src/app/pages/home/home.tsx @@ -11,6 +11,7 @@ import { useTotalBalance } from '@app/common/hooks/balance/use-total-balance'; import { useOnMount } from '@app/common/hooks/use-on-mount'; import { useSwitchAccountSheet } from '@app/common/switch-account/use-switch-account-sheet-context'; import { whenPageMode } from '@app/common/utils'; +import { openInNewTab } from '@app/common/utils/open-in-new-tab'; import { ActivityList } from '@app/features/activity-list/activity-list'; import { FeedbackButton } from '@app/features/feedback-button/feedback-button'; import { SbtcPromoCard } from '@app/features/sbtc-promo-card/sbtc-promo-card'; @@ -27,6 +28,8 @@ import { AccountCard } from '@app/ui/components/account/account.card'; import { AccountActions } from './components/account-actions'; import { HomeTabs } from './components/home-tabs'; +const leatherEarnUrl = 'https://earn.leather.io'; + export function Home() { const { decodedAuthRequest } = useOnboardingState(); const { toggleSwitchAccount } = useSwitchAccountSheet(); @@ -76,7 +79,7 @@ export function Home() { > - navigate('test-deposit-sbtc')} /> + openInNewTab(leatherEarnUrl)} /> {whenPageMode({ full: , popup: null })} diff --git a/src/app/pages/swap/bitflow-swap-container.tsx b/src/app/pages/swap/bitflow-swap-container.tsx index b9cbd5f00f..71babf38ab 100644 --- a/src/app/pages/swap/bitflow-swap-container.tsx +++ b/src/app/pages/swap/bitflow-swap-container.tsx @@ -77,10 +77,9 @@ function BitflowSwapContainer() { onSetSwapSubmissionData({ ...swapData, fee: satToBtc(sBtcDepositData?.fee ?? 0).toNumber(), - txData: sBtcDepositData?.deposit, + txData: { deposit: sBtcDepositData?.deposit }, }); - swapNavigate(RouteUrls.SwapReview); - return; + return swapNavigate(RouteUrls.SwapReview); } const routeQuote = await fetchRouteQuote( diff --git a/src/app/pages/swap/components/swap-asset-dialog/components/use-swap-asset-list.tsx b/src/app/pages/swap/components/swap-asset-dialog/components/use-swap-asset-list.tsx index e648025b7e..0b266ca120 100644 --- a/src/app/pages/swap/components/swap-asset-dialog/components/use-swap-asset-list.tsx +++ b/src/app/pages/swap/components/swap-asset-dialog/components/use-swap-asset-list.tsx @@ -15,7 +15,6 @@ import { import type { SwapFormValues } from '@shared/models/form.model'; import { RouteUrls } from '@shared/route-urls'; -// import { bitflow } from '@shared/utils/bitflow-sdk'; import { useSwapContext } from '@app/pages/swap/swap.context'; import type { SwapAssetListProps } from './swap-asset-list'; @@ -29,13 +28,6 @@ export function useSwapAssetList({ assets, type }: SwapAssetListProps) { const isBaseList = type === 'base'; const isQuoteList = type === 'quote'; - // async function getBtcToken() { - // // const tokens = await bitflow.getAvailableTokens(); - // // console.log(tokens.filter(token => token.tokenId === 'token-xbtc')); - // const result = await bitflow.getQuoteForRoute('token-stx', 'token-xbtc', 1); - // console.log(result); - // } - // Filter out selected asset from selectable assets const selectableAssets = assets .filter( @@ -77,9 +69,8 @@ export function useSwapAssetList({ assets, type }: SwapAssetListProps) { const onFetchQuoteAmount = useCallback( async (baseAsset: SwapAsset, quoteAsset: SwapAsset) => { - // await getBtcToken(); const quoteAmount = await fetchQuoteAmount(baseAsset, quoteAsset, values.swapAmountBase); - // Handle race condition; make sure quote amount is 1:1 for BTC swap + // Handle race condition; make sure quote amount is 1:1 if (baseAsset.name === 'BTC') { void setFieldValue('swapAmountQuote', values.swapAmountBase); return; diff --git a/src/app/pages/swap/hooks/use-bitflow-swap.tsx b/src/app/pages/swap/hooks/use-bitflow-swap.tsx index 8aced38615..fa26cff637 100644 --- a/src/app/pages/swap/hooks/use-bitflow-swap.tsx +++ b/src/app/pages/swap/hooks/use-bitflow-swap.tsx @@ -44,20 +44,10 @@ export function useBitflowSwap() { baseAmount: string ): Promise => { if (!baseAmount || !base || !quote || isCrossChainSwap) return; - let baseTokenId = base.tokenId; - let quoteTokenId = quote.tokenId; - // Temporarily handle sBTC exchange rate; force as BTC - // TODO: Remove once Bitflow supports sBTC exchange rate - if (base.tokenId === 'token-sbtc') { - baseTokenId = 'token-xbtc'; - } - if (quote.tokenId === 'token-sbtc') { - quoteTokenId = 'token-xbtc'; - } try { const result = await bitflow.getQuoteForRoute( - baseTokenId, - quoteTokenId, + base.tokenId, + quote.tokenId, Number(baseAmount) ); if (!result.bestRoute) { diff --git a/src/app/pages/swap/hooks/use-sbtc-deposit-transaction.tsx b/src/app/pages/swap/hooks/use-sbtc-deposit-transaction.tsx index ff1bf7631c..9f13ec9d60 100644 --- a/src/app/pages/swap/hooks/use-sbtc-deposit-transaction.tsx +++ b/src/app/pages/swap/hooks/use-sbtc-deposit-transaction.tsx @@ -1,9 +1,18 @@ /* eslint-disable */ +// TODO: Enable eslint and remove test logs +import { useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import * as btc from '@scure/btc-signer'; import type { P2TROut } from '@scure/btc-signer/payment'; -import { MAINNET, REGTEST, SbtcApiClientTestnet, TESTNET, buildSbtcDepositTx } from 'sbtc'; +import { + MAINNET, + REGTEST, + SbtcApiClient, + SbtcApiClientTestnet, + TESTNET, + buildSbtcDepositTx, +} from 'sbtc'; import type { BitcoinNetworkModes } from '@leather.io/models'; import { useAverageBitcoinFeeRates } from '@leather.io/query'; @@ -24,6 +33,7 @@ import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; import type { SwapSubmissionData } from '../swap.context'; +// Suggested to use as defaults const maxSignerFee = 80_000; const reclaimLockTime = 6_000; @@ -35,9 +45,6 @@ interface SbtcDeposit { trOut: P2TROut; } -// Check network for correct client -const client = new SbtcApiClientTestnet(); - function getSbtcNetworkConfig(network: BitcoinNetworkModes) { const networkMap = { mainnet: MAINNET, @@ -49,6 +56,15 @@ function getSbtcNetworkConfig(network: BitcoinNetworkModes) { return networkMap[network]; } +// TODO: Set config paths, or likely remove when defaults are published +const clientMainnet = new SbtcApiClient({ + sbtcContract: '', + btcApiUrl: '', + stxApiUrl: '', + sbtcApiUrl: '', +}); +const clientTestnet = new SbtcApiClientTestnet(); + export function useSbtcDepositTransaction() { const toast = useToast(); const { setIsIdle } = useLoading(LoadingKeys.SUBMIT_SWAP_TRANSACTION); @@ -60,6 +76,11 @@ export function useSbtcDepositTransaction() { const navigate = useNavigate(); const network = useCurrentNetwork(); + const client = useMemo( + () => (network.chain.bitcoin.mode === 'mainnet' ? clientMainnet : clientTestnet), + [network] + ); + // Check if the signer is compliant useBreakOnNonCompliantEntity(); diff --git a/src/app/pages/test-deposit-sbtc/test-deposit-sbtc.tsx b/src/app/pages/test-deposit-sbtc/test-deposit-sbtc.tsx deleted file mode 100644 index cf71644089..0000000000 --- a/src/app/pages/test-deposit-sbtc/test-deposit-sbtc.tsx +++ /dev/null @@ -1,109 +0,0 @@ -/* eslint-disable */ -import * as btc from '@scure/btc-signer'; -import { styled } from 'leather-styles/jsx'; -import { REGTEST, SbtcApiClientTestnet, buildSbtcDepositTx } from 'sbtc'; - -import { useAverageBitcoinFeeRates } from '@leather.io/query'; -import { Button, Input } from '@leather.io/ui'; -import { createMoney } from '@leather.io/utils'; - -import { determineUtxosForSpend } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection'; -import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; -import { useBitcoinScureLibNetworkConfig } from '@app/store/accounts/blockchain/bitcoin/bitcoin-keychain'; -import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; -import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; - -// import { notifySbtc } from './deposit'; - -// const client = new SbtcApiClientTestnet({ -// sbtcApiUrl: 'https://beta.sbtc-emily.com/deposit', -// btcApiUrl: 'https://beta.sbtc-mempool.tech/api/proxy', -// stxApiUrl: 'https://api.testnet.hiro.so', -// sbtcContract: 'SNGWPN3XDAQE673MXYXF81016M50NHF5X5PWWM70', -// }); - -const client = new SbtcApiClientTestnet(); - -// Demo component to create the swap deposit transaction -export function TestDepositSbtc() { - const stacksAccount = useCurrentStacksAccount(); - const { data: utxos } = useCurrentNativeSegwitUtxos(); - const { data: feeRates } = useAverageBitcoinFeeRates(); - const signer = useCurrentAccountNativeSegwitIndexZeroSigner(); - const networkMode = useBitcoinScureLibNetworkConfig(); - - async function onSubmit() { - if (!stacksAccount) throw new Error('no stacks account'); - if (!utxos) throw new Error('no utxos'); - - try { - const deposit = buildSbtcDepositTx({ - amountSats: 100_000, - network: REGTEST, - stacksAddress: stacksAccount.address, - signersPublicKey: await client.fetchSignersPublicKey(), - maxSignerFee: 80_000, - reclaimLockTime: 6_000, - }); - - const { inputs, outputs } = determineUtxosForSpend({ - feeRate: feeRates?.halfHourFee.toNumber() ?? 0, - recipients: [ - { - address: deposit.address, - amount: createMoney(Number(deposit.transaction.getOutput(0).amount), 'BTC'), - }, - ], - utxos, - }); - console.log('inputs', inputs); - console.log('outputs', outputs); - const p2wpkh = btc.p2wpkh(signer.publicKey, networkMode); - - for (const input of inputs) { - deposit.transaction.addInput({ - txid: input.txid, - index: input.vout, - sequence: 0, - witnessUtxo: { - // script = 0014 + pubKeyHash - script: p2wpkh.script, - amount: BigInt(input.value), - }, - }); - } - - outputs.forEach(output => { - // Add change output - if (!output.address) { - deposit.transaction.addOutputAddress(signer.address, BigInt(output.value), networkMode); - return; - } - }); - - signer.sign(deposit.transaction); - deposit.transaction.finalize(); - - console.log('deposit tx', deposit.transaction); - console.log('tx hex', deposit.transaction.hex); - - const txid = await client.broadcastTx(deposit.transaction); - console.log('broadcasted tx', txid); - const registeredDeposit = await client.notifySbtc(deposit); - console.log('registered deposit', registeredDeposit); - } catch (error) { - console.error(error); - } - } - - return ( - -

sbtc test tx construction

- - Amount - - - -
- ); -} diff --git a/src/app/query/common/remote-config/remote-config.query.ts b/src/app/query/common/remote-config/remote-config.query.ts index bcbd5347f8..9bdb9a36b6 100644 --- a/src/app/query/common/remote-config/remote-config.query.ts +++ b/src/app/query/common/remote-config/remote-config.query.ts @@ -42,9 +42,9 @@ export function useConfigBitcoinSendEnabled() { export function useConfigSbtc() { const config = useRemoteConfig(); - // TODO: Update with new mono version - const sbtc = config?.sbtc; const network = useCurrentNetwork(); + const sbtc = config?.sbtc; + return useMemo(() => { const displayPromoCardOnNetworks = (sbtc as any)?.showPromoLinkOnNetworks ?? []; return { diff --git a/src/app/query/sbtc/sbtc-deposits.query.ts b/src/app/query/sbtc/sbtc-deposits.query.ts index 5d7da310ac..184d64c283 100644 --- a/src/app/query/sbtc/sbtc-deposits.query.ts +++ b/src/app/query/sbtc/sbtc-deposits.query.ts @@ -28,6 +28,7 @@ interface GetSbtcDepositsResponse { nextToken?: string; } +// TODO: Verify this is correct for mainnet launch const emilyUrl = 'https://beta.sbtc-emily.com/deposit'; async function getSbtcDeposits(status: string): Promise { @@ -53,7 +54,6 @@ function useGetSbtcDeposits(stxAddress: string, status: string) { }); } -// Possibly also include status `accepted` here, but need to test when testnet is working export function useSbtcPendingDeposits(stxAddress: string) { const { data: pendingDeposits = [], isLoading: isLoadingStatusPending } = useGetSbtcDeposits( stxAddress, @@ -61,9 +61,13 @@ export function useSbtcPendingDeposits(stxAddress: string) { ); const { data: reprocessingDeposits = [], isLoading: isLoadingStatusReprocessing } = useGetSbtcDeposits(stxAddress, 'reprocessing'); + const { data: acceptedDeposits = [], isLoading: isLoadingStatusAccepted } = useGetSbtcDeposits( + stxAddress, + 'accepted' + ); return { - isLoading: isLoadingStatusPending || isLoadingStatusReprocessing, - pendingSbtcDeposits: [...pendingDeposits, ...reprocessingDeposits], + isLoading: isLoadingStatusPending || isLoadingStatusReprocessing || isLoadingStatusAccepted, + pendingSbtcDeposits: [...pendingDeposits, ...reprocessingDeposits, ...acceptedDeposits], }; } diff --git a/src/app/routes/app-routes.tsx b/src/app/routes/app-routes.tsx index bba1d9369b..11848ea703 100644 --- a/src/app/routes/app-routes.tsx +++ b/src/app/routes/app-routes.tsx @@ -44,7 +44,6 @@ import { BroadcastError } from '@app/pages/send/broadcast-error/broadcast-error' import { sendOrdinalRoutes } from '@app/pages/send/ordinal-inscription/ordinal-routes'; import { sendCryptoAssetFormRoutes } from '@app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes'; import { bitflowSwapRoutes } from '@app/pages/swap/bitflow-swap-container'; -import { TestDepositSbtc } from '@app/pages/test-deposit-sbtc/test-deposit-sbtc'; import { UnauthorizedRequest } from '@app/pages/unauthorized-request/unauthorized-request'; import { Unlock } from '@app/pages/unlock'; import { ViewSecretKey } from '@app/pages/view-secret-key/view-secret-key'; @@ -203,8 +202,6 @@ function useAppRoutes() { {bitflowSwapRoutes} - } /> - {/* OnBoarding Routes */} { await expect(toast).toBeVisible(); }); - test('that it preselects cross chain swap assets', async ({ swapPage }) => { + test('that it preselects cross chain swap assets and restricts quote list', async ({ + swapPage, + }) => { await swapPage.selectBtcAsBaseAsset(); const quoteAsset = await swapPage.page.locator('text="sBTC"').innerText(); test.expect(quoteAsset).toEqual('sBTC'); - await swapPage.selectAssetToReceive(); - await swapPage.selectSbtcAsQuoteAsset(); - - const baseAsset = await swapPage.page.locator('text="BTC"').innerText(); - test.expect(baseAsset).toEqual('BTC'); + await swapPage.selectQuoteAsset(); + await swapPage.page.locator(swapPage.chooseAssetList).waitFor(); + const quoteAssets = await swapPage.page.locator(swapPage.chooseAssetListItem).all(); + test.expect(quoteAssets.length).toEqual(1); }); });