diff --git a/packages/e2e/pages/trade-page.ts b/packages/e2e/pages/trade-page.ts index 41f0605bc0..656345430e 100644 --- a/packages/e2e/pages/trade-page.ts +++ b/packages/e2e/pages/trade-page.ts @@ -3,414 +3,414 @@ import { type Locator, type Page, expect, -} from "@playwright/test"; +} from '@playwright/test' -import { BasePage } from "./base-page"; +import { BasePage } from './base-page' export class TradePage extends BasePage { - readonly page: Page; - readonly swapBtn: Locator; - readonly swapMaxBtn: Locator; - readonly flipAssetsBtn: Locator; - readonly exchangeRate: Locator; - readonly trxSuccessful: Locator; - readonly trxBroadcasting: Locator; - readonly trxLink: Locator; - readonly inputAmount: Locator; - readonly confirmSwapBtn: Locator; - readonly buyTabBtn: Locator; - readonly buyBtn: Locator; - readonly sellTabBtn: Locator; - readonly sellBtn: Locator; - readonly limitTabBtn: Locator; - readonly orderHistoryLink: Locator; - readonly limitPrice: Locator; + readonly page: Page + readonly swapBtn: Locator + readonly swapMaxBtn: Locator + readonly flipAssetsBtn: Locator + readonly exchangeRate: Locator + readonly trxSuccessful: Locator + readonly trxBroadcasting: Locator + readonly trxLink: Locator + readonly inputAmount: Locator + readonly confirmSwapBtn: Locator + readonly buyTabBtn: Locator + readonly buyBtn: Locator + readonly sellTabBtn: Locator + readonly sellBtn: Locator + readonly limitTabBtn: Locator + readonly orderHistoryLink: Locator + readonly limitPrice: Locator constructor(page: Page) { - super(page); - this.page = page; - this.swapBtn = page.locator('//button[@data-testid="trade-button-swap"]'); - this.buyTabBtn = page.locator('//div[@class]/button[.="Buy"]/p[@class]/..'); - this.buyBtn = page.locator('//div[@class]/button[@class]/h6[.="Buy"]/..'); - this.sellBtn = page.locator('//div[@class]/button[@class]/h6[.="Sell"]/..'); + super(page) + this.page = page + this.swapBtn = page.locator('//button[@data-testid="trade-button-swap"]') + this.buyTabBtn = page.locator('//div[@class]/button[.="Buy"]/p[@class]/..') + this.buyBtn = page.locator('//div[@class]/button[@class]/h6[.="Buy"]/..') + this.sellBtn = page.locator('//div[@class]/button[@class]/h6[.="Sell"]/..') this.sellTabBtn = page.locator( - '//div[@class]/button[.="Sell"]/p[@class]/..' - ); - this.confirmSwapBtn = page.locator('//div[@class]/button[.="Confirm"]'); - this.swapMaxBtn = page.locator('//span[.="Max"]'); + '//div[@class]/button[.="Sell"]/p[@class]/..', + ) + this.confirmSwapBtn = page.locator('//div[@class]/button[.="Confirm"]') + this.swapMaxBtn = page.locator('//span[.="Max"]') this.flipAssetsBtn = page.locator( - '//div/button[contains(@class, "ease-bounce")]' - ); - this.exchangeRate = page.locator('//span[@data-testid="token-price"]'); - this.trxSuccessful = page.getByText("Transaction Successful"); - this.trxLink = page.getByText("View explorer"); - this.trxBroadcasting = page.locator('//h6[.="Transaction Broadcasting"]'); + '//div/button[contains(@class, "ease-bounce")]', + ) + this.exchangeRate = page.locator('//span[@data-testid="token-price"]') + this.trxSuccessful = page.getByText('Transaction Successful') + this.trxLink = page.getByText('View explorer') + this.trxBroadcasting = page.locator('//h6[.="Transaction Broadcasting"]') this.inputAmount = page.locator( - "//input[contains(@data-testid, 'trade-input')]" - ); - this.limitTabBtn = page.locator('//div[@class="w-full"]/button[.="Limit"]'); - this.orderHistoryLink = page.getByText("Order history"); - this.limitPrice = page.locator("//div/input[@type='text']"); + "//input[contains(@data-testid, 'trade-input')]", + ) + this.limitTabBtn = page.locator('//div[@class="w-full"]/button[.="Limit"]') + this.orderHistoryLink = page.getByText('Order history') + this.limitPrice = page.locator("//div/input[@type='text']") } async goto() { - const assetPromise = this.page.waitForRequest("**/assets.json"); - await this.page.goto("/"); - const request = await assetPromise; - expect(request).toBeTruthy(); + const assetPromise = this.page.waitForRequest('**/assets.json') + await this.page.goto('/') + const request = await assetPromise + expect(request).toBeTruthy() // we expect that after 2 seconds tokens are loaded and any failure after this point should be considered a bug. - await this.page.waitForTimeout(2000); - const currentUrl = this.page.url(); - console.log(`FE opened at: ${currentUrl}`); + await this.page.waitForTimeout(2000) + const currentUrl = this.page.url() + console.log(`FE opened at: ${currentUrl}`) } async gotoOrdersHistory(timeout = 1) { - await this.page.waitForTimeout(1000); - await this.orderHistoryLink.click(); - await this.page.waitForTimeout(1000); - await new Promise((f) => setTimeout(f, timeout * 1000)); - const currentUrl = this.page.url(); - console.log(`FE opened at: ${currentUrl}`); + await this.page.waitForTimeout(1000) + await this.orderHistoryLink.click() + await this.page.waitForTimeout(1000) + await new Promise((f) => setTimeout(f, timeout * 1000)) + const currentUrl = this.page.url() + console.log(`FE opened at: ${currentUrl}`) } async openBuyTab() { - await this.buyTabBtn.click(); + await this.buyTabBtn.click() } async openSellTab() { - await this.sellTabBtn.click(); + await this.sellTabBtn.click() } async openLimit() { - await this.limitTabBtn.click(); - await this.page.waitForTimeout(1000); + await this.limitTabBtn.click() + await this.page.waitForTimeout(1000) } async getLimitPrice() { - const lp = await this.limitPrice.inputValue(); - console.log(`Current limit price is: ${lp}`); - return lp; + const lp = await this.limitPrice.inputValue() + console.log(`Current limit price is: ${lp}`) + return lp } async setLimitPriceChange(change: string) { - const locator = `//button/span[contains(@class, "body2") and .="${change}"]`; - await this.page.locator(locator).click(); - await this.page.waitForTimeout(1000); + const locator = `//button/span[contains(@class, "body2") and .="${change}"]` + await this.page.locator(locator).click() + await this.page.waitForTimeout(1000) } async setLimitPrice(price: string) { - console.log(`Set Order Limit Price to: ${price}`); - await this.limitPrice.fill(price, { timeout: 2000 }); + console.log(`Set Order Limit Price to: ${price}`) + await this.limitPrice.fill(price, { timeout: 2000 }) } async flipTokenPair() { - await this.flipAssetsBtn.click(); - await this.page.waitForTimeout(2000); - console.log("Fliped token pair."); + await this.flipAssetsBtn.click() + await this.page.waitForTimeout(2000) + console.log('Fliped token pair.') } async clickMaxAmountButton() { - await this.swapMaxBtn.click({ timeout: 2000 }); - await this.page.waitForTimeout(1000); - console.log("Clicked Max token amount button."); + await this.swapMaxBtn.click({ timeout: 2000 }) + await this.page.waitForTimeout(1000) + console.log('Clicked Max token amount button.') } async enterAmount(amount: string) { // Just enter an amount for the swap and wait for a quote - await this.inputAmount.fill(amount, { timeout: 2000 }); - await this.page.waitForTimeout(1000); - await expect(this.inputAmount).toHaveValue(amount, { timeout: 1000 }); - const exchangeRate = await this.getExchangeRate(); - console.log(`Swap ${amount} with rate: ${exchangeRate}`); + await this.inputAmount.fill(amount, { timeout: 2000 }) + await this.page.waitForTimeout(1000) + await expect(this.inputAmount).toHaveValue(amount, { timeout: 1000 }) + const exchangeRate = await this.getExchangeRate() + console.log(`Swap ${amount} with rate: ${exchangeRate}`) } private async approveInKeplrAndGetMsg(context: BrowserContext) { - console.log("Wait for 5 seconds for any popup"); - await this.page.waitForTimeout(5_000); - const pages = context.pages(); - console.log(`Number of Open Pages: ${pages.length}`); + console.log('Wait for 5 seconds for any popup') + await this.page.waitForTimeout(5_000) + const pages = context.pages() + console.log(`Number of Open Pages: ${pages.length}`) if (pages.length === 2) { - const approvePage = pages[1]; - const approvePageTitle = approvePage.url(); - console.log(`Approve page is opened at: ${approvePageTitle}`); + const approvePage = pages[1] + const approvePageTitle = approvePage.url() + console.log(`Approve page is opened at: ${approvePageTitle}`) const msgContent = await approvePage - .getByText("type: osmosis/poolmanager/") - .textContent(); - console.log(`Wallet is approving this msg: \n${msgContent}`); + .getByText('type: osmosis/poolmanager/') + .textContent() + console.log(`Wallet is approving this msg: \n${msgContent}`) await approvePage - .getByRole("button", { name: "Approve" }) - .click({ timeout: 4000 }); - return msgContent; + .getByRole('button', { name: 'Approve' }) + .click({ timeout: 4000 }) + return msgContent } - console.log("Second page was not opened in 5 seconds."); + console.log('Second page was not opened in 5 seconds.') } async justApproveIfNeeded(context: BrowserContext) { - console.log("Wait for 7 seconds for any popup"); - await this.page.waitForTimeout(7_000); - const pages = context.pages(); - console.log(`Number of Open Pages: ${pages.length}`); + console.log('Wait for 7 seconds for any popup') + await this.page.waitForTimeout(7_000) + const pages = context.pages() + console.log(`Number of Open Pages: ${pages.length}`) if (pages.length === 2) { - const approvePage = pages[1]; - const approvePageTitle = approvePage.url(); - console.log(`Approve page is opened at: ${approvePageTitle}`); + const approvePage = pages[1] + const approvePageTitle = approvePage.url() + console.log(`Approve page is opened at: ${approvePageTitle}`) await approvePage - .getByRole("button", { name: "Approve" }) - .click({ timeout: 4000 }); + .getByRole('button', { name: 'Approve' }) + .click({ timeout: 4000 }) } - console.log("Second page was not opened in 7 seconds."); + console.log('Second page was not opened in 7 seconds.') } async swapAndGetWalletMsg(context: BrowserContext) { // Make sure to have sufficient balance and swap button is enabled expect( await this.isInsufficientBalanceForSwap(), - "Insufficient balance for the swap!" - ).toBeFalsy(); - console.log("Swap and Sign now.."); - await expect(this.swapBtn, "Swap button is disabled!").toBeEnabled({ + 'Insufficient balance for the swap!', + ).toBeFalsy() + console.log('Swap and Sign now..') + await expect(this.swapBtn, 'Swap button is disabled!').toBeEnabled({ timeout: 7000, - }); - await this.swapBtn.click({ timeout: 4000 }); + }) + await this.swapBtn.click({ timeout: 4000 }) // Handle 1-click by default - const oneClick = '//div[@role="dialog"]//button[@data-state="checked"]'; + const oneClick = '//div[@role="dialog"]//button[@data-state="checked"]' if (await this.page.locator(oneClick).isVisible({ timeout: 2000 })) { - await this.page.locator(oneClick).click({ timeout: 3000 }); + await this.page.locator(oneClick).click({ timeout: 3000 }) } - await this.confirmSwapBtn.click({ timeout: 5000 }); - return await this.approveInKeplrAndGetMsg(context); + await this.confirmSwapBtn.click({ timeout: 5000 }) + return await this.approveInKeplrAndGetMsg(context) } async selectAsset(token: string) { - const tokenLocator = "//div//button[@type]//img[@alt]"; - const fromToken = this.page.locator(tokenLocator).nth(0); - await fromToken.click(); + const tokenLocator = '//div//button[@type]//img[@alt]' + const fromToken = this.page.locator(tokenLocator).nth(0) + await fromToken.click() // we expect that after 1 second token filter is displayed. - await this.page.waitForTimeout(1000); - await this.page.getByPlaceholder("Search").fill(token); + await this.page.waitForTimeout(1000) + await this.page.getByPlaceholder('Search').fill(token) const fromLocator = this.page.locator( - `//div/button[@data-testid='token-select-asset']//span[.='${token}']` - ); - await fromLocator.click(); + `//div/button[@data-testid='token-select-asset']//span[.='${token}']`, + ) + await fromLocator.click() } async selectPair(from: string, to: string) { // Filter does not show already selected tokens - console.log(`Select pair ${from} to ${to}`); + console.log(`Select pair ${from} to ${to}`) const fromToken = this.page.locator( - "//div//button[@data-testid='token-in']//img[@alt]" - ); + "//div//button[@data-testid='token-in']//img[@alt]", + ) const toToken = this.page.locator( - "//div//button[@data-testid='token-out']//img[@alt]" - ); + "//div//button[@data-testid='token-out']//img[@alt]", + ) // Select From Token - await fromToken.click({ timeout: 4000 }); + await fromToken.click({ timeout: 4000 }) // we expect that after 1 second token filter is displayed. - await this.page.waitForTimeout(1000); - await this.page.getByPlaceholder("Search").fill(from); + await this.page.waitForTimeout(1000) + await this.page.getByPlaceholder('Search').fill(from) const fromLocator = this.page .locator( - `//div/button[@data-testid='token-select-asset']//span[.='${from}']` + `//div/button[@data-testid='token-select-asset']//span[.='${from}']`, ) - .first(); - await fromLocator.click({ timeout: 4000 }); + .first() + await fromLocator.click({ timeout: 4000 }) // Select To Token - await toToken.click({ timeout: 4000 }); + await toToken.click({ timeout: 4000 }) // we expect that after 1 second token filter is displayed. - await this.page.waitForTimeout(1000); - await this.page.getByPlaceholder("Search").fill(to); + await this.page.waitForTimeout(1000) + await this.page.getByPlaceholder('Search').fill(to) const toLocator = this.page .locator( - `//div/button[@data-testid='token-select-asset']//span[.='${to}']` + `//div/button[@data-testid='token-select-asset']//span[.='${to}']`, ) - .first(); - await toLocator.click(); + .first() + await toLocator.click() // we expect that after 2 seconds exchange rate is populated. - await this.page.waitForTimeout(2000); - expect(await this.getExchangeRate()).toContain(from); - expect(await this.getExchangeRate()).toContain(to); + await this.page.waitForTimeout(2000) + expect(await this.getExchangeRate()).toContain(from) + expect(await this.getExchangeRate()).toContain(to) } async getExchangeRate() { - return await this.exchangeRate.innerText(); + return await this.exchangeRate.innerText() } async isTransactionSuccesful(delay = 7) { - console.log(`Wait for a transaction success for ${delay} seconds.`); + console.log(`Wait for a transaction success for ${delay} seconds.`) await expect(this.trxSuccessful).toBeVisible({ timeout: delay * 1000, visible: true, - }); + }) } async getTransactionUrl() { - const trxUrl = await this.trxLink.getAttribute("href"); - console.log(`Trx url: ${trxUrl}`); - await this.page.reload(); - return trxUrl; + const trxUrl = await this.trxLink.getAttribute('href') + console.log(`Trx url: ${trxUrl}`) + await this.page.reload() + return trxUrl } async isTransactionBroadcasted(delay = 5) { - console.log(`Wait for a transaction broadcasting for ${delay} seconds.`); - return await this.trxBroadcasting.isVisible({ timeout: delay * 1000 }); + console.log(`Wait for a transaction broadcasting for ${delay} seconds.`) + return await this.trxBroadcasting.isVisible({ timeout: delay * 1000 }) } async isInsufficientBalance() { const issufBalanceBtn = this.page.locator( - '//span[.="Insufficient balance"]' - ); - return await issufBalanceBtn.isVisible({ timeout: 2000 }); + '//span[.="Insufficient balance"]', + ) + return await issufBalanceBtn.isVisible({ timeout: 2000 }) } async isInsufficientBalanceForSwap() { const issufBalanceBtn = this.page.locator( - '//button[.="Insufficient balance"]' - ); - return await issufBalanceBtn.isVisible({ timeout: 2000 }); + '//button[.="Insufficient balance"]', + ) + return await issufBalanceBtn.isVisible({ timeout: 2000 }) } async isSufficientBalanceForTrade() { // Make sure to have sufficient balance for a trade expect( await this.isInsufficientBalance(), - "Insufficient balance for the swap!" - ).toBeFalsy(); + 'Insufficient balance for the swap!', + ).toBeFalsy() } async isError() { - const errorBtn = this.page.locator('//button[.="Error"]'); - return await errorBtn.isVisible({ timeout: 2000 }); + const errorBtn = this.page.locator('//button[.="Error"]') + return await errorBtn.isVisible({ timeout: 2000 }) } async showSwapInfo() { - const swapInfo = this.page.locator("//button//span[.='Show details']"); - await expect(swapInfo, "Show Swap Info button not visible!").toBeVisible({ + const swapInfo = this.page.locator("//button//span[.='Show details']") + await expect(swapInfo, 'Show Swap Info button not visible!').toBeVisible({ timeout: 4000, - }); - await swapInfo.click({ timeout: 2000 }); + }) + await swapInfo.click({ timeout: 2000 }) } async getPriceInpact() { const priceInpactSpan = this.page.locator( - '//span[.="Price Impact"]/..//span[@class="text-bullish-400"]' - ); - return await priceInpactSpan.textContent(); + '//span[.="Price Impact"]/..//span[@class="text-bullish-400"]', + ) + return await priceInpactSpan.textContent() } async takeScreenshot(name: string) { await this.page.screenshot({ path: `screenshot-trade-${name}.png`, fullPage: true, - }); + }) } async getSelectedSwapPair() { - const tokenLocator = "//div//button[@type]//img[@alt]/../h5"; - const fromToken = this.page.locator(tokenLocator).nth(0); - const toToken = this.page.locator(tokenLocator).nth(1); - await expect(fromToken).toBeVisible({ timeout: 2000 }); - const fromTokenText = await fromToken.innerText(); - const toTokenText = await toToken.innerText(); - console.log(`Current pair: ${fromTokenText}/${toTokenText}`); - return `${fromTokenText}/${toTokenText}`; + const tokenLocator = '//div//button[@type]//img[@alt]/../h5' + const fromToken = this.page.locator(tokenLocator).nth(0) + const toToken = this.page.locator(tokenLocator).nth(1) + await expect(fromToken).toBeVisible({ timeout: 2000 }) + const fromTokenText = await fromToken.innerText() + const toTokenText = await toToken.innerText() + console.log(`Current pair: ${fromTokenText}/${toTokenText}`) + return `${fromTokenText}/${toTokenText}` } async buyAndGetWalletMsg(context: BrowserContext, limit = false) { - await expect(this.buyBtn, "Buy button is disabled!").toBeEnabled({ + await expect(this.buyBtn, 'Buy button is disabled!').toBeEnabled({ timeout: 9000, - }); + }) // Handle Pop-up page -> - await this.buyBtn.click(); - const pageApprove = context.waitForEvent("page"); - await this.confirmSwapBtn.click(); - await this.page.waitForTimeout(200); - const approvePage = await pageApprove; - await approvePage.waitForLoadState(); - const approveBtn = approvePage.getByRole("button", { - name: "Approve", - }); - await expect(approveBtn).toBeEnabled(); - let msgTextLocator = "type: osmosis/poolmanager/"; + await this.buyBtn.click() + const pageApprove = context.waitForEvent('page') + await this.confirmSwapBtn.click() + await this.page.waitForTimeout(200) + const approvePage = await pageApprove + await approvePage.waitForLoadState() + const approveBtn = approvePage.getByRole('button', { + name: 'Approve', + }) + await expect(approveBtn).toBeEnabled() + let msgTextLocator = 'type: osmosis/poolmanager/' if (limit) { - msgTextLocator = "Execute contract"; + msgTextLocator = 'Execute contract' } const msgContentAmount = await approvePage .getByText(msgTextLocator) - .textContent(); - console.log(`Wallet is approving this msg: \n${msgContentAmount}`); + .textContent() + console.log(`Wallet is approving this msg: \n${msgContentAmount}`) // Approve trx - await approveBtn.click(); + await approveBtn.click() // wait for trx confirmation - await this.page.waitForTimeout(2000); + await this.page.waitForTimeout(2000) // Handle Pop-up page <- - return { msgContentAmount }; + return { msgContentAmount } } async sellAndGetWalletMsg(context: BrowserContext, limit = false) { // Make sure Sell button is enabled - await expect(this.sellBtn, "Sell button is disabled!").toBeEnabled({ + await expect(this.sellBtn, 'Sell button is disabled!').toBeEnabled({ timeout: 9000, - }); + }) // Handle Pop-up page -> - await this.sellBtn.click(); - const pageApprove = context.waitForEvent("page"); - await this.confirmSwapBtn.click(); - await this.page.waitForTimeout(200); - const approvePage = await pageApprove; - await approvePage.waitForLoadState(); - const approveBtn = approvePage.getByRole("button", { - name: "Approve", - }); - await expect(approveBtn).toBeEnabled(); - let msgTextLocator = "type: osmosis/poolmanager/"; + await this.sellBtn.click() + const pageApprove = context.waitForEvent('page') + await this.confirmSwapBtn.click() + await this.page.waitForTimeout(200) + const approvePage = await pageApprove + await approvePage.waitForLoadState() + const approveBtn = approvePage.getByRole('button', { + name: 'Approve', + }) + await expect(approveBtn).toBeEnabled() + let msgTextLocator = 'type: osmosis/poolmanager/' if (limit) { - msgTextLocator = "Execute contract"; + msgTextLocator = 'Execute contract' } const msgContentAmount = await approvePage .getByText(msgTextLocator) - .textContent(); - console.log(`Wallet is approving this msg: \n${msgContentAmount}`); + .textContent() + console.log(`Wallet is approving this msg: \n${msgContentAmount}`) // Approve trx - await approveBtn.click(); + await approveBtn.click() // wait for trx confirmation - await this.page.waitForTimeout(2000); + await this.page.waitForTimeout(2000) // Handle Pop-up page <- - return { msgContentAmount }; + return { msgContentAmount } } async sellAndApprove(context: BrowserContext) { // Make sure Sell button is enabled - await expect(this.sellBtn, "Sell button is disabled!").toBeEnabled({ + await expect(this.sellBtn, 'Sell button is disabled!').toBeEnabled({ timeout: 9000, - }); - await this.sellBtn.click(); - await this.confirmSwapBtn.click(); - await this.justApproveIfNeeded(context); - await this.page.waitForTimeout(1000); + }) + await this.sellBtn.click() + await this.confirmSwapBtn.click() + await this.justApproveIfNeeded(context) + await this.page.waitForTimeout(1000) } - async buyAndApprove(context: BrowserContext, limit = false) { - await expect(this.buyBtn, "Buy button is disabled!").toBeEnabled({ + async buyAndApprove(context: BrowserContext, _limit = false) { + await expect(this.buyBtn, 'Buy button is disabled!').toBeEnabled({ timeout: 9000, - }); - await this.buyBtn.click(); - await this.confirmSwapBtn.click(); - await this.justApproveIfNeeded(context); - await this.page.waitForTimeout(1000); + }) + await this.buyBtn.click() + await this.confirmSwapBtn.click() + await this.justApproveIfNeeded(context) + await this.page.waitForTimeout(1000) } async swapAndApprove(context: BrowserContext) { // Make sure to have sufficient balance and swap button is enabled expect( await this.isInsufficientBalanceForSwap(), - "Insufficient balance for the swap!" - ).toBeFalsy(); - console.log("Swap and Sign now.."); - await expect(this.swapBtn, "Swap button is disabled!").toBeEnabled({ + 'Insufficient balance for the swap!', + ).toBeFalsy() + console.log('Swap and Sign now..') + await expect(this.swapBtn, 'Swap button is disabled!').toBeEnabled({ timeout: 7000, - }); - await this.swapBtn.click({ timeout: 4000 }); - await this.confirmSwapBtn.click({ timeout: 5000 }); - await this.justApproveIfNeeded(context); + }) + await this.swapBtn.click({ timeout: 4000 }) + await this.confirmSwapBtn.click({ timeout: 5000 }) + await this.justApproveIfNeeded(context) } } diff --git a/packages/e2e/tests/monitoring.limit.wallet.spec.ts b/packages/e2e/tests/monitoring.limit.wallet.spec.ts index 9de6eaf2ab..51b1e714b6 100644 --- a/packages/e2e/tests/monitoring.limit.wallet.spec.ts +++ b/packages/e2e/tests/monitoring.limit.wallet.spec.ts @@ -1,83 +1,83 @@ -import { type BrowserContext, chromium, expect, test } from "@playwright/test"; -import { TestConfig } from "../test-config"; -import { UnzipExtension } from "../unzip-extension"; +import { type BrowserContext, chromium, expect, test } from '@playwright/test' +import { TestConfig } from '../test-config' +import { UnzipExtension } from '../unzip-extension' -import { WalletPage } from "../pages/keplr-page"; -import { TradePage } from "../pages/trade-page"; +import { WalletPage } from '../pages/keplr-page' +import { TradePage } from '../pages/trade-page' -test.describe("Test Filled Limit Order feature", () => { - let context: BrowserContext; - const privateKey = process.env.PRIVATE_KEY ?? "private_key"; - let tradePage: TradePage; +test.describe('Test Filled Limit Order feature', () => { + let context: BrowserContext + const privateKey = process.env.PRIVATE_KEY ?? 'private_key' + let tradePage: TradePage test.beforeAll(async () => { - const pathToExtension = new UnzipExtension().getPathToExtension(); - console.log("\nSetup Wallet Extension before tests."); + const pathToExtension = new UnzipExtension().getPathToExtension() + console.log('\nSetup Wallet Extension before tests.') // Launch Chrome with a Keplr wallet extension context = await chromium.launchPersistentContext( - "", - new TestConfig().getBrowserExtensionConfig(false, pathToExtension) - ); + '', + new TestConfig().getBrowserExtensionConfig(false, pathToExtension), + ) // Get all new pages (including Extension) in the context and wait - const emptyPage = context.pages()[0]; - await emptyPage.waitForTimeout(2000); - const page = context.pages()[1]; - const walletPage = new WalletPage(page); + const emptyPage = context.pages()[0] + await emptyPage.waitForTimeout(2000) + const page = context.pages()[1] + const walletPage = new WalletPage(page) // Import existing Wallet (could be aggregated in one function). - await walletPage.importWalletWithPrivateKey(privateKey); - await walletPage.setWalletNameAndPassword("Monitoring E2E Tests"); - await walletPage.selectChainsAndSave(); - await walletPage.finish(); + await walletPage.importWalletWithPrivateKey(privateKey) + await walletPage.setWalletNameAndPassword('Monitoring E2E Tests') + await walletPage.selectChainsAndSave() + await walletPage.finish() // Switch to Application - tradePage = new TradePage(context.pages()[0]); - await tradePage.goto(); - await tradePage.connectWallet(); - expect(await tradePage.isError(), "Swap is not available!").toBeFalsy(); - }); + tradePage = new TradePage(context.pages()[0]) + await tradePage.goto() + await tradePage.connectWallet() + expect(await tradePage.isError(), 'Swap is not available!').toBeFalsy() + }) test.afterAll(async () => { - await context.close(); - }); + await context.close() + }) // biome-ignore lint/correctness/noEmptyPattern: test.afterEach(async ({}, testInfo) => { - console.log(`Test [${testInfo.title}] status: ${testInfo.status}`); - if (testInfo.status === "failed") { - const name = testInfo.title; - process.env.GITHUB_STEP_SUMMARY = `Test ${name} failed.`; + console.log(`Test [${testInfo.title}] status: ${testInfo.status}`) + if (testInfo.status === 'failed') { + const name = testInfo.title + process.env.GITHUB_STEP_SUMMARY = `Test ${name} failed.` } - }); + }) - test("User should be able to limit sell OSMO", async () => { - await tradePage.goto(); - await tradePage.openSellTab(); - await tradePage.openLimit(); - await tradePage.selectAsset("OSMO"); - await tradePage.enterAmount("1.08"); - await tradePage.setLimitPriceChange("Market"); - await tradePage.sellAndApprove(context); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); + test('User should be able to limit sell OSMO', async () => { + await tradePage.goto() + await tradePage.openSellTab() + await tradePage.openLimit() + await tradePage.selectAsset('OSMO') + await tradePage.enterAmount('1.08') + await tradePage.setLimitPriceChange('Market') + await tradePage.sellAndApprove(context) + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) - test("User should be able to limit buy OSMO", async () => { - const PRICE_INCREASE_FACTOR = 1.07; // 7% increase for limit price - const ORDER_HISTORY_TIMEOUT = 30; // Seconds to wait for order history - await tradePage.goto(); - await tradePage.openBuyTab(); - await tradePage.openLimit(); - await tradePage.selectAsset("OSMO"); - await tradePage.enterAmount("1.04"); - await tradePage.setLimitPriceChange("Market"); - const limitPrice = Number(await tradePage.getLimitPrice()); - const highLimitPrice = (limitPrice * PRICE_INCREASE_FACTOR).toFixed(4); - await tradePage.setLimitPrice(String(highLimitPrice)); - await tradePage.buyAndApprove(context); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); + test('User should be able to limit buy OSMO', async () => { + const PRICE_INCREASE_FACTOR = 1.07 // 7% increase for limit price + const _ORDER_HISTORY_TIMEOUT = 30 // Seconds to wait for order history + await tradePage.goto() + await tradePage.openBuyTab() + await tradePage.openLimit() + await tradePage.selectAsset('OSMO') + await tradePage.enterAmount('1.04') + await tradePage.setLimitPriceChange('Market') + const limitPrice = Number(await tradePage.getLimitPrice()) + const highLimitPrice = (limitPrice * PRICE_INCREASE_FACTOR).toFixed(4) + await tradePage.setLimitPrice(String(highLimitPrice)) + await tradePage.buyAndApprove(context) + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() //await tradePage.gotoOrdersHistory(ORDER_HISTORY_TIMEOUT); //const p = context.pages()[0] //const trxPage = new TransactionsPage(p) //await trxPage.isFilledByLimitPrice(highLimitPrice) - }); -}); + }) +}) diff --git a/packages/e2e/tests/monitoring.market.wallet.spec.ts b/packages/e2e/tests/monitoring.market.wallet.spec.ts index d584ba4755..f31d4d1185 100644 --- a/packages/e2e/tests/monitoring.market.wallet.spec.ts +++ b/packages/e2e/tests/monitoring.market.wallet.spec.ts @@ -1,93 +1,93 @@ -import { type BrowserContext, chromium, expect, test } from "@playwright/test"; +import { type BrowserContext, chromium, expect, test } from '@playwright/test' -import { TestConfig } from "../test-config"; -import { UnzipExtension } from "../unzip-extension"; +import { TestConfig } from '../test-config' +import { UnzipExtension } from '../unzip-extension' -import { WalletPage } from "../pages/keplr-page"; -import { TradePage } from "../pages/trade-page"; +import { WalletPage } from '../pages/keplr-page' +import { TradePage } from '../pages/trade-page' -test.describe("Test Market Buy/Sell Order feature", () => { - let context: BrowserContext; - const privateKey = process.env.PRIVATE_KEY ?? "private_key"; - let tradePage: TradePage; - const TRX_SUCCESS_TIMEOUT = 10000; +test.describe('Test Market Buy/Sell Order feature', () => { + let context: BrowserContext + const privateKey = process.env.PRIVATE_KEY ?? 'private_key' + let tradePage: TradePage + const TRX_SUCCESS_TIMEOUT = 10000 test.beforeAll(async () => { - const pathToExtension = new UnzipExtension().getPathToExtension(); - console.log("\nSetup Wallet Extension before tests."); + const pathToExtension = new UnzipExtension().getPathToExtension() + console.log('\nSetup Wallet Extension before tests.') // Launch Chrome with a Keplr wallet extension context = await chromium.launchPersistentContext( - "", - new TestConfig().getBrowserExtensionConfig(false, pathToExtension) - ); + '', + new TestConfig().getBrowserExtensionConfig(false, pathToExtension), + ) // Get all new pages (including Extension) in the context and wait - const emptyPage = context.pages()[0]; - await emptyPage.waitForTimeout(2000); - const page = context.pages()[1]; - const walletPage = new WalletPage(page); + const emptyPage = context.pages()[0] + await emptyPage.waitForTimeout(2000) + const page = context.pages()[1] + const walletPage = new WalletPage(page) // Import existing Wallet (could be aggregated in one function). - await walletPage.importWalletWithPrivateKey(privateKey); - await walletPage.setWalletNameAndPassword("Monitoring E2E Tests"); - await walletPage.selectChainsAndSave(); - await walletPage.finish(); + await walletPage.importWalletWithPrivateKey(privateKey) + await walletPage.setWalletNameAndPassword('Monitoring E2E Tests') + await walletPage.selectChainsAndSave() + await walletPage.finish() // Switch to Application - tradePage = new TradePage(context.pages()[0]); - await tradePage.goto(); - await tradePage.connectWallet(); - expect(await tradePage.isError(), "Swap is not available!").toBeFalsy(); - }); + tradePage = new TradePage(context.pages()[0]) + await tradePage.goto() + await tradePage.connectWallet() + expect(await tradePage.isError(), 'Swap is not available!').toBeFalsy() + }) test.afterAll(async () => { - await context.close(); - }); + await context.close() + }) // biome-ignore lint/correctness/noEmptyPattern: test.afterEach(async ({}, testInfo) => { - console.log(`Test [${testInfo.title}] status: ${testInfo.status}`); - if (testInfo.status === "failed") { - const name = testInfo.title; - process.env.GITHUB_STEP_SUMMARY = `Test ${name} failed.`; - console.log(`GITHUB_STEP_SUMMARY: ${process.env.GITHUB_STEP_SUMMARY}`); + console.log(`Test [${testInfo.title}] status: ${testInfo.status}`) + if (testInfo.status === 'failed') { + const name = testInfo.title + process.env.GITHUB_STEP_SUMMARY = `Test ${name} failed.` + console.log(`GITHUB_STEP_SUMMARY: ${process.env.GITHUB_STEP_SUMMARY}`) } - }); + }) // biome-ignore lint/complexity/noForEach: - [{ name: "BTC" }, { name: "OSMO" }].forEach(({ name }) => { + ;[{ name: 'BTC' }, { name: 'OSMO' }].forEach(({ name }) => { test(`User should be able to Market Buy ${name}`, async () => { - await tradePage.goto(); - await tradePage.openBuyTab(); - await tradePage.selectAsset(name); - await tradePage.enterAmount("1.55"); - await tradePage.isSufficientBalanceForTrade(); - await tradePage.showSwapInfo(); - await tradePage.buyAndApprove(context); - await tradePage.isTransactionSuccesful(TRX_SUCCESS_TIMEOUT); - await tradePage.getTransactionUrl(); - }); - }); + await tradePage.goto() + await tradePage.openBuyTab() + await tradePage.selectAsset(name) + await tradePage.enterAmount('1.55') + await tradePage.isSufficientBalanceForTrade() + await tradePage.showSwapInfo() + await tradePage.buyAndApprove(context) + await tradePage.isTransactionSuccesful(TRX_SUCCESS_TIMEOUT) + await tradePage.getTransactionUrl() + }) + }) // unwrapped market sell tests just in case this affects anything. - test("User should be able to Market Sell BTC", async () => { - await tradePage.goto(); - await tradePage.openSellTab(); - await tradePage.selectAsset("BTC"); - await tradePage.enterAmount("1.54"); - await tradePage.isSufficientBalanceForTrade(); - await tradePage.showSwapInfo(); - await tradePage.sellAndApprove(context); - await tradePage.isTransactionSuccesful(TRX_SUCCESS_TIMEOUT); - await tradePage.getTransactionUrl(); - }); + test('User should be able to Market Sell BTC', async () => { + await tradePage.goto() + await tradePage.openSellTab() + await tradePage.selectAsset('BTC') + await tradePage.enterAmount('1.54') + await tradePage.isSufficientBalanceForTrade() + await tradePage.showSwapInfo() + await tradePage.sellAndApprove(context) + await tradePage.isTransactionSuccesful(TRX_SUCCESS_TIMEOUT) + await tradePage.getTransactionUrl() + }) - test("User should be able to Market Sell OSMO", async () => { - await tradePage.goto(); - await tradePage.openSellTab(); - await tradePage.selectAsset("OSMO"); - await tradePage.enterAmount("1.54"); - await tradePage.isSufficientBalanceForTrade(); - await tradePage.showSwapInfo(); - await tradePage.sellAndApprove(context); - await tradePage.isTransactionSuccesful(TRX_SUCCESS_TIMEOUT); - await tradePage.getTransactionUrl(); - }); -}); + test('User should be able to Market Sell OSMO', async () => { + await tradePage.goto() + await tradePage.openSellTab() + await tradePage.selectAsset('OSMO') + await tradePage.enterAmount('1.54') + await tradePage.isSufficientBalanceForTrade() + await tradePage.showSwapInfo() + await tradePage.sellAndApprove(context) + await tradePage.isTransactionSuccesful(TRX_SUCCESS_TIMEOUT) + await tradePage.getTransactionUrl() + }) +}) diff --git a/packages/e2e/tests/monitoring.swap.wallet.spec.ts b/packages/e2e/tests/monitoring.swap.wallet.spec.ts index 2c47d52a3b..e5dfdbf919 100644 --- a/packages/e2e/tests/monitoring.swap.wallet.spec.ts +++ b/packages/e2e/tests/monitoring.swap.wallet.spec.ts @@ -1,61 +1,61 @@ -import { type BrowserContext, chromium, expect, test } from "@playwright/test"; +import { type BrowserContext, chromium, expect, test } from '@playwright/test' -import { WalletPage } from "../pages/keplr-page"; -import { TradePage } from "../pages/trade-page"; -import { TestConfig } from "../test-config"; -import { UnzipExtension } from "../unzip-extension"; +import { WalletPage } from '../pages/keplr-page' +import { TradePage } from '../pages/trade-page' +import { TestConfig } from '../test-config' +import { UnzipExtension } from '../unzip-extension' -test.describe("Test Swap Stables feature", () => { - let context: BrowserContext; - const privateKey = process.env.PRIVATE_KEY ?? "private_key"; - let tradePage: TradePage; - const swapAmount = "0.55"; +test.describe('Test Swap Stables feature', () => { + let context: BrowserContext + const privateKey = process.env.PRIVATE_KEY ?? 'private_key' + let tradePage: TradePage + const swapAmount = '0.55' test.beforeAll(async () => { - const pathToExtension = new UnzipExtension().getPathToExtension(); - console.log("\nSetup Wallet Extension before tests."); + const pathToExtension = new UnzipExtension().getPathToExtension() + console.log('\nSetup Wallet Extension before tests.') // Launch Chrome with a Keplr wallet extension context = await chromium.launchPersistentContext( - "", - new TestConfig().getBrowserExtensionConfig(false, pathToExtension) - ); + '', + new TestConfig().getBrowserExtensionConfig(false, pathToExtension), + ) // Get all new pages (including Extension) in the context and wait - const emptyPage = context.pages()[0]; - await emptyPage.waitForTimeout(2000); - let page = context.pages()[1]; - const walletPage = new WalletPage(page); + const emptyPage = context.pages()[0] + await emptyPage.waitForTimeout(2000) + let page = context.pages()[1] + const walletPage = new WalletPage(page) // Import existing Wallet (could be aggregated in one function). - await walletPage.importWalletWithPrivateKey(privateKey); - await walletPage.setWalletNameAndPassword("Test Stables"); - await walletPage.selectChainsAndSave(); - await walletPage.finish(); - page = context.pages()[0]; - tradePage = new TradePage(page); + await walletPage.importWalletWithPrivateKey(privateKey) + await walletPage.setWalletNameAndPassword('Test Stables') + await walletPage.selectChainsAndSave() + await walletPage.finish() + page = context.pages()[0] + tradePage = new TradePage(page) - await tradePage.goto(); - await tradePage.connectWallet(); - expect(await tradePage.isError(), "Swap is not available!").toBeFalsy(); - }); + await tradePage.goto() + await tradePage.connectWallet() + expect(await tradePage.isError(), 'Swap is not available!').toBeFalsy() + }) test.afterAll(async () => { - await context.close(); - }); + await context.close() + }) // biome-ignore lint/complexity/noForEach: - [ - { from: "USDC", to: "USDC.eth.axl" }, - { from: "USDC.eth.axl", to: "USDC" }, - { from: "USDC", to: "USDT" }, - { from: "USDT", to: "USDC" }, + ;[ + { from: 'USDC', to: 'USDC.eth.axl' }, + { from: 'USDC.eth.axl', to: 'USDC' }, + { from: 'USDC', to: 'USDT' }, + { from: 'USDT', to: 'USDC' }, ].forEach(({ from, to }) => { test(`User should be able to swap ${from} to ${to}`, async () => { - await tradePage.goto(); - await tradePage.selectPair(from, to); - await tradePage.enterAmount(swapAmount); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); - }); -}); + await tradePage.goto() + await tradePage.selectPair(from, to) + await tradePage.enterAmount(swapAmount) + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) + }) +}) diff --git a/packages/e2e/tests/select.spec.ts b/packages/e2e/tests/select.spec.ts index f78f4bc23f..5dca8db5be 100644 --- a/packages/e2e/tests/select.spec.ts +++ b/packages/e2e/tests/select.spec.ts @@ -4,104 +4,104 @@ import { type Page, chromium, test, -} from "@playwright/test"; +} from '@playwright/test' -import { TradePage } from "../pages/trade-page"; -import { TestConfig } from "../test-config"; +import { TradePage } from '../pages/trade-page' +import { TestConfig } from '../test-config' // Pairs are selected from top 10 -test.describe("Test Select Swap Pair feature", () => { - let context: BrowserContext; - let swapPage: TradePage; - let page: Page; +test.describe('Test Select Swap Pair feature', () => { + let context: BrowserContext + let swapPage: TradePage + let page: Page test.beforeAll(async () => { context = await chromium.launchPersistentContext( - "", - new TestConfig().getBrowserConfig(true) - ); - page = context.pages()[0]; - swapPage = new TradePage(page); - await swapPage.goto(); - }); + '', + new TestConfig().getBrowserConfig(true), + ) + page = context.pages()[0] + swapPage = new TradePage(page) + await swapPage.goto() + }) test.afterAll(async () => { - await context.close(); - }); - - test("User should be able to select BTC/USDC", async () => { - await swapPage.goto(); - await swapPage.selectPair("nBTC", "USDC"); - await swapPage.enterAmount("0.01"); - await swapPage.showSwapInfo(); - }); - - test("User should be able to select WBTC/USDC", async () => { - await swapPage.goto(); - await swapPage.selectPair("WBTC", "USDC"); - await swapPage.enterAmount("0.1"); - await swapPage.showSwapInfo(); - }); - - test("User should be able to select OSMO/USDC", async () => { - await swapPage.goto(); - await swapPage.selectPair("OSMO", "USDC"); - await swapPage.enterAmount("1"); - await swapPage.showSwapInfo(); - }); - - test("User should be able to select INJ/USDC", async () => { - await swapPage.goto(); - await swapPage.selectPair("INJ", "USDC"); - await swapPage.enterAmount("10"); - await swapPage.showSwapInfo(); - }); - - test("User should be able to select TIA/USDC", async () => { - await swapPage.goto(); - await swapPage.selectPair("TIA", "USDC"); - await swapPage.enterAmount("100"); - await swapPage.showSwapInfo(); - }); - - test("User should be able to select ATOM/USDC", async () => { - await swapPage.selectPair("ATOM", "USDC"); - await swapPage.enterAmount("100"); - }); - - test("User should be able to select USDT/USDC", async () => { - await swapPage.selectPair("USDT", "USDC"); - await swapPage.enterAmount("10000"); - }); - - test("User should be able to select TIA/OSMO", async () => { - await swapPage.selectPair("TIA", "OSMO"); - await swapPage.enterAmount("100"); - }); - - test("User should be able to select AKT/OSMO", async () => { - await swapPage.selectPair("AKT", "OSMO"); - await swapPage.enterAmount("100"); - }); - - test("User should be able to select PICA/OSMO", async () => { - await swapPage.selectPair("PICA", "OSMO"); - await swapPage.enterAmount("100"); - }); - - test("User should be able to select USDT/OSMO", async () => { - await swapPage.selectPair("USDT", "OSMO"); - await swapPage.enterAmount("100"); - }); - - test("User should be able to select TIA/BOOT", async () => { + await context.close() + }) + + test('User should be able to select BTC/USDC', async () => { + await swapPage.goto() + await swapPage.selectPair('nBTC', 'USDC') + await swapPage.enterAmount('0.01') + await swapPage.showSwapInfo() + }) + + test('User should be able to select WBTC/USDC', async () => { + await swapPage.goto() + await swapPage.selectPair('WBTC', 'USDC') + await swapPage.enterAmount('0.1') + await swapPage.showSwapInfo() + }) + + test('User should be able to select OSMO/USDC', async () => { + await swapPage.goto() + await swapPage.selectPair('OSMO', 'USDC') + await swapPage.enterAmount('1') + await swapPage.showSwapInfo() + }) + + test('User should be able to select INJ/USDC', async () => { + await swapPage.goto() + await swapPage.selectPair('INJ', 'USDC') + await swapPage.enterAmount('10') + await swapPage.showSwapInfo() + }) + + test('User should be able to select TIA/USDC', async () => { + await swapPage.goto() + await swapPage.selectPair('TIA', 'USDC') + await swapPage.enterAmount('100') + await swapPage.showSwapInfo() + }) + + test('User should be able to select ATOM/USDC', async () => { + await swapPage.selectPair('ATOM', 'USDC') + await swapPage.enterAmount('100') + }) + + test('User should be able to select USDT/USDC', async () => { + await swapPage.selectPair('USDT', 'USDC') + await swapPage.enterAmount('10000') + }) + + test('User should be able to select TIA/OSMO', async () => { + await swapPage.selectPair('TIA', 'OSMO') + await swapPage.enterAmount('100') + }) + + test('User should be able to select AKT/OSMO', async () => { + await swapPage.selectPair('AKT', 'OSMO') + await swapPage.enterAmount('100') + }) + + test('User should be able to select PICA/OSMO', async () => { + await swapPage.selectPair('PICA', 'OSMO') + await swapPage.enterAmount('100') + }) + + test('User should be able to select USDT/OSMO', async () => { + await swapPage.selectPair('USDT', 'OSMO') + await swapPage.enterAmount('100') + }) + + test('User should be able to select TIA/BOOT', async () => { // Just to verify some odd pair - await swapPage.selectPair("TIA", "BOOT"); - await swapPage.enterAmount("100"); - }); - - test("User should be able to select stATOM/USDC", async () => { - await swapPage.selectPair("stATOM", "USDC"); - await swapPage.enterAmount("100"); - }); -}); + await swapPage.selectPair('TIA', 'BOOT') + await swapPage.enterAmount('100') + }) + + test('User should be able to select stATOM/USDC', async () => { + await swapPage.selectPair('stATOM', 'USDC') + await swapPage.enterAmount('100') + }) +}) diff --git a/packages/e2e/tests/swap.osmo.wallet.spec.ts b/packages/e2e/tests/swap.osmo.wallet.spec.ts index 06302eaca7..3ca6a138f5 100644 --- a/packages/e2e/tests/swap.osmo.wallet.spec.ts +++ b/packages/e2e/tests/swap.osmo.wallet.spec.ts @@ -1,83 +1,83 @@ -import { type BrowserContext, chromium, expect, test } from "@playwright/test"; +import { type BrowserContext, chromium, expect, test } from '@playwright/test' -import { TestConfig } from "../test-config"; -import { UnzipExtension } from "../unzip-extension"; +import { TestConfig } from '../test-config' +import { UnzipExtension } from '../unzip-extension' -import { WalletPage } from "../pages/keplr-page"; -import { TradePage } from "../pages/trade-page"; +import { WalletPage } from '../pages/keplr-page' +import { TradePage } from '../pages/trade-page' -test.describe("Test Swap to/from OSMO feature", () => { - let context: BrowserContext; - const walletId = - process.env.WALLET_ID ?? "osmo1qyc8u7cn0zjxcu9dvrjz5zwfnn0ck92v62ak9l"; - const privateKey = process.env.PRIVATE_KEY ?? "private_key"; - let tradePage: TradePage; - const ATOM = - "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"; +test.describe('Test Swap to/from OSMO feature', () => { + let context: BrowserContext + const _walletId = + process.env.WALLET_ID ?? 'osmo1qyc8u7cn0zjxcu9dvrjz5zwfnn0ck92v62ak9l' + const privateKey = process.env.PRIVATE_KEY ?? 'private_key' + let tradePage: TradePage + const _ATOM = + 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2' test.beforeAll(async () => { - const pathToExtension = new UnzipExtension().getPathToExtension(); - console.log("\nSetup Wallet Extension before tests."); + const pathToExtension = new UnzipExtension().getPathToExtension() + console.log('\nSetup Wallet Extension before tests.') // Launch Chrome with a Keplr wallet extension context = await chromium.launchPersistentContext( - "", - new TestConfig().getBrowserExtensionConfig(false, pathToExtension) - ); + '', + new TestConfig().getBrowserExtensionConfig(false, pathToExtension), + ) // Get all new pages (including Extension) in the context and wait - const emptyPage = context.pages()[0]; - await emptyPage.waitForTimeout(2000); - const page = context.pages()[1]; - const walletPage = new WalletPage(page); + const emptyPage = context.pages()[0] + await emptyPage.waitForTimeout(2000) + const page = context.pages()[1] + const walletPage = new WalletPage(page) // Import existing Wallet (could be aggregated in one function). - await walletPage.importWalletWithPrivateKey(privateKey); - await walletPage.setWalletNameAndPassword("Test Swaps"); - await walletPage.selectChainsAndSave(); - await walletPage.finish(); + await walletPage.importWalletWithPrivateKey(privateKey) + await walletPage.setWalletNameAndPassword('Test Swaps') + await walletPage.selectChainsAndSave() + await walletPage.finish() // Switch to Application - tradePage = new TradePage(context.pages()[0]); - await tradePage.goto(); - await tradePage.connectWallet(); - expect(await tradePage.isError(), "Swap is not available!").toBeFalsy(); - }); + tradePage = new TradePage(context.pages()[0]) + await tradePage.goto() + await tradePage.connectWallet() + expect(await tradePage.isError(), 'Swap is not available!').toBeFalsy() + }) test.afterAll(async () => { - await context.close(); - }); + await context.close() + }) - test.skip("User should be able to swap OSMO to WBTC", async () => { - await tradePage.goto(); - await tradePage.selectPair("OSMO", "WBTC"); - await tradePage.enterAmount("0.9"); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); + test.skip('User should be able to swap OSMO to WBTC', async () => { + await tradePage.goto() + await tradePage.selectPair('OSMO', 'WBTC') + await tradePage.enterAmount('0.9') + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain("denom: uosmo"); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) - test("User should be able to swap OSMO to ATOM", async () => { - await tradePage.goto(); - await tradePage.selectPair("OSMO", "ATOM"); - await tradePage.enterAmount("0.2"); - await tradePage.swapAndApprove(context); + test('User should be able to swap OSMO to ATOM', async () => { + await tradePage.goto() + await tradePage.selectPair('OSMO', 'ATOM') + await tradePage.enterAmount('0.2') + await tradePage.swapAndApprove(context) //expect(msgContent).toContain(`token_out_denom: ${ATOM}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain("denom: uosmo"); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) - test("User should be able to swap ATOM to OSMO", async () => { - await tradePage.goto(); - await tradePage.selectPair("ATOM", "OSMO"); - await tradePage.enterAmount("0.01"); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); + test('User should be able to swap ATOM to OSMO', async () => { + await tradePage.goto() + await tradePage.selectPair('ATOM', 'OSMO') + await tradePage.enterAmount('0.01') + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) //expect(msgContent).toContain(`denom: ${ATOM}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain("token_out_denom: uosmo"); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); -}); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) +}) diff --git a/packages/e2e/tests/swap.usdc.wallet.spec.ts b/packages/e2e/tests/swap.usdc.wallet.spec.ts index b7e157a7e7..7e72daf5c7 100644 --- a/packages/e2e/tests/swap.usdc.wallet.spec.ts +++ b/packages/e2e/tests/swap.usdc.wallet.spec.ts @@ -1,184 +1,184 @@ -import { type BrowserContext, chromium, expect, test } from "@playwright/test"; +import { type BrowserContext, chromium, expect, test } from '@playwright/test' -import { TestConfig } from "../test-config"; -import { UnzipExtension } from "../unzip-extension"; +import { TestConfig } from '../test-config' +import { UnzipExtension } from '../unzip-extension' -import { WalletPage } from "../pages/keplr-page"; -import { TradePage } from "../pages/trade-page"; +import { WalletPage } from '../pages/keplr-page' +import { TradePage } from '../pages/trade-page' -test.describe("Test Swap to/from USDC feature", () => { - let context: BrowserContext; - const walletId = - process.env.WALLET_ID ?? "osmo1qyc8u7cn0zjxcu9dvrjz5zwfnn0ck92v62ak9l"; - const privateKey = process.env.PRIVATE_KEY ?? "private_key"; - let tradePage: TradePage; - const USDC = - "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4"; - const ATOM = - "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"; - const TIA = - "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877"; - const INJ = - "ibc/64BA6E31FE887D66C6F8F31C7B1A80C7CA179239677B4088BB55F5EA07DBE273"; - const AKT = - "ibc/1480B8FD20AD5FCAE81EA87584D269547DD4D436843C1D20F15E00EB64743EF4"; +test.describe('Test Swap to/from USDC feature', () => { + let context: BrowserContext + const _walletId = + process.env.WALLET_ID ?? 'osmo1qyc8u7cn0zjxcu9dvrjz5zwfnn0ck92v62ak9l' + const privateKey = process.env.PRIVATE_KEY ?? 'private_key' + let tradePage: TradePage + const _USDC = + 'ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4' + const _ATOM = + 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2' + const _TIA = + 'ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877' + const _INJ = + 'ibc/64BA6E31FE887D66C6F8F31C7B1A80C7CA179239677B4088BB55F5EA07DBE273' + const _AKT = + 'ibc/1480B8FD20AD5FCAE81EA87584D269547DD4D436843C1D20F15E00EB64743EF4' test.beforeAll(async () => { - const pathToExtension = new UnzipExtension().getPathToExtension(); - console.log("\nSetup Wallet Extension before tests."); + const pathToExtension = new UnzipExtension().getPathToExtension() + console.log('\nSetup Wallet Extension before tests.') // Launch Chrome with a Keplr wallet extension context = await chromium.launchPersistentContext( - "", - new TestConfig().getBrowserExtensionConfig(false, pathToExtension) - ); + '', + new TestConfig().getBrowserExtensionConfig(false, pathToExtension), + ) // Get all new pages (including Extension) in the context and wait - const emptyPage = context.pages()[0]; - await emptyPage.waitForTimeout(2000); - const page = context.pages()[1]; - const walletPage = new WalletPage(page); + const emptyPage = context.pages()[0] + await emptyPage.waitForTimeout(2000) + const page = context.pages()[1] + const walletPage = new WalletPage(page) // Import existing Wallet (could be aggregated in one function). - await walletPage.importWalletWithPrivateKey(privateKey); - await walletPage.setWalletNameAndPassword("Test Swaps"); - await walletPage.selectChainsAndSave(); - await walletPage.finish(); + await walletPage.importWalletWithPrivateKey(privateKey) + await walletPage.setWalletNameAndPassword('Test Swaps') + await walletPage.selectChainsAndSave() + await walletPage.finish() // Switch to Application - tradePage = new TradePage(context.pages()[0]); - await tradePage.goto(); - await tradePage.connectWallet(); - expect(await tradePage.isError(), "Swap is not available!").toBeFalsy(); - }); + tradePage = new TradePage(context.pages()[0]) + await tradePage.goto() + await tradePage.connectWallet() + expect(await tradePage.isError(), 'Swap is not available!').toBeFalsy() + }) test.afterAll(async () => { - await context.close(); - }); + await context.close() + }) - test("User should be able to swap OSMO to USDC", async () => { - await tradePage.goto(); - await tradePage.selectPair("OSMO", "USDC"); - await tradePage.enterAmount("0.2"); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); + test('User should be able to swap OSMO to USDC', async () => { + await tradePage.goto() + await tradePage.selectPair('OSMO', 'USDC') + await tradePage.enterAmount('0.2') + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) //expect(msgContent).toContain(`token_out_denom: ${USDC}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain("denom: uosmo"); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) - test("User should be able to swap USDC to OSMO", async () => { - await tradePage.goto(); - await tradePage.selectPair("USDC", "OSMO"); - await tradePage.enterAmount("0.1"); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); + test('User should be able to swap USDC to OSMO', async () => { + await tradePage.goto() + await tradePage.selectPair('USDC', 'OSMO') + await tradePage.enterAmount('0.1') + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) //expect(msgContent).toContain("token_out_denom: uosmo"); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain(`denom: ${USDC}`); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) - test("User should be able to swap ATOM to USDC", async () => { - await tradePage.goto(); - await tradePage.selectPair("ATOM", "USDC"); - await tradePage.enterAmount("0.015"); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); + test('User should be able to swap ATOM to USDC', async () => { + await tradePage.goto() + await tradePage.selectPair('ATOM', 'USDC') + await tradePage.enterAmount('0.015') + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) //expect(msgContent).toContain(`denom: ${ATOM}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain(`token_out_denom: ${USDC}`); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) - test("User should be able to swap USDC to ATOM", async () => { - await tradePage.goto(); - await tradePage.selectPair("USDC", "ATOM"); - await tradePage.enterAmount("0.1"); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); + test('User should be able to swap USDC to ATOM', async () => { + await tradePage.goto() + await tradePage.selectPair('USDC', 'ATOM') + await tradePage.enterAmount('0.1') + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) //expect(msgContent).toContain(`denom: ${USDC}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain(`token_out_denom: ${ATOM}`); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) - test("User should be able to swap USDC to TIA", async () => { - await tradePage.goto(); - await tradePage.selectPair("USDC", "TIA"); - await tradePage.enterAmount("0.1"); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); + test('User should be able to swap USDC to TIA', async () => { + await tradePage.goto() + await tradePage.selectPair('USDC', 'TIA') + await tradePage.enterAmount('0.1') + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) //expect(msgContent).toContain(`denom: ${USDC}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain(`token_out_denom: ${TIA}`); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) - test("User should be able to swap TIA to USDC", async () => { - await tradePage.goto(); - await tradePage.selectPair("TIA", "USDC"); - await tradePage.enterAmount("0.02"); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); + test('User should be able to swap TIA to USDC', async () => { + await tradePage.goto() + await tradePage.selectPair('TIA', 'USDC') + await tradePage.enterAmount('0.02') + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) //expect(msgContent).toContain(`denom: ${TIA}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain(`token_out_denom: ${USDC}`); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) - test("User should be able to swap USDC to INJ", async () => { - await tradePage.goto(); - await tradePage.selectPair("USDC", "INJ"); - await tradePage.enterAmount("0.2"); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); + test('User should be able to swap USDC to INJ', async () => { + await tradePage.goto() + await tradePage.selectPair('USDC', 'INJ') + await tradePage.enterAmount('0.2') + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) //expect(msgContent).toContain(`denom: ${USDC}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain(`token_out_denom: ${INJ}`); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) - test("User should be able to swap INJ to USDC", async () => { - await tradePage.goto(); - await tradePage.selectPair("INJ", "USDC"); - await tradePage.enterAmount("0.01"); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); + test('User should be able to swap INJ to USDC', async () => { + await tradePage.goto() + await tradePage.selectPair('INJ', 'USDC') + await tradePage.enterAmount('0.01') + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) //expect(msgContent).toContain(`denom: ${INJ}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain(`token_out_denom: ${USDC}`); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) - test("User should be able to swap USDC to AKT", async () => { - await tradePage.goto(); - await tradePage.selectPair("USDC", "AKT"); - await tradePage.enterAmount("0.1"); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); + test('User should be able to swap USDC to AKT', async () => { + await tradePage.goto() + await tradePage.selectPair('USDC', 'AKT') + await tradePage.enterAmount('0.1') + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) //expect(msgContent).toContain(`denom: ${USDC}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain(`token_out_denom: ${AKT}`); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) - test("User should be able to swap AKT to USDC", async () => { - await tradePage.goto(); - await tradePage.selectPair("AKT", "USDC"); - await tradePage.enterAmount("0.025"); - await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context); + test('User should be able to swap AKT to USDC', async () => { + await tradePage.goto() + await tradePage.selectPair('AKT', 'USDC') + await tradePage.enterAmount('0.025') + await tradePage.showSwapInfo() + await tradePage.swapAndApprove(context) //expect(msgContent).toContain(`denom: ${AKT}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain(`token_out_denom: ${USDC}`); - await tradePage.isTransactionSuccesful(); - await tradePage.getTransactionUrl(); - }); -}); + await tradePage.isTransactionSuccesful() + await tradePage.getTransactionUrl() + }) +}) diff --git a/packages/mobile/app/(tabs)/index.tsx b/packages/mobile/app/(tabs)/index.tsx index 669ba9c143..054962c80b 100644 --- a/packages/mobile/app/(tabs)/index.tsx +++ b/packages/mobile/app/(tabs)/index.tsx @@ -10,9 +10,9 @@ import { toast } from "sonner-native"; import { CopyIcon } from "~/components/icons/copy"; import { ProfileWoz } from "~/components/icons/profile-woz"; import { SettingsIcon } from "~/components/icons/settings"; +import { ManageWalletBottomSheet } from "~/components/portfolio/manage-wallet-bottom-sheet"; import { PortfolioAssetBalancesTable } from "~/components/portfolio/portfolio-asset-balances-table"; import { PortfolioValue } from "~/components/portfolio/portfolio-value"; -import { WalletBottomSheet } from "~/components/portfolio/wallet-bottom-sheet"; import { Text } from "~/components/ui/text"; import { Colors } from "~/constants/theme-colors"; import { useClipboard } from "~/hooks/use-clipboard"; @@ -31,6 +31,8 @@ export default function PortfolioScreen() { }; const handleAvatarPress = () => { + // TODO: Support multiple wallets + return; bottomSheetModalRef.current?.present(); }; @@ -54,6 +56,7 @@ export default function PortfolioScreen() { @@ -92,7 +95,7 @@ export default function PortfolioScreen() { - + ); } diff --git a/packages/mobile/app/_layout.tsx b/packages/mobile/app/_layout.tsx index 8f073b8d0c..1ffe0c5492 100644 --- a/packages/mobile/app/_layout.tsx +++ b/packages/mobile/app/_layout.tsx @@ -9,6 +9,7 @@ import { ThemeProvider } from "@react-navigation/native"; import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { persistQueryClient } from "@tanstack/react-query-persist-client"; +import { loggerLink } from "@trpc/client"; import { useFonts } from "expo-font"; import { Redirect, Stack } from "expo-router"; import * as SplashScreen from "expo-splash-screen"; @@ -59,6 +60,7 @@ persistQueryClient({ const trpcKey = key.join(".") as RouterKeys; const excludedKeys: RouterKeys[] = [ "local.assets.getAssetHistoricalPrice", + "local.oneClickTrading.getSessionAuthenticator", ]; /** @@ -94,11 +96,11 @@ export default function RootLayout() { api.createClient({ transformer: superjson, links: [ - // loggerLink({ - // enabled: (opts) => - // process.env.NODE_ENV === "development" || - // (opts.direction === "down" && opts.result instanceof Error), - // }), + loggerLink({ + enabled: (opts) => + process.env.NODE_ENV === "development" || + (opts.direction === "down" && opts.result instanceof Error), + }), (runtime) => { const removeLastSlash = (url: string) => url.replace(/\/$/, ""); const servers = { @@ -205,7 +207,7 @@ const OnboardingObserver = () => { const { currentWallet, wallets } = useWallets(); if (!currentWallet && wallets.length === 0) { - return ; + return ; } return null; diff --git a/packages/mobile/app/onboarding/camera-scan.tsx b/packages/mobile/app/onboarding/camera-scan.tsx index 3153403db9..b533faddf4 100644 --- a/packages/mobile/app/onboarding/camera-scan.tsx +++ b/packages/mobile/app/onboarding/camera-scan.tsx @@ -1,6 +1,6 @@ -import { AvailableOneClickTradingMessages } from "@osmosis-labs/types"; import { deserializeWebRTCMessage, + MobileSessionEncryptedDataSchema, serializeWebRTCMessage, STUN_SERVER, } from "@osmosis-labs/utils"; @@ -17,7 +17,9 @@ import { ConnectionProgressModal } from "~/components/connection-progress-modal" import { RouteHeader } from "~/components/route-header"; import { Text } from "~/components/ui/text"; import { Colors } from "~/constants/theme-colors"; +import { useOsBiometricAuthEnabled } from "~/hooks/biometrics"; import { useStateRef } from "~/hooks/use-state-ref"; +import { useCurrentWalletStore } from "~/stores/current-wallet"; import { decryptAES } from "~/utils/encryption"; import { api } from "~/utils/trpc"; import { WalletFactory } from "~/utils/wallet-factory"; @@ -142,14 +144,10 @@ const useWalletCreationWebRTC = ({ data.encryptedData, secretRef.current ); - const { address, allowedMessages, key, publicKey } = JSON.parse( - decryptedData - ) as { - address: string; - allowedMessages: AvailableOneClickTradingMessages[]; - key: string; - publicKey: string; - }; + const { address, allowedMessages, key, publicKey } = + MobileSessionEncryptedDataSchema.parse( + JSON.parse(decryptedData) + ); // Store the decrypted data in secure storage await new WalletFactory().createWallet({ @@ -160,9 +158,12 @@ const useWalletCreationWebRTC = ({ privateKey: key, publicKey, name: "Wallet 1", - version: 1, + version: data.version, }, }); + useCurrentWalletStore.setState({ + currentSelectedWalletIndex: 0, + }); setStatus("Verified"); } @@ -259,6 +260,8 @@ export default function Welcome() { const [autoFocus, setAutoFocus] = useState("off"); const [sessionToken, setSessionToken] = useState(""); const [showConnectionModal, setShowConnectionModal] = useState(false); + const { isCheckingBiometricAvailability, isBiometricEnabled } = + useOsBiometricAuthEnabled(); const shouldFreezeCamera = !!sessionToken; @@ -294,7 +297,9 @@ export default function Welcome() { }; const handleClose = () => { - if (status === "Verified") { + if (status === "Verified" && isBiometricEnabled) { + router.replace("/onboarding/set-up-biometrics"); + } else if (status === "Verified" && !isBiometricEnabled) { router.replace("/(tabs)"); } else { setShowConnectionModal(false); @@ -361,6 +366,7 @@ export default function Welcome() { )} - - - - - {t("profile.viewAllAssets")} - - - - - {featureFlags.transactionsPage && ( - -
-
- - - {t("profile.transactions")} - -
- -
- - )} - - -
-
- -

