mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-09 15:38:06 -05:00
[Feat] Bridge UI Test Workflow (#814)
* add first test * create connectMetamaskToDapp fixture * bump testing framework version * correctly working connectMetamaskToDapp function * working metamask e2e test * ci fix * fix * some changes * working transaction history list test * try version bump * try version bump * work on toggleShowTestNetworksInNativeBridgeForm * try playwright 1.51.1 * ci file change * test fixture changes * try * try * try * try * test * Revert "test" This reverts commit 2592b6835530feb912d537a29eefaba37f74560e. * try fix * try fix * test * test * Revert "test" This reverts commit a21634ca9224b3d025290e215ba61917bec043ff. * working toggle network e2e test * working confirm and bridge flow on e2e test * refactor metamask fixtures * successful e2e case for bridge eth * did usdc bridge e2e test * fix pnpm lock * refactor * refactor * lint and try single text * try * test * test ci change * test * test ci change * run once to get cache * first unit test for bridge-ui * redo test * more test logs * test * test * test * removed console logs * address one more edge case * activate all tests * try * more comments * adjust fixture * did some unit tests * add more unit tests * disable unit tests in ci * new e2e test * new comment * new things * fix * comment out blockchain tx tests * add reporter for playwright * removed pause * clean-up * test * change get token balance mechanic * fix import * fixes * clean up * test * try headless * redo headful
This commit is contained in:
2
.github/actions/setup-nodejs/action.yml
vendored
2
.github/actions/setup-nodejs/action.yml
vendored
@@ -27,6 +27,6 @@ runs:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Npm install
|
||||
- name: pnpm install
|
||||
run: pnpm i ${{ inputs.pnpm-install-options }}
|
||||
shell: bash
|
||||
|
||||
37
.github/workflows/bridge-ui-e2e-tests.yml
vendored
37
.github/workflows/bridge-ui-e2e-tests.yml
vendored
@@ -24,28 +24,41 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Use for testing workflow only (and not publish to Docker)
|
||||
- uses: actions/cache@v4.2.0
|
||||
with:
|
||||
path: |
|
||||
~/.npm
|
||||
~/.cache/ms-playwright
|
||||
~/_work/.pnpm-store/v3
|
||||
${{ github.workspace }}/bridge-ui/.next/cache
|
||||
${{ github.workspace }}/bridge-ui/.cache-synpress
|
||||
key: ${{ runner.os }}-bridge-ui-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/bridge-ui/src/**/*.js', '**/bridge-ui/src/**/*.ts', '**/bridge-ui/src/**/*.tsx') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bridge-ui-${{ hashFiles('**/pnpm-lock.yaml') }}-
|
||||
|
||||
- name: Setup nodejs environment
|
||||
uses: ./.github/actions/setup-nodejs
|
||||
with:
|
||||
node-version: 18.17.0
|
||||
node-version: 20.17.0
|
||||
pnpm-install-options: '--frozen-lockfile --prefer-offline'
|
||||
|
||||
- name: Install Playwright
|
||||
run: pnpm dlx playwright@1.45.3 install --with-deps
|
||||
run: pnpm dlx playwright@1.51.1 install --with-deps chromium
|
||||
|
||||
- name: Build Bridge UI
|
||||
run: pnpm run -F bridge-ui build;
|
||||
run: pnpm run -F bridge-ui build
|
||||
env:
|
||||
NEXT_PUBLIC_WALLET_CONNECT_ID: ${{ secrets.PUBLIC_WALLET_CONNECT_ID }}
|
||||
NEXT_PUBLIC_INFURA_ID: ${{ secrets.PUBLIC_BRIDGE_UI_INFURA_ID }}
|
||||
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID: ${{ secrets.PUBLIC_DYNAMIC_ENVIRONMENT_ID }}
|
||||
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID: ${{ secrets.PUBLIC_DYNAMIC_SANDBOX_ENVIRONMENT_ID }}
|
||||
NEXT_PUBLIC_LIFI_API_KEY: ${{ secrets.PUBLIC_LIFI_API_KEY }}
|
||||
|
||||
- name: Install linux dependencies
|
||||
run: |
|
||||
sudo apt-get install --no-install-recommends -y xvfb
|
||||
|
||||
- name: Build synpress cache
|
||||
- name: Initialize Metamask testing fixture
|
||||
run: xvfb-run pnpm run -F bridge-ui build:cache
|
||||
env:
|
||||
E2E_TEST_PRIVATE_KEY: ${{ secrets.BRIDGE_UI_E2E_TESTS_PRIVATE_KEY }}
|
||||
@@ -53,9 +66,21 @@ jobs:
|
||||
E2E_TEST_WALLET_PASSWORD: "TestPassword!"
|
||||
NEXT_PUBLIC_INFURA_ID: ${{ secrets.PUBLIC_BRIDGE_UI_INFURA_ID }}
|
||||
|
||||
- name: Run tests
|
||||
- name: Run unit tests
|
||||
run: DOTENV_CONFIG_PATH=.env.production pnpm run -F bridge-ui test:unit
|
||||
env:
|
||||
# Can run unit tests in parallel
|
||||
CI: "false"
|
||||
NEXT_PUBLIC_INFURA_ID: ${{ secrets.PUBLIC_BRIDGE_UI_INFURA_ID }}
|
||||
NEXT_PUBLIC_LIFI_API_KEY: "placeholder"
|
||||
NEXT_PUBLIC_WALLET_CONNECT_ID: "placeholder"
|
||||
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID: "placeholder"
|
||||
|
||||
# Prerequisite - Testing wallet must have >0 ETH and USDC on Sepolia
|
||||
- name: Run E2E tests
|
||||
run: xvfb-run pnpm run -F bridge-ui test:e2e:headful
|
||||
env:
|
||||
# Do not run E2E tests in parallel. Especially blockchain tx where nonces can collide.
|
||||
CI: "true"
|
||||
E2E_TEST_PRIVATE_KEY: ${{ secrets.BRIDGE_UI_E2E_TESTS_PRIVATE_KEY }}
|
||||
E2E_TEST_SEED_PHRASE: "test test test test test test test test test test test junk"
|
||||
|
||||
@@ -7,17 +7,20 @@
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"lint:fix": "pnpm run lint:ts:fix",
|
||||
"lint": "next lint && pnpm run lint:test",
|
||||
"lint:test": "npx eslint 'test/**/*.{js,ts}'",
|
||||
"lint:fix": "pnpm run lint:ts:fix && pnpm run lint:test:ts:fix",
|
||||
"lint:ts:fix": "next lint --fix",
|
||||
"lint:test:ts:fix": "npx eslint --fix 'test/**/*.{js,ts}'",
|
||||
"clean": "rimraf node_modules .next .next-env.d.ts",
|
||||
"install:playwright": "playwright install --with-deps",
|
||||
"build:cache": "synpress",
|
||||
"build:cache:force": "synpress --force",
|
||||
"build:cache:headless": "synpress --headless",
|
||||
"test:e2e:headful": "playwright test",
|
||||
"test:e2e:headless": "HEADLESS=true playwright test",
|
||||
"test:e2e:headless:ui": "HEADLESS=true playwright test --ui"
|
||||
"test:unit": "HEADLESS=true playwright test src",
|
||||
"test:e2e:headful": "playwright test test/e2e",
|
||||
"test:e2e:headless": "HEADLESS=true playwright test test/e2e",
|
||||
"test:e2e:headless:ui": "HEADLESS=true playwright test test/e2e --ui"
|
||||
},
|
||||
"dependencies": {
|
||||
"@consensys/linea-sdk": "0.3.0",
|
||||
@@ -48,15 +51,16 @@
|
||||
"zustand": "4.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.45.3",
|
||||
"@playwright/test": "1.51.1",
|
||||
"@svgr/webpack": "8.1.0",
|
||||
"@synthetixio/synpress": "4.0.0-alpha.7",
|
||||
"@synthetixio/synpress": "4.0.10",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"@types/react": "18.3.11",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"autoprefixer": "10.4.21",
|
||||
"dotenv": "16.4.7",
|
||||
"eslint-config-next": "14.2.15",
|
||||
"nock": "14.0.1",
|
||||
"postcss": "8.5.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
import "dotenv/config";
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./test/e2e",
|
||||
timeout: 60_000,
|
||||
testDir: ".",
|
||||
testMatch: '**/*.spec.ts',
|
||||
// Timeout for tests that don't involve blockchain transactions
|
||||
timeout: 40_000,
|
||||
fullyParallel: true,
|
||||
maxFailures: process.env.CI ? 1 : 0,
|
||||
// To consider - cannot really run E2E tests involving blockchain tx in parallel. There is a high risk of reusing the same tx nonce -> leading to dropped transactions
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
reporter: process.env.CI
|
||||
? [["html", { open: "never", outputFolder: `playwright-report-${process.env.HEADLESS ? "headless" : "headful"}` }]]
|
||||
: "html",
|
||||
? [
|
||||
["html", { open: "never", outputFolder: `playwright-report-${process.env.HEADLESS ? "headless" : "headful"}` }],
|
||||
["list"]
|
||||
]
|
||||
: [["html"],["list"]],
|
||||
use: {
|
||||
baseURL: "http://localhost:3000",
|
||||
trace: process.env.CI ? "on" : "retain-on-failure",
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function ReceivedAmount() {
|
||||
|
||||
return (
|
||||
<div className={styles.value}>
|
||||
<p className={styles.crypto}>
|
||||
<p className={styles.crypto} data-testid="received-amount-text">
|
||||
{formatBalance(receivedAmount, 6)} {token.symbol}
|
||||
</p>
|
||||
{tokenPrices?.[token[fromChain.layer].toLowerCase()] &&
|
||||
|
||||
@@ -58,10 +58,11 @@ export default function BridgeForm() {
|
||||
setIsBridgeOpen(false);
|
||||
setIsTransactionHistoryOpen(true);
|
||||
}}
|
||||
data-testid="native-bridge-transaction-history-icon"
|
||||
>
|
||||
<TransactionPaperIcon className={styles["transaction-icon"]} />
|
||||
</Button>
|
||||
<Setting />
|
||||
<Setting data-testid="native-bridge-form-settings-icon" />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles["content"]}>
|
||||
|
||||
@@ -32,7 +32,7 @@ export default function ConfirmDestinationAddress({ isModalOpen, recipient, onCl
|
||||
{formatAddress(recipient)}
|
||||
<UnionIcon />
|
||||
</Link>
|
||||
<Button fullWidth onClick={onConfirm}>
|
||||
<Button fullWidth onClick={onConfirm} data-testid="confirm-and-bridge-btn">
|
||||
Confirm and bridge
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -46,6 +46,7 @@ export default function TokenDetails({ isConnected, token, onTokenClick, tokenPr
|
||||
return (
|
||||
<button
|
||||
id={`token-details-${token.symbol}-btn`}
|
||||
data-testid={`token-details-${token.symbol.toLowerCase()}-btn`}
|
||||
className={styles["token-wrapper"]}
|
||||
type="button"
|
||||
disabled={tokenNotFromCurrentLayer}
|
||||
@@ -60,7 +61,7 @@ export default function TokenDetails({ isConnected, token, onTokenClick, tokenPr
|
||||
</div>
|
||||
{isConnected && !tokenNotFromCurrentLayer && (
|
||||
<div className={styles.rìght}>
|
||||
<p className={styles["balance"]}>
|
||||
<p className={styles["balance"]} data-testid={`token-details-${token.symbol.toLowerCase()}-amount`}>
|
||||
{formatBalance(formattedBalance, 8)} {token.symbol}
|
||||
</p>
|
||||
{totalValue !== undefined && (
|
||||
|
||||
@@ -16,7 +16,11 @@ export default function TokenList() {
|
||||
return (
|
||||
<div className={styles["wrapper"]}>
|
||||
{token && (
|
||||
<Button className={styles["token-select-btn"]} onClick={openModal}>
|
||||
<Button
|
||||
className={styles["token-select-btn"]}
|
||||
onClick={openModal}
|
||||
data-testid="native-bridge-open-token-list-modal"
|
||||
>
|
||||
<Image src={token.image} alt={token.name} width={24} height={24} />
|
||||
{token.symbol}
|
||||
<CaretDownIcon className={styles["arrow-down-icon"]} />
|
||||
|
||||
@@ -29,6 +29,7 @@ export default function TransactionHistory() {
|
||||
setIsTransactionHistoryOpen(false);
|
||||
setIsBridgeOpen(true);
|
||||
}}
|
||||
data-testid="transaction-history-close-btn"
|
||||
>
|
||||
<ArrowLeftIcon className={styles["go-back-icon"]} />
|
||||
</Button>
|
||||
@@ -51,6 +52,7 @@ export default function TransactionHistory() {
|
||||
setIsTransactionHistoryOpen(false);
|
||||
setIsBridgeOpen(true);
|
||||
}}
|
||||
data-testid="transaction-history-close-btn"
|
||||
>
|
||||
<ArrowLeftIcon className={styles["go-back-icon"]} />
|
||||
</Button>
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function ListTransaction({ transactions }: Props) {
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<ul className={styles["list"]}>
|
||||
<ul className={styles["list"]} data-testid="native-bridge-transaction-history-list">
|
||||
{transactions.map((item, index) => (
|
||||
<Transaction key={`transaction-${item.bridgingTx}-${index}`} onClick={handleClickTransaction} {...item} />
|
||||
))}
|
||||
|
||||
@@ -3,13 +3,17 @@ import styles from "./setting.module.scss";
|
||||
import clsx from "clsx";
|
||||
import SettingIcon from "@/assets/icons/setting.svg";
|
||||
import ToggleSwitch from "@/components/ui/toggle-switch";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { HTMLAttributes, useEffect, useRef, useState } from "react";
|
||||
import CurrencyDropdown from "@/components/bridge/currency-dropdown";
|
||||
import { useConfigStore, useChainStore, useFormStore } from "@/stores";
|
||||
import { useChains } from "@/hooks";
|
||||
import { ChainLayer } from "@/types";
|
||||
|
||||
export default function Setting() {
|
||||
interface SettingProps extends HTMLAttributes<HTMLDivElement> {
|
||||
"data-testid": string;
|
||||
}
|
||||
|
||||
export default function Setting(props: SettingProps) {
|
||||
const dropdownRef = useRef<HTMLDivElement | null>(null);
|
||||
const [isDropdownVisible, setDropdownVisible] = useState<boolean>(false);
|
||||
const setShowTestnet = useConfigStore.useSetShowTestnet();
|
||||
@@ -56,6 +60,7 @@ export default function Setting() {
|
||||
[styles["visible"]]: isDropdownVisible,
|
||||
})}
|
||||
onClick={toggleDropdown}
|
||||
data-testid={props["data-testid"]}
|
||||
>
|
||||
<SettingIcon />
|
||||
</div>
|
||||
@@ -76,6 +81,7 @@ export default function Setting() {
|
||||
onChange={(checked) => {
|
||||
setShowTestnet(checked);
|
||||
}}
|
||||
data-testid="native-bridge-test-network-toggle"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -8,7 +8,7 @@ type Props = {
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export default function ToggleSwitch({ checked = false, onChange, disabled }: Props) {
|
||||
export default function ToggleSwitch({ checked = false, onChange, disabled, ...rest }: Props) {
|
||||
const [isChecked, setIsChecked] = useState<boolean>(checked);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -32,7 +32,7 @@ export default function ToggleSwitch({ checked = false, onChange, disabled }: Pr
|
||||
})}
|
||||
>
|
||||
<input type="checkbox" checked={isChecked} onChange={handleChange} disabled={disabled} />
|
||||
<span className={styles["slider"]} />
|
||||
<span className={styles["slider"]} {...rest} />
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ export enum CctpAttestationMessageStatus {
|
||||
COMPLETE = "complete",
|
||||
}
|
||||
export type CctpAttestationMessage = {
|
||||
attestation: `0x${string}`;
|
||||
attestation: `0x${string}` | "PENDING";
|
||||
message: `0x${string}`;
|
||||
eventNonce: `0x${string}`;
|
||||
cctpVersion: 1 | 2;
|
||||
|
||||
165
bridge-ui/src/utils/cctp.spec.ts
Normal file
165
bridge-ui/src/utils/cctp.spec.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { test } from "@playwright/test";
|
||||
import nock from "nock";
|
||||
import { getCctpMessageExpiryBlock, getCctpTransactionStatus } from "./cctp";
|
||||
import {
|
||||
CctpAttestationMessage,
|
||||
CctpAttestationMessageStatus,
|
||||
CctpV2ReattestationApiResponse,
|
||||
Chain,
|
||||
ChainLayer,
|
||||
TransactionStatus,
|
||||
} from "@/types";
|
||||
|
||||
const { expect, describe } = test;
|
||||
|
||||
describe("getCctpMessageExpiryBlock", () => {
|
||||
test("should return undefined for empty byte string", () => {
|
||||
const message = "0x";
|
||||
const resp = getCctpMessageExpiryBlock(message);
|
||||
expect(resp).toBeUndefined();
|
||||
});
|
||||
|
||||
test("should return 0 if 0 expiryBlock encoded", () => {
|
||||
const message =
|
||||
"0x00000001000000000000000b41d77498ae6f504499ff1ead8c1c2a3318d48063b8022f8215f7631153534d210000000000000000000000008fe6b999dc680ccfdd5bf7eb0974218be2542daa0000000000000000000000008fe6b999dc680ccfdd5bf7eb0974218be2542daa0000000000000000000000000000000000000000000000000000000000000000000003e8000007d0000000010000000000000000000000001c7d4b196cb0c7b01d743fbc6116a902379c7238000000000000000000000000558d9534cac58f743a3a9e5382f77575a2595dcb0000000000000000000000000000000000000000000000000000000000002710000000000000000000000000558d9534cac58f743a3a9e5382f77575a2595dcb000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000";
|
||||
const resp = getCctpMessageExpiryBlock(message);
|
||||
expect(resp).toBe(BigInt(0));
|
||||
});
|
||||
|
||||
test("should parse encoded expiryBlock", () => {
|
||||
const message =
|
||||
"0x00000001000000000000000b41d77498ae6f504499ff1ead8c1c2a3318d48063b8022f8215f7631153534d210000000000000000000000008fe6b999dc680ccfdd5bf7eb0974218be2542daa0000000000000000000000008fe6b999dc680ccfdd5bf7eb0974218be2542daa0000000000000000000000000000000000000000000000000000000000000000000003e8000007d0000000010000000000000000000000001c7d4b196cb0c7b01d743fbc6116a902379c7238000000000000000000000000558d9534cac58f743a3a9e5382f77575a2595dcb0000000000000000000000000000000000000000000000000000000000002710000000000000000000000000558d9534cac58f743a3a9e5382f77575a2595dcb00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000151caa0";
|
||||
const resp = getCctpMessageExpiryBlock(message);
|
||||
expect(resp).toBe(BigInt(22137504));
|
||||
});
|
||||
});
|
||||
|
||||
describe("getCctpTransactionStatus", () => {
|
||||
const toChainStub: Chain = {
|
||||
id: 59141,
|
||||
cctpDomain: 11,
|
||||
cctpMessageTransmitterV2Address: "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",
|
||||
cctpTokenMessengerV2Address: "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
|
||||
gasLimitSurplus: 6000n,
|
||||
profitMargin: 2n,
|
||||
testnet: true,
|
||||
tokenBridgeAddress: "0x93DcAdf238932e6e6a85852caC89cBd71798F463",
|
||||
iconPath: "/images/logo/linea-sepolia.svg",
|
||||
messageServiceAddress: "0x971e727e956690b9957be6d51Ec16E73AcAC83A7",
|
||||
name: "Linea Sepolia",
|
||||
layer: ChainLayer.L2,
|
||||
nativeCurrency: {
|
||||
decimals: 18,
|
||||
name: "Linea Ether",
|
||||
symbol: "ETH",
|
||||
},
|
||||
blockExplorers: {
|
||||
default: {
|
||||
apiUrl: "https://api-sepolia.lineascan.build/api",
|
||||
name: "Etherscan",
|
||||
url: "https://sepolia.lineascan.build",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const cctpApiRespPending: CctpAttestationMessage = {
|
||||
attestation: "PENDING",
|
||||
message: "0x",
|
||||
eventNonce: "0xaf75c4d910592bc593c34bf6eb89937b1326ad43d9e3cf45581512efcf3e7da7",
|
||||
cctpVersion: 2,
|
||||
status: CctpAttestationMessageStatus.PENDING_CONFIRMATIONS,
|
||||
};
|
||||
|
||||
// Comment out because didn't find a use yet for test, but is a valid API response
|
||||
// const cctpApiRespReady: CctpAttestationMessage = {
|
||||
// attestation:
|
||||
// "0x362dff8a6f0a2c55345242a652bc8bef85dd206d02660c606b34a1f89574c25a58918896870a036f3271da65a9bff30f87d65f50f212dda4a3034fb77d110f511b3d44e4f71c4cdbb53f4e1f6d3f6357b87d056722151259f00f7beaa5af3ee35d21e48a00f932ba0ea02b19f26cb9b5dcd328f491ea91cb6b9ad896f493abe9c91c",
|
||||
// message:
|
||||
// "0x00000001000000000000000baf75c4d910592bc593c34bf6eb89937b1326ad43d9e3cf45581512efcf3e7da70000000000000000000000008fe6b999dc680ccfdd5bf7eb0974218be2542daa0000000000000000000000008fe6b999dc680ccfdd5bf7eb0974218be2542daa0000000000000000000000000000000000000000000000000000000000000000000003e8000007d0000000010000000000000000000000001c7d4b196cb0c7b01d743fbc6116a902379c7238000000000000000000000000558d9534cac58f743a3a9e5382f77575a2595dcb000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000558d9534cac58f743a3a9e5382f77575a2595dcb000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
// eventNonce: "0xaf75c4d910592bc593c34bf6eb89937b1326ad43d9e3cf45581512efcf3e7da7",
|
||||
// cctpVersion: 2,
|
||||
// status: CctpAttestationMessageStatus.COMPLETE,
|
||||
// };
|
||||
|
||||
const cctpApiRespNoExpiry: CctpAttestationMessage = {
|
||||
attestation:
|
||||
"0x06207a5ca18bc3860b5c546e8a18f6032180b3792ece47747774b9de14f62b717c51f439f0263b733b223e507a1aa0e56c823633dbb6931f864598f5c428237b1ca042a1555747bf4d7dac0b4184dad34dfed9be62ce978c18d619c8c537254a2e7b8331068215677ea900e02dd00ffee67fb61a93280c0eec789a52eb03f385b51c",
|
||||
message:
|
||||
"0x00000001000000000000000b41d77498ae6f504499ff1ead8c1c2a3318d48063b8022f8215f7631153534d210000000000000000000000008fe6b999dc680ccfdd5bf7eb0974218be2542daa0000000000000000000000008fe6b999dc680ccfdd5bf7eb0974218be2542daa0000000000000000000000000000000000000000000000000000000000000000000003e8000007d0000000010000000000000000000000001c7d4b196cb0c7b01d743fbc6116a902379c7238000000000000000000000000558d9534cac58f743a3a9e5382f77575a2595dcb0000000000000000000000000000000000000000000000000000000000002710000000000000000000000000558d9534cac58f743a3a9e5382f77575a2595dcb000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000",
|
||||
eventNonce: "0x41d77498ae6f504499ff1ead8c1c2a3318d48063b8022f8215f7631153534d21",
|
||||
cctpVersion: 2,
|
||||
status: CctpAttestationMessageStatus.COMPLETE,
|
||||
};
|
||||
|
||||
// Used on Linea Sepolia
|
||||
const usedCctpNonce = "0xaf75c4d910592bc593c34bf6eb89937b1326ad43d9e3cf45581512efcf3e7da7";
|
||||
|
||||
// Random nonce, should be unused
|
||||
const randomUnusedNonce = "0x97bce01d925f4152bbdc464a774bb8fcfd161557946b33930f0be0294e97eedf";
|
||||
|
||||
test("should return PENDING for an empty message", async () => {
|
||||
const resp = await getCctpTransactionStatus(toChainStub, cctpApiRespPending, cctpApiRespPending.eventNonce);
|
||||
expect(resp).toBe(TransactionStatus.PENDING);
|
||||
});
|
||||
|
||||
test("should return COMPLETE for a used nonce", async () => {
|
||||
const resp = await getCctpTransactionStatus(toChainStub, cctpApiRespNoExpiry, usedCctpNonce);
|
||||
expect(resp).toBe(TransactionStatus.COMPLETED);
|
||||
});
|
||||
|
||||
test("should return PENDING for a truncated message", async () => {
|
||||
// Immutable creation of new object
|
||||
const corruptedCctpApiResp = { ...cctpApiRespNoExpiry };
|
||||
// Chop off last character. Last 32-bytes of message should be expirationBlock as per https://developers.circle.com/stablecoins/message-format
|
||||
corruptedCctpApiResp.message = corruptedCctpApiResp.message.slice(0, -1) as `0x${string}`;
|
||||
|
||||
const resp = await getCctpTransactionStatus(toChainStub, corruptedCctpApiResp, randomUnusedNonce);
|
||||
expect(resp).toBe(TransactionStatus.PENDING);
|
||||
});
|
||||
|
||||
// TODO later - Address edge case where 'parseInt("0ILLEGAL", 16) == 0',
|
||||
|
||||
test("should return PENDING for a corrupted message", async () => {
|
||||
const corruptedCctpApiResp = { ...cctpApiRespNoExpiry };
|
||||
// Replace hex characters with non-hex characters
|
||||
corruptedCctpApiResp.message = (corruptedCctpApiResp.message.slice(0, -64) +
|
||||
"ILLEGALILLEGALILLEGALILLEGALILLEGALILLEGALILLEGALILLEGALILLEGALI") as `0x${string}`;
|
||||
|
||||
const resp = await getCctpTransactionStatus(toChainStub, corruptedCctpApiResp, randomUnusedNonce);
|
||||
expect(resp).toBe(TransactionStatus.PENDING);
|
||||
});
|
||||
|
||||
test("should return PENDING if i.) CCTP API response has pending status, ii.) message has no expiry and iii.) nonce unused", async () => {
|
||||
const stubbedCctpApiResp = { ...cctpApiRespNoExpiry };
|
||||
stubbedCctpApiResp.status = CctpAttestationMessageStatus.PENDING_CONFIRMATIONS;
|
||||
const resp = await getCctpTransactionStatus(toChainStub, stubbedCctpApiResp, randomUnusedNonce);
|
||||
expect(resp).toBe(TransactionStatus.PENDING);
|
||||
});
|
||||
|
||||
test("should return READY_TO_CLAIM if i.) CCTP API response has complete status, ii.) message has no expiry and iii.) nonce unused", async () => {
|
||||
const stubbedCctpApiResp = { ...cctpApiRespNoExpiry };
|
||||
stubbedCctpApiResp.status = CctpAttestationMessageStatus.COMPLETE;
|
||||
|
||||
const resp = await getCctpTransactionStatus(toChainStub, stubbedCctpApiResp, randomUnusedNonce);
|
||||
expect(resp).toBe(TransactionStatus.READY_TO_CLAIM);
|
||||
});
|
||||
|
||||
test("should return PENDING and call reattest API if i.) message is expired and ii.) nonce unused", async () => {
|
||||
const expiredCctpApiResp = { ...cctpApiRespNoExpiry };
|
||||
// Put expiry block of 1
|
||||
expiredCctpApiResp.message = (expiredCctpApiResp.message.slice(0, -1) + "1") as `0x${string}`;
|
||||
// Intercept request to CCTP reattest API
|
||||
const stubbedReattestApiResp: CctpV2ReattestationApiResponse = {
|
||||
message: "message",
|
||||
nonce: randomUnusedNonce,
|
||||
};
|
||||
const interceptor = nock("https://iris-api-sandbox.circle.com")
|
||||
.post(/.*/) // Intercept all POST requests
|
||||
.reply(200, stubbedReattestApiResp);
|
||||
|
||||
const resp = await getCctpTransactionStatus(toChainStub, expiredCctpApiResp, randomUnusedNonce);
|
||||
expect(resp).toBe(TransactionStatus.PENDING);
|
||||
// Assert we made a request to CCTP reattest API
|
||||
expect(interceptor.isDone()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,13 @@
|
||||
import MessageTransmitterV2 from "@/abis/MessageTransmitterV2.json";
|
||||
import MessageTransmitterV2 from "@/abis/MessageTransmitterV2.json" assert { type: "json" };
|
||||
import { CctpAttestationMessage, Chain, TransactionStatus, CctpAttestationMessageStatus } from "@/types";
|
||||
import { GetPublicClientReturnType } from "@wagmi/core";
|
||||
import { fetchCctpAttestationByTxHash, reattestCctpV2PreFinalityMessage } from "@/services/cctp";
|
||||
import { getPublicClient } from "@wagmi/core";
|
||||
import { config as wagmiConfig } from "@/lib/wagmi";
|
||||
import {
|
||||
CCTP_V2_MESSAGE_HEADER_LENGTH,
|
||||
CCTP_V2_EXPIRATION_BLOCK_LENGTH,
|
||||
CCTP_V2_EXPIRATION_BLOCK_OFFSET,
|
||||
CCTP_V2_MESSAGE_HEADER_LENGTH,
|
||||
} from "@/constants";
|
||||
|
||||
const isCctpNonceUsed = async (
|
||||
@@ -25,12 +25,14 @@ const isCctpNonceUsed = async (
|
||||
return resp === 1n;
|
||||
};
|
||||
|
||||
const getCctpMessageExpiryBlock = (message: string): bigint | undefined => {
|
||||
export const getCctpMessageExpiryBlock = (message: string): bigint | undefined => {
|
||||
// See CCTPV2 message format at https://developers.circle.com/stablecoins/message-format
|
||||
const expiryInHex = message.substring(
|
||||
CCTP_V2_EXPIRATION_BLOCK_OFFSET,
|
||||
CCTP_V2_EXPIRATION_BLOCK_OFFSET + CCTP_V2_EXPIRATION_BLOCK_LENGTH,
|
||||
);
|
||||
// Should be 32-bytes
|
||||
if (expiryInHex.length !== 64) return undefined;
|
||||
const expiryInInt = parseInt(expiryInHex, 16);
|
||||
if (Number.isNaN(expiryInInt)) return undefined;
|
||||
// Return bigint because this is also returned by Viem client.getBlockNumber()
|
||||
|
||||
@@ -1,31 +1,150 @@
|
||||
import { metaMaskFixtures } from "@synthetixio/synpress";
|
||||
import { metaMaskFixtures } from "@synthetixio/synpress/playwright";
|
||||
import setup from "./wallet-setup/metamask.setup";
|
||||
|
||||
import { Locator } from "@playwright/test";
|
||||
import { getNativeBridgeTransactionsCountImpl, selectTokenAndWaitForBalance } from "./utils";
|
||||
import { LINEA_SEPOLIA_NETWORK, POLLING_INTERVAL } from "./constants";
|
||||
/**
|
||||
* NB: There is an issue with Synpress `metaMaskFixtures` extension functions wherein extension functions
|
||||
* may not be able to reuse other extension functions. This is especially the case when advanced operations
|
||||
* on the 'Page' object are done. It seems that the 'Page' object does not remain the same in a nested
|
||||
* extension function call between the different layers of nesting.
|
||||
*
|
||||
* Nested `Metamask` object uses however seem ok.
|
||||
*/
|
||||
export const test = metaMaskFixtures(setup).extend<{
|
||||
initUI: (firstInit?: boolean) => Promise<void>;
|
||||
waitForTransactionToConfirm: () => Promise<void>;
|
||||
getBridgeTransactionsCount: () => Promise<number>;
|
||||
sendTokens: (amount: string, isETH?: boolean) => Promise<void>;
|
||||
waitForTransactionListUpdate: (txCountBeforeUpdate: number) => Promise<boolean>;
|
||||
selectToken: (tokenName: string) => Promise<void>;
|
||||
}>({
|
||||
initUI: async ({ page }, use) => {
|
||||
await use(async (firstInit: boolean = false) => {
|
||||
const nativeBridgeBtn = await page.waitForSelector("#native-bridge-btn");
|
||||
await nativeBridgeBtn.click();
|
||||
// Bridge UI Actions
|
||||
clickNativeBridgeButton: () => Promise<Locator>;
|
||||
openNativeBridgeTransactionHistory: () => Promise<void>;
|
||||
closeNativeBridgeTransactionHistory: () => Promise<void>;
|
||||
openNativeBridgeFormSettings: () => Promise<void>;
|
||||
toggleShowTestNetworksInNativeBridgeForm: () => Promise<void>;
|
||||
getNativeBridgeTransactionsCount: () => Promise<number>;
|
||||
selectTokenAndInputAmount: (tokenSymbol: string, amount: string) => Promise<void>;
|
||||
waitForNewTxAdditionToTxList: (txCountBeforeUpdate: number) => Promise<void>;
|
||||
waitForTxListUpdateForClaimTx: (claimTxCountBeforeUpdate: number) => Promise<void>;
|
||||
|
||||
if (firstInit) {
|
||||
const agreeTermsBtn = await page.waitForSelector("#agree-terms-btn");
|
||||
await agreeTermsBtn.click();
|
||||
}
|
||||
// Metamask Actions - Should be ok to reuse within other fixture functions
|
||||
connectMetamaskToDapp: () => Promise<void>;
|
||||
waitForTransactionToConfirm: () => Promise<void>;
|
||||
confirmTransactionAndWaitForInclusion: () => Promise<void>;
|
||||
switchToLineaSepolia: () => Promise<void>;
|
||||
|
||||
// Composite Bridge UI + Metamask Actions
|
||||
doTokenApprovalIfNeeded: () => Promise<void>;
|
||||
doInitiateBridgeTransaction: () => Promise<void>;
|
||||
doClaimTransaction: () => Promise<void>;
|
||||
}>({
|
||||
// Bridge UI Actions
|
||||
clickNativeBridgeButton: async ({ page }, use) => {
|
||||
await use(async () => {
|
||||
const nativeBridgeBtn = page.getByRole("link", { name: "Native Bridge", exact: true });
|
||||
await nativeBridgeBtn.click();
|
||||
return nativeBridgeBtn;
|
||||
});
|
||||
},
|
||||
openNativeBridgeTransactionHistory: async ({ page }, use) => {
|
||||
await use(async () => {
|
||||
const txHistoryIconButton = page.getByTestId("native-bridge-transaction-history-icon");
|
||||
await txHistoryIconButton.click();
|
||||
});
|
||||
},
|
||||
closeNativeBridgeTransactionHistory: async ({ page }, use) => {
|
||||
await use(async () => {
|
||||
const backButton = page.getByTestId("transaction-history-close-btn");
|
||||
await backButton.click();
|
||||
});
|
||||
},
|
||||
openNativeBridgeFormSettings: async ({ page }, use) => {
|
||||
await use(async () => {
|
||||
const formSettingsIconButton = page.getByTestId("native-bridge-form-settings-icon");
|
||||
await formSettingsIconButton.click();
|
||||
});
|
||||
},
|
||||
toggleShowTestNetworksInNativeBridgeForm: async ({ page }, use) => {
|
||||
await use(async () => {
|
||||
await page.getByTestId("native-bridge-test-network-toggle").click();
|
||||
});
|
||||
},
|
||||
getNativeBridgeTransactionsCount: async ({ page }, use) => {
|
||||
await use(async () => {
|
||||
return await getNativeBridgeTransactionsCountImpl(page);
|
||||
});
|
||||
},
|
||||
selectTokenAndInputAmount: async ({ page }, use) => {
|
||||
await use(async (tokenSymbol: string, amount: string) => {
|
||||
// Wait for page to retrieve blockchain token balance
|
||||
await selectTokenAndWaitForBalance(tokenSymbol, page);
|
||||
|
||||
// Input amount
|
||||
const amountInput = page.getByRole("textbox", { name: "0", exact: true });
|
||||
await amountInput.fill(amount);
|
||||
|
||||
// Wait for "Receive amount" to populate, we need to fetch blockchain data before proceeding
|
||||
const receivedAmountField = page.getByTestId("received-amount-text");
|
||||
await receivedAmountField.waitFor({ state: "visible" });
|
||||
|
||||
// Check if there are sufficient funds available
|
||||
const insufficientFundsButton = page.getByRole("button", { name: "Insufficient funds", exact: true });
|
||||
if ((await insufficientFundsButton.count()) > 0)
|
||||
throw "Insufficient funds available, please add some funds before running the test";
|
||||
});
|
||||
},
|
||||
waitForNewTxAdditionToTxList: async ({ page }, use) => {
|
||||
await use(async (txCountBeforeUpdate: number) => {
|
||||
const maxTries = 10;
|
||||
let tryCount = 0;
|
||||
let listUpdated = false;
|
||||
do {
|
||||
const newTxCount = await getNativeBridgeTransactionsCountImpl(page);
|
||||
listUpdated = newTxCount !== txCountBeforeUpdate;
|
||||
tryCount++;
|
||||
await page.waitForTimeout(POLLING_INTERVAL);
|
||||
} while (!listUpdated && tryCount < maxTries);
|
||||
});
|
||||
},
|
||||
waitForTxListUpdateForClaimTx: async ({ page }, use) => {
|
||||
await use(async (claimTxCountBeforeUpdate: number) => {
|
||||
const maxTries = 10;
|
||||
const readyToClaimTx = page.getByRole("listitem").filter({ hasText: "Ready to claim" });
|
||||
let tryCount = 0;
|
||||
let listUpdated = false;
|
||||
do {
|
||||
const newReadyToClaimCount = await readyToClaimTx.count();
|
||||
listUpdated = newReadyToClaimCount === claimTxCountBeforeUpdate + 1;
|
||||
tryCount++;
|
||||
await page.waitForTimeout(POLLING_INTERVAL);
|
||||
} while (!listUpdated && tryCount < maxTries);
|
||||
});
|
||||
},
|
||||
|
||||
// Metamask Actions - Should be ok to reuse within other fixture functions
|
||||
connectMetamaskToDapp: async ({ page, metamask }, use) => {
|
||||
await use(async () => {
|
||||
// Click Connect button
|
||||
const connectBtn = page.getByRole("button", { name: "Connect", exact: true }).first();
|
||||
await connectBtn.click();
|
||||
|
||||
// Click on 'Metamask' on the wallet dropdown menu
|
||||
const metamaskBtnInDropdownList = page.getByRole("button").filter({ hasText: "MetaMask" }).first();
|
||||
await metamaskBtnInDropdownList.click();
|
||||
|
||||
await metamask.connectToDapp();
|
||||
await metamask.goBackToHomePage();
|
||||
await page.bringToFront();
|
||||
});
|
||||
},
|
||||
waitForTransactionToConfirm: async ({ metamask }, use) => {
|
||||
await use(async () => {
|
||||
await metamask.page.bringToFront();
|
||||
await metamask.page.reload();
|
||||
|
||||
const activityButton = metamask.page.locator("button", { hasText: "Activity" });
|
||||
await activityButton.waitFor();
|
||||
// Sometimes a "What's new" modal pops up on Metamask. We assume this becomes visible at the same time as the Activity button
|
||||
// This modal causes flaky tests because it appears unpredictably, and blocks other actions.
|
||||
const gotItButton = metamask.page.locator("button", { hasText: "Got it" });
|
||||
if (await gotItButton.isVisible()) await gotItButton.click();
|
||||
// Click Activity button
|
||||
await activityButton.click();
|
||||
|
||||
let txCount = await metamask.page
|
||||
@@ -38,74 +157,72 @@ export const test = metaMaskFixtures(setup).extend<{
|
||||
}
|
||||
});
|
||||
},
|
||||
getBridgeTransactionsCount: async ({ page }, use) => {
|
||||
confirmTransactionAndWaitForInclusion: async ({ page, metamask, waitForTransactionToConfirm }, use) => {
|
||||
await use(async () => {
|
||||
const transactionsCount = await page.locator("#transactions-list").locator("ul").count();
|
||||
return transactionsCount;
|
||||
});
|
||||
},
|
||||
sendTokens: async ({ page, metamask, waitForTransactionToConfirm }, use) => {
|
||||
await use(async (amount: string, isETH = false) => {
|
||||
const amountInput = await page.waitForSelector("#amount-input");
|
||||
|
||||
// Sending the smallest amount of USDC
|
||||
await amountInput.fill(amount);
|
||||
|
||||
// Check if there are funds available
|
||||
const approveBtnDisabled = await page.locator("#approve-btn.btn-disabled").count();
|
||||
const submitBtnDisabled = await page.locator("#submit-erc-btn.btn-disabled").count();
|
||||
if (approveBtnDisabled === 1 && submitBtnDisabled === 1) {
|
||||
throw "No funds available, please add some funds before running the test";
|
||||
}
|
||||
|
||||
const tokenType = isETH ? "eth" : "erc";
|
||||
|
||||
// Check that this amount has been approved
|
||||
if (tokenType === "erc" && submitBtnDisabled === 1) {
|
||||
//We need to approve the amount first
|
||||
const approveBtn = await page.waitForSelector(`#approve-btn`);
|
||||
await approveBtn.click();
|
||||
|
||||
await metamask.page.bringToFront();
|
||||
await metamask.page.reload();
|
||||
const nextBtn = metamask.page.locator("button", {
|
||||
hasText: "Next",
|
||||
});
|
||||
await nextBtn.waitFor();
|
||||
await nextBtn.click();
|
||||
|
||||
const approveMMBtn = metamask.page.locator("button", { hasText: "Approve" });
|
||||
await approveMMBtn.waitFor();
|
||||
await approveMMBtn.click();
|
||||
|
||||
await metamask.confirmTransaction();
|
||||
await waitForTransactionToConfirm();
|
||||
await page.bringToFront();
|
||||
}
|
||||
const submitBtn = await page.waitForSelector(`#submit-${tokenType}-btn`);
|
||||
await submitBtn.click();
|
||||
});
|
||||
},
|
||||
waitForTransactionListUpdate: async ({ getBridgeTransactionsCount }, use) => {
|
||||
await use(async (txCountBeforeUpdate: number) => {
|
||||
const maxTries = 20;
|
||||
let tryCount = 0;
|
||||
let listUpdated = false;
|
||||
do {
|
||||
const newTxCount = await getBridgeTransactionsCount();
|
||||
|
||||
listUpdated = newTxCount !== txCountBeforeUpdate;
|
||||
tryCount++;
|
||||
} while (!listUpdated && tryCount < maxTries);
|
||||
|
||||
return listUpdated;
|
||||
switchToLineaSepolia: async ({ metamask }, use) => {
|
||||
await use(async () => {
|
||||
await metamask.switchNetwork(LINEA_SEPOLIA_NETWORK.name, true);
|
||||
});
|
||||
},
|
||||
selectToken: async ({ page }, use) => {
|
||||
await use(async (tokenName: string) => {
|
||||
const tokenETHBtn = await page.waitForSelector("#token-select-btn");
|
||||
await tokenETHBtn.click();
|
||||
const tokenUSDCBtn = await page.waitForSelector(`#token-details-${tokenName}-btn`);
|
||||
await tokenUSDCBtn.click();
|
||||
|
||||
// Composite Bridge UI + Metamask Actions
|
||||
doTokenApprovalIfNeeded: async ({ page, metamask, waitForTransactionToConfirm }, use) => {
|
||||
await use(async () => {
|
||||
// Check if approval required
|
||||
const approvalButton = page.getByRole("button", { name: "Approve Token", exact: true });
|
||||
if ((await approvalButton.count()) === 0) return;
|
||||
await approvalButton.click();
|
||||
|
||||
// Handle Metamask approval UI
|
||||
await metamask.approveTokenPermission();
|
||||
await waitForTransactionToConfirm();
|
||||
|
||||
// Close 'Transaction successful' modal
|
||||
await page.bringToFront();
|
||||
const closeModalBtn = page.getByRole("button", { name: "Bridge your token", exact: true });
|
||||
await closeModalBtn.click();
|
||||
});
|
||||
},
|
||||
doInitiateBridgeTransaction: async ({ page, confirmTransactionAndWaitForInclusion }, use) => {
|
||||
await use(async () => {
|
||||
// Click "Bridge" button
|
||||
const bridgeButton = page.getByRole("button", { name: "Bridge", exact: true });
|
||||
await bridgeButton.waitFor();
|
||||
await bridgeButton.click();
|
||||
|
||||
// Click "Confirm and Bridge" button
|
||||
const confirmAndBridgeButton = page.getByTestId("confirm-and-bridge-btn");
|
||||
await expect(confirmAndBridgeButton).toBeVisible();
|
||||
await expect(confirmAndBridgeButton).toBeEnabled();
|
||||
await confirmAndBridgeButton.click();
|
||||
|
||||
// Confirm Metamask Tx and wait for blockchain inclusion
|
||||
// Should be ok to reuse this fixture function because it doesn't do much on the `Page` object
|
||||
await confirmTransactionAndWaitForInclusion();
|
||||
|
||||
// Click on 'View transactions' button on the 'Transaction confirmed' modal
|
||||
const viewTxButton = page.getByRole("button", { name: "View transactions", exact: true });
|
||||
await viewTxButton.click();
|
||||
});
|
||||
},
|
||||
doClaimTransaction: async ({ page, confirmTransactionAndWaitForInclusion }, use) => {
|
||||
await use(async () => {
|
||||
// Click on 'Claim' button
|
||||
const claimButton = page.getByRole("button", { name: "Claim", exact: true });
|
||||
await expect(claimButton).toBeVisible();
|
||||
await expect(claimButton).toBeEnabled();
|
||||
await claimButton.click();
|
||||
|
||||
// Confirm Metamask Tx and wait for blockchain inclusion
|
||||
// Should be ok to reuse this fixture function because it doesn't do much on the `Page` object
|
||||
await confirmTransactionAndWaitForInclusion();
|
||||
|
||||
// Should finish on tx history page
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "dotenv/config";
|
||||
import { formatEther, formatUnits } from "viem";
|
||||
|
||||
export const METAMASK_SEED_PHRASE = process.env.E2E_TEST_SEED_PHRASE;
|
||||
@@ -15,6 +14,10 @@ export const LINEA_SEPOLIA_NETWORK = {
|
||||
};
|
||||
|
||||
export const TEST_URL = "http://localhost:3000/";
|
||||
export const SEPOLIA_NETWORK_NAME = "Sepolia";
|
||||
export const WEI_AMOUNT = formatEther(BigInt(1)).toString();
|
||||
export const USDC_AMOUNT = formatUnits(BigInt(1), 6).toString();
|
||||
export const WEI_AMOUNT = formatEther(1n).toString();
|
||||
// Must be > minimum CCTP fee
|
||||
export const USDC_AMOUNT = formatUnits(10n, 6).toString();
|
||||
export const ETH_SYMBOL = "ETH";
|
||||
export const USDC_SYMBOL = "USDC";
|
||||
|
||||
export const POLLING_INTERVAL = 250;
|
||||
|
||||
@@ -1,189 +1,164 @@
|
||||
import { testWithSynpress } from "@synthetixio/synpress";
|
||||
import { test as advancedFixtures } from "../advancedFixtures";
|
||||
import { SEPOLIA_NETWORK_NAME, TEST_URL, USDC_AMOUNT, WEI_AMOUNT } from "../constants";
|
||||
import { TEST_URL, USDC_SYMBOL, USDC_AMOUNT, WEI_AMOUNT, ETH_SYMBOL } from "../constants";
|
||||
|
||||
const test = testWithSynpress(advancedFixtures);
|
||||
|
||||
const { expect, describe } = test;
|
||||
|
||||
describe("Bridge L1 > L2", () => {
|
||||
test.skip("should set up the UI and metamask correctly", async ({ page, metamask, initUI }) => {
|
||||
await initUI(true);
|
||||
|
||||
await page.locator("#wallet-connect-btn").click();
|
||||
await page.locator("wui-list-wallet", { hasText: "MetaMask" }).nth(1).click();
|
||||
|
||||
await metamask.connectToDapp();
|
||||
|
||||
await page.bringToFront();
|
||||
});
|
||||
|
||||
// To consider in a later ticket - Bridge ERC20 tokens case when ERC20 token is available in Sepolia token list
|
||||
describe("L1 > L2 via Native Bridge", () => {
|
||||
test("should successfully go to the bridge UI page", async ({ page }) => {
|
||||
const pageUrl = page.url();
|
||||
expect(pageUrl).toEqual(TEST_URL);
|
||||
});
|
||||
|
||||
test.skip("should successfully display the correct heading", async ({ page, initUI }) => {
|
||||
await initUI(true);
|
||||
|
||||
const header = "Bridge";
|
||||
await page.locator("h2", { hasText: header }).waitFor({ state: "visible" });
|
||||
test("should have 'Native Bridge' button link on homepage", async ({ clickNativeBridgeButton }) => {
|
||||
const nativeBridgeBtn = await clickNativeBridgeButton();
|
||||
await expect(nativeBridgeBtn).toBeVisible();
|
||||
});
|
||||
|
||||
test.skip("metamask should be connected to the right network", async ({ page, metamask, initUI }) => {
|
||||
await initUI(true);
|
||||
|
||||
await page.locator("#wallet-connect-btn").click();
|
||||
await page.locator("wui-list-wallet", { hasText: "MetaMask" }).nth(1).click();
|
||||
|
||||
await metamask.connectToDapp();
|
||||
|
||||
await page.bringToFront();
|
||||
await page
|
||||
.locator("#active-chain-name", {
|
||||
hasText: SEPOLIA_NETWORK_NAME,
|
||||
})
|
||||
.waitFor();
|
||||
test("should connect MetaMask to dapp correctly", async ({ connectMetamaskToDapp, clickNativeBridgeButton }) => {
|
||||
await clickNativeBridgeButton();
|
||||
await connectMetamaskToDapp();
|
||||
});
|
||||
|
||||
test.skip("should be able to reload the transaction history", async ({ page, metamask, initUI }) => {
|
||||
await initUI(true);
|
||||
await page.locator("#wallet-connect-btn").click();
|
||||
await page.locator("wui-list-wallet", { hasText: "MetaMask" }).nth(1).click();
|
||||
|
||||
await metamask.connectToDapp();
|
||||
|
||||
const reloadHistoryBtn = await page.waitForSelector("#reload-history-btn");
|
||||
await reloadHistoryBtn.click();
|
||||
|
||||
const reloadConfirmBtn = await page.waitForSelector("#reload-history-confirm-btn");
|
||||
await reloadConfirmBtn.click();
|
||||
|
||||
await page.locator("#transactions-list").locator("ul").nth(1).waitFor({ timeout: 10_000 });
|
||||
});
|
||||
|
||||
test.skip("should be able to switch network", async ({ page, metamask, initUI }) => {
|
||||
await initUI(true);
|
||||
await page.locator("#wallet-connect-btn").click();
|
||||
await page.locator("wui-list-wallet", { hasText: "MetaMask" }).nth(1).click();
|
||||
|
||||
await metamask.connectToDapp();
|
||||
|
||||
await page.locator("#chain-select").click();
|
||||
await page.locator("#switch-alternative-chain-btn").click();
|
||||
|
||||
await metamask.approveSwitchNetwork();
|
||||
|
||||
await page.bringToFront();
|
||||
|
||||
await page.locator("#active-chain-name").getByText("Linea Sepolia Testnet").waitFor();
|
||||
});
|
||||
|
||||
test.skip("should be able to claim funds if available", async ({
|
||||
test("should be able to load the transaction history", async ({
|
||||
page,
|
||||
metamask,
|
||||
initUI,
|
||||
waitForTransactionToConfirm,
|
||||
connectMetamaskToDapp,
|
||||
clickNativeBridgeButton,
|
||||
openNativeBridgeTransactionHistory,
|
||||
}) => {
|
||||
await initUI(true);
|
||||
await page.locator("#wallet-connect-btn").click();
|
||||
await page.locator("wui-list-wallet", { hasText: "MetaMask" }).nth(1).click();
|
||||
await connectMetamaskToDapp();
|
||||
await clickNativeBridgeButton();
|
||||
await openNativeBridgeTransactionHistory();
|
||||
|
||||
await metamask.connectToDapp();
|
||||
|
||||
// Check if there is a claim button available
|
||||
const checkClaimBtn = await page.locator("#claim-funds-btn").all();
|
||||
if (checkClaimBtn.length > 0) {
|
||||
const claimBtn = page.locator("#claim-funds-btn").nth(1);
|
||||
await claimBtn.click();
|
||||
|
||||
await metamask.confirmTransaction();
|
||||
|
||||
await waitForTransactionToConfirm();
|
||||
} else {
|
||||
console.warn("Claim funds could not be tested since no funds are waiting to be claimed");
|
||||
}
|
||||
const txHistoryHeading = page.getByRole("heading").filter({ hasText: "Transaction History" });
|
||||
await expect(txHistoryHeading).toBeVisible();
|
||||
});
|
||||
|
||||
test.skip("should be able to bridge ETH from L1 to L2", async ({
|
||||
test("should be able to switch to test networks", async ({
|
||||
page,
|
||||
metamask,
|
||||
getBridgeTransactionsCount,
|
||||
sendTokens,
|
||||
waitForTransactionToConfirm,
|
||||
waitForTransactionListUpdate,
|
||||
connectMetamaskToDapp,
|
||||
clickNativeBridgeButton,
|
||||
openNativeBridgeFormSettings,
|
||||
toggleShowTestNetworksInNativeBridgeForm,
|
||||
}) => {
|
||||
await page.bringToFront();
|
||||
const txnsLengthBefore = await getBridgeTransactionsCount();
|
||||
await connectMetamaskToDapp();
|
||||
await clickNativeBridgeButton();
|
||||
await openNativeBridgeFormSettings();
|
||||
await toggleShowTestNetworksInNativeBridgeForm();
|
||||
|
||||
await sendTokens(WEI_AMOUNT, true);
|
||||
await metamask.confirmTransaction();
|
||||
|
||||
// Wait for transaction to finish
|
||||
await waitForTransactionToConfirm();
|
||||
|
||||
await page.bringToFront();
|
||||
// We check at the end that the transacton list is updated on the bridge UI
|
||||
const listUpdated = await waitForTransactionListUpdate(txnsLengthBefore);
|
||||
|
||||
// Check that that new transaction is added to the list on the UI
|
||||
expect(listUpdated).toBeTruthy();
|
||||
// Should have Sepolia text visible
|
||||
const sepoliaText = page.getByText("Sepolia").first();
|
||||
await expect(sepoliaText).toBeVisible();
|
||||
});
|
||||
|
||||
test.skip("should be able to bridge USDC from L1 to L2", async ({
|
||||
page,
|
||||
metamask,
|
||||
getBridgeTransactionsCount,
|
||||
selectToken,
|
||||
sendTokens,
|
||||
waitForTransactionToConfirm,
|
||||
waitForTransactionListUpdate,
|
||||
test("should be able to initiate bridging ETH from L1 to L2 in testnet", async ({
|
||||
getNativeBridgeTransactionsCount,
|
||||
waitForNewTxAdditionToTxList,
|
||||
connectMetamaskToDapp,
|
||||
clickNativeBridgeButton,
|
||||
openNativeBridgeFormSettings,
|
||||
toggleShowTestNetworksInNativeBridgeForm,
|
||||
selectTokenAndInputAmount,
|
||||
doInitiateBridgeTransaction,
|
||||
openNativeBridgeTransactionHistory,
|
||||
closeNativeBridgeTransactionHistory,
|
||||
}) => {
|
||||
const txnsLengthBefore = await getBridgeTransactionsCount();
|
||||
// Code smell that we may need to refactor E2E tests with blockchain tx into another describe block with a separate timeout
|
||||
test.setTimeout(90_000);
|
||||
|
||||
// Select USDC in the token list
|
||||
await selectToken("USDC");
|
||||
// Setup testnet UI
|
||||
await connectMetamaskToDapp();
|
||||
await clickNativeBridgeButton();
|
||||
await openNativeBridgeFormSettings();
|
||||
await toggleShowTestNetworksInNativeBridgeForm();
|
||||
|
||||
await sendTokens(USDC_AMOUNT);
|
||||
// Get # of txs in txHistory before doing bridge tx, so that we can later confirm that our bridge tx shows up in the txHistory.
|
||||
await openNativeBridgeTransactionHistory();
|
||||
const txnsLengthBefore = await getNativeBridgeTransactionsCount();
|
||||
await closeNativeBridgeTransactionHistory();
|
||||
|
||||
await metamask.confirmTransaction();
|
||||
// // Actual bridging actions
|
||||
await selectTokenAndInputAmount(ETH_SYMBOL, WEI_AMOUNT);
|
||||
await doInitiateBridgeTransaction();
|
||||
|
||||
// Wait for transaction to finish
|
||||
await waitForTransactionToConfirm();
|
||||
|
||||
await page.bringToFront();
|
||||
// We check at the end that the transacton list is updated on the bridge UI
|
||||
const listUpdated = await waitForTransactionListUpdate(txnsLengthBefore);
|
||||
|
||||
// Check that that new transaction is added to the list on the UI
|
||||
expect(listUpdated).toBeTruthy();
|
||||
// Check that our bridge tx shows up in the tx history
|
||||
await waitForNewTxAdditionToTxList(txnsLengthBefore);
|
||||
});
|
||||
|
||||
test.skip("should be able to bridge ERC20 tokens from L1 to L2", async ({
|
||||
page,
|
||||
metamask,
|
||||
getBridgeTransactionsCount,
|
||||
selectToken,
|
||||
sendTokens,
|
||||
waitForTransactionListUpdate,
|
||||
waitForTransactionToConfirm,
|
||||
test("should be able to initiate bridging USDC from L1 to L2 in testnet", async ({
|
||||
getNativeBridgeTransactionsCount,
|
||||
waitForNewTxAdditionToTxList,
|
||||
connectMetamaskToDapp,
|
||||
clickNativeBridgeButton,
|
||||
openNativeBridgeFormSettings,
|
||||
toggleShowTestNetworksInNativeBridgeForm,
|
||||
selectTokenAndInputAmount,
|
||||
doInitiateBridgeTransaction,
|
||||
openNativeBridgeTransactionHistory,
|
||||
closeNativeBridgeTransactionHistory,
|
||||
doTokenApprovalIfNeeded,
|
||||
}) => {
|
||||
const txnsLengthBefore = await getBridgeTransactionsCount();
|
||||
// At least 2 blockchain tx in this test
|
||||
test.setTimeout(120_000);
|
||||
|
||||
// Select WETH in the token list (Easiest to get)
|
||||
await selectToken("WETH");
|
||||
// Setup testnet UI
|
||||
await connectMetamaskToDapp();
|
||||
await clickNativeBridgeButton();
|
||||
await openNativeBridgeFormSettings();
|
||||
await toggleShowTestNetworksInNativeBridgeForm();
|
||||
|
||||
await sendTokens(WEI_AMOUNT);
|
||||
await metamask.confirmTransaction();
|
||||
// Get # of txs in txHistory before doing bridge tx, so that we can later confirm that our bridge tx shows up in the txHistory.
|
||||
await openNativeBridgeTransactionHistory();
|
||||
const txnsLengthBefore = await getNativeBridgeTransactionsCount();
|
||||
await closeNativeBridgeTransactionHistory();
|
||||
|
||||
// Wait for transaction to finish
|
||||
await waitForTransactionToConfirm();
|
||||
// Actual bridging actions
|
||||
await selectTokenAndInputAmount(USDC_SYMBOL, USDC_AMOUNT);
|
||||
await doTokenApprovalIfNeeded();
|
||||
await doInitiateBridgeTransaction();
|
||||
|
||||
await page.bringToFront();
|
||||
// We check at the end that the transacton list is updated on the bridge UI
|
||||
const listUpdated = await waitForTransactionListUpdate(txnsLengthBefore);
|
||||
// Check that our bridge tx shows up in the tx history
|
||||
await waitForNewTxAdditionToTxList(txnsLengthBefore);
|
||||
});
|
||||
|
||||
// Check that that new transaction is added to the list on the UI
|
||||
expect(listUpdated).toBeTruthy();
|
||||
test("should be able to claim if available READY_TO_CLAIM transactions", async ({
|
||||
page,
|
||||
connectMetamaskToDapp,
|
||||
clickNativeBridgeButton,
|
||||
openNativeBridgeFormSettings,
|
||||
toggleShowTestNetworksInNativeBridgeForm,
|
||||
openNativeBridgeTransactionHistory,
|
||||
getNativeBridgeTransactionsCount,
|
||||
switchToLineaSepolia,
|
||||
doClaimTransaction,
|
||||
waitForTxListUpdateForClaimTx,
|
||||
}) => {
|
||||
test.setTimeout(90_000);
|
||||
|
||||
await connectMetamaskToDapp();
|
||||
await clickNativeBridgeButton();
|
||||
await openNativeBridgeFormSettings();
|
||||
await toggleShowTestNetworksInNativeBridgeForm();
|
||||
|
||||
// Switch to L2 network
|
||||
await switchToLineaSepolia();
|
||||
|
||||
// Load tx history
|
||||
await openNativeBridgeTransactionHistory();
|
||||
await getNativeBridgeTransactionsCount();
|
||||
|
||||
// Find and click READY_TO_CLAIM TX
|
||||
const readyToClaimTx = page.getByRole("listitem").filter({ hasText: "Ready to claim" });
|
||||
const readyToClaimCount = await readyToClaimTx.count();
|
||||
if (readyToClaimCount === 0) return;
|
||||
await readyToClaimTx.first().click();
|
||||
|
||||
await doClaimTransaction();
|
||||
|
||||
// Check that tx history has updated accordingly
|
||||
await waitForTxListUpdateForClaimTx(readyToClaimCount);
|
||||
});
|
||||
});
|
||||
|
||||
15
bridge-ui/test/utils/getNativeBridgeTransactionsCountImpl.ts
Normal file
15
bridge-ui/test/utils/getNativeBridgeTransactionsCountImpl.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Page } from "@playwright/test";
|
||||
|
||||
export async function getNativeBridgeTransactionsCountImpl(page: Page): Promise<number> {
|
||||
const txList = page.getByTestId("native-bridge-transaction-history-list");
|
||||
const noTransactionsYetText = page.getByText("No transactions yet");
|
||||
// Either `txList` or `noTransactionsYetText` will appear. Should be mutually exclusive.
|
||||
await Promise.race([txList.waitFor({ state: "visible" }), noTransactionsYetText.waitFor({ state: "visible" })]);
|
||||
|
||||
// Check which element is actually visible
|
||||
const isTxListVisible = await txList.isVisible();
|
||||
if (!isTxListVisible) return 0;
|
||||
const txs = txList.getByRole("listitem");
|
||||
const txCount = txs.count();
|
||||
return txCount;
|
||||
}
|
||||
2
bridge-ui/test/utils/index.ts
Normal file
2
bridge-ui/test/utils/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { selectTokenAndWaitForBalance } from "./selectTokenAndWaitForBalance";
|
||||
export { getNativeBridgeTransactionsCountImpl } from "./getNativeBridgeTransactionsCountImpl";
|
||||
24
bridge-ui/test/utils/selectTokenAndWaitForBalance.ts
Normal file
24
bridge-ui/test/utils/selectTokenAndWaitForBalance.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Page } from "@playwright/test";
|
||||
import { POLLING_INTERVAL } from "../constants";
|
||||
|
||||
export async function selectTokenAndWaitForBalance(tokenSymbol: string, page: Page) {
|
||||
const openModalBtn = page.getByTestId("native-bridge-open-token-list-modal");
|
||||
await openModalBtn.click();
|
||||
// Wait for API request to retrieve blockchain balance.
|
||||
const tokenBalance = page.getByTestId(`token-details-${tokenSymbol.toLowerCase()}-amount`);
|
||||
console.log(`Fetching token balance for ${tokenSymbol}`);
|
||||
|
||||
// Timeout implementation
|
||||
const fetchTokenBalanceTimeout = 5000;
|
||||
let fetchTokenTimeUsed = 0;
|
||||
while ((await tokenBalance.textContent()) === `0 ${tokenSymbol}`) {
|
||||
if (fetchTokenTimeUsed >= fetchTokenBalanceTimeout)
|
||||
throw `Could not find any balance for ${tokenSymbol}, does the testing wallet have funds?`;
|
||||
await page.waitForTimeout(POLLING_INTERVAL);
|
||||
fetchTokenTimeUsed += POLLING_INTERVAL;
|
||||
}
|
||||
console.log(`Selected token balance: ${await tokenBalance.textContent()}`);
|
||||
|
||||
// Select token
|
||||
await page.getByTestId(`token-details-${tokenSymbol.toLowerCase()}-btn`).click();
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { MetaMask, defineWalletSetup, getExtensionId } from "@synthetixio/synpress";
|
||||
import { LINEA_SEPOLIA_NETWORK, METAMASK_PASSWORD, METAMASK_SEED_PHRASE, TEST_PRIVATE_KEY } from "../constants";
|
||||
import { defineWalletSetup } from "@synthetixio/synpress";
|
||||
import { MetaMask, getExtensionId } from "@synthetixio/synpress/playwright";
|
||||
|
||||
export default defineWalletSetup(METAMASK_PASSWORD, async (context, walletPage) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
@@ -8,7 +9,6 @@ export default defineWalletSetup(METAMASK_PASSWORD, async (context, walletPage)
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
const metamask = new MetaMask(context, walletPage, METAMASK_PASSWORD, extensionId);
|
||||
|
||||
await metamask.importWallet(METAMASK_SEED_PHRASE);
|
||||
await metamask.importWalletFromPrivateKey(TEST_PRIVATE_KEY);
|
||||
|
||||
|
||||
215
pnpm-lock.yaml
generated
215
pnpm-lock.yaml
generated
@@ -88,10 +88,10 @@ importers:
|
||||
version: 1.9.2
|
||||
next:
|
||||
specifier: 14.2.25
|
||||
version: 14.2.25(@babel/core@7.25.7)(@playwright/test@1.45.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.86.0)
|
||||
version: 14.2.25(@babel/core@7.25.7)(@playwright/test@1.51.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.86.0)
|
||||
next-seo:
|
||||
specifier: 6.6.0
|
||||
version: 6.6.0(next@14.2.25(@babel/core@7.25.7)(@playwright/test@1.45.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.86.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
version: 6.6.0(next@14.2.25(@babel/core@7.25.7)(@playwright/test@1.51.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.86.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
pino-pretty:
|
||||
specifier: 13.0.0
|
||||
version: 13.0.0
|
||||
@@ -124,14 +124,14 @@ importers:
|
||||
version: 4.5.4(@types/react@18.3.11)(react@18.3.1)
|
||||
devDependencies:
|
||||
'@playwright/test':
|
||||
specifier: 1.45.3
|
||||
version: 1.45.3
|
||||
specifier: 1.51.1
|
||||
version: 1.51.1
|
||||
'@svgr/webpack':
|
||||
specifier: 8.1.0
|
||||
version: 8.1.0(typescript@5.4.5)
|
||||
'@synthetixio/synpress':
|
||||
specifier: 4.0.0-alpha.7
|
||||
version: 4.0.0-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(playwright-core@1.45.3)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.24.2)
|
||||
specifier: 4.0.10
|
||||
version: 4.0.10(@depay/solana-web3.js@1.27.0)(@depay/web3-blockchains@9.6.7)(@playwright/test@1.51.1)(bufferutil@4.0.8)(ethers@6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(playwright-core@1.51.1)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.24.2)
|
||||
'@types/fs-extra':
|
||||
specifier: 11.0.4
|
||||
version: 11.0.4
|
||||
@@ -150,6 +150,9 @@ importers:
|
||||
eslint-config-next:
|
||||
specifier: 14.2.15
|
||||
version: 14.2.15(eslint@8.57.0)(typescript@5.4.5)
|
||||
nock:
|
||||
specifier: 14.0.1
|
||||
version: 14.0.1
|
||||
postcss:
|
||||
specifier: 8.5.3
|
||||
version: 8.5.3
|
||||
@@ -248,8 +251,6 @@ importers:
|
||||
specifier: 17.7.2
|
||||
version: 17.7.2
|
||||
|
||||
contracts/lib/forge-std: {}
|
||||
|
||||
e2e:
|
||||
devDependencies:
|
||||
'@jest/globals':
|
||||
@@ -1290,8 +1291,20 @@ packages:
|
||||
resolution: {integrity: sha512-RqVsm/aURJ2npRm0/0qr/GiMdBsGSbRA2GpzG75Vy7qODYScbYjA/CitMjhw9ktzGjiFN9oh/ooq9GBjPIhEdA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
'@depay/web3-mock@14.17.0':
|
||||
resolution: {integrity: sha512-0WCIpHqGUTPmOb5l3iN+4wCY+P3nHnGWd3uyWB+Wrt5DygS6MWI2b50gwtSCgYUCfgmEv9KlRuCnHDC4TDKCeA==}
|
||||
'@depay/web3-client@10.18.6':
|
||||
resolution: {integrity: sha512-JeUAZ04/dsIra1ao3mvqAdVhRf4U1YxSoH0mE+XxhcXPgLC7KQTzH6oCLp07tAmoxholEL2cf5Oo20n6q1fZ/w==}
|
||||
engines: {node: '>=16'}
|
||||
peerDependencies:
|
||||
'@depay/solana-web3.js': ^1.25.1
|
||||
'@depay/web3-blockchains': ^9.3.6
|
||||
ethers: ^5.7.1
|
||||
|
||||
'@depay/web3-mock-evm@14.19.1':
|
||||
resolution: {integrity: sha512-Gx5n87gwya5dGv4JwDdlJFWshLbM9nDj6co8Z25FTf7/xKsTUD1en971B2QweXqZJxYadnumOPb+n19lgRofpQ==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
'@depay/web3-mock@14.19.1':
|
||||
resolution: {integrity: sha512-bBM1J0EWDWXJKVPtzo8YrX7fbGwUATYWN8kaJniQU2z5V+UK3kVhjQi+en0JMF9cCjinkERK7MqoZLaYR+cb+Q==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
'@dynamic-labs/assert-package-version@4.9.5':
|
||||
@@ -2283,6 +2296,10 @@ packages:
|
||||
resolution: {integrity: sha512-z10PF9JV6SbjFq+/rYabM+8CVlMokgl8RFGvieSGNTmrkQanfHn+15XBrhG3BgUfvmTeSeyShfOHpG0i9zEdcg==}
|
||||
deprecated: Motion One for Vue is deprecated. Use Oku Motion instead https://oku-ui.com/motion
|
||||
|
||||
'@mswjs/interceptors@0.37.6':
|
||||
resolution: {integrity: sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@mui/core-downloads-tracker@6.4.8':
|
||||
resolution: {integrity: sha512-vjP4+A1ybyCRhDZC7r5EPWu/gLseFZxaGyPdDl94vzVvk6Yj6gahdaqcjbhkaCrJjdZj90m3VioltWPAnWF/zw==}
|
||||
|
||||
@@ -2669,6 +2686,15 @@ packages:
|
||||
peerDependencies:
|
||||
'@oclif/core': '>= 3.0.0'
|
||||
|
||||
'@open-draft/deferred-promise@2.2.0':
|
||||
resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==}
|
||||
|
||||
'@open-draft/logger@0.3.0':
|
||||
resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==}
|
||||
|
||||
'@open-draft/until@2.1.0':
|
||||
resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
|
||||
|
||||
'@openzeppelin/contracts-upgradeable@4.9.6':
|
||||
resolution: {integrity: sha512-m4iHazOsOCv1DgM7eD7GupTJ+NFVujRZt1wzddDPSVGpWdKq1SKkla5htKG7+IS4d2XOCtzkUNwRZ7Vq5aEUMA==}
|
||||
|
||||
@@ -2799,8 +2825,8 @@ packages:
|
||||
resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
|
||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||
|
||||
'@playwright/test@1.45.3':
|
||||
resolution: {integrity: sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==}
|
||||
'@playwright/test@1.51.1':
|
||||
resolution: {integrity: sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
@@ -3340,32 +3366,32 @@ packages:
|
||||
'@swc/helpers@0.5.5':
|
||||
resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==}
|
||||
|
||||
'@synthetixio/ethereum-wallet-mock@0.0.1-alpha.7':
|
||||
resolution: {integrity: sha512-afyAVC2b39OC/mxfVeISFQ7TVleS15GYeCPte6VrdFYWi/2SXPml6myAZx9FlRqRlyaPLhRrtRRjdno44S6/6Q==}
|
||||
'@synthetixio/ethereum-wallet-mock@0.0.11':
|
||||
resolution: {integrity: sha512-J/8hyHoh5lG5vIQ9iNVApyKKRCMRMqEmuv97s6zZVmqe/gJR0+cSWW+mnYHXcIiWZ82WLYnr4TK4OssqZ/4C3w==}
|
||||
peerDependencies:
|
||||
'@playwright/test': 1.44.0
|
||||
'@playwright/test': '*'
|
||||
|
||||
'@synthetixio/synpress-cache@0.0.1-alpha.7':
|
||||
resolution: {integrity: sha512-vIR8f4XMJSKsSezmloT8A0yuEZON+Ovbkoy/5ELcrfLFORW1Ssu2pjOioWGgQEVZBCdGTQ4wVIWmo8MbvCrAqA==}
|
||||
'@synthetixio/synpress-cache@0.0.11':
|
||||
resolution: {integrity: sha512-5blQD9RQ/LF6buuNAhok/ouHm0Ch4sNas40J7rWVXrim6UA5sCAHPFfSVdavKpBURGN8t6Zce2+uiZs0srbtwA==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
playwright-core: 1.44.0
|
||||
playwright-core: 1.48.2
|
||||
|
||||
'@synthetixio/synpress-core@0.0.1-alpha.7':
|
||||
resolution: {integrity: sha512-El23vK/FlYiliT2JworRc8zXGVdUIvzxwzWtGMZwC/KOKy0RGgNA7rQfwc3VSPhp1ceUd4w3uIRtv6Ccjo8WbA==}
|
||||
'@synthetixio/synpress-core@0.0.11':
|
||||
resolution: {integrity: sha512-IZtA0KfG1qO0JJVzswTLT6VBmteut7NvqbUcVaEOL12FBPIaZCglyd7gdyTkUCWusp7sHuSA/Bysre0OaBnuQg==}
|
||||
peerDependencies:
|
||||
'@playwright/test': 1.44.0
|
||||
'@playwright/test': '*'
|
||||
|
||||
'@synthetixio/synpress-metamask@0.0.1-alpha.7':
|
||||
resolution: {integrity: sha512-xzBsF5wnak25uw7ZO9RJEmCuMtT03W9pyDaEpO7T1VkDcZT8MBxKHs9vR4UKqHHd9IPKXc9EvsUJ/oSwW4saKQ==}
|
||||
'@synthetixio/synpress-metamask@0.0.11':
|
||||
resolution: {integrity: sha512-nYNtFYolyeus0WqTqEot6xikyHzSXb/bSK9NP4aQ5vhKFkT6KR+8EjC9G4QKZ/+lM0alBJfvu2BOLDnYtKEOAw==}
|
||||
peerDependencies:
|
||||
'@playwright/test': 1.44.0
|
||||
'@playwright/test': '*'
|
||||
|
||||
'@synthetixio/synpress@4.0.0-alpha.7':
|
||||
resolution: {integrity: sha512-hdVV+LR/aCO+o7FyeJiFxJ+o797mDqNqkh+huz3Ic4LDbTW18HkLKwLwUzRKt8ngIOf60K/PFL9QyBLujTc76g==}
|
||||
'@synthetixio/synpress@4.0.10':
|
||||
resolution: {integrity: sha512-UXHGb9lylznEdXtDw5TYFR095XsMh5I2iqAHRUfaYgYRJlm+MQAlnFINqREt7g52VqFJ1EzyCeIgrs6Aufk+/g==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@playwright/test': 1.44.0
|
||||
'@playwright/test': '*'
|
||||
|
||||
'@szmarczak/http-timer@4.0.6':
|
||||
resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
|
||||
@@ -6568,6 +6594,9 @@ packages:
|
||||
resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-node-process@1.2.0:
|
||||
resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==}
|
||||
|
||||
is-number-object@1.0.7:
|
||||
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -7576,6 +7605,10 @@ packages:
|
||||
resolution: {integrity: sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
nock@14.0.1:
|
||||
resolution: {integrity: sha512-IJN4O9pturuRdn60NjQ7YkFt6Rwei7ZKaOwb1tvUIIqTgeD0SDDAX3vrqZD4wcXczeEy/AsUXxpGpP/yHqV7xg==}
|
||||
engines: {node: '>=18.20.0 <20 || >=20.12.1'}
|
||||
|
||||
node-abi@3.68.0:
|
||||
resolution: {integrity: sha512-7vbj10trelExNjFSBm5kTvZXXa7pZyKWx9RCKIyqe6I9Ev3IzGpQoqBP3a+cOdxY+pWj6VkP28n/2wWysBHD/A==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -7884,6 +7917,9 @@ packages:
|
||||
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
outvariant@1.4.3:
|
||||
resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==}
|
||||
|
||||
ox@0.6.7:
|
||||
resolution: {integrity: sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==}
|
||||
peerDependencies:
|
||||
@@ -8113,13 +8149,13 @@ packages:
|
||||
pkg-types@1.2.1:
|
||||
resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==}
|
||||
|
||||
playwright-core@1.45.3:
|
||||
resolution: {integrity: sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==}
|
||||
playwright-core@1.51.1:
|
||||
resolution: {integrity: sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
playwright@1.45.3:
|
||||
resolution: {integrity: sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==}
|
||||
playwright@1.51.1:
|
||||
resolution: {integrity: sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
@@ -8258,6 +8294,10 @@ packages:
|
||||
prop-types@15.8.1:
|
||||
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
||||
|
||||
propagate@2.0.1:
|
||||
resolution: {integrity: sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
proper-lockfile@4.1.2:
|
||||
resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==}
|
||||
|
||||
@@ -9043,6 +9083,9 @@ packages:
|
||||
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
strict-event-emitter@0.5.1:
|
||||
resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==}
|
||||
|
||||
strict-uri-encode@1.1.0:
|
||||
resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -11497,7 +11540,21 @@ snapshots:
|
||||
|
||||
'@depay/web3-blockchains@9.6.7': {}
|
||||
|
||||
'@depay/web3-mock@14.17.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)':
|
||||
'@depay/web3-client@10.18.6(@depay/solana-web3.js@1.27.0)(@depay/web3-blockchains@9.6.7)(ethers@6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))':
|
||||
dependencies:
|
||||
'@depay/solana-web3.js': 1.27.0
|
||||
'@depay/web3-blockchains': 9.6.7
|
||||
ethers: 6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
|
||||
'@depay/web3-mock-evm@14.19.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@depay/web3-blockchains': 9.6.7
|
||||
ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@depay/web3-mock@14.19.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@depay/solana-web3.js': 1.27.0
|
||||
'@depay/web3-blockchains': 9.6.7
|
||||
@@ -13027,6 +13084,15 @@ snapshots:
|
||||
'@motionone/dom': 10.18.0
|
||||
tslib: 2.7.0
|
||||
|
||||
'@mswjs/interceptors@0.37.6':
|
||||
dependencies:
|
||||
'@open-draft/deferred-promise': 2.2.0
|
||||
'@open-draft/logger': 0.3.0
|
||||
'@open-draft/until': 2.1.0
|
||||
is-node-process: 1.2.0
|
||||
outvariant: 1.4.3
|
||||
strict-event-emitter: 0.5.1
|
||||
|
||||
'@mui/core-downloads-tracker@6.4.8': {}
|
||||
|
||||
'@mui/icons-material@6.0.2(@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)':
|
||||
@@ -13408,6 +13474,15 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@open-draft/deferred-promise@2.2.0': {}
|
||||
|
||||
'@open-draft/logger@0.3.0':
|
||||
dependencies:
|
||||
is-node-process: 1.2.0
|
||||
outvariant: 1.4.3
|
||||
|
||||
'@open-draft/until@2.1.0': {}
|
||||
|
||||
'@openzeppelin/contracts-upgradeable@4.9.6': {}
|
||||
|
||||
'@openzeppelin/contracts@4.9.6': {}
|
||||
@@ -13558,9 +13633,9 @@ snapshots:
|
||||
|
||||
'@pkgr/core@0.1.1': {}
|
||||
|
||||
'@playwright/test@1.45.3':
|
||||
'@playwright/test@1.51.1':
|
||||
dependencies:
|
||||
playwright: 1.45.3
|
||||
playwright: 1.51.1
|
||||
|
||||
'@pnpm/config.env-replace@1.1.0': {}
|
||||
|
||||
@@ -14448,19 +14523,24 @@ snapshots:
|
||||
'@swc/counter': 0.1.3
|
||||
tslib: 2.7.0
|
||||
|
||||
'@synthetixio/ethereum-wallet-mock@0.0.1-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.24.2)':
|
||||
'@synthetixio/ethereum-wallet-mock@0.0.11(@depay/solana-web3.js@1.27.0)(@depay/web3-blockchains@9.6.7)(@playwright/test@1.51.1)(bufferutil@4.0.8)(ethers@6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.24.2)':
|
||||
dependencies:
|
||||
'@depay/web3-mock': 14.17.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
'@playwright/test': 1.45.3
|
||||
'@synthetixio/synpress-core': 0.0.1-alpha.7(@playwright/test@1.45.3)
|
||||
'@depay/web3-client': 10.18.6(@depay/solana-web3.js@1.27.0)(@depay/web3-blockchains@9.6.7)(ethers@6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))
|
||||
'@depay/web3-mock': 14.19.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
'@depay/web3-mock-evm': 14.19.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
'@playwright/test': 1.51.1
|
||||
'@synthetixio/synpress-core': 0.0.11(@playwright/test@1.51.1)
|
||||
viem: 2.9.9(bufferutil@4.0.8)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.24.2)
|
||||
transitivePeerDependencies:
|
||||
- '@depay/solana-web3.js'
|
||||
- '@depay/web3-blockchains'
|
||||
- bufferutil
|
||||
- ethers
|
||||
- typescript
|
||||
- utf-8-validate
|
||||
- zod
|
||||
|
||||
'@synthetixio/synpress-cache@0.0.1-alpha.7(playwright-core@1.45.3)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)':
|
||||
'@synthetixio/synpress-cache@0.0.11(playwright-core@1.51.1)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)':
|
||||
dependencies:
|
||||
axios: 1.6.7
|
||||
chalk: 5.3.0
|
||||
@@ -14469,7 +14549,7 @@ snapshots:
|
||||
fs-extra: 11.2.0
|
||||
glob: 10.3.10
|
||||
gradient-string: 2.0.2
|
||||
playwright-core: 1.45.3
|
||||
playwright-core: 1.51.1
|
||||
progress: 2.0.3
|
||||
tsup: 8.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)
|
||||
unzipper: 0.10.14
|
||||
@@ -14483,15 +14563,15 @@ snapshots:
|
||||
- ts-node
|
||||
- typescript
|
||||
|
||||
'@synthetixio/synpress-core@0.0.1-alpha.7(@playwright/test@1.45.3)':
|
||||
'@synthetixio/synpress-core@0.0.11(@playwright/test@1.51.1)':
|
||||
dependencies:
|
||||
'@playwright/test': 1.45.3
|
||||
'@playwright/test': 1.51.1
|
||||
|
||||
'@synthetixio/synpress-metamask@0.0.1-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(playwright-core@1.45.3)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)(utf-8-validate@5.0.10)':
|
||||
'@synthetixio/synpress-metamask@0.0.11(@playwright/test@1.51.1)(bufferutil@4.0.8)(playwright-core@1.51.1)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@playwright/test': 1.45.3
|
||||
'@synthetixio/synpress-cache': 0.0.1-alpha.7(playwright-core@1.45.3)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)
|
||||
'@synthetixio/synpress-core': 0.0.1-alpha.7(@playwright/test@1.45.3)
|
||||
'@playwright/test': 1.51.1
|
||||
'@synthetixio/synpress-cache': 0.0.11(playwright-core@1.51.1)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)
|
||||
'@synthetixio/synpress-core': 0.0.11(@playwright/test@1.51.1)
|
||||
'@viem/anvil': 0.0.7(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
fs-extra: 11.2.0
|
||||
zod: 3.22.4
|
||||
@@ -14507,18 +14587,21 @@ snapshots:
|
||||
- typescript
|
||||
- utf-8-validate
|
||||
|
||||
'@synthetixio/synpress@4.0.0-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(playwright-core@1.45.3)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.24.2)':
|
||||
'@synthetixio/synpress@4.0.10(@depay/solana-web3.js@1.27.0)(@depay/web3-blockchains@9.6.7)(@playwright/test@1.51.1)(bufferutil@4.0.8)(ethers@6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(playwright-core@1.51.1)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.24.2)':
|
||||
dependencies:
|
||||
'@playwright/test': 1.45.3
|
||||
'@synthetixio/ethereum-wallet-mock': 0.0.1-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.24.2)
|
||||
'@synthetixio/synpress-cache': 0.0.1-alpha.7(playwright-core@1.45.3)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)
|
||||
'@synthetixio/synpress-core': 0.0.1-alpha.7(@playwright/test@1.45.3)
|
||||
'@synthetixio/synpress-metamask': 0.0.1-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(playwright-core@1.45.3)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)(utf-8-validate@5.0.10)
|
||||
'@playwright/test': 1.51.1
|
||||
'@synthetixio/ethereum-wallet-mock': 0.0.11(@depay/solana-web3.js@1.27.0)(@depay/web3-blockchains@9.6.7)(@playwright/test@1.51.1)(bufferutil@4.0.8)(ethers@6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.24.2)
|
||||
'@synthetixio/synpress-cache': 0.0.11(playwright-core@1.51.1)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)
|
||||
'@synthetixio/synpress-core': 0.0.11(@playwright/test@1.51.1)
|
||||
'@synthetixio/synpress-metamask': 0.0.11(@playwright/test@1.51.1)(bufferutil@4.0.8)(playwright-core@1.51.1)(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.7.5)(typescript@5.4.5))(typescript@5.4.5)(utf-8-validate@5.0.10)
|
||||
transitivePeerDependencies:
|
||||
- '@depay/solana-web3.js'
|
||||
- '@depay/web3-blockchains'
|
||||
- '@microsoft/api-extractor'
|
||||
- '@swc/core'
|
||||
- bufferutil
|
||||
- debug
|
||||
- ethers
|
||||
- playwright-core
|
||||
- postcss
|
||||
- supports-color
|
||||
@@ -18810,6 +18893,8 @@ snapshots:
|
||||
|
||||
is-negative-zero@2.0.3: {}
|
||||
|
||||
is-node-process@1.2.0: {}
|
||||
|
||||
is-number-object@1.0.7:
|
||||
dependencies:
|
||||
has-tostringtag: 1.0.2
|
||||
@@ -20193,15 +20278,15 @@ snapshots:
|
||||
|
||||
neo-async@2.6.2: {}
|
||||
|
||||
next-seo@6.6.0(next@14.2.25(@babel/core@7.25.7)(@playwright/test@1.45.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.86.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
next-seo@6.6.0(next@14.2.25(@babel/core@7.25.7)(@playwright/test@1.51.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.86.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
next: 14.2.25(@babel/core@7.25.7)(@playwright/test@1.45.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.86.0)
|
||||
next: 14.2.25(@babel/core@7.25.7)(@playwright/test@1.51.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.86.0)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
|
||||
next-tick@1.1.0: {}
|
||||
|
||||
next@14.2.25(@babel/core@7.25.7)(@playwright/test@1.45.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.86.0):
|
||||
next@14.2.25(@babel/core@7.25.7)(@playwright/test@1.51.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.86.0):
|
||||
dependencies:
|
||||
'@next/env': 14.2.25
|
||||
'@swc/helpers': 0.5.5
|
||||
@@ -20222,7 +20307,7 @@ snapshots:
|
||||
'@next/swc-win32-arm64-msvc': 14.2.25
|
||||
'@next/swc-win32-ia32-msvc': 14.2.25
|
||||
'@next/swc-win32-x64-msvc': 14.2.25
|
||||
'@playwright/test': 1.45.3
|
||||
'@playwright/test': 1.51.1
|
||||
sass: 1.86.0
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
@@ -20235,6 +20320,12 @@ snapshots:
|
||||
|
||||
nocache@3.0.4: {}
|
||||
|
||||
nock@14.0.1:
|
||||
dependencies:
|
||||
'@mswjs/interceptors': 0.37.6
|
||||
json-stringify-safe: 5.0.1
|
||||
propagate: 2.0.1
|
||||
|
||||
node-abi@3.68.0:
|
||||
dependencies:
|
||||
semver: 7.6.3
|
||||
@@ -20480,6 +20571,8 @@ snapshots:
|
||||
|
||||
os-tmpdir@1.0.2: {}
|
||||
|
||||
outvariant@1.4.3: {}
|
||||
|
||||
ox@0.6.7(typescript@5.4.5)(zod@3.24.2):
|
||||
dependencies:
|
||||
'@adraffy/ens-normalize': 1.11.0
|
||||
@@ -20718,11 +20811,11 @@ snapshots:
|
||||
mlly: 1.7.2
|
||||
pathe: 1.1.2
|
||||
|
||||
playwright-core@1.45.3: {}
|
||||
playwright-core@1.51.1: {}
|
||||
|
||||
playwright@1.45.3:
|
||||
playwright@1.51.1:
|
||||
dependencies:
|
||||
playwright-core: 1.45.3
|
||||
playwright-core: 1.51.1
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
|
||||
@@ -20847,6 +20940,8 @@ snapshots:
|
||||
object-assign: 4.1.1
|
||||
react-is: 16.13.1
|
||||
|
||||
propagate@2.0.1: {}
|
||||
|
||||
proper-lockfile@4.1.2:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
@@ -21834,6 +21929,8 @@ snapshots:
|
||||
|
||||
streamsearch@1.1.0: {}
|
||||
|
||||
strict-event-emitter@0.5.1: {}
|
||||
|
||||
strict-uri-encode@1.1.0: {}
|
||||
|
||||
strict-uri-encode@2.0.0: {}
|
||||
|
||||
@@ -6,3 +6,4 @@ packages:
|
||||
- 'operations/**'
|
||||
- 'bridge-ui/**'
|
||||
- 'ts-libs/**'
|
||||
- '!contracts/lib/**'
|
||||
Reference in New Issue
Block a user