- {t("profile.wallet")} -

-
- -
-
-
- wallet-icon -
- -
-

Cosmos

-
-

- {shorten(address)} -

- -
-
-
- -
- { - logEvent([ - EventName.ProfileModal.blockExplorerLinkOutClicked, - ]); - }} - > - - - - { - logEvent([EventName.ProfileModal.qrCodeClicked]); - onOpenQR(); - }} - onClose={onCloseQR} - > - - - - - - - - - -
Cosmos
-
- -
- -
-

- {shorten(address, { prefixLength: 10 })} -

- -
-
-
-
- - { - logEvent([EventName.ProfileModal.logOutClicked]); - try { - setIsDisconnecting(true); - await wallet?.disconnect(true); - props.onRequestClose(); - } catch (e) { - throw e; - } finally { - setIsDisconnecting(false); - } - }} - className="group hover:text-rust-500" - > - {isDisconnecting ? ( - - ) : ( - - )} - -
-
-
- {show1CT && !isNewAccount && ( - { - props.onRequestClose(); - }} - /> - )} - - )} - - - - ); -}); - -const OneClickTradingProfileSection: FunctionComponent<{ - setShow1CTSettings: (value: boolean) => void; - onRestartSession: () => void; -}> = ({ setShow1CTSettings, onRestartSession }) => { - const { logEvent } = useAmplitudeAnalytics(); - const { accountStore } = useStore(); - const { t } = useTranslation(); - const { isOneClickTradingExpired, oneClickTradingInfo } = - useOneClickTradingSession(); - - const create1CTSession = useCreateOneClickTradingSession(); - const account = accountStore.getWallet(accountStore.osmosisChainId); - - const shouldFetchSessionAuthenticator = - !!account?.address && !!oneClickTradingInfo; - const { data: sessionAuthenticator } = - api.local.oneClickTrading.getSessionAuthenticator.useQuery( - { - userOsmoAddress: account?.address ?? "", - publicKey: oneClickTradingInfo?.publicKey ?? "", - }, - { - enabled: shouldFetchSessionAuthenticator, - cacheTime: 15_000, // 15 seconds - staleTime: 15_000, // 15 seconds - retry: false, - } - ); - - return ( -
{ - setShow1CTSettings(true); - logEvent([ - EventName.OneClickTrading.accessed, - { - source: "profile-section", - }, - ]); - }} - className="group flex w-full cursor-pointer items-center justify-between rounded-b-2xl border border-t-0 border-osmoverse-700 bg-osmoverse-800 px-5 py-3" - > -
- 1-Click trading icon -

- {t("profile.oneClickTrading")} -

- {isOneClickTradingExpired && oneClickTradingInfo && ( - - )} -
- - -
- ); -}; - -const ActionButton = forwardRef< - any, - ButtonHTMLAttributes & - AnchorHTMLAttributes & { isLink?: boolean } ->((props, ref) => { - const { isLink, ...rest } = props; - const Component = (isLink ? "a" : "button") as ElementType; - return ( - - {props.children} - - ); -}); - -const BaseAvatar = forwardRef< - any, - HTMLAttributes & { - isSelectable?: boolean; - isSelected?: boolean; - onSelect?: () => void; - } ->(({ isSelectable, isSelected, ...props }, ref) => { - return ( - - ); -}); - -const WosmongtonAvatar = forwardRef>( - (props, ref) => { - return ( - - Wosmongton profile avatar - - ); - } -); - -const AmmeliaAvatar = forwardRef>( - (props, ref) => { - return ( - - Wosmongton profile avatar - - ); - } -); diff --git a/packages/web/modals/profile/index.tsx b/packages/web/modals/profile/index.tsx new file mode 100644 index 0000000000..44e034f074 --- /dev/null +++ b/packages/web/modals/profile/index.tsx @@ -0,0 +1,719 @@ +import { DEFAULT_VS_CURRENCY } from "@osmosis-labs/server"; +import { Dec, PricePretty } from "@osmosis-labs/unit"; +import { formatICNSName, formatPretty, shorten } from "@osmosis-labs/utils"; +import classNames from "classnames"; +import { observer } from "mobx-react-lite"; +import dynamic from "next/dynamic"; +import Image from "next/image"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import { + AnchorHTMLAttributes, + ButtonHTMLAttributes, + ComponentProps, + ElementType, + forwardRef, + FunctionComponent, + HTMLAttributes, + useEffect, + useState, +} from "react"; +import { useCopyToClipboard, useTimeoutFn } from "react-use"; + +import { + CopyIcon, + ExternalLinkIcon, + Icon, + LogOutIcon, + QRIcon, +} from "~/components/assets"; +import { CreditCardIcon } from "~/components/assets/credit-card-icon"; +import { MobileSessionIcon } from "~/components/assets/mobile-session-icon"; +import { + Drawer, + DrawerButton, + DrawerContent, + DrawerOverlay, + DrawerPanel, +} from "~/components/drawers"; +import { Spinner } from "~/components/loaders/spinner"; +import { OneClickTradingRemainingTime } from "~/components/one-click-trading/one-click-remaining-time"; +import { ProfileOneClickTradingSettings } from "~/components/one-click-trading/profile-one-click-trading-settings"; +import { Screen, ScreenManager } from "~/components/screen-manager"; +import { ArrowButton, Button } from "~/components/ui/button"; +import { EventName } from "~/config"; +import { + getParametersFromOneClickTradingInfo, + useFeatureFlags, + useOneClickTradingSession, + useTranslation, +} from "~/hooks"; +import { useAmplitudeAnalytics, useDisclosure, useWindowSize } from "~/hooks"; +import { useBridgeStore } from "~/hooks/bridge"; +import { useCreateOneClickTradingSession } from "~/hooks/mutations/one-click-trading"; +import { useIsCosmosNewAccount } from "~/hooks/use-is-cosmos-new-account"; +import { ModalBase, ModalBaseProps } from "~/modals/base"; +import { useStore } from "~/stores"; +import { api } from "~/utils/trpc"; + +import { MobileSessions } from "./mobile-sessions"; + +const QRCode = dynamic(() => + import("~/components/qrcode").then((module) => module.QRCode) +); + +export const ProfileModal: FunctionComponent< + ModalBaseProps & { icnsName?: string } +> = observer((props) => { + const { t } = useTranslation(); + const { width } = useWindowSize(); + const { accountStore, profileStore } = useStore(); + const { logEvent } = useAmplitudeAnalytics(); + const router = useRouter(); + const fiatRampSelection = useBridgeStore((state) => state.fiatRampSelection); + const featureFlags = useFeatureFlags(); + + const { + isOpen: isAvatarSelectOpen, + onClose: onCloseAvatarSelect, + onOpen: onOpenAvatarSelect, + } = useDisclosure(); + const { + isOpen: isQROpen, + onClose: onCloseQR, + onOpen: onOpenQR, + } = useDisclosure(); + + const wallet = accountStore.getWallet(accountStore.osmosisChainId); + const { isNewAccount } = useIsCosmosNewAccount({ address: wallet?.address }); + const show1CT = featureFlags.oneClickTrading && !isNewAccount; + + const [show1CTSettings, setShow1CTSettings] = useState(false); + const [isMobileSessionOpen, setIsMobileSessionOpen] = useState(false); + const [hasCopied, setHasCopied] = useState(false); + const [isDisconnecting, setIsDisconnecting] = useState(false); + const [_state, copyToClipboard] = useCopyToClipboard(); + const [_isReady, _cancel, reset] = useTimeoutFn( + () => setHasCopied(false), + 2000 + ); + + const address = wallet?.address ?? ""; + + const { data: userOsmoAsset } = api.edge.assets.getUserAsset.useQuery( + { + findMinDenomOrSymbol: "OSMO", + userOsmoAddress: wallet?.address ?? "", + }, + { + enabled: Boolean(wallet?.address) && typeof wallet?.address === "string", + } + ); + + const onCopyAddress = () => { + copyToClipboard(address); + logEvent([EventName.ProfileModal.copyWalletAddressClicked]); + setHasCopied(true); + reset(); + }; + + useEffect(() => { + const onCloseModal = () => props.onRequestClose?.(); + router.events.on("routeChangeComplete", onCloseModal); + return () => router.events.off("routeChangeComplete", onCloseModal); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + <> + { + if (isMobileSessionOpen) return t("profile.mobileSessions"); + if (show1CTSettings) return ""; + return t("profile.modalTitle"); + })()} + {...props} + isOpen={props.isOpen} + hideCloseButton={show1CTSettings} + onRequestClose={() => { + // Do not close the modal if the drawers are open + if (!isQROpen && !isAvatarSelectOpen) return props.onRequestClose?.(); + + // Close the drawers + onCloseAvatarSelect(); + onCloseQR(); + }} + onAfterClose={() => { + setShow1CTSettings(false); + }} + className="relative max-h-screen overflow-hidden sm:mx-0" + > +
+ { + if (isMobileSessionOpen) return "mobile-sessions"; + if (show1CT && show1CTSettings) return "one-click-trading"; + return "profile"; + })()} + > + + <> + + + {profileStore.currentAvatar === "ammelia" ? ( + + ) : ( + + )} + + + + + +
Select an avatar
+
+
+ { + onCloseAvatarSelect(); + logEvent([ + EventName.ProfileModal.selectAvatarClicked, + { avatar: "wosmongton" }, + ]); + profileStore.setCurrentAvatar("wosmongton"); + }} + className="outline-none" + /> +

+ Wosmongton +

+
+ +
+ { + onCloseAvatarSelect(); + logEvent([ + EventName.ProfileModal.selectAvatarClicked, + { avatar: "ammelia" }, + ]); + profileStore.setCurrentAvatar("ammelia"); + }} + className="outline-none" + /> +

+ Ammelia +

+
+
+
+
+
+ +
+

+ {Boolean(props.icnsName) + ? formatICNSName(props.icnsName, width < 768 ? 32 : 48) + : shorten(address)} +

+
+ +
+
+
+
+ Osmo icon +

+ {t("profile.balance")} +

+
+ +
+
+ {formatPretty( + userOsmoAsset?.usdValue ?? + new PricePretty(DEFAULT_VS_CURRENCY, new Dec(0)), + { + minimumFractionDigits: 2, + maximumSignificantDigits: undefined, + notation: "standard", + } + )} +
+

+ {formatPretty(userOsmoAsset?.amount ?? new Dec(0), { + minimumFractionDigits: 2, + maximumSignificantDigits: undefined, + notation: "standard", + })} +

+
+
+
+ + + + + {t("profile.viewAllAssets")} + + +
+
+ {featureFlags.transactionsPage && ( + +
+
+ + + {t("profile.transactions")} + +
+ +
+ + )} +
+ +
+
+ +

+ {t("profile.wallet")} +

+
+ +
+
+
+ wallet-icon +
+ +
+

Cosmos

+
+

+ {shorten(address)} +

+ +
+
+
+ +
+ setIsMobileSessionOpen(true)} + > + + + + { + logEvent([ + EventName.ProfileModal.blockExplorerLinkOutClicked, + ]); + }} + > + + + + { + logEvent([EventName.ProfileModal.qrCodeClicked]); + onOpenQR(); + }} + onClose={onCloseQR} + > + + + + + + + + + +
Cosmos
+
+ +
+ +
+

+ {shorten(address, { prefixLength: 10 })} +

+ +
+
+
+
+ + { + logEvent([EventName.ProfileModal.logOutClicked]); + try { + setIsDisconnecting(true); + await wallet?.disconnect(true); + props.onRequestClose(); + } catch (e) { + throw e; + } finally { + setIsDisconnecting(false); + } + }} + className="group hover:text-rust-500" + > + {isDisconnecting ? ( + + ) : ( + + )} + +
+
+
+ {show1CT && !isNewAccount && ( + { + props.onRequestClose(); + }} + /> + )} + +
+ + + setShow1CTSettings(false)} + onClose={props.onRequestClose} + /> + + + + setIsMobileSessionOpen(false)} /> + +
+
+
+ + ); +}); + +const OneClickTradingProfileSection: FunctionComponent<{ + setShow1CTSettings: (value: boolean) => void; + onRestartSession: () => void; +}> = ({ setShow1CTSettings, onRestartSession }) => { + const { logEvent } = useAmplitudeAnalytics(); + const { accountStore } = useStore(); + const { t } = useTranslation(); + const { isOneClickTradingExpired, oneClickTradingInfo } = + useOneClickTradingSession(); + + const create1CTSession = useCreateOneClickTradingSession(); + const account = accountStore.getWallet(accountStore.osmosisChainId); + + const shouldFetchSessionAuthenticator = + !!account?.address && !!oneClickTradingInfo; + const { data: sessionAuthenticator } = + api.local.oneClickTrading.getSessionAuthenticator.useQuery( + { + userOsmoAddress: account?.address ?? "", + publicKey: oneClickTradingInfo?.publicKey ?? "", + }, + { + enabled: shouldFetchSessionAuthenticator, + cacheTime: 15_000, // 15 seconds + staleTime: 15_000, // 15 seconds + retry: false, + } + ); + + return ( +
{ + setShow1CTSettings(true); + logEvent([ + EventName.OneClickTrading.accessed, + { + source: "profile-section", + }, + ]); + }} + className="group flex w-full cursor-pointer items-center justify-between rounded-b-2xl border border-t-0 border-osmoverse-700 bg-osmoverse-800 px-5 py-3" + > +
+ 1-Click trading icon +

+ {t("profile.oneClickTrading")} +

+ {isOneClickTradingExpired && oneClickTradingInfo && ( + + )} +
+ + +
+ ); +}; + +const ActionButton = forwardRef< + any, + ButtonHTMLAttributes & + AnchorHTMLAttributes & { isLink?: boolean } +>((props, ref) => { + const { isLink, ...rest } = props; + const Component = (isLink ? "a" : "button") as ElementType; + return ( + + {props.children} + + ); +}); + +const BaseAvatar = forwardRef< + any, + HTMLAttributes & { + isSelectable?: boolean; + isSelected?: boolean; + onSelect?: () => void; + } +>(({ isSelectable, isSelected, ...props }, ref) => { + return ( + + ); +}); + +const WosmongtonAvatar = forwardRef>( + (props, ref) => { + return ( + + Wosmongton profile avatar + + ); + } +); + +const AmmeliaAvatar = forwardRef>( + (props, ref) => { + return ( + + Wosmongton profile avatar + + ); + } +); diff --git a/packages/web/modals/profile/mobile-sessions.tsx b/packages/web/modals/profile/mobile-sessions.tsx new file mode 100644 index 0000000000..da03cdba2c --- /dev/null +++ b/packages/web/modals/profile/mobile-sessions.tsx @@ -0,0 +1,142 @@ +import { useState } from "react"; + +import { Icon } from "~/components/assets"; +import { MenuToggle } from "~/components/control"; +import { Spinner } from "~/components/loaders"; +import { CreateMobileSession } from "~/components/mobile-sessions/create-mobile-session"; +import { Screen, ScreenManager } from "~/components/screen-manager"; +import { GoBackButton, IconButton } from "~/components/ui/button"; +import { useTranslation } from "~/hooks/language"; +import { isAuthenticatorMobileSession } from "~/hooks/mutations/mobile-session/use-create-mobile-session"; +import { useRemoveMobileSession } from "~/hooks/mutations/mobile-session/use-remove-mobile-session"; +import { useStore } from "~/stores"; +import { api } from "~/utils/trpc"; + +interface MobileSessionsProps { + onClose: () => void; +} + +type ViewIds = "existing-sessions" | "create-session"; + +export function MobileSessions({ onClose }: MobileSessionsProps) { + const { accountStore } = useStore(); + const accountAddress = accountStore.getWallet( + accountStore.osmosisChainId + )?.address; + const { t } = useTranslation(); + const [currentScreen, setCurrentScreen] = useState("create-session"); + + const { data, isLoading } = + api.local.oneClickTrading.getAuthenticators.useQuery( + { + userOsmoAddress: accountAddress!, + }, + { + enabled: !!accountAddress, + select: (data) => { + if (!data.authenticators) { + return { authenticators: [] }; + } + return { + authenticators: data.authenticators + .filter((authenticator) => + isAuthenticatorMobileSession({ authenticator }) + ) + // Sort by createdAt in descending order + .reverse(), + }; + }, + } + ); + + return ( +
+ { + onClose(); + }} + className="absolute top-7 left-8" + /> + setCurrentScreen(optionId as ViewIds)} + classes={{ + root: "max-w-xs w-full", + }} + /> + + + +
+ {isLoading ? ( +
+ +
+ ) : ( + data?.authenticators.map((authenticator) => ( + + )) + )} +
+
+ + + +
+
+ ); +} + +interface AuthenticatorItemProps { + id: string; +} + +export function AuthenticatorItem({ id }: AuthenticatorItemProps) { + const removeMobileSession = useRemoveMobileSession(); + const apiUtils = api.useUtils(); + + const onDisconnect = async (authenticatorId: string) => { + removeMobileSession.mutate( + { + authenticatorId, + }, + { + onSuccess: () => { + apiUtils.local.oneClickTrading.getAuthenticators.invalidate(); + }, + } + ); + }; + + return ( +
+

Connected Device {id}

+ onDisconnect(id)} + disabled={removeMobileSession.isLoading} + > + {removeMobileSession.isLoading ? ( + + ) : ( + + )} + +
+ ); +} diff --git a/packages/web/public/icons/sprite.svg b/packages/web/public/icons/sprite.svg index 2e797c0ffb..5cd63c3931 100644 --- a/packages/web/public/icons/sprite.svg +++ b/packages/web/public/icons/sprite.svg @@ -165,7 +165,12 @@ - + @@ -1631,102 +1636,351 @@ /> - - - - - - - - + + + + + + + + - - - + + + - - - - + + + + - - - + + + - - - - - - - - - + + + + + + + + - + - - - - - - - + + + + + + + - - - + + + - - - - + + + - - - - - - - + + + + + + + - + - - - - - - - + + + + + + + - + - - - + + + - - - + + + - - - + + + - - + + + + + + + + - +