From 63ad897beb03dfde84736332355b3872d0baf7fa Mon Sep 17 00:00:00 2001 From: Javier Bullrich Date: Thu, 8 Jun 2023 15:03:48 +0200 Subject: [PATCH 01/94] client: added support for westend (#281) Created a new page with the Westend chain. Added a popup so you can easily change the chin. Updated the content so it automatically assigns information based on the chain. Added the network data object of the westend network. Added a new page `/westend` where the chain is automatically set based on that information. Added a network selection dropdown to switch between rococo and westend --- .github/workflows/deploy-site.yml | 3 +- client/.env | 3 +- client/playwright.config.ts | 3 +- client/src/app.css | 16 + client/{static => src/lib/assets}/FAQ.md | 12 +- client/src/{ => lib}/assets/logo.png | Bin client/src/{ => lib}/assets/logo.svg | 0 client/src/{ => lib}/assets/polkadot.png | Bin client/src/lib/components/Card.svelte | 22 +- client/src/lib/components/Faucet.svelte | 59 ++++ client/src/lib/components/Form.svelte | 4 +- client/src/lib/components/NavBar.svelte | 21 +- .../src/lib/components/NetworkDropdown.svelte | 41 +++ client/src/lib/components/NetworkInput.svelte | 5 +- client/src/lib/components/SocialTags.svelte | 2 +- client/src/lib/utils/faucetRequest.ts | 11 +- client/src/lib/utils/networkData.ts | 30 ++ client/src/routes/+layout.svelte | 3 +- client/src/routes/+page.svelte | 63 +--- client/src/routes/+page.ts | 10 - client/src/routes/westend/+page.svelte | 12 + client/tailwind.config.cjs | 3 +- client/tests/faucet.ts | 325 ++++++++++++++++++ client/tests/rococo.test.ts | 12 + client/tests/switch.test.ts | 62 ++++ client/tests/test.ts | 321 ----------------- client/tests/westend.test.ts | 10 + 27 files changed, 626 insertions(+), 427 deletions(-) rename client/{static => src/lib/assets}/FAQ.md (75%) rename client/src/{ => lib}/assets/logo.png (100%) rename client/src/{ => lib}/assets/logo.svg (100%) rename client/src/{ => lib}/assets/polkadot.png (100%) create mode 100644 client/src/lib/components/Faucet.svelte create mode 100644 client/src/lib/components/NetworkDropdown.svelte delete mode 100644 client/src/routes/+page.ts create mode 100644 client/src/routes/westend/+page.svelte create mode 100644 client/tests/faucet.ts create mode 100644 client/tests/rococo.test.ts create mode 100644 client/tests/switch.test.ts delete mode 100644 client/tests/test.ts create mode 100644 client/tests/westend.test.ts diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index 1c19dd1a..c969add5 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -32,7 +32,8 @@ jobs: - run: yarn run build env: PUBLIC_CAPTCHA_KEY: 6LdU5kckAAAAANktvvAKJ0auYUBRP0su94G7WXwe - PUBLIC_FAUCET_URL: https://ink-docs-rococo-faucet.parity-testnet.parity.io/drip/web + PUBLIC_FAUCET_ROCOCO_URL: https://ink-docs-rococo-faucet.parity-testnet.parity.io/drip/web + PUBLIC_FAUCET_WESTEND_URL: "https://westend-faucet.polkadot.io/drip/web" GITHUB_PAGES: "/${{ github.event.repository.name }}" STATIC: true - uses: actions/upload-artifact@master diff --git a/client/.env b/client/.env index e741ad6d..7aff1a12 100644 --- a/client/.env +++ b/client/.env @@ -1,5 +1,6 @@ PUBLIC_DEMO_MODE= PUBLIC_CAPTCHA_KEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI -PUBLIC_FAUCET_URL= +PUBLIC_FAUCET_ROCOCO_URL= +PUBLIC_FAUCET_WESTEND_URL= PUBLIC_ISSUE_LINK=https://github.com/paritytech/polkadot-testnet-faucet/issues/new/choose PUBLIC_FORUM="https://forum.polkadot.network/t/experiencing-trouble-accessing-our-rococo-faucet-please-post-here/2952" diff --git a/client/playwright.config.ts b/client/playwright.config.ts index 9387f67e..9dc0989c 100644 --- a/client/playwright.config.ts +++ b/client/playwright.config.ts @@ -6,7 +6,8 @@ const config: PlaywrightTestConfig = { port: 4173, env: { PUBLIC_CAPTCHA_KEY: "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI", - PUBLIC_FAUCET_URL: "https://example.com/test", + PUBLIC_FAUCET_WESTEND_URL: "https://example.com/test", + PUBLIC_FAUCET_ROCOCO_URL: "https://test.com/example", PUBLIC_DEMO_MODE: "" } }, diff --git a/client/src/app.css b/client/src/app.css index e61c5797..c0a7b4bd 100644 --- a/client/src/app.css +++ b/client/src/app.css @@ -111,4 +111,20 @@ button:focus-visible { background-color: #282837; } +.faucet-card { + background-color: #191924; +} + +.card-title { + font-family: "Unbounded", sans-serif; + @apply text-white text-4xl; + font-weight: 700; +} + +.card-subtitle { + @apply text-white opacity-70; + font-weight: 400; + font-size: 16px; +} + @tailwind utilities; diff --git a/client/static/FAQ.md b/client/src/lib/assets/FAQ.md similarity index 75% rename from client/static/FAQ.md rename to client/src/lib/assets/FAQ.md index a91e4946..9eea9c2f 100644 --- a/client/static/FAQ.md +++ b/client/src/lib/assets/FAQ.md @@ -4,21 +4,21 @@ This is a polkadot-testnet faucet. You can use this to load testnet tokens into ## What is a faucet? -A faucet is a developer tool to get a small amount of testnet tokens (ROC) in order to test and troubleshoot a decentralized application or protocol before going live on Polkadot, where one must use real DOT. +A faucet is a developer tool to get a small amount of testnet tokens () in order to test and troubleshoot a decentralized application or protocol before going live on Polkadot, where one must use real DOT. Most faucets require social authentication (e.g. Twitter post or login confirming you are a real human) or place you in a queue to wait for a testnet token through the faucet. The Polkadot faucet is free, fast, and does not require authentication. ## How do I use this? -To request funds, simply enter your Rococo wallet address, fill the captcha, and hit "Submit". +To request funds, simply enter your wallet address, fill the captcha, and hit "Submit". -## How much ROC will I receive? +## How much will I receive? -You will receive 100 ROC per request. +You will receive 100 per request. ## How often can I request tokens? -You can request tokens every 24h! If you request ROC for one account, you can't request more for another parachain in that period. +You can request tokens every 24h! If you request for one account, you can't request more for another parachain in that period. ## Can I request tokens for a specific parachain? @@ -28,7 +28,7 @@ If you want to learn more about Parachains, check out [the docs](https://polkado ## What is a testnet token? -Testnet tokens are a test currency that allows you to test your Polkadot application before going live. Testnet tokens can be used in place of real tokens on testnets like Rococo. +Testnet tokens are a test currency that allows you to test your Polkadot application before going live. Testnet tokens can be used in place of real tokens on testnets like . You can read more [here](https://polkadot.network/blog/rococo-v1-a-holiday-gift-to-the-polkadot-community/). ## Can I get real DOTs? diff --git a/client/src/assets/logo.png b/client/src/lib/assets/logo.png similarity index 100% rename from client/src/assets/logo.png rename to client/src/lib/assets/logo.png diff --git a/client/src/assets/logo.svg b/client/src/lib/assets/logo.svg similarity index 100% rename from client/src/assets/logo.svg rename to client/src/lib/assets/logo.svg diff --git a/client/src/assets/polkadot.png b/client/src/lib/assets/polkadot.png similarity index 100% rename from client/src/assets/polkadot.png rename to client/src/lib/assets/polkadot.png diff --git a/client/src/lib/components/Card.svelte b/client/src/lib/components/Card.svelte index 6dbf7cc7..1db883da 100644 --- a/client/src/lib/components/Card.svelte +++ b/client/src/lib/components/Card.svelte @@ -5,8 +5,8 @@
-

{$testnet.networkName} Faucet

-

+

{$testnet.networkName} Faucet

+

Get {$testnet.currency} tokens for Polkadot's {$testnet.networkName} testnet and its parachains.

@@ -24,21 +24,3 @@
{/if} - - diff --git a/client/src/lib/components/Faucet.svelte b/client/src/lib/components/Faucet.svelte new file mode 100644 index 00000000..d5f12580 --- /dev/null +++ b/client/src/lib/components/Faucet.svelte @@ -0,0 +1,59 @@ + + +
+ +
+ + {#if !$operation} +
+ {:else} +
+ {#if $operation.success} + + {:else} + + {/if} +
+ {/if} + +
+ +
+ + diff --git a/client/src/lib/components/Form.svelte b/client/src/lib/components/Form.svelte index a8cccffb..20e63da3 100644 --- a/client/src/lib/components/Form.svelte +++ b/client/src/lib/components/Form.svelte @@ -32,7 +32,7 @@ } async function request(address: string): Promise { - return faucetRequest(address, token, network); + return faucetRequest(address, token, $testnet, network); } @@ -70,7 +70,7 @@ Your funds have been sent.
- import logo from "../../assets/logo.svg"; + import logo from "$lib/assets/logo.svg"; + import NetworkDropdown from "./NetworkDropdown.svelte"; + + import { Networks, type NetworkData } from "$lib/utils/networkData"; + export let currentUrl: string; + + let currentNetwork: NetworkData; + + function getCurrentNetwork(url: string): NetworkData { + const index = Networks.findIndex((n) => n.url === url); + if (index < 0) { + throw new Error(`Network for ${url} not found!`); + } + return Networks[index].network; + } + + $: currentNetwork = getCurrentNetwork(currentUrl); diff --git a/client/src/lib/components/NetworkDropdown.svelte b/client/src/lib/components/NetworkDropdown.svelte new file mode 100644 index 00000000..eaedfc72 --- /dev/null +++ b/client/src/lib/components/NetworkDropdown.svelte @@ -0,0 +1,41 @@ + + + + + diff --git a/client/src/lib/components/NetworkInput.svelte b/client/src/lib/components/NetworkInput.svelte index 388304ba..05c962f8 100644 --- a/client/src/lib/components/NetworkInput.svelte +++ b/client/src/lib/components/NetworkInput.svelte @@ -1,4 +1,5 @@ { if (DEMO) { return boilerplateRequest(address, recaptcha); } const chain = parachain && parachain > 0 ? parachain.toString() : undefined; - return faucetRequest(address, recaptcha, chain); + return faucetRequest(address, recaptcha, network, chain); } export async function faucetRequest( address: string, recaptcha: string, + network: NetworkData, parachain_id?: string ): Promise { const body = { @@ -23,9 +26,9 @@ export async function faucetRequest( recaptcha }; - const url = PUBLIC_FAUCET_URL; + const url = network.endpoint; if (!url) { - throw new Error("PUBLIC_FAUCET_URL is not defined"); + throw new Error(`Endpoint for ${network.networkName} is not defined`); } const fetchResult = await fetch(url, { method: "POST", diff --git a/client/src/lib/utils/networkData.ts b/client/src/lib/utils/networkData.ts index b33172cc..77f59339 100644 --- a/client/src/lib/utils/networkData.ts +++ b/client/src/lib/utils/networkData.ts @@ -1,3 +1,5 @@ +import { PUBLIC_FAUCET_ROCOCO_URL, PUBLIC_FAUCET_WESTEND_URL } from "$env/static/public"; + export interface ChainData { name: string; id: number; @@ -7,6 +9,8 @@ export interface NetworkData { networkName: string; currency: string; chains: ChainData[]; + endpoint: string; + explorer: string; getChainName(id: number): string | null; } @@ -20,6 +24,8 @@ export const Rococo: NetworkData = { { name: "Encointer Lietaer", id: 1003 }, { name: "Bridgehub", id: 1013 } ], + endpoint: PUBLIC_FAUCET_ROCOCO_URL, + explorer: "https://rococo.subscan.io", getChainName: function (id: number): string | null { const index = this.chains.findIndex((ch) => ch.id === id); if (index < 0) { @@ -28,3 +34,27 @@ export const Rococo: NetworkData = { return this.chains[index].name; } }; + +export const Westend: NetworkData = { + networkName: "Westend", + currency: "$WND", + chains: [ + { name: "Westend Relay Chain", id: -1 }, + { name: "Westmint", id: 1000 }, + { name: "Collectives", id: 1001 } + ], + endpoint: PUBLIC_FAUCET_WESTEND_URL, + explorer: "https://westend.subscan.io", + getChainName: function (id: number): string | null { + const index = this.chains.findIndex((ch) => ch.id === id); + if (index < 0) { + return null; + } + return this.chains[index].name; + } +}; + +export const Networks: { network: NetworkData; url: string }[] = [ + { network: Rococo, url: "/" }, + { network: Westend, url: "/westend" } +]; diff --git a/client/src/routes/+layout.svelte b/client/src/routes/+layout.svelte index 49959c2b..f3cb3279 100644 --- a/client/src/routes/+layout.svelte +++ b/client/src/routes/+layout.svelte @@ -2,10 +2,11 @@ import Footer from "$lib/components/Footer.svelte"; import NavBar from "$lib/components/NavBar.svelte"; import "../app.css"; + import { page } from "$app/stores";
- +
diff --git a/client/src/routes/+page.svelte b/client/src/routes/+page.svelte index 88da94a6..7b415101 100644 --- a/client/src/routes/+page.svelte +++ b/client/src/routes/+page.svelte @@ -1,57 +1,12 @@ -
- -
- - {#if !$operation} - - {:else} -
- {#if $operation.success} - - {:else} - - {/if} -
- {/if} -
-
- -
- - + diff --git a/client/src/routes/+page.ts b/client/src/routes/+page.ts deleted file mode 100644 index 39c015a9..00000000 --- a/client/src/routes/+page.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { PageLoad } from "./$types"; - -export const load: PageLoad<{ faq: string }> = async ({ fetch }) => { - const data = await fetch("/FAQ.md"); - const textData = await data.text(); - - return { - faq: textData - }; -}; diff --git a/client/src/routes/westend/+page.svelte b/client/src/routes/westend/+page.svelte new file mode 100644 index 00000000..e460f084 --- /dev/null +++ b/client/src/routes/westend/+page.svelte @@ -0,0 +1,12 @@ + + + diff --git a/client/tailwind.config.cjs b/client/tailwind.config.cjs index 5a1b315a..524f29f4 100644 --- a/client/tailwind.config.cjs +++ b/client/tailwind.config.cjs @@ -2,6 +2,7 @@ module.exports = { content: ["./src/**/*.{js,svelte,ts}", "./src/**/*.html"], plugins: [require("daisyui"), require("@tailwindcss/typography")], daisyui: { - themes: ["dark"] + themes: ["dark"], + logs: false } }; diff --git a/client/tests/faucet.ts b/client/tests/faucet.ts new file mode 100644 index 00000000..d495bb41 --- /dev/null +++ b/client/tests/faucet.ts @@ -0,0 +1,325 @@ +import { + expect, + test, + type Frame, + type FullConfig, + type Locator, + type Page +} from "@playwright/test"; + +type FormSubmit = { + address: string; + recaptcha: string; + parachain_id?: string; +}; + +const getFormElements = async (page: Page, getCaptcha = false) => { + let captcha: Locator = {} as Locator; + if (getCaptcha) { + // ?: Hack. We need to wait for the frame to load and then invade it. + await page.reload(); + const captchaFrame = await new Promise(function (resolve, reject) { + let i = 0; + // function that waits for the frame and timeouts after 3 seconds + (function waitForFrame() { + const captchaFrame = page + .frames() + .filter((f) => f.url().includes("https://www.google.com/recaptcha/api2/")); + if (captchaFrame.length > 0) { + return resolve(captchaFrame[0]); + } else { + i++; + if (i > 10) { + reject(new Error("Timeout")); + } + } + setTimeout(waitForFrame, 300); + })(); + }); + captcha = captchaFrame?.locator("#recaptcha-anchor") as Locator; + } + return { + address: page.getByTestId("address"), + network: page.getByTestId("network"), + captcha, + submit: page.getByTestId("submit-button"), + dropdown: page.getByTestId("dropdown") + }; +}; + +export class FaucetTests { + private readonly dropdownId = "dropdown"; + constructor( + readonly url: string, + readonly envVariable: string, + readonly chainName: string, + readonly chains: { name: string; id: number }[] + ) {} + + /** + * Gets the faucet url from the config file + * @param config The second value that is given on the tests arrow function + */ + getFaucetUrl = (config: FullConfig): string => { + const URL_VAR = this.envVariable; + const env = config.webServer?.env; + if (!env) { + throw new Error("No env vars in project"); + } + const faucetUrl = env[URL_VAR]; + if (!faucetUrl) { + throw new Error(`No env var value found for ${URL_VAR}`); + } + + return faucetUrl; + }; + + runTests() { + test.describe(`${this.chainName} tests`, () => { + test.describe("on page load", () => { + test("page has expected header", async ({ page }) => { + await page.goto(this.url); + await expect(page.getByRole("heading", { name: this.chainName })).toBeVisible(); + }); + + test("page has disabled submit button", async ({ page }) => { + await page.goto(this.url); + const { submit } = await getFormElements(page); + await expect(submit).toBeVisible(); + await expect(submit).toBeDisabled(); + }); + + test("page has form elements", async ({ page }) => { + await page.goto(this.url); + const { address, network, captcha } = await getFormElements(page, true); + await expect(address).toBeVisible(); + await expect(network).toBeHidden(); + await expect(captcha).toBeVisible(); + }); + + test("page loads with default value in parachain field", async ({ page }) => { + await page.goto(this.url); + const { network } = await getFormElements(page); + await expect(network).toHaveValue("-1"); + }); + + test("page with get parameter loads with value in parachain field", async ({ page }) => { + const parachainId = "1234"; + await page.goto(`${this.url}?parachain=${parachainId}`); + const { network } = await getFormElements(page); + await expect(network).toHaveValue(parachainId); + }); + + test("page has captcha", async ({ page }) => { + await page.goto(this.url); + const { captcha } = await getFormElements(page, true); + await expect(captcha).toBeVisible(); + }); + }); + + test.describe("dropdown interaction", () => { + const networkName = this.chains[1].name; + test("dropdown appears on click", async ({ page }) => { + await page.goto(this.url); + const dropdown = page.getByTestId(this.dropdownId); + await expect(dropdown).toBeVisible(); + await expect(page.getByText(networkName)).toBeHidden(); + await dropdown.click(); + await expect(page.getByText(networkName)).toBeVisible(); + }); + + test("dropdown closes on network selection", async ({ page }) => { + await page.goto(this.url); + const dropdown = page.getByTestId(this.dropdownId); + await expect(dropdown).toBeVisible(); + const networkBtn = page.getByTestId("network-1"); + await dropdown.click(); + await expect(networkBtn).toBeVisible(); + await networkBtn.click(); + await expect(networkBtn).not.toBeVisible(); + }); + + test("network changes on modal selection", async ({ page }) => { + await page.goto(this.url); + const dropdown = page.getByTestId(this.dropdownId); + const { network } = await getFormElements(page); + await expect(dropdown).toBeVisible(); + const networkBtn = page.getByTestId("network-1"); + await dropdown.click(); + await expect(networkBtn).toBeVisible(); + await networkBtn.click(); + await expect(networkBtn).not.toBeVisible(); + await expect(network).toHaveValue("1000"); + }); + }); + + test.describe("Custom networks", () => { + let network: Locator; + let customChainDiv: Locator; + + test.beforeEach(async ({ page }) => { + await page.goto(this.url); + network = (await getFormElements(page)).network; + customChainDiv = page.getByTestId("custom-network-button"); + await expect(customChainDiv).toBeEnabled(); + await expect(customChainDiv).toContainText("Use custom chain id"); + await customChainDiv.click(); + expect(network).toBeVisible(); + }); + + test("Value is empty on network pick", async () => { + await expect(network).toHaveValue(""); + }); + + test("Value restores to -1 when picking preselected network", async () => { + await customChainDiv.click(); + await expect(network).toBeHidden(); + await expect(network).toHaveValue("-1"); + }); + }); + + test.describe("form interaction", () => { + test("submit form becomes valid on data entry", async ({ page }) => { + await page.goto(this.url); + const { address, captcha, submit } = await getFormElements(page, true); + await expect(submit).toBeDisabled(); + await address.fill("address"); + await captcha.click(); + await expect(submit).toBeEnabled(); + }); + + test("sends data on submit", async ({ page }, { config }) => { + await page.goto(this.url); + const { address, captcha, submit } = await getFormElements(page, true); + await expect(submit).toBeDisabled(); + const myAddress = "0x000000001"; + await address.fill(myAddress); + await captcha.click(); + const faucetUrl = this.getFaucetUrl(config); + await page.route(faucetUrl, (route) => + route.fulfill({ + body: JSON.stringify({ hash: "hash" }) + }) + ); + + const request = page.waitForRequest((req) => { + if (req.url() === faucetUrl) { + const data = req.postDataJSON() as FormSubmit; + expect(data.address).toEqual(myAddress); + return !!data.recaptcha; + } + return false; + }); + await submit.click(); + // verify that the post request is correct + await request; + }); + + for (let i = 1; i < this.chains.length; i++) { + const chain = this.chains[i]; + test(`sends data with ${chain.name} chain on submit`, async ({ page }, { config }) => { + await page.goto(this.url); + const { address, captcha, submit } = await getFormElements(page, true); + const dropdown = page.getByTestId(this.dropdownId); + await expect(submit).toBeDisabled(); + const myAddress = "0x000000002"; + await address.fill(myAddress); + await dropdown.click(); + const networkBtn = page.getByTestId(`network-${i}`); + await expect(networkBtn).toBeVisible(); + await networkBtn.click(); + await captcha.click(); + await expect(submit).toBeEnabled(); + const faucetUrl = this.getFaucetUrl(config); + await page.route(faucetUrl, (route) => + route.fulfill({ + body: JSON.stringify({ hash: "hash" }) + }) + ); + + const request = page.waitForRequest((req) => { + if (req.url() === faucetUrl) { + const data = req.postDataJSON() as FormSubmit; + const parachain_id = chain.id > 0 ? chain.id.toString() : undefined; + expect(data).toMatchObject({ address: myAddress, parachain_id }); + return !!data.recaptcha; + } + return false; + }); + + await submit.click(); + await request; + }); + } + + test("sends data with custom chain on submit", async ({ page }, { config }) => { + await page.goto(this.url); + const { address, network, captcha, submit } = await getFormElements(page, true); + await expect(submit).toBeDisabled(); + const myAddress = "0x000000002"; + await address.fill(myAddress); + const customChainDiv = page.getByTestId("custom-network-button"); + await customChainDiv.click(); + await network.fill("9999"); + await captcha.click(); + await expect(submit).toBeEnabled(); + const faucetUrl = this.getFaucetUrl(config); + await page.route(faucetUrl, (route) => + route.fulfill({ + body: JSON.stringify({ hash: "hash" }) + }) + ); + + const request = page.waitForRequest((req) => { + if (req.url() === faucetUrl) { + const data = req.postDataJSON() as FormSubmit; + expect(data).toMatchObject({ address: myAddress, parachain_id: "9999" }); + return !!data.recaptcha; + } + return false; + }); + + await submit.click(); + await request; + }); + + test("display link to transaction", async ({ page }, { config }) => { + await page.goto(this.url); + const operationHash = "0x0123435423412343214"; + const { address, captcha, submit } = await getFormElements(page, true); + await expect(submit).toBeDisabled(); + const myAddress = "0x000000001"; + await address.fill(myAddress); + await captcha.click(); + await page.route(this.getFaucetUrl(config), (route) => + route.fulfill({ + body: JSON.stringify({ hash: operationHash }) + }) + ); + await submit.click(); + const transactionLink = page.getByTestId("success-button"); + await expect(transactionLink).toBeVisible(); + expect(await transactionLink.getAttribute("href")).toContain(operationHash); + }); + + test("throw error", async ({ page }, { config }) => { + await page.goto(this.url); + const error = "Things failed because you are a naughty boy!"; + const { address, captcha, submit } = await getFormElements(page, true); + await expect(submit).toBeDisabled(); + await address.fill("0x123"); + await captcha.click(); + await page.route(this.getFaucetUrl(config), (route) => + route.fulfill({ + body: JSON.stringify({ error }) + }) + ); + await submit.click(); + const errorMessage = page.getByTestId("error"); + await expect(errorMessage).toBeVisible(); + expect((await errorMessage.allInnerTexts())[0]).toContain(error); + }); + }); + }); + } +} diff --git a/client/tests/rococo.test.ts b/client/tests/rococo.test.ts new file mode 100644 index 00000000..fb3445b6 --- /dev/null +++ b/client/tests/rococo.test.ts @@ -0,0 +1,12 @@ +import { FaucetTests } from "./faucet.js"; + +const chains = [ + { name: "Relay Chain", id: -1 }, + { name: "Rockmine", id: 1000 }, + { name: "Contracts", id: 1002 }, + { name: "Encointer Lietaer", id: 1003 }, + { name: "Bridgehub", id: 1013 } +]; + +const test = new FaucetTests("/", "PUBLIC_FAUCET_ROCOCO_URL", "Rococo Faucet", chains); +test.runTests(); diff --git a/client/tests/switch.test.ts b/client/tests/switch.test.ts new file mode 100644 index 00000000..1ff86313 --- /dev/null +++ b/client/tests/switch.test.ts @@ -0,0 +1,62 @@ +import { + expect, + test, + type Frame, + type FullConfig, + type Locator, + type Page +} from "@playwright/test"; + +export const Networks = [ + { name: "Rococo", url: "/" }, + { name: "Westend", url: "/westend" } +]; + +const networkSelectId = "network-select"; + +const networkTestId = Networks.map(({ name }) => `network-${name}`); + +test.describe("Test network switch component", () => { + for (let i = 0; i < Networks.length; i++) { + const network = Networks[i]; + test(`page for ${network.name} loads`, async ({ page }) => { + await page.goto(network.url); + await expect(page.getByRole("heading", { name: `${network.name} Faucet` })).toBeVisible(); + }); + + test(`network switch has correct name for ${network.url}`, async ({ page }) => { + await page.goto(network.url); + const selector = page.getByTestId(networkSelectId); + await expect(selector).toContainText(network.name); + }); + } + + test("network switch shows all available networks", async ({ page }) => { + await page.goto("/"); + const selector = page.getByTestId(networkSelectId); + await selector.click(); + for (let i = 0; i < networkTestId.length; i++) { + const link = page.getByTestId(networkTestId[i]); + await expect(link).toBeVisible(); + await expect(link).toContainText(Networks[i].name); + } + }); + + test("network switch are not visible by default", async ({ page }) => { + await page.goto("/"); + for (let i = 0; i < networkTestId.length; i++) { + const link = page.getByTestId(networkTestId[i]); + await expect(link).toBeHidden(); + } + }); + + test("network switch contains links", async ({ page }) => { + await page.goto("/"); + const selector = page.getByTestId(networkSelectId); + await selector.click(); + for (let i = 0; i < networkTestId.length; i++) { + const link = page.getByTestId(networkTestId[i]); + expect(await link.getAttribute("href")).toContain(Networks[i].url); + } + }); +}); diff --git a/client/tests/test.ts b/client/tests/test.ts deleted file mode 100644 index ceb7e271..00000000 --- a/client/tests/test.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { - expect, - test, - type Frame, - type FullConfig, - type Locator, - type Page -} from "@playwright/test"; - -const chains = [ - { name: "Relay Chain", id: -1 }, - { name: "Rockmine", id: 1000 }, - { name: "Contracts", id: 1002 }, - { name: "Encointer Lietaer", id: 1003 }, - { name: "Bridgehub", id: 1013 } -]; - -type FormSubmit = { - address: string; - recaptcha: string; - parachain_id?: string; -}; - -const getFormElements = async (page: Page, getCaptcha = false) => { - let captcha: Locator = {} as Locator; - if (getCaptcha) { - // ?: Hack. We need to wait for the frame to load and then invade it. - await page.reload(); - const captchaFrame = await new Promise(function (resolve, reject) { - let i = 0; - // function that waits for the frame and timeouts after 3 seconds - (function waitForFrame() { - const captchaFrame = page - .frames() - .filter((f) => f.url().includes("https://www.google.com/recaptcha/api2/")); - if (captchaFrame.length > 0) { - return resolve(captchaFrame[0]); - } else { - i++; - if (i > 10) { - reject(new Error("Timout")); - } - } - setTimeout(waitForFrame, 300); - })(); - }); - captcha = captchaFrame?.locator("#recaptcha-anchor") as Locator; - } - return { - address: page.getByTestId("address"), - network: page.getByTestId("network"), - captcha, - submit: page.getByTestId("submit-button"), - dropdown: page.getByTestId("dropdown") - }; -}; - -/** - * Gets the faucet url from the config file - * @param config The second value that is given on the tests arrow function - */ -const getFaucetUrl = (config: FullConfig): string => { - const URL_VAR = "PUBLIC_FAUCET_URL"; - const env = config.webServer?.env; - if (!env) { - throw new Error("No env vars in project"); - } - const faucetUrl = env[URL_VAR]; - if (!faucetUrl) { - throw new Error(`No env var value found for ${URL_VAR}`); - } - - return faucetUrl; -}; - -const dropdownId = "dropdown"; - -test.describe("on page load", () => { - test("page has expected header", async ({ page }) => { - await page.goto("/"); - await expect(page.getByRole("heading", { name: "Rococo Faucet" })).toBeVisible(); - }); - - test("page has disabled submit button", async ({ page }) => { - await page.goto("/"); - const { submit } = await getFormElements(page); - await expect(submit).toBeVisible(); - await expect(submit).toBeDisabled(); - }); - - test("page has form elements", async ({ page }) => { - await page.goto("/"); - const { address, network, captcha } = await getFormElements(page, true); - await expect(address).toBeVisible(); - await expect(network).toBeHidden(); - await expect(captcha).toBeVisible(); - }); - - test("page loads with default value in parachain field", async ({ page }) => { - await page.goto(`/`); - const { network } = await getFormElements(page); - await expect(network).toHaveValue("-1"); - }); - - test("page with get parameter loads with value in parachain field", async ({ page }) => { - const parachainId = "1234"; - await page.goto(`/?parachain=${parachainId}`); - const { network } = await getFormElements(page); - await expect(network).toHaveValue(parachainId); - }); - - test("page has captcha", async ({ page }) => { - await page.goto("/"); - const { captcha } = await getFormElements(page, true); - await expect(captcha).toBeVisible(); - }); -}); - -test.describe("dropdown interaction", () => { - const networkName = "Rockmine"; - test("dropdown appears on click", async ({ page }) => { - await page.goto("/"); - const dropdown = page.getByTestId(dropdownId); - await expect(dropdown).toBeVisible(); - await expect(page.getByText(networkName)).toBeHidden(); - await dropdown.click(); - await expect(page.getByText(networkName)).toBeVisible(); - }); - - test("dropdown closes on network selection", async ({ page }) => { - await page.goto("/"); - const dropdown = page.getByTestId(dropdownId); - await expect(dropdown).toBeVisible(); - const networkBtn = page.getByTestId("network-1"); - await dropdown.click(); - await expect(networkBtn).toBeVisible(); - await networkBtn.click(); - await expect(networkBtn).not.toBeVisible(); - }); - - test("network changes on modal selection", async ({ page }) => { - await page.goto("/"); - const dropdown = page.getByTestId(dropdownId); - const { network } = await getFormElements(page); - await expect(dropdown).toBeVisible(); - const networkBtn = page.getByTestId("network-1"); - await dropdown.click(); - await expect(networkBtn).toBeVisible(); - await networkBtn.click(); - await expect(networkBtn).not.toBeVisible(); - await expect(network).toHaveValue("1000"); - }); -}); - -test.describe("Custom networks", () => { - let network: Locator; - let customChainDiv: Locator; - - test.beforeEach(async ({ page }) => { - await page.goto("/"); - network = (await getFormElements(page)).network; - customChainDiv = page.getByTestId("custom-network-button"); - await expect(customChainDiv).toBeEnabled(); - await expect(customChainDiv).toContainText("Use custom chain id"); - await customChainDiv.click(); - expect(network).toBeVisible(); - }); - - test("Value is empty on network pick", async () => { - await expect(network).toHaveValue(""); - }); - - test("Value restores to -1 when picking preselected network", async () => { - await customChainDiv.click(); - await expect(network).toBeHidden(); - await expect(network).toHaveValue("-1"); - }); -}); - -test.describe("form interaction", () => { - test("submit form becomes valid on data entry", async ({ page }) => { - await page.goto("/"); - const { address, captcha, submit } = await getFormElements(page, true); - await expect(submit).toBeDisabled(); - await address.fill("address"); - await captcha.click(); - await expect(submit).toBeEnabled(); - }); - - test("sends data on submit", async ({ page }, { config }) => { - await page.goto("/"); - const { address, captcha, submit } = await getFormElements(page, true); - await expect(submit).toBeDisabled(); - const myAddress = "0x000000001"; - await address.fill(myAddress); - await captcha.click(); - const url = getFaucetUrl(config); - await page.route(url, (route) => - route.fulfill({ - body: JSON.stringify({ hash: "hash" }) - }) - ); - - const request = page.waitForRequest((req) => { - if (req.url() === url) { - const data = req.postDataJSON() as FormSubmit; - expect(data.address).toEqual(myAddress); - return !!data.recaptcha; - } - return false; - }); - await submit.click(); - // verify that the post request is correct - await request; - }); - - for (let i = 1; i < chains.length; i++) { - const chain = chains[i]; - test(`sends data with ${chain.name} chain on submit`, async ({ page }, { config }) => { - await page.goto("/"); - const { address, captcha, submit } = await getFormElements(page, true); - const dropdown = page.getByTestId(dropdownId); - await expect(submit).toBeDisabled(); - const myAddress = "0x000000002"; - await address.fill(myAddress); - await dropdown.click(); - const networkBtn = page.getByTestId(`network-${i}`); - await expect(networkBtn).toBeVisible(); - await networkBtn.click(); - await captcha.click(); - await expect(submit).toBeEnabled(); - const url = getFaucetUrl(config); - await page.route(url, (route) => - route.fulfill({ - body: JSON.stringify({ hash: "hash" }) - }) - ); - - const request = page.waitForRequest((req) => { - if (req.url() === url) { - const data = req.postDataJSON() as FormSubmit; - const parachain_id = chain.id > 0 ? chain.id.toString() : undefined; - expect(data).toMatchObject({ address: myAddress, parachain_id }); - return !!data.recaptcha; - } - return false; - }); - - await submit.click(); - await request; - }); - } - - test("sends data with custom chain on submit", async ({ page }, { config }) => { - await page.goto("/"); - const { address, network, captcha, submit } = await getFormElements(page, true); - await expect(submit).toBeDisabled(); - const myAddress = "0x000000002"; - await address.fill(myAddress); - const customChainDiv = page.getByTestId("custom-network-button"); - await customChainDiv.click(); - await network.fill("9999"); - await captcha.click(); - await expect(submit).toBeEnabled(); - const url = getFaucetUrl(config); - await page.route(url, (route) => - route.fulfill({ - body: JSON.stringify({ hash: "hash" }) - }) - ); - - const request = page.waitForRequest((req) => { - if (req.url() === url) { - const data = req.postDataJSON() as FormSubmit; - expect(data).toMatchObject({ address: myAddress, parachain_id: "9999" }); - return !!data.recaptcha; - } - return false; - }); - - await submit.click(); - await request; - }); - - test("display link to transaction", async ({ page }, { config }) => { - await page.goto("/"); - const operationHash = "0x0123435423412343214"; - const { address, captcha, submit } = await getFormElements(page, true); - await expect(submit).toBeDisabled(); - const myAddress = "0x000000001"; - await address.fill(myAddress); - await captcha.click(); - await page.route(getFaucetUrl(config), (route) => - route.fulfill({ - body: JSON.stringify({ hash: operationHash }) - }) - ); - await submit.click(); - const transactionLink = page.getByTestId("success-button"); - await expect(transactionLink).toBeVisible(); - expect(await transactionLink.getAttribute("href")).toContain(operationHash); - }); - - test("throw error", async ({ page }, { config }) => { - await page.goto("/"); - const error = "Things failed because you are a naughty boy!"; - const { address, captcha, submit } = await getFormElements(page, true); - await expect(submit).toBeDisabled(); - await address.fill("0x123"); - await captcha.click(); - await page.route(getFaucetUrl(config), (route) => - route.fulfill({ - body: JSON.stringify({ error }) - }) - ); - await submit.click(); - const errorMessage = page.getByTestId("error"); - await expect(errorMessage).toBeVisible(); - await expect((await errorMessage.allInnerTexts())[0]).toContain(error); - }); -}); diff --git a/client/tests/westend.test.ts b/client/tests/westend.test.ts new file mode 100644 index 00000000..ce4c2f60 --- /dev/null +++ b/client/tests/westend.test.ts @@ -0,0 +1,10 @@ +import { FaucetTests } from "./faucet.js"; + +const chains = [ + { name: "Westend Relay Chain", id: -1 }, + { name: "Westmint", id: 1000 }, + { name: "Collectives", id: 1001 } +]; + +const tests = new FaucetTests("/westend", "PUBLIC_FAUCET_WESTEND_URL", "Westend Faucet", chains); +tests.runTests(); From 47ad8ffa7ff1952320c58e00045f4304565ab492 Mon Sep 17 00:00:00 2001 From: Javier Bullrich Date: Thu, 8 Jun 2023 15:56:55 +0200 Subject: [PATCH 02/94] Fixed error on different base (#287) Fixed errors caused when the base of the project is not `/`. This changes get into consideration having a different base for the occasion that it is being built in a root directory, or, like in github pages, in a `/polkadot-testnet-faucet` directory. --- .github/workflows/deploy-site.yml | 1 + client/src/lib/components/NavBar.svelte | 3 ++- client/src/lib/utils/networkData.ts | 5 +++-- client/svelte.config.js | 4 +++- client/vite.config.ts | 3 +-- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index c969add5..a04adbd6 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -36,6 +36,7 @@ jobs: PUBLIC_FAUCET_WESTEND_URL: "https://westend-faucet.polkadot.io/drip/web" GITHUB_PAGES: "/${{ github.event.repository.name }}" STATIC: true + BASE: "/polkadot-testnet-faucet" - uses: actions/upload-artifact@master with: name: faucet diff --git a/client/src/lib/components/NavBar.svelte b/client/src/lib/components/NavBar.svelte index 6c850d43..18aed5c5 100644 --- a/client/src/lib/components/NavBar.svelte +++ b/client/src/lib/components/NavBar.svelte @@ -1,6 +1,7 @@ -
-
+
+

Frequently Asked Questions

Date: Mon, 12 Jun 2023 10:11:05 +0200 Subject: [PATCH 06/94] Update the E2E tests to run against Polkadot v0.9.42 (#295) --- .github/workflows/E2E.yml | 4 ++-- e2e/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/E2E.yml b/.github/workflows/E2E.yml index 543248c0..ef647895 100644 --- a/.github/workflows/E2E.yml +++ b/.github/workflows/E2E.yml @@ -18,8 +18,8 @@ jobs: - run: yarn install --network-concurrency 1 --frozen-lockfile - name: Download Polkadot and parachain binaries run: | - wget --no-verbose https://github.com/paritytech/cumulus/releases/download/v0.9.380/polkadot-parachain - wget --no-verbose https://github.com/paritytech/polkadot/releases/download/v0.9.38/polkadot + wget --no-verbose https://github.com/paritytech/cumulus/releases/download/v0.9.420/polkadot-parachain + wget --no-verbose https://github.com/paritytech/polkadot/releases/download/v0.9.42/polkadot chmod +x ./polkadot* working-directory: e2e - name: Run a local relaychain with a parachain using zombienet diff --git a/e2e/README.md b/e2e/README.md index b322d6d9..df1ffe5b 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -96,11 +96,11 @@ and by asserting that it responds to RPC requests: ```bash # Relay chain -curl localhost:9933 +curl localhost:9933 # Expecting: Used HTTP Method is not allowed. POST or OPTIONS is required # Parachain -curl localhost:9934 +curl localhost:9934 # Expecting: Used HTTP Method is not allowed. POST or OPTIONS is required ``` From 66c6d3d6656613f0a3d333ee3a81924b0fadb7e1 Mon Sep 17 00:00:00 2001 From: Yuri Volkov <0@mcornholio.ru> Date: Mon, 12 Jun 2023 13:15:11 +0200 Subject: [PATCH 07/94] Removing ui image and docker-compose files (#296) 1. UI image. The problem with that one, is that it's not being used. We deploy UI to GitHub pages currently, and even if we potentially will switch from it, deploying static site as a docker image is far from a good solution with much higher operation costs and downtimes on deployment. Also, fixes #279 2. docker-compose.yml's These were made for the local development by DevOps, however, they aren't fit for the local development (the don't provide watch mode, re-building is painful, debug...), and they are actually fit for running it locally if you aren't developing the app. --- .github/workflows/deploy-site.yml | 6 ----- .gitlab-ci.yml | 30 ----------------------- client/README.md | 9 ------- docker/docker-compose.rococo-external.yml | 13 ---------- docker/docker-compose.rococo.yml | 13 ---------- docker/docker-compose.versi.yml | 14 ----------- docker/docker-compose.westend.yml | 12 --------- docker/docker-compose.yml | 19 -------------- e2e/docker-compose.deployment.yml | 6 ++--- 9 files changed, 3 insertions(+), 119 deletions(-) delete mode 100644 docker/docker-compose.rococo-external.yml delete mode 100644 docker/docker-compose.rococo.yml delete mode 100644 docker/docker-compose.versi.yml delete mode 100644 docker/docker-compose.westend.yml delete mode 100644 docker/docker-compose.yml diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index a04adbd6..ee38bf19 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -42,12 +42,6 @@ jobs: name: faucet path: ./client/build if-no-files-found: error - test-image: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3.3.0 - - name: Check that the image builds - run: docker build ./client --file ./client/Dockerfile deploy-to-github-pages: environment: name: github-pages diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f769d125..24f029a5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,7 +24,6 @@ variables: ARGOCD_IMAGE: argoproj/argocd:v2.5.5 DOCKERHUB_REPO: "paritytech" IMAGE_NAME: docker.io/$DOCKERHUB_REPO/faucet - UI_IMAGE_NAME: docker.io/$DOCKERHUB_REPO/faucet-ui DOCKER_TAG: "${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHORT_SHA}" .common-refs: &common-refs @@ -72,22 +71,11 @@ variables: --tag "$IMAGE_NAME:${DOCKER_TAG}" --tag "$IMAGE_NAME:latest" --file "$DOCKERFILE" . - - $BUILDAH_COMMAND build - --format=docker - --build-arg VCS_REF="${CI_COMMIT_SHA}" - --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" - --build-arg VERSION="${DOCKER_TAG}" - --build-arg PROJECT_NAME="${CI_PROJECT_NAME}" - --tag "$UI_IMAGE_NAME:${DOCKER_TAG}" - --tag "$UI_IMAGE_NAME:latest" - --file "$DOCKERFILE" ./client - echo "$Docker_Hub_Pass_Parity" | buildah login --username "$Docker_Hub_User_Parity" --password-stdin docker.io - $BUILDAH_COMMAND info - $BUILDAH_COMMAND push --format=v2s2 "$IMAGE_NAME:${DOCKER_TAG}" - - $BUILDAH_COMMAND push --format=v2s2 "$UI_IMAGE_NAME:${DOCKER_TAG}" - $BUILDAH_COMMAND push --format=v2s2 "$IMAGE_NAME:latest" - - $BUILDAH_COMMAND push --format=v2s2 "$UI_IMAGE_NAME:latest" after_script: - buildah logout --all @@ -103,13 +91,6 @@ variables: --build-arg PROJECT_NAME="${CI_PROJECT_NAME}" --tag "$IMAGE_NAME:latest" --file "$DOCKERFILE" . - - $BUILDAH_COMMAND build - --format=docker - --build-arg VCS_REF="${CI_COMMIT_SHA}" - --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" - --build-arg PROJECT_NAME="${CI_PROJECT_NAME}" - --tag "$UI_IMAGE_NAME:latest" - --file "$DOCKERFILE" ./client - $BUILDAH_COMMAND info #### stage: test @@ -230,14 +211,3 @@ publish-docker-image-description-faucet: - if: $CI_COMMIT_REF_NAME == "main" changes: - Dockerfile.README.md - -publish-docker-image-description-faucet-ui: - extends: .publish-docker-image-description - variables: - DOCKERHUB_REPOSITORY: $DOCKERHUB_REPO/$UI_IMAGE_NAME - README_FILEPATH: $CI_PROJECT_DIR/client/Dockerfile.README.md - SHORT_DESCRIPTION: "Generic Faucet-UI for Substrate based chains" - rules: - - if: $CI_COMMIT_REF_NAME == "main" - changes: - - client/Dockerfile.README.md diff --git a/client/README.md b/client/README.md index dc7cb1b2..a7d29bfe 100644 --- a/client/README.md +++ b/client/README.md @@ -41,12 +41,3 @@ We have a GitHub action that evaluates and builds the website, deploying it to G If you want to have a parachain id set by default, you can add the get property with the `parachain` query: `https://paritytech.github.io/polkadot-testnet-faucet/?parachain=1234` - -## Building the docker image - -You can build and run the docker image as a node server with the following command - -```bash -docker build -t faucet-ui . -docker run -e PUBLIC_CAPTCHA_KEY="your-key" --env PUBLIC_FAUCET_URL="the-url-to-contact" -p 80:3000 faucet-ui -``` diff --git a/docker/docker-compose.rococo-external.yml b/docker/docker-compose.rococo-external.yml deleted file mode 100644 index 354b8f85..00000000 --- a/docker/docker-compose.rococo-external.yml +++ /dev/null @@ -1,13 +0,0 @@ -services: - faucet: - extends: - file: docker-compose.yml - service: faucet - command: yarn start:backend - ports: - - "${SMF_BACKEND_PORT}:${SMF_BACKEND_PORT}" - environment: - SMF_BACKEND_NETWORK_DECIMALS: 12 - SMF_BACKEND_RPC_ENDPOINT: "wss://rococo-rpc.polkadot.io/" - SMF_BACKEND_EXTERNAL_ACCESS: true - SMF_BACKEND_DRIP_AMOUNT: "100" diff --git a/docker/docker-compose.rococo.yml b/docker/docker-compose.rococo.yml deleted file mode 100644 index bd06a3b2..00000000 --- a/docker/docker-compose.rococo.yml +++ /dev/null @@ -1,13 +0,0 @@ -services: - faucet: - extends: - file: docker-compose.yml - service: faucet - environment: - SMF_BACKEND_NETWORK_DECIMALS: 12 - SMF_BACKEND_RPC_ENDPOINT: "wss://rococo-rpc.polkadot.io/" - - SMF_BOT_MATRIX_SERVER: https://m.parity.io - SMF_BOT_MATRIX_BOT_USER_ID: '@rococo-faucet-bot:matrix.org' - SMF_BOT_DRIP_AMOUNT: 100 - SMF_BOT_NETWORK_UNIT: ROC diff --git a/docker/docker-compose.versi.yml b/docker/docker-compose.versi.yml deleted file mode 100644 index 0d609100..00000000 --- a/docker/docker-compose.versi.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - faucet: - extends: - file: docker-compose.yml - service: faucet - environment: - SMF_BACKEND_NETWORK_DECIMALS: 12 - SMF_BACKEND_RPC_ENDPOINT: 'wss://http-versi-rpc-node-0.parity-versi.parity.io' - - SMF_BOT_DRIP_AMOUNT: 100 - SMF_BOT_NETWORK_UNIT: VRS - SMF_BOT_BACKEND_URL: http://127.0.0.1:5555 - SMF_BOT_MATRIX_SERVER: https://parity.io - SMF_BOT_MATRIX_BOT_USER_ID: '@versi-faucet:parity.io' diff --git a/docker/docker-compose.westend.yml b/docker/docker-compose.westend.yml deleted file mode 100644 index a755ce27..00000000 --- a/docker/docker-compose.westend.yml +++ /dev/null @@ -1,12 +0,0 @@ -services: - faucet: - extends: - file: docker-compose.yml - service: faucet - environment: - SMF_BACKEND_RPC_ENDPOINT: "wss://westend-rpc.polkadot.io/" - - SMF_BOT_MATRIX_BOT_USER_ID: '@westend-faucet:matrix.org' - SMF_BOT_DRIP_AMOUNT: 1 - SMF_BOT_NETWORK_UNIT: WND - SMF_BOT_FAUCET_IGNORE_LIST: '' diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml deleted file mode 100644 index 10bff032..00000000 --- a/docker/docker-compose.yml +++ /dev/null @@ -1,19 +0,0 @@ -services: - faucet: - container_name: faucet - build: - dockerfile: Dockerfile - context: ../ - command: yarn start - environment: - SMF_BACKEND_INJECTED_TYPES: "{}" - SMF_BACKEND_PORT: 5555 - SMF_BACKEND_FAUCET_ACCOUNT_MNEMONIC: ${SMF_BACKEND_FAUCET_ACCOUNT_MNEMONIC:?BackendFaucetAccountMnemonicNotSet} - SMF_BACKEND_DEPLOYED_REF: ${SMF_BACKEND_DEPLOYED_REF:?VERSION} - SMF_BACKEND_DEPLOYED_TIME: ${SMF_BACKEND_DEPLOYED_TIME:?BUILD_DATE} - - SMF_BOT_FAUCET_IGNORE_LIST: '' - SMF_BOT_BACKEND_URL: http://127.0.0.1:5555 - SMF_BOT_MATRIX_ACCESS_TOKEN: ${SMF_BOT_MATRIX_ACCESS_TOKEN:?BotmatrixAccessTokenNotSet} - SMF_BOT_DEPLOYED_REF: ${SMF_BOT_DEPLOYED_REF:?VERSION} - SMF_BOT_DEPLOYED_TIME: ${SMF_BOT_DEPLOYED_TIME:?BUILD_DATE} diff --git a/e2e/docker-compose.deployment.yml b/e2e/docker-compose.deployment.yml index f691c5a5..8f29f963 100644 --- a/e2e/docker-compose.deployment.yml +++ b/e2e/docker-compose.deployment.yml @@ -3,9 +3,9 @@ networks: external: true services: faucet: - extends: - file: ../docker/docker-compose.yml - service: faucet + build: + dockerfile: Dockerfile + context: ../ ports: - "${SMF_BACKEND_PORT}:${SMF_BACKEND_PORT}" env_file: From af98ba1217d92483353095b8a6ab25c4e2048f71 Mon Sep 17 00:00:00 2001 From: Yuri Volkov <0@mcornholio.ru> Date: Mon, 12 Jun 2023 15:23:30 +0200 Subject: [PATCH 08/94] Internal dependencies update (#297) --- .eslintrc.cjs | 2 +- .prettierrc.cjs | 2 +- package.json | 4 +- src/config.ts | 2 + src/faucet.e2e.ts | 2 +- yarn.lock | 182 ++++++++-------------------------------------- 6 files changed, 38 insertions(+), 156 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index cfa7ebd0..22ccd266 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,4 +1,4 @@ -const { getConfiguration, getTypescriptOverride } = require("opstooling-js-style/src/eslint/configuration"); +const { getConfiguration, getTypescriptOverride } = require("@eng-automation/js-style/src/eslint/configuration"); const tsConfParams = { rootDir: __dirname }; diff --git a/.prettierrc.cjs b/.prettierrc.cjs index 7ad47895..d47d39b9 100644 --- a/.prettierrc.cjs +++ b/.prettierrc.cjs @@ -1 +1 @@ -module.exports = require("opstooling-js-style/src/prettier/configuration") +module.exports = require("@eng-automation/js-style/src/prettier/configuration") diff --git a/package.json b/package.json index ee9ea583..5f9f99a0 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,8 @@ "sqlite3": "^5.1.2" }, "devDependencies": { + "@eng-automation/js": "^0.0.22", + "@eng-automation/js-style": "^2.0.0", "@types/body-parser": "^1.19.2", "@types/express": "^4.17.13", "@types/jest": "^29.4.0", @@ -73,8 +75,6 @@ "jest": "^29.4.2", "lint-staged": "^12.3.8", "nodemon": "^2.0.19", - "opstooling-js": "https://github.com/paritytech/opstooling-js#v0.0.18", - "opstooling-js-style": "https://github.com/paritytech/opstooling-js-style#5e7bd72b4082f264e3f1e2616fd6fc4560dda365", "simple-git-hooks": "^2.7.0", "supertest": "^6.3.3", "ts-jest": "^29.0.5", diff --git a/src/config.ts b/src/config.ts index 9dde0d92..13d8d742 100644 --- a/src/config.ts +++ b/src/config.ts @@ -34,6 +34,8 @@ export function validateConfig(appName: keyof typeof faucetConfigSpec["SMF"]) { // Delete all keys but the app in question that is being validated. for (const key of Object.keys(specs.config)) { if (key !== appName) { + // In this case we only delete properties, no injection is possible + // eslint-disable-next-line security/detect-object-injection delete (specs.config as ModuleDictionnary)[key]; } } diff --git a/src/faucet.e2e.ts b/src/faucet.e2e.ts index b1fcb3af..49ed1e6d 100644 --- a/src/faucet.e2e.ts +++ b/src/faucet.e2e.ts @@ -1,10 +1,10 @@ +import { until } from "@eng-automation/js"; import { ApiPromise } from "@polkadot/api"; import { createTestKeyring } from "@polkadot/keyring"; import { HttpProvider } from "@polkadot/rpc-provider"; import { BN } from "@polkadot/util"; import { randomAsU8a } from "@polkadot/util-crypto"; import axios from "axios"; -import { until } from "opstooling-js"; const randomAddress = () => createTestKeyring().addFromSeed(randomAsU8a(32)).address; diff --git a/yarn.lock b/yarn.lock index 8a220a20..217c4ca6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -492,6 +492,37 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@eng-automation/js-style@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@eng-automation/js-style/-/js-style-2.0.0.tgz#62aee05d0d3ff6d5cf809188d1f226c35f93d569" + integrity sha512-6AqIVIpcLD9lg3zh9J+BqCwWRki7R20AHdhgUjfqYjUcC0JR1ZWk61rvusOAwASeSL77xMTTxVG/PExHVT5Dbg== + dependencies: + "@typescript-eslint/eslint-plugin" "^5.21.0" + "@typescript-eslint/parser" "^5.21.0" + eslint "^8.14.0" + eslint-config-prettier "^8.5.0" + eslint-plugin-import "^2.27.5" + eslint-plugin-jest "^26.5.3" + eslint-plugin-prettier "^4.0.0" + eslint-plugin-simple-import-sort "^7.0.0" + eslint-plugin-sonarjs "^0.13.0" + eslint-plugin-unused-imports "^2.0.0" + prettier "^2.6.2" + prettier-plugin-compactify "^0.1.6" + +"@eng-automation/js@^0.0.22": + version "0.0.22" + resolved "https://registry.yarnpkg.com/@eng-automation/js/-/js-0.0.22.tgz#6abb457e767874179a349f86b26e78a66b7048c2" + integrity sha512-m2p+c/w1jyTMmc1jaEQleuibb0EUXgaxyJXmGx9tDG3EDQ12JJuls9vziUTsdJPRP6aMDrBtAaOz4VteGZiI0w== + dependencies: + async-mutex "^0.3.2" + commander "^9.3.0" + deepdash "^5.3.9" + joi "^17.6.4" + joi-to-typescript "^4.0.5" + lodash "^4.17.21" + node-fetch "^2.6.7" + "@eslint/eslintrc@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" @@ -876,107 +907,6 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@octokit/auth-token@^2.4.4": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" - integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== - dependencies: - "@octokit/types" "^6.0.3" - -"@octokit/core@^3.5.1": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" - integrity sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.6.3" - "@octokit/request-error" "^2.0.5" - "@octokit/types" "^6.0.3" - before-after-hook "^2.2.0" - universal-user-agent "^6.0.0" - -"@octokit/endpoint@^6.0.1": - version "6.0.12" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" - integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== - dependencies: - "@octokit/types" "^6.0.3" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql@^4.5.8": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" - integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== - dependencies: - "@octokit/request" "^5.6.0" - "@octokit/types" "^6.0.3" - universal-user-agent "^6.0.0" - -"@octokit/openapi-types@^12.11.0": - version "12.11.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" - integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== - -"@octokit/plugin-paginate-rest@^2.16.8": - version "2.21.3" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e" - integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw== - dependencies: - "@octokit/types" "^6.40.0" - -"@octokit/plugin-request-log@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" - integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== - -"@octokit/plugin-rest-endpoint-methods@^5.12.0": - version "5.16.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" - integrity sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw== - dependencies: - "@octokit/types" "^6.39.0" - deprecation "^2.3.1" - -"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" - integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== - dependencies: - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^5.6.0", "@octokit/request@^5.6.3": - version "5.6.3" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" - integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.1.0" - "@octokit/types" "^6.16.1" - is-plain-object "^5.0.0" - node-fetch "^2.6.7" - universal-user-agent "^6.0.0" - -"@octokit/rest@^18.12.0": - version "18.12.0" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" - integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== - dependencies: - "@octokit/core" "^3.5.1" - "@octokit/plugin-paginate-rest" "^2.16.8" - "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^5.12.0" - -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": - version "6.41.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" - integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== - dependencies: - "@octokit/openapi-types" "^12.11.0" - "@polkadot/api-augment@10.0.1": version "10.0.1" resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-10.0.1.tgz#b8e7766e1d89e3ee753ae053e3edd21ddc0c780c" @@ -2052,11 +1982,6 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -before-after-hook@^2.2.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" - integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== - bigfloat.js@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/bigfloat.js/-/bigfloat.js-3.0.1.tgz#a806188a82fb83f79af00532654ea35f36ccd995" @@ -2592,11 +2517,6 @@ depd@^1.1.2, depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" @@ -3912,11 +3832,6 @@ is-path-inside@^3.0.3: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -5093,36 +5008,6 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -"opstooling-js-style@https://github.com/paritytech/opstooling-js-style#5e7bd72b4082f264e3f1e2616fd6fc4560dda365": - version "0.0.0" - resolved "https://github.com/paritytech/opstooling-js-style#5e7bd72b4082f264e3f1e2616fd6fc4560dda365" - dependencies: - "@typescript-eslint/eslint-plugin" "^5.21.0" - "@typescript-eslint/parser" "^5.21.0" - eslint "^8.14.0" - eslint-config-prettier "^8.5.0" - eslint-plugin-import "^2.27.5" - eslint-plugin-jest "^26.5.3" - eslint-plugin-prettier "^4.0.0" - eslint-plugin-simple-import-sort "^7.0.0" - eslint-plugin-sonarjs "^0.13.0" - eslint-plugin-unused-imports "^2.0.0" - prettier "^2.6.2" - prettier-plugin-compactify "^0.1.6" - -"opstooling-js@https://github.com/paritytech/opstooling-js#v0.0.18": - version "0.0.0" - resolved "https://github.com/paritytech/opstooling-js#dfc8601fed2287ebed1c3ff12192db864b610f35" - dependencies: - "@octokit/rest" "^18.12.0" - async-mutex "^0.3.2" - commander "^9.3.0" - deepdash "^5.3.9" - joi "^17.6.4" - joi-to-typescript "^4.0.5" - lodash "^4.17.21" - node-fetch "^2.6.7" - optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -6300,11 +6185,6 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" From b8cc267416baa03239445cca1fb734cfa722a655 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Fri, 16 Jun 2023 13:03:30 +0200 Subject: [PATCH 09/94] Bump XCM types to V3 (#303) --- src/dripper/polkadot/PolkadotActions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dripper/polkadot/PolkadotActions.ts b/src/dripper/polkadot/PolkadotActions.ts index 3918f942..e3c57e5d 100644 --- a/src/dripper/polkadot/PolkadotActions.ts +++ b/src/dripper/polkadot/PolkadotActions.ts @@ -103,7 +103,7 @@ export class PolkadotActions { const dest = await Promise.resolve( polkadotApi.createType("XcmVersionedMultiLocation", { - V2: polkadotApi.createType("MultiLocationV2", { + V3: polkadotApi.createType("MultiLocationV2", { interior: polkadotApi.createType("JunctionsV2", { X1: polkadotApi.createType("JunctionV2", { Parachain: polkadotApi.createType("Compact", parachain_id), @@ -116,7 +116,7 @@ export class PolkadotActions { const beneficiary = await Promise.resolve( polkadotApi.createType("XcmVersionedMultiLocation", { - V2: polkadotApi.createType("MultiLocationV2", { + V3: polkadotApi.createType("MultiLocationV2", { interior: polkadotApi.createType("JunctionsV2", { X1: polkadotApi.createType("JunctionV2", { AccountId32: { id: address, network: polkadotApi.createType("NetworkId", "Any") }, @@ -129,7 +129,7 @@ export class PolkadotActions { const assets = await Promise.resolve( polkadotApi.createType("XcmVersionedMultiAssets", { - V2: [ + V3: [ polkadotApi.createType("XcmV2MultiAsset", { fun: polkadotApi.createType("FungibilityV2", { Fungible: dripAmount }), id: polkadotApi.createType("XcmAssetId", { From fd22e0b098b24882312f5cb190093c8dac096f24 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Wed, 14 Jun 2023 11:23:19 +0200 Subject: [PATCH 10/94] Apply common codestyle to client This is a step to move client/ to src/, furthermore joining what was three different repos before. I tried to minimize the amount of changes here, so there is still client/.eslintrc.cjs, and still a separate package.json. However, the lint and format commands and checks are unified. --- .github/workflows/deploy-site.yml | 1 - .gitlab-ci.yml | 9 +- client/.eslintrc.cjs | 44 +- client/.prettierrc | 9 - client/package.json | 71 +- client/playwright.config.ts | 22 +- client/postcss.config.cjs | 4 +- client/src/app.css | 144 ++-- client/src/app.d.ts | 48 +- client/src/app.html | 33 +- client/src/lib/components/CaptchaV2.svelte | 103 ++- client/src/lib/components/Card.svelte | 36 +- client/src/lib/components/Faucet.svelte | 93 ++- client/src/lib/components/Footer.svelte | 32 +- client/src/lib/components/Form.svelte | 134 ++-- client/src/lib/components/NavBar.svelte | 79 +- .../src/lib/components/NetworkDropdown.svelte | 57 +- client/src/lib/components/NetworkInput.svelte | 211 +++--- client/src/lib/components/SocialTags.svelte | 26 +- .../lib/components/icons/CheckCircle.svelte | 22 +- .../src/lib/components/icons/Chevron.svelte | 14 +- client/src/lib/components/icons/Cross.svelte | 19 +- .../lib/components/icons/ErrorCircle.svelte | 22 +- client/src/lib/components/icons/Tick.svelte | 19 +- .../src/lib/components/screens/Error.svelte | 36 +- .../screens/FrequentlyAskedQuestions.svelte | 91 +-- .../src/lib/components/screens/Success.svelte | 41 +- client/src/lib/utils/faucetRequest.ts | 83 ++- client/src/lib/utils/networkData.ts | 81 +-- client/src/lib/utils/stores.ts | 9 +- client/src/routes/+layout.svelte | 14 +- client/src/routes/+page.svelte | 14 +- client/src/routes/westend/+page.svelte | 14 +- client/svelte.config.js | 17 +- client/tailwind.config.cjs | 9 +- client/tests/faucet.ts | 558 +++++++-------- client/tests/rococo.test.ts | 10 +- client/tests/switch.test.ts | 106 ++- client/tests/westend.test.ts | 6 +- client/tsconfig.json | 41 +- client/vite.config.ts | 5 +- client/yarn.lock | 672 +----------------- package.json | 22 +- src/config.ts | 6 +- yarn.lock | 253 +++++-- 45 files changed, 1379 insertions(+), 1961 deletions(-) delete mode 100644 client/.prettierrc diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index ee38bf19..04967163 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -25,7 +25,6 @@ jobs: node-version: 18 - run: yarn install --frozen-lockfile - run: yarn run check - - run: yarn run lint - name: Install Playwright run: npx playwright install - run: yarn run test diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 24f029a5..cc7d8f80 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,7 +18,7 @@ variables: KUBE_NAMESPACE: "faucetbots" CI_REGISTRY: "docker.io/paritytech" GIT_STRATEGY: fetch - CI_IMAGE: node:16.10-alpine + CI_IMAGE: node:16.20-alpine BUILDAH_IMAGE: "quay.io/buildah/stable:v1.29" BUILDAH_COMMAND: "buildah --storage-driver overlay2" ARGOCD_IMAGE: argoproj/argocd:v2.5.5 @@ -100,8 +100,8 @@ check-linting: <<: *common-refs <<: *kubernetes-env script: - - apk add git==2.30.6-r0 - - yarn --network-concurrency 1 --frozen-lockfile + - yarn --frozen-lockfile + - cd client && yarn --frozen-lockfile && cd .. - yarn typecheck && yarn format && yarn lint check-tests: @@ -109,8 +109,7 @@ check-tests: <<: *common-refs <<: *kubernetes-env script: - - apk add git==2.30.6-r0 - - yarn --network-concurrency 1 --frozen-lockfile + - yarn --frozen-lockfile - yarn test #### stage: build diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs index bb656cef..7ecff701 100644 --- a/client/.eslintrc.cjs +++ b/client/.eslintrc.cjs @@ -1,29 +1,15 @@ -module.exports = { - root: true, - parser: "@typescript-eslint/parser", - extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], - plugins: ["svelte3", "@typescript-eslint"], - ignorePatterns: ["*.cjs"], - overrides: [ - { - files: ["*.svelte"], - processor: "svelte3/svelte3", - rules: { - "@typescript-eslint/no-inferrable-types": "off", - quotes: [2, "double", { avoidEscape: true }] - } - } - ], - settings: { - "svelte3/typescript": () => require("typescript") - }, - parserOptions: { - sourceType: "module", - ecmaVersion: 2020 - }, - env: { - browser: true, - es2017: true, - node: true - } -}; +const { getConfiguration, getTypescriptOverride } = require("@eng-automation/js-style/src/eslint/configuration"); + +const tsConfParams = { rootDir: __dirname }; +const conf = getConfiguration({ typescript: tsConfParams, browser: true }); + +const tsConfOverride = getTypescriptOverride(tsConfParams); +conf.overrides.push(tsConfOverride); + +// Unfortunately, this rule has no fine-tuning, +// and it doesn't live well with Tailwind CSSª +conf.rules["svelte/valid-compile"] = ["error", { ignoreWarnings: true }]; + +conf.rules["no-restricted-imports"] = "off"; + +module.exports = conf; diff --git a/client/.prettierrc b/client/.prettierrc deleted file mode 100644 index 2d38c79a..00000000 --- a/client/.prettierrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "useTabs": true, - "singleQuote": false, - "trailingComma": "none", - "printWidth": 100, - "plugins": ["prettier-plugin-svelte"], - "pluginSearchDirs": ["."], - "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] -} diff --git a/client/package.json b/client/package.json index 9985289d..41003339 100644 --- a/client/package.json +++ b/client/package.json @@ -1,42 +1,33 @@ { - "name": "substrate-faucet", - "version": "0.0.1", - "private": true, - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "test": "playwright test", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "prettier --plugin-search-dir . --check . && eslint .", - "format": "prettier --plugin-search-dir . --write ." - }, - "devDependencies": { - "@playwright/test": "^1.28.1", - "@sveltejs/adapter-auto": "^2.0.0", - "@sveltejs/adapter-node": "^1.2.3", - "@sveltejs/adapter-static": "^2.0.1", - "@sveltejs/kit": "^1.15.2", - "@tailwindcss/typography": "^0.5.9", - "@typescript-eslint/eslint-plugin": "^5.45.0", - "@typescript-eslint/parser": "^5.45.0", - "autoprefixer": "^10.4.14", - "daisyui": "^2.51.4", - "eslint": "^8.28.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-svelte3": "^4.0.0", - "postcss": "^8.4.21", - "prettier": "^2.8.0", - "prettier-plugin-svelte": "^2.8.1", - "svelte": "^3.54.0", - "svelte-check": "^3.0.1", - "svelte-markdown": "^0.2.3", - "svelte-meta-tags": "^2.7.0", - "tailwindcss": "^3.2.7", - "tslib": "^2.4.1", - "typescript": "^4.9.3", - "vite": "^4.2.3" - }, - "type": "module" + "name": "substrate-faucet", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "test": "playwright test", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "devDependencies": { + "@playwright/test": "^1.28.1", + "@sveltejs/adapter-auto": "^2.0.0", + "@sveltejs/adapter-node": "^1.2.3", + "@sveltejs/adapter-static": "^2.0.1", + "@sveltejs/kit": "^1.15.2", + "@tailwindcss/typography": "^0.5.9", + "autoprefixer": "^10.4.14", + "daisyui": "^2.51.4", + "postcss": "^8.4.21", + "svelte": "^3.54.0", + "svelte-check": "^3.0.1", + "svelte-markdown": "^0.2.3", + "svelte-meta-tags": "^2.7.0", + "tailwindcss": "^3.2.7", + "tslib": "^2.4.1", + "typescript": "^4.9.3", + "vite": "^4.2.3" + }, + "type": "module" } diff --git a/client/playwright.config.ts b/client/playwright.config.ts index 9dc0989c..b098203c 100644 --- a/client/playwright.config.ts +++ b/client/playwright.config.ts @@ -1,17 +1,17 @@ import type { PlaywrightTestConfig } from "@playwright/test"; const config: PlaywrightTestConfig = { - webServer: { - command: "npm run build && npm run preview", - port: 4173, - env: { - PUBLIC_CAPTCHA_KEY: "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI", - PUBLIC_FAUCET_WESTEND_URL: "https://example.com/test", - PUBLIC_FAUCET_ROCOCO_URL: "https://test.com/example", - PUBLIC_DEMO_MODE: "" - } - }, - testDir: "tests" + webServer: { + command: "npm run build && npm run preview", + port: 4173, + env: { + PUBLIC_CAPTCHA_KEY: "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI", + PUBLIC_FAUCET_WESTEND_URL: "https://example.com/test", + PUBLIC_FAUCET_ROCOCO_URL: "https://test.com/example", + PUBLIC_DEMO_MODE: "", + }, + }, + testDir: "tests", }; export default config; diff --git a/client/postcss.config.cjs b/client/postcss.config.cjs index 3886e62b..43af1bb5 100644 --- a/client/postcss.config.cjs +++ b/client/postcss.config.cjs @@ -1,3 +1 @@ -module.exports = { - plugins: [require("tailwindcss"), require("autoprefixer")] -}; +module.exports = { plugins: [require("tailwindcss"), require("autoprefixer")] }; diff --git a/client/src/app.css b/client/src/app.css index c0a7b4bd..925958f8 100644 --- a/client/src/app.css +++ b/client/src/app.css @@ -3,128 +3,128 @@ @tailwind components; :root { - font-family: Inter, Avenir, Helvetica, Arial, sans-serif; - font-size: 16px; - line-height: 24px; - font-weight: 400; + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; - scroll-behavior: smooth; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + scroll-behavior: smooth; } a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; + font-weight: 500; + color: #646cff; + text-decoration: inherit; } a:hover { - color: #535bf2; + color: #535bf2; } body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; - background-color: #101015; + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; + background-color: #101015; } @media (min-width: 768px) { - .background { - background-image: url("/circle.svg"), url("/circle.svg"), url("/circle.svg"); - background-repeat: no-repeat; - position: absolute; - background-position-y: 10%, 73%, 50%; - background-position-x: 25%, -13%, 115%; - background-size: 20%, 20%, 30%; - bottom: 0px; - right: 0px; - width: 100%; - height: 100%; - pointer-events: none; - } + .background { + background-image: url("/circle.svg"), url("/circle.svg"), url("/circle.svg"); + background-repeat: no-repeat; + position: absolute; + background-position-y: 10%, 73%, 50%; + background-position-x: 25%, -13%, 115%; + background-size: 20%, 20%, 30%; + bottom: 0px; + right: 0px; + width: 100%; + height: 100%; + pointer-events: none; + } } h1 { - font-size: 3.2em; - line-height: 1.1; + font-size: 3.2em; + line-height: 1.1; } .card { - padding: 2em; + padding: 2em; } button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; } button:hover { - border-color: #646cff; + border-color: #646cff; } button:focus, button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; + outline: 4px auto -webkit-focus-ring-color; } @media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } + :root { + color: #213547; + background-color: #ffffff; + } - a:hover { - color: #747bff; - } + a:hover { + color: #747bff; + } - button { - background-color: #f9f9f9; - } + button { + background-color: #f9f9f9; + } } .submit-btn { - @apply btn w-full mt-6 rounded-3xl text-white; - background-color: #282837; - font-family: "Inter", sans-serif; - font-weight: 500; - text-transform: none; + @apply btn w-full mt-6 rounded-3xl text-white; + background-color: #282837; + font-family: "Inter", sans-serif; + font-weight: 500; + text-transform: none; } .submit-btn:disabled { - background-color: #282837; + background-color: #282837; } .faucet-card { - background-color: #191924; + background-color: #191924; } .card-title { - font-family: "Unbounded", sans-serif; - @apply text-white text-4xl; - font-weight: 700; + font-family: "Unbounded", sans-serif; + @apply text-white text-4xl; + font-weight: 700; } .card-subtitle { - @apply text-white opacity-70; - font-weight: 400; - font-size: 16px; + @apply text-white opacity-70; + font-weight: 400; + font-size: 16px; } @tailwind utilities; diff --git a/client/src/app.d.ts b/client/src/app.d.ts index b69f6329..ed4ecf16 100644 --- a/client/src/app.d.ts +++ b/client/src/app.d.ts @@ -1,33 +1,33 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface Platform {} - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface Platform {} + } - declare interface Window { - grecaptcha?: Captcha; - captchaLoaded: () => void; - onToken: (token: string) => void; - onExpiredToken: () => void; - } + declare interface Window { + grecaptcha?: Captcha; + captchaLoaded: () => void; + onToken: (token: string) => void; + onExpiredToken: () => void; + } } interface Captcha { - render: ( - element: string, - key: { - sitekey: string; - callback?: string; - "expired-callback"?: string; - theme?: "light" | "dark"; - size?: "normal" | "compact"; - } - ) => void; - getResponse: () => string; + render: ( + element: string, + key: { + sitekey: string; + callback?: string; + "expired-callback"?: string; + theme?: "light" | "dark"; + size?: "normal" | "compact"; + }, + ) => void; + getResponse: () => string; } -export { Window }; +export {}; diff --git a/client/src/app.html b/client/src/app.html index 6f0b54f2..69801d35 100644 --- a/client/src/app.html +++ b/client/src/app.html @@ -1,22 +1,19 @@ - - - - - %sveltekit.head% - + + + + + %sveltekit.head% + - -
-
%sveltekit.body%
- - - - + +
+
%sveltekit.body%
+ + + + diff --git a/client/src/lib/components/CaptchaV2.svelte b/client/src/lib/components/CaptchaV2.svelte index 6ebfcfc3..fba9b121 100644 --- a/client/src/lib/components/CaptchaV2.svelte +++ b/client/src/lib/components/CaptchaV2.svelte @@ -1,70 +1,67 @@ - {#if componentMounted} - - {/if} + {#if componentMounted} + + {/if} {#if captchaError} -
-
- - Error loading Google Captcha. Please reload the page. -
-
+
+
+ + Error loading Google Captcha. Please reload the page. +
+
{/if}
diff --git a/client/src/lib/components/Card.svelte b/client/src/lib/components/Card.svelte index 1db883da..8e80321d 100644 --- a/client/src/lib/components/Card.svelte +++ b/client/src/lib/components/Card.svelte @@ -1,26 +1,26 @@
-
-

{$testnet.networkName} Faucet

-

- Get {$testnet.currency} tokens for Polkadot's {$testnet.networkName} testnet and its parachains. -

-
- -
-
+
+

{$testnet.networkName} Faucet

+

+ Get {$testnet.currency} tokens for Polkadot's {$testnet.networkName} testnet and its parachains. +

+
+ +
+
{#if PUBLIC_DEMO_MODE} -
-
-
- Demo mode enabled. Faucet will not be contacted -
-
-
+
+
+
+ Demo mode enabled. Faucet will not be contacted +
+
+
{/if} diff --git a/client/src/lib/components/Faucet.svelte b/client/src/lib/components/Faucet.svelte index 2536f63f..ead1b5ab 100644 --- a/client/src/lib/components/Faucet.svelte +++ b/client/src/lib/components/Faucet.svelte @@ -1,59 +1,58 @@
- -
- - {#if !$operation} - - {:else} -
- {#if $operation.success} - - {:else} - - {/if} -
- {/if} -
-
- + +
+ + {#if !$operation} + + {:else} +
+ {#if $operation.success} + + {:else} + + {/if} +
+ {/if} +
+
+
diff --git a/client/src/lib/components/Footer.svelte b/client/src/lib/components/Footer.svelte index c1222fc3..45bc163b 100644 --- a/client/src/lib/components/Footer.svelte +++ b/client/src/lib/components/Footer.svelte @@ -1,26 +1,24 @@
diff --git a/client/src/lib/components/Form.svelte b/client/src/lib/components/Form.svelte index 1ccd192d..21a6d8c9 100644 --- a/client/src/lib/components/Form.svelte +++ b/client/src/lib/components/Form.svelte @@ -1,84 +1,84 @@ - + -
- - -
- {#if !webRequest} -
- -
- - {:else} - - {/if} +
+ + +
+ {#if !webRequest} +
+ +
+ + {:else} + + {/if} diff --git a/client/src/lib/components/NavBar.svelte b/client/src/lib/components/NavBar.svelte index f7a7fc46..36a089a3 100644 --- a/client/src/lib/components/NavBar.svelte +++ b/client/src/lib/components/NavBar.svelte @@ -1,51 +1,52 @@ diff --git a/client/src/lib/components/NetworkDropdown.svelte b/client/src/lib/components/NetworkDropdown.svelte index eaedfc72..fbcdf2f0 100644 --- a/client/src/lib/components/NetworkDropdown.svelte +++ b/client/src/lib/components/NetworkDropdown.svelte @@ -1,41 +1,38 @@ diff --git a/client/src/lib/components/NetworkInput.svelte b/client/src/lib/components/NetworkInput.svelte index 05c962f8..2e5a2ed7 100644 --- a/client/src/lib/components/NetworkInput.svelte +++ b/client/src/lib/components/NetworkInput.svelte @@ -1,127 +1,124 @@
- - - {#if !customValue} - - {/if} -
- → {customBtnMessage} -
+ + + {#if !customValue} + + {/if} +
+ → {customBtnMessage} +
diff --git a/client/src/lib/components/SocialTags.svelte b/client/src/lib/components/SocialTags.svelte index 822dd4fa..3ffd1624 100644 --- a/client/src/lib/components/SocialTags.svelte +++ b/client/src/lib/components/SocialTags.svelte @@ -1,18 +1,18 @@ diff --git a/client/src/lib/components/icons/CheckCircle.svelte b/client/src/lib/components/icons/CheckCircle.svelte index e15035dd..ca260e16 100644 --- a/client/src/lib/components/icons/CheckCircle.svelte +++ b/client/src/lib/components/icons/CheckCircle.svelte @@ -1,14 +1,14 @@ - + diff --git a/client/src/lib/components/icons/Chevron.svelte b/client/src/lib/components/icons/Chevron.svelte index 053e2b47..f37bc79f 100644 --- a/client/src/lib/components/icons/Chevron.svelte +++ b/client/src/lib/components/icons/Chevron.svelte @@ -1,10 +1,10 @@ - + diff --git a/client/src/lib/components/icons/Cross.svelte b/client/src/lib/components/icons/Cross.svelte index 38755982..8031a983 100644 --- a/client/src/lib/components/icons/Cross.svelte +++ b/client/src/lib/components/icons/Cross.svelte @@ -1,13 +1,8 @@ - - + + diff --git a/client/src/lib/components/icons/ErrorCircle.svelte b/client/src/lib/components/icons/ErrorCircle.svelte index 874b4420..7432da67 100644 --- a/client/src/lib/components/icons/ErrorCircle.svelte +++ b/client/src/lib/components/icons/ErrorCircle.svelte @@ -1,14 +1,14 @@ - + diff --git a/client/src/lib/components/icons/Tick.svelte b/client/src/lib/components/icons/Tick.svelte index aec2a922..8e0e713b 100644 --- a/client/src/lib/components/icons/Tick.svelte +++ b/client/src/lib/components/icons/Tick.svelte @@ -1,13 +1,8 @@ - - + + diff --git a/client/src/lib/components/screens/Error.svelte b/client/src/lib/components/screens/Error.svelte index 3b494d37..6e52c90e 100644 --- a/client/src/lib/components/screens/Error.svelte +++ b/client/src/lib/components/screens/Error.svelte @@ -1,32 +1,32 @@
- +
- {error ?? "There was an error during the transaction."} + {error ?? "There was an error during the transaction."}
diff --git a/client/src/lib/components/screens/FrequentlyAskedQuestions.svelte b/client/src/lib/components/screens/FrequentlyAskedQuestions.svelte index 119ed180..35ad175d 100644 --- a/client/src/lib/components/screens/FrequentlyAskedQuestions.svelte +++ b/client/src/lib/components/screens/FrequentlyAskedQuestions.svelte @@ -1,53 +1,54 @@
-
-
-

Frequently Asked Questions

-
- -
-
-
+
+
+

Frequently Asked Questions

+
+ +
+
+
diff --git a/client/src/lib/components/screens/Success.svelte b/client/src/lib/components/screens/Success.svelte index fd4c6e4b..4bcae9ba 100644 --- a/client/src/lib/components/screens/Success.svelte +++ b/client/src/lib/components/screens/Success.svelte @@ -1,36 +1,31 @@
- +
- Successfully sent {$testnet.currency} to your address. + Successfully sent {$testnet.currency} to your address.
- - + + diff --git a/client/src/lib/utils/faucetRequest.ts b/client/src/lib/utils/faucetRequest.ts index 957b984b..de396dae 100644 --- a/client/src/lib/utils/faucetRequest.ts +++ b/client/src/lib/utils/faucetRequest.ts @@ -1,57 +1,54 @@ import { PUBLIC_DEMO_MODE as DEMO } from "$env/static/public"; + import type { NetworkData } from "./networkData"; export async function request( - address: string, - recaptcha: string, - network: NetworkData, - parachain?: number + address: string, + recaptcha: string, + network: NetworkData, + parachain?: number, ): Promise { - if (DEMO) { - return boilerplateRequest(address, recaptcha); - } - const chain = parachain && parachain > 0 ? parachain.toString() : undefined; - return faucetRequest(address, recaptcha, network, chain); + if (DEMO !== undefined && DEMO !== "") { + return await boilerplateRequest(address); + } + const chain = parachain && parachain > 0 ? parachain.toString() : undefined; + return await faucetRequest(address, recaptcha, network, chain); } export async function faucetRequest( - address: string, - recaptcha: string, - network: NetworkData, - parachain_id?: string + address: string, + recaptcha: string, + network: NetworkData, + parachain_id?: string, ): Promise { - const body = { - address, - parachain_id, - recaptcha - }; + const body = { address, parachain_id, recaptcha }; - const url = network.endpoint; - if (!url) { - throw new Error(`Endpoint for ${network.networkName} is not defined`); - } - const fetchResult = await fetch(url, { - method: "POST", - body: JSON.stringify(body), - headers: { - Accept: "application/json", - "Content-Type": "application/json" - } - }); - const result = await fetchResult.json(); - if ("error" in result) { - throw new Error(result.error); - } else { - return result.hash; - } + const url = network.endpoint; + if (!url) { + throw new Error(`Endpoint for ${network.networkName} is not defined`); + } + const fetchResult = await fetch(url, { + method: "POST", + body: JSON.stringify(body), + headers: { Accept: "application/json", "Content-Type": "application/json" }, + }); + // FIXME + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const result = await fetchResult.json(); + if ("error" in result) { + // FIXME + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + throw new Error(result.error); + } else { + return result.hash; + } } /** Use this method if you want to test the flow of the app without contacting the faucet */ -export async function boilerplateRequest(address: string, token: string): Promise { - await new Promise((resolve) => setTimeout(resolve, 1000)); - if (address === "error") { - throw new Error("This is a terrible error!"); - } - console.log(token); - return "0x7824400bf61a99c51b946454376a84c636a2d86070996a6a5f55999b26e7df51"; +export async function boilerplateRequest(address: string): Promise { + await new Promise((resolve) => setTimeout(resolve, 1000)); + if (address === "error") { + throw new Error("This is a terrible error!"); + } + return "0x7824400bf61a99c51b946454376a84c636a2d86070996a6a5f55999b26e7df51"; } diff --git a/client/src/lib/utils/networkData.ts b/client/src/lib/utils/networkData.ts index b9a0a12d..ab34898a 100644 --- a/client/src/lib/utils/networkData.ts +++ b/client/src/lib/utils/networkData.ts @@ -2,60 +2,53 @@ import { base } from "$app/paths"; import { PUBLIC_FAUCET_ROCOCO_URL, PUBLIC_FAUCET_WESTEND_URL } from "$env/static/public"; export interface ChainData { - name: string; - id: number; + name: string; + id: number; } export interface NetworkData { - networkName: string; - currency: string; - chains: ChainData[]; - endpoint: string; - explorer: string; - getChainName(id: number): string | null; + networkName: string; + currency: string; + chains: ChainData[]; + endpoint: string; + explorer: string; } export const Rococo: NetworkData = { - networkName: "Rococo", - currency: "$ROC", - chains: [ - { name: "Rococo Relay Chain", id: -1 }, - { name: "Rockmine", id: 1000 }, - { name: "Contracts", id: 1002 }, - { name: "Encointer Lietaer", id: 1003 }, - { name: "Bridgehub", id: 1013 } - ], - endpoint: PUBLIC_FAUCET_ROCOCO_URL, - explorer: "https://rococo.subscan.io", - getChainName: function (id: number): string | null { - const index = this.chains.findIndex((ch) => ch.id === id); - if (index < 0) { - return null; - } - return this.chains[index].name; - } + networkName: "Rococo", + currency: "$ROC", + chains: [ + { name: "Rococo Relay Chain", id: -1 }, + { name: "Rockmine", id: 1000 }, + { name: "Contracts", id: 1002 }, + { name: "Encointer Lietaer", id: 1003 }, + { name: "Bridgehub", id: 1013 }, + ], + endpoint: PUBLIC_FAUCET_ROCOCO_URL as string, + explorer: "https://rococo.subscan.io", }; export const Westend: NetworkData = { - networkName: "Westend", - currency: "$WND", - chains: [ - { name: "Westend Relay Chain", id: -1 }, - { name: "Westmint", id: 1000 }, - { name: "Collectives", id: 1001 } - ], - endpoint: PUBLIC_FAUCET_WESTEND_URL, - explorer: "https://westend.subscan.io", - getChainName: function (id: number): string | null { - const index = this.chains.findIndex((ch) => ch.id === id); - if (index < 0) { - return null; - } - return this.chains[index].name; - } + networkName: "Westend", + currency: "$WND", + chains: [ + { name: "Westend Relay Chain", id: -1 }, + { name: "Westmint", id: 1000 }, + { name: "Collectives", id: 1001 }, + ], + endpoint: PUBLIC_FAUCET_WESTEND_URL as string, + explorer: "https://westend.subscan.io", }; export const Networks: { network: NetworkData; url: string }[] = [ - { network: Rococo, url: base || "/" }, - { network: Westend, url: `${base}/westend` } + { network: Rococo, url: (base as string) || "/" }, + { network: Westend, url: `${base as string}/westend` }, ]; + +export function getChainName(network: NetworkData, id: number): string | null { + const index = network.chains.findIndex((ch) => ch.id === id); + if (index < 0) { + return null; + } + return network.chains[index].name; +} diff --git a/client/src/lib/utils/stores.ts b/client/src/lib/utils/stores.ts index ce79b05b..f3ca9be4 100644 --- a/client/src/lib/utils/stores.ts +++ b/client/src/lib/utils/stores.ts @@ -1,5 +1,6 @@ import { derived, writable } from "svelte/store"; -import { Rococo, type NetworkData } from "./networkData"; + +import { type NetworkData, Rococo } from "./networkData"; // If we want to have a new network we need to change this hardcoded value. export const testnet = writable(Rococo); @@ -7,9 +8,9 @@ export const testnet = writable(Rococo); export const testnetName = derived(testnet, ($net) => $net.networkName); interface FaucetOperation { - success: boolean; - hash: string; - error?: string; + success: boolean; + hash: string; + error?: string; } export const operation = writable(); diff --git a/client/src/routes/+layout.svelte b/client/src/routes/+layout.svelte index f3cb3279..2021a93b 100644 --- a/client/src/routes/+layout.svelte +++ b/client/src/routes/+layout.svelte @@ -1,12 +1,12 @@
- - -
+ + +
diff --git a/client/src/routes/+page.svelte b/client/src/routes/+page.svelte index 7b415101..db8d9f80 100644 --- a/client/src/routes/+page.svelte +++ b/client/src/routes/+page.svelte @@ -1,12 +1,12 @@ diff --git a/client/src/routes/westend/+page.svelte b/client/src/routes/westend/+page.svelte index e460f084..92da4861 100644 --- a/client/src/routes/westend/+page.svelte +++ b/client/src/routes/westend/+page.svelte @@ -1,12 +1,12 @@ diff --git a/client/svelte.config.js b/client/svelte.config.js index 34a2e73f..d11adc31 100644 --- a/client/svelte.config.js +++ b/client/svelte.config.js @@ -5,19 +5,12 @@ import preprocess from "svelte-preprocess"; /** @type {import('@sveltejs/kit').Config} */ const config = { - preprocess: [ - preprocess({ - postcss: true - }) - ], + preprocess: [preprocess({ postcss: true })], - kit: { - adapter: process.env.STATIC ? staticAdapter() : nodeAdapter(), - paths: { - base: process.env.BASE ?? "", - relative: !process.env.BASE - } - } + kit: { + adapter: process.env.STATIC ? staticAdapter() : nodeAdapter(), + paths: { base: process.env.BASE ?? "", relative: !process.env.BASE }, + }, }; export default config; diff --git a/client/tailwind.config.cjs b/client/tailwind.config.cjs index 524f29f4..f1b4a943 100644 --- a/client/tailwind.config.cjs +++ b/client/tailwind.config.cjs @@ -1,8 +1,5 @@ module.exports = { - content: ["./src/**/*.{js,svelte,ts}", "./src/**/*.html"], - plugins: [require("daisyui"), require("@tailwindcss/typography")], - daisyui: { - themes: ["dark"], - logs: false - } + content: ["./src/**/*.{js,svelte,ts}", "./src/**/*.html"], + plugins: [require("daisyui"), require("@tailwindcss/typography")], + daisyui: { themes: ["dark"], logs: false }, }; diff --git a/client/tests/faucet.ts b/client/tests/faucet.ts index d495bb41..2b7a95bc 100644 --- a/client/tests/faucet.ts +++ b/client/tests/faucet.ts @@ -1,325 +1,303 @@ -import { - expect, - test, - type Frame, - type FullConfig, - type Locator, - type Page -} from "@playwright/test"; +import { type Frame, type FullConfig, type Locator, type Page, expect, test } from "@playwright/test"; type FormSubmit = { - address: string; - recaptcha: string; - parachain_id?: string; + address: string; + recaptcha: string; + parachain_id?: string; }; const getFormElements = async (page: Page, getCaptcha = false) => { - let captcha: Locator = {} as Locator; - if (getCaptcha) { - // ?: Hack. We need to wait for the frame to load and then invade it. - await page.reload(); - const captchaFrame = await new Promise(function (resolve, reject) { - let i = 0; - // function that waits for the frame and timeouts after 3 seconds - (function waitForFrame() { - const captchaFrame = page - .frames() - .filter((f) => f.url().includes("https://www.google.com/recaptcha/api2/")); - if (captchaFrame.length > 0) { - return resolve(captchaFrame[0]); - } else { - i++; - if (i > 10) { - reject(new Error("Timeout")); - } - } - setTimeout(waitForFrame, 300); - })(); - }); - captcha = captchaFrame?.locator("#recaptcha-anchor") as Locator; - } - return { - address: page.getByTestId("address"), - network: page.getByTestId("network"), - captcha, - submit: page.getByTestId("submit-button"), - dropdown: page.getByTestId("dropdown") - }; + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + let captcha: Locator = {} as Locator; + if (getCaptcha) { + // ?: Hack. We need to wait for the frame to load and then invade it. + await page.reload(); + const captchaFrame = await new Promise((resolve, reject) => { + let i = 0; + // function that waits for the frame and timeouts after 3 seconds + // FIXME consider "until" from "@eng-automation/js"? + // eslint-disable-next-line no-restricted-syntax + (function waitForFrame() { + const captchaFrames = page.frames().filter((f) => f.url().includes("https://www.google.com/recaptcha/api2/")); + if (captchaFrames.length > 0) { + return resolve(captchaFrames[0]); + } else { + i++; + if (i > 10) { + reject(new Error("Timeout")); + } + } + setTimeout(waitForFrame, 300); + })(); + }); + captcha = captchaFrame?.locator("#recaptcha-anchor") as Locator; + } + return { + address: page.getByTestId("address"), + network: page.getByTestId("network"), + captcha, + submit: page.getByTestId("submit-button"), + dropdown: page.getByTestId("dropdown"), + }; }; export class FaucetTests { - private readonly dropdownId = "dropdown"; - constructor( - readonly url: string, - readonly envVariable: string, - readonly chainName: string, - readonly chains: { name: string; id: number }[] - ) {} + private readonly dropdownId = "dropdown"; - /** - * Gets the faucet url from the config file - * @param config The second value that is given on the tests arrow function - */ - getFaucetUrl = (config: FullConfig): string => { - const URL_VAR = this.envVariable; - const env = config.webServer?.env; - if (!env) { - throw new Error("No env vars in project"); - } - const faucetUrl = env[URL_VAR]; - if (!faucetUrl) { - throw new Error(`No env var value found for ${URL_VAR}`); - } + constructor( + readonly url: string, + readonly envVariable: string, + readonly chainName: string, + readonly chains: { name: string; id: number }[], + ) {} - return faucetUrl; - }; + /** + * Gets the faucet url from the config file + * @param config The second value that is given on the tests arrow function + */ + getFaucetUrl = (config: FullConfig): string => { + const URL_VAR = this.envVariable; + const env = config.webServer?.env; + if (!env) { + throw new Error("No env vars in project"); + } + const faucetUrl = env[URL_VAR]; + if (!faucetUrl) { + throw new Error(`No env var value found for ${URL_VAR}`); + } - runTests() { - test.describe(`${this.chainName} tests`, () => { - test.describe("on page load", () => { - test("page has expected header", async ({ page }) => { - await page.goto(this.url); - await expect(page.getByRole("heading", { name: this.chainName })).toBeVisible(); - }); + return faucetUrl; + }; - test("page has disabled submit button", async ({ page }) => { - await page.goto(this.url); - const { submit } = await getFormElements(page); - await expect(submit).toBeVisible(); - await expect(submit).toBeDisabled(); - }); + runTests(): void { + test.describe(`${this.chainName} tests`, () => { + test.describe("on page load", () => { + test("page has expected header", async ({ page }) => { + await page.goto(this.url); + await expect(page.getByRole("heading", { name: this.chainName })).toBeVisible(); + }); - test("page has form elements", async ({ page }) => { - await page.goto(this.url); - const { address, network, captcha } = await getFormElements(page, true); - await expect(address).toBeVisible(); - await expect(network).toBeHidden(); - await expect(captcha).toBeVisible(); - }); + test("page has disabled submit button", async ({ page }) => { + await page.goto(this.url); + const { submit } = await getFormElements(page); + await expect(submit).toBeVisible(); + await expect(submit).toBeDisabled(); + }); - test("page loads with default value in parachain field", async ({ page }) => { - await page.goto(this.url); - const { network } = await getFormElements(page); - await expect(network).toHaveValue("-1"); - }); + test("page has form elements", async ({ page }) => { + await page.goto(this.url); + const { address, network, captcha } = await getFormElements(page, true); + await expect(address).toBeVisible(); + await expect(network).toBeHidden(); + await expect(captcha).toBeVisible(); + }); - test("page with get parameter loads with value in parachain field", async ({ page }) => { - const parachainId = "1234"; - await page.goto(`${this.url}?parachain=${parachainId}`); - const { network } = await getFormElements(page); - await expect(network).toHaveValue(parachainId); - }); + test("page loads with default value in parachain field", async ({ page }) => { + await page.goto(this.url); + const { network } = await getFormElements(page); + await expect(network).toHaveValue("-1"); + }); - test("page has captcha", async ({ page }) => { - await page.goto(this.url); - const { captcha } = await getFormElements(page, true); - await expect(captcha).toBeVisible(); - }); - }); + test("page with get parameter loads with value in parachain field", async ({ page }) => { + const parachainId = "1234"; + await page.goto(`${this.url}?parachain=${parachainId}`); + const { network } = await getFormElements(page); + await expect(network).toHaveValue(parachainId); + }); - test.describe("dropdown interaction", () => { - const networkName = this.chains[1].name; - test("dropdown appears on click", async ({ page }) => { - await page.goto(this.url); - const dropdown = page.getByTestId(this.dropdownId); - await expect(dropdown).toBeVisible(); - await expect(page.getByText(networkName)).toBeHidden(); - await dropdown.click(); - await expect(page.getByText(networkName)).toBeVisible(); - }); + test("page has captcha", async ({ page }) => { + await page.goto(this.url); + const { captcha } = await getFormElements(page, true); + await expect(captcha).toBeVisible(); + }); + }); - test("dropdown closes on network selection", async ({ page }) => { - await page.goto(this.url); - const dropdown = page.getByTestId(this.dropdownId); - await expect(dropdown).toBeVisible(); - const networkBtn = page.getByTestId("network-1"); - await dropdown.click(); - await expect(networkBtn).toBeVisible(); - await networkBtn.click(); - await expect(networkBtn).not.toBeVisible(); - }); + test.describe("dropdown interaction", () => { + const networkName = this.chains[1].name; + test("dropdown appears on click", async ({ page }) => { + await page.goto(this.url); + const dropdown = page.getByTestId(this.dropdownId); + await expect(dropdown).toBeVisible(); + await expect(page.getByText(networkName)).toBeHidden(); + await dropdown.click(); + await expect(page.getByText(networkName)).toBeVisible(); + }); - test("network changes on modal selection", async ({ page }) => { - await page.goto(this.url); - const dropdown = page.getByTestId(this.dropdownId); - const { network } = await getFormElements(page); - await expect(dropdown).toBeVisible(); - const networkBtn = page.getByTestId("network-1"); - await dropdown.click(); - await expect(networkBtn).toBeVisible(); - await networkBtn.click(); - await expect(networkBtn).not.toBeVisible(); - await expect(network).toHaveValue("1000"); - }); - }); + test("dropdown closes on network selection", async ({ page }) => { + await page.goto(this.url); + const dropdown = page.getByTestId(this.dropdownId); + await expect(dropdown).toBeVisible(); + const networkBtn = page.getByTestId("network-1"); + await dropdown.click(); + await expect(networkBtn).toBeVisible(); + await networkBtn.click(); + await expect(networkBtn).not.toBeVisible(); + }); - test.describe("Custom networks", () => { - let network: Locator; - let customChainDiv: Locator; + test("network changes on modal selection", async ({ page }) => { + await page.goto(this.url); + const dropdown = page.getByTestId(this.dropdownId); + const { network } = await getFormElements(page); + await expect(dropdown).toBeVisible(); + const networkBtn = page.getByTestId("network-1"); + await dropdown.click(); + await expect(networkBtn).toBeVisible(); + await networkBtn.click(); + await expect(networkBtn).not.toBeVisible(); + await expect(network).toHaveValue("1000"); + }); + }); - test.beforeEach(async ({ page }) => { - await page.goto(this.url); - network = (await getFormElements(page)).network; - customChainDiv = page.getByTestId("custom-network-button"); - await expect(customChainDiv).toBeEnabled(); - await expect(customChainDiv).toContainText("Use custom chain id"); - await customChainDiv.click(); - expect(network).toBeVisible(); - }); + test.describe("Custom networks", () => { + let network: Locator; + let customChainDiv: Locator; - test("Value is empty on network pick", async () => { - await expect(network).toHaveValue(""); - }); + test.beforeEach(async ({ page }) => { + await page.goto(this.url); + network = (await getFormElements(page)).network; + customChainDiv = page.getByTestId("custom-network-button"); + await expect(customChainDiv).toBeEnabled(); + await expect(customChainDiv).toContainText("Use custom chain id"); + await customChainDiv.click(); + await expect(network).toBeVisible(); + }); - test("Value restores to -1 when picking preselected network", async () => { - await customChainDiv.click(); - await expect(network).toBeHidden(); - await expect(network).toHaveValue("-1"); - }); - }); + test("Value is empty on network pick", async () => { + await expect(network).toHaveValue(""); + }); - test.describe("form interaction", () => { - test("submit form becomes valid on data entry", async ({ page }) => { - await page.goto(this.url); - const { address, captcha, submit } = await getFormElements(page, true); - await expect(submit).toBeDisabled(); - await address.fill("address"); - await captcha.click(); - await expect(submit).toBeEnabled(); - }); + test("Value restores to -1 when picking preselected network", async () => { + await customChainDiv.click(); + await expect(network).toBeHidden(); + await expect(network).toHaveValue("-1"); + }); + }); - test("sends data on submit", async ({ page }, { config }) => { - await page.goto(this.url); - const { address, captcha, submit } = await getFormElements(page, true); - await expect(submit).toBeDisabled(); - const myAddress = "0x000000001"; - await address.fill(myAddress); - await captcha.click(); - const faucetUrl = this.getFaucetUrl(config); - await page.route(faucetUrl, (route) => - route.fulfill({ - body: JSON.stringify({ hash: "hash" }) - }) - ); + test.describe("form interaction", () => { + test("submit form becomes valid on data entry", async ({ page }) => { + await page.goto(this.url); + const { address, captcha, submit } = await getFormElements(page, true); + await expect(submit).toBeDisabled(); + await address.fill("address"); + await captcha.click(); + await expect(submit).toBeEnabled(); + }); - const request = page.waitForRequest((req) => { - if (req.url() === faucetUrl) { - const data = req.postDataJSON() as FormSubmit; - expect(data.address).toEqual(myAddress); - return !!data.recaptcha; - } - return false; - }); - await submit.click(); - // verify that the post request is correct - await request; - }); + test("sends data on submit", async ({ page }, { config }) => { + await page.goto(this.url); + const { address, captcha, submit } = await getFormElements(page, true); + await expect(submit).toBeDisabled(); + const myAddress = "0x000000001"; + await address.fill(myAddress); + await captcha.click(); + const faucetUrl = this.getFaucetUrl(config); - for (let i = 1; i < this.chains.length; i++) { - const chain = this.chains[i]; - test(`sends data with ${chain.name} chain on submit`, async ({ page }, { config }) => { - await page.goto(this.url); - const { address, captcha, submit } = await getFormElements(page, true); - const dropdown = page.getByTestId(this.dropdownId); - await expect(submit).toBeDisabled(); - const myAddress = "0x000000002"; - await address.fill(myAddress); - await dropdown.click(); - const networkBtn = page.getByTestId(`network-${i}`); - await expect(networkBtn).toBeVisible(); - await networkBtn.click(); - await captcha.click(); - await expect(submit).toBeEnabled(); - const faucetUrl = this.getFaucetUrl(config); - await page.route(faucetUrl, (route) => - route.fulfill({ - body: JSON.stringify({ hash: "hash" }) - }) - ); + await page.route(faucetUrl, (route) => route.fulfill({ body: JSON.stringify({ hash: "hash" }) })); - const request = page.waitForRequest((req) => { - if (req.url() === faucetUrl) { - const data = req.postDataJSON() as FormSubmit; - const parachain_id = chain.id > 0 ? chain.id.toString() : undefined; - expect(data).toMatchObject({ address: myAddress, parachain_id }); - return !!data.recaptcha; - } - return false; - }); + const request = page.waitForRequest((req) => { + if (req.url() === faucetUrl) { + const data = req.postDataJSON() as FormSubmit; + expect(data.address).toEqual(myAddress); + return !!data.recaptcha; + } + return false; + }); + await submit.click(); + // verify that the post request is correct + await request; + }); - await submit.click(); - await request; - }); - } + for (let i = 1; i < this.chains.length; i++) { + const chain = this.chains[i]; + test(`sends data with ${chain.name} chain on submit`, async ({ page }, { config }) => { + await page.goto(this.url); + const { address, captcha, submit } = await getFormElements(page, true); + const dropdown = page.getByTestId(this.dropdownId); + await expect(submit).toBeDisabled(); + const myAddress = "0x000000002"; + await address.fill(myAddress); + await dropdown.click(); + const networkBtn = page.getByTestId(`network-${i}`); + await expect(networkBtn).toBeVisible(); + await networkBtn.click(); + await captcha.click(); + await expect(submit).toBeEnabled(); + const faucetUrl = this.getFaucetUrl(config); + await page.route(faucetUrl, (route) => route.fulfill({ body: JSON.stringify({ hash: "hash" }) })); - test("sends data with custom chain on submit", async ({ page }, { config }) => { - await page.goto(this.url); - const { address, network, captcha, submit } = await getFormElements(page, true); - await expect(submit).toBeDisabled(); - const myAddress = "0x000000002"; - await address.fill(myAddress); - const customChainDiv = page.getByTestId("custom-network-button"); - await customChainDiv.click(); - await network.fill("9999"); - await captcha.click(); - await expect(submit).toBeEnabled(); - const faucetUrl = this.getFaucetUrl(config); - await page.route(faucetUrl, (route) => - route.fulfill({ - body: JSON.stringify({ hash: "hash" }) - }) - ); + const request = page.waitForRequest((req) => { + if (req.url() === faucetUrl) { + const data = req.postDataJSON() as FormSubmit; + const parachain_id = chain.id > 0 ? chain.id.toString() : undefined; + expect(data).toMatchObject({ address: myAddress, parachain_id }); + return !!data.recaptcha; + } + return false; + }); - const request = page.waitForRequest((req) => { - if (req.url() === faucetUrl) { - const data = req.postDataJSON() as FormSubmit; - expect(data).toMatchObject({ address: myAddress, parachain_id: "9999" }); - return !!data.recaptcha; - } - return false; - }); + await submit.click(); + await request; + }); + } - await submit.click(); - await request; - }); + test("sends data with custom chain on submit", async ({ page }, { config }) => { + await page.goto(this.url); + const { address, network, captcha, submit } = await getFormElements(page, true); + await expect(submit).toBeDisabled(); + const myAddress = "0x000000002"; + await address.fill(myAddress); + const customChainDiv = page.getByTestId("custom-network-button"); + await customChainDiv.click(); + await network.fill("9999"); + await captcha.click(); + await expect(submit).toBeEnabled(); + const faucetUrl = this.getFaucetUrl(config); + await page.route(faucetUrl, (route) => route.fulfill({ body: JSON.stringify({ hash: "hash" }) })); - test("display link to transaction", async ({ page }, { config }) => { - await page.goto(this.url); - const operationHash = "0x0123435423412343214"; - const { address, captcha, submit } = await getFormElements(page, true); - await expect(submit).toBeDisabled(); - const myAddress = "0x000000001"; - await address.fill(myAddress); - await captcha.click(); - await page.route(this.getFaucetUrl(config), (route) => - route.fulfill({ - body: JSON.stringify({ hash: operationHash }) - }) - ); - await submit.click(); - const transactionLink = page.getByTestId("success-button"); - await expect(transactionLink).toBeVisible(); - expect(await transactionLink.getAttribute("href")).toContain(operationHash); - }); + const request = page.waitForRequest((req) => { + if (req.url() === faucetUrl) { + const data = req.postDataJSON() as FormSubmit; + expect(data).toMatchObject({ address: myAddress, parachain_id: "9999" }); + return !!data.recaptcha; + } + return false; + }); - test("throw error", async ({ page }, { config }) => { - await page.goto(this.url); - const error = "Things failed because you are a naughty boy!"; - const { address, captcha, submit } = await getFormElements(page, true); - await expect(submit).toBeDisabled(); - await address.fill("0x123"); - await captcha.click(); - await page.route(this.getFaucetUrl(config), (route) => - route.fulfill({ - body: JSON.stringify({ error }) - }) - ); - await submit.click(); - const errorMessage = page.getByTestId("error"); - await expect(errorMessage).toBeVisible(); - expect((await errorMessage.allInnerTexts())[0]).toContain(error); - }); - }); - }); - } + await submit.click(); + await request; + }); + + test("display link to transaction", async ({ page }, { config }) => { + await page.goto(this.url); + const operationHash = "0x0123435423412343214"; + const { address, captcha, submit } = await getFormElements(page, true); + await expect(submit).toBeDisabled(); + const myAddress = "0x000000001"; + await address.fill(myAddress); + await captcha.click(); + await page.route(this.getFaucetUrl(config), (route) => + route.fulfill({ body: JSON.stringify({ hash: operationHash }) }), + ); + await submit.click(); + const transactionLink = page.getByTestId("success-button"); + await expect(transactionLink).toBeVisible(); + expect(await transactionLink.getAttribute("href")).toContain(operationHash); + }); + + test("throw error", async ({ page }, { config }) => { + await page.goto(this.url); + const error = "Things failed because you are a naughty boy!"; + const { address, captcha, submit } = await getFormElements(page, true); + await expect(submit).toBeDisabled(); + await address.fill("0x123"); + await captcha.click(); + await page.route(this.getFaucetUrl(config), (route) => route.fulfill({ body: JSON.stringify({ error }) })); + await submit.click(); + const errorMessage = page.getByTestId("error"); + await expect(errorMessage).toBeVisible(); + expect((await errorMessage.allInnerTexts())[0]).toContain(error); + }); + }); + }); + } } diff --git a/client/tests/rococo.test.ts b/client/tests/rococo.test.ts index fb3445b6..a962ad17 100644 --- a/client/tests/rococo.test.ts +++ b/client/tests/rococo.test.ts @@ -1,11 +1,11 @@ import { FaucetTests } from "./faucet.js"; const chains = [ - { name: "Relay Chain", id: -1 }, - { name: "Rockmine", id: 1000 }, - { name: "Contracts", id: 1002 }, - { name: "Encointer Lietaer", id: 1003 }, - { name: "Bridgehub", id: 1013 } + { name: "Relay Chain", id: -1 }, + { name: "Rockmine", id: 1000 }, + { name: "Contracts", id: 1002 }, + { name: "Encointer Lietaer", id: 1003 }, + { name: "Bridgehub", id: 1013 }, ]; const test = new FaucetTests("/", "PUBLIC_FAUCET_ROCOCO_URL", "Rococo Faucet", chains); diff --git a/client/tests/switch.test.ts b/client/tests/switch.test.ts index 1ff86313..c6d55602 100644 --- a/client/tests/switch.test.ts +++ b/client/tests/switch.test.ts @@ -1,62 +1,58 @@ -import { - expect, - test, - type Frame, - type FullConfig, - type Locator, - type Page -} from "@playwright/test"; - -export const Networks = [ - { name: "Rococo", url: "/" }, - { name: "Westend", url: "/westend" } +import { expect, test } from "@playwright/test"; + +type Network = { name: string; url: string }; + +export const networks: Network[] = [ + { name: "Rococo", url: "/" }, + { name: "Westend", url: "/westend" }, ]; const networkSelectId = "network-select"; -const networkTestId = Networks.map(({ name }) => `network-${name}`); +function getTestId(network: Network): string { + return `network-${network.name}`; +} test.describe("Test network switch component", () => { - for (let i = 0; i < Networks.length; i++) { - const network = Networks[i]; - test(`page for ${network.name} loads`, async ({ page }) => { - await page.goto(network.url); - await expect(page.getByRole("heading", { name: `${network.name} Faucet` })).toBeVisible(); - }); - - test(`network switch has correct name for ${network.url}`, async ({ page }) => { - await page.goto(network.url); - const selector = page.getByTestId(networkSelectId); - await expect(selector).toContainText(network.name); - }); - } - - test("network switch shows all available networks", async ({ page }) => { - await page.goto("/"); - const selector = page.getByTestId(networkSelectId); - await selector.click(); - for (let i = 0; i < networkTestId.length; i++) { - const link = page.getByTestId(networkTestId[i]); - await expect(link).toBeVisible(); - await expect(link).toContainText(Networks[i].name); - } - }); - - test("network switch are not visible by default", async ({ page }) => { - await page.goto("/"); - for (let i = 0; i < networkTestId.length; i++) { - const link = page.getByTestId(networkTestId[i]); - await expect(link).toBeHidden(); - } - }); - - test("network switch contains links", async ({ page }) => { - await page.goto("/"); - const selector = page.getByTestId(networkSelectId); - await selector.click(); - for (let i = 0; i < networkTestId.length; i++) { - const link = page.getByTestId(networkTestId[i]); - expect(await link.getAttribute("href")).toContain(Networks[i].url); - } - }); + for (const currentNetwork of networks) { + test(`page for ${currentNetwork.name} loads`, async ({ page }) => { + await page.goto(currentNetwork.url); + await expect(page.getByRole("heading", { name: `${currentNetwork.name} Faucet` })).toBeVisible(); + }); + + test(`network switch has correct name for ${currentNetwork.url}`, async ({ page }) => { + await page.goto(currentNetwork.url); + const selector = page.getByTestId(networkSelectId); + await expect(selector).toContainText(currentNetwork.name); + }); + } + + test("network switch shows all available networks", async ({ page }) => { + await page.goto("/"); + const selector = page.getByTestId(networkSelectId); + await selector.click(); + for (const network of networks) { + const link = page.getByTestId(getTestId(network)); + await expect(link).toBeVisible(); + await expect(link).toContainText(network.name); + } + }); + + test("network switch are not visible by default", async ({ page }) => { + await page.goto("/"); + for (const network of networks) { + const link = page.getByTestId(getTestId(network)); + await expect(link).toBeHidden(); + } + }); + + test("network switch contains links", async ({ page }) => { + await page.goto("/"); + const selector = page.getByTestId(networkSelectId); + await selector.click(); + for (const network of networks) { + const link = page.getByTestId(getTestId(network)); + expect(await link.getAttribute("href")).toContain(network.url); + } + }); }); diff --git a/client/tests/westend.test.ts b/client/tests/westend.test.ts index ce4c2f60..29121719 100644 --- a/client/tests/westend.test.ts +++ b/client/tests/westend.test.ts @@ -1,9 +1,9 @@ import { FaucetTests } from "./faucet.js"; const chains = [ - { name: "Westend Relay Chain", id: -1 }, - { name: "Westmint", id: 1000 }, - { name: "Collectives", id: 1001 } + { name: "Westend Relay Chain", id: -1 }, + { name: "Westmint", id: 1000 }, + { name: "Collectives", id: 1001 }, ]; const tests = new FaucetTests("/westend", "PUBLIC_FAUCET_WESTEND_URL", "Westend Faucet", chains); diff --git a/client/tsconfig.json b/client/tsconfig.json index 6ae0c8c4..9f09aac8 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -1,17 +1,28 @@ { - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true - } - // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias - // - // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes - // from the referenced tsconfig.json - TypeScript does not merge them in + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true + }, + "include": [ + ".svelte-kit/ambient.d.ts", + "./src/**/*.js", + "./src/**/*.ts", + "./src/**/*.svelte", + "./tests/**/*.js", + "./tests/**/*.ts", + "./tests/**/*.svelte", + "playwright.config.ts", + "vite.config.ts" + ] + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in } diff --git a/client/vite.config.ts b/client/vite.config.ts index 328b1f5f..7102f34d 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -1,6 +1,5 @@ import { sveltekit } from "@sveltejs/kit/vite"; import { defineConfig } from "vite"; -export default defineConfig({ - plugins: [sveltekit()] -}); +// eslint-disable-next-line @typescript-eslint/no-unsafe-call +export default defineConfig({ plugins: [sveltekit()] }); diff --git a/client/yarn.lock b/client/yarn.lock index 09e986de..a672240d 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -112,57 +112,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.12.tgz#3a11d13e9a5b0c05db88991b234d8baba1f96487" integrity sha512-JOOxw49BVZx2/5tW3FqkdjSD/5gXYeVGPDcB0lvap0gLQshkh1Nyel1QazC+wNxus3xPlsYAgqU1BUmrmCvWtw== -"@eslint-community/eslint-utils@^4.2.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz#a556790523a351b4e47e9d385f47265eaaf9780a" - integrity sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA== - dependencies: - eslint-visitor-keys "^3.3.0" - -"@eslint-community/regexpp@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403" - integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ== - -"@eslint/eslintrc@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.1.tgz#7888fe7ec8f21bc26d646dbd2c11cd776e21192d" - integrity sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.5.0" - globals "^13.19.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@eslint/js@8.36.0": - version "8.36.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe" - integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== - -"@humanwhocodes/config-array@^0.11.8": - version "0.11.8" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" - integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== - dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" - minimatch "^3.0.5" - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== - "@jridgewell/resolve-uri@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" @@ -194,7 +143,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": +"@nodelib/fs.walk@^1.2.3": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -330,11 +279,6 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== -"@types/json-schema@^7.0.9": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== - "@types/marked@^4.0.1": version "4.3.1" resolved "https://registry.yarnpkg.com/@types/marked/-/marked-4.3.1.tgz#45fb6dfd47afb595766c71ed7749ead23f137de3" @@ -355,100 +299,6 @@ resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== -"@types/semver@^7.3.12": - version "7.3.13" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" - integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== - -"@typescript-eslint/eslint-plugin@^5.45.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.56.0.tgz#e4fbb4d6dd8dab3e733485c1a44a02189ae75364" - integrity sha512-ZNW37Ccl3oMZkzxrYDUX4o7cnuPgU+YrcaYXzsRtLB16I1FR5SHMqga3zGsaSliZADCWo2v8qHWqAYIj8nWCCg== - dependencies: - "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.56.0" - "@typescript-eslint/type-utils" "5.56.0" - "@typescript-eslint/utils" "5.56.0" - debug "^4.3.4" - grapheme-splitter "^1.0.4" - ignore "^5.2.0" - natural-compare-lite "^1.4.0" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/parser@^5.45.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.56.0.tgz#42eafb44b639ef1dbd54a3dbe628c446ca753ea6" - integrity sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg== - dependencies: - "@typescript-eslint/scope-manager" "5.56.0" - "@typescript-eslint/types" "5.56.0" - "@typescript-eslint/typescript-estree" "5.56.0" - debug "^4.3.4" - -"@typescript-eslint/scope-manager@5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.56.0.tgz#62b4055088903b5254fa20403010e1c16d6ab725" - integrity sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw== - dependencies: - "@typescript-eslint/types" "5.56.0" - "@typescript-eslint/visitor-keys" "5.56.0" - -"@typescript-eslint/type-utils@5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.56.0.tgz#e6f004a072f09c42e263dc50e98c70b41a509685" - integrity sha512-8WxgOgJjWRy6m4xg9KoSHPzBNZeQbGlQOH7l2QEhQID/+YseaFxg5J/DLwWSsi9Axj4e/cCiKx7PVzOq38tY4A== - dependencies: - "@typescript-eslint/typescript-estree" "5.56.0" - "@typescript-eslint/utils" "5.56.0" - debug "^4.3.4" - tsutils "^3.21.0" - -"@typescript-eslint/types@5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.56.0.tgz#b03f0bfd6fa2afff4e67c5795930aff398cbd834" - integrity sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w== - -"@typescript-eslint/typescript-estree@5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz#48342aa2344649a03321e74cab9ccecb9af086c3" - integrity sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg== - dependencies: - "@typescript-eslint/types" "5.56.0" - "@typescript-eslint/visitor-keys" "5.56.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/utils@5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.56.0.tgz#db64705409b9a15546053fb4deb2888b37df1f41" - integrity sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@types/json-schema" "^7.0.9" - "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.56.0" - "@typescript-eslint/types" "5.56.0" - "@typescript-eslint/typescript-estree" "5.56.0" - eslint-scope "^5.1.1" - semver "^7.3.7" - -"@typescript-eslint/visitor-keys@5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.56.0.tgz#f19eb297d972417eb13cb69b35b3213e13cc214f" - integrity sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q== - dependencies: - "@typescript-eslint/types" "5.56.0" - eslint-visitor-keys "^3.3.0" - -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - acorn-node@^1.8.2: version "1.8.2" resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" @@ -468,33 +318,6 @@ acorn@^7.0.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.8.0: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - -ajv@^6.10.0, ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -508,16 +331,6 @@ arg@^5.0.2: resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - autoprefixer@^10.4.14: version "10.4.14" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d" @@ -604,14 +417,6 @@ caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001464: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001469.tgz#3dd505430c8522fdc9f94b4a19518e330f5c945a" integrity sha512-Rcp7221ScNqQPP3W+lVOYDyjdR6dC+neEQCttoNr5bAyz54AboB4iwpnWgyi8P4YUsPybVzT4LgWiBbI3drL4g== -chalk@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chokidar@^3.4.1, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -670,15 +475,6 @@ cookie@^0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - css-selector-tokenizer@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz#88267ef6238e64f2215ea2764b3e2cf498b845dd" @@ -702,18 +498,13 @@ daisyui@^2.51.4: postcss-js "^4.0.0" tailwindcss "^3" -debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: +debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -deep-is@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - deepmerge@^4.2.2, deepmerge@^4.3.0: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" @@ -748,25 +539,11 @@ didyoumean@^1.2.2: resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - dlv@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - electron-to-chromium@^1.4.284: version "1.4.334" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.334.tgz#eacdcb1145534202d569610c5915b63a3fec0eb9" @@ -810,142 +587,17 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-config-prettier@^8.5.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" - integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== - -eslint-plugin-svelte3@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-4.0.0.tgz#3d4f3dcaec5761dac8bc697f81de3613b485b4e3" - integrity sha512-OIx9lgaNzD02+MDFNLw0GEUbuovNcglg+wnd/UY0fbZmlQSz7GlQiQ1f+yX0XvC07XPcDOnFcichqI3xCwp71g== - -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== - -eslint@^8.28.0: - version "8.36.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf" - integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.1" - "@eslint/js" "8.36.0" - "@humanwhocodes/config-array" "^0.11.8" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-visitor-keys "^3.3.0" - espree "^9.5.0" - esquery "^1.4.2" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.19.0" - grapheme-splitter "^1.0.4" - ignore "^5.2.0" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-sdsl "^4.1.4" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.1" - strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" - text-table "^0.2.0" - esm-env@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/esm-env/-/esm-env-1.0.0.tgz#b124b40b180711690a4cb9b00d16573391950413" integrity sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA== -espree@^9.5.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113" - integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw== - dependencies: - acorn "^8.8.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" - -esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - estree-walker@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-glob@^3.2.12, fast-glob@^3.2.7, fast-glob@^3.2.9: +fast-glob@^3.2.12, fast-glob@^3.2.7: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -956,16 +608,6 @@ fast-glob@^3.2.12, fast-glob@^3.2.7, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - fastparse@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" @@ -978,13 +620,6 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -992,27 +627,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== - dependencies: - flatted "^3.1.0" - rimraf "^3.0.2" - -flatted@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== - fraction.js@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" @@ -1070,30 +684,11 @@ glob@^8.0.3: minimatch "^5.0.1" once "^1.3.0" -globals@^13.19.0: - version "13.20.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" - integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== - dependencies: - type-fest "^0.20.2" - globalyzer@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== -globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - globrex@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" @@ -1104,16 +699,6 @@ graceful-fs@^4.1.3: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -1121,12 +706,7 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -ignore@^5.2.0: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== - -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -1139,11 +719,6 @@ import-meta-resolve@^2.2.0: resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-2.2.2.tgz#75237301e72d1f0fbd74dbc6cca9324b164c2cc9" integrity sha512-f8KcQ1D80V7RnqVm+/lirO9zkOxjGxhaTC1IPrBGd3MEfNgmNG67tSUO9gTi2F3Blr2Az6g1vocaxzkVnWl9MA== -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -1188,7 +763,7 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -1205,11 +780,6 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - is-reference@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" @@ -1217,58 +787,16 @@ is-reference@1.2.1: dependencies: "@types/estree" "*" -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -js-sdsl@^4.1.4: - version "4.3.0" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" - integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== - -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - kleur@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - lilconfig@^2.0.5, lilconfig@^2.0.6: version "2.1.0" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - lodash.castarray@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115" @@ -1284,13 +812,6 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - magic-string@^0.27.0: version "0.27.0" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" @@ -1317,7 +838,7 @@ marked@^4.0.10: resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== -merge2@^1.3.0, merge2@^1.4.1: +merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -1340,7 +861,7 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -1386,16 +907,6 @@ nanoid@^3.3.4: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== -natural-compare-lite@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" - integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - node-releases@^2.0.8: version "2.0.10" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" @@ -1423,32 +934,6 @@ once@^1.3.0: dependencies: wrappy "1" -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.3" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -1456,31 +941,16 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -1562,26 +1032,6 @@ postcss@^8.0.9, postcss@^8.4.21: picocolors "^1.0.0" source-map-js "^1.0.2" -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prettier-plugin-svelte@^2.8.1: - version "2.9.0" - resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.9.0.tgz#16cc7fa73fa96eaef48b44089753ac9f1f1175e5" - integrity sha512-3doBi5NO4IVgaNPtwewvrgPpqAcvNv0NwJNflr76PIGgi9nf1oguQV1Hpdm9TI2ALIQVn/9iIwLpBO5UcD2Jiw== - -prettier@^2.8.0: - version "2.8.6" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.6.tgz#5c174b29befd507f14b83e3c19f83fdc0e974b71" - integrity sha512-mtuzdiBbHwPEgl7NxWlqOkithPyp4VN93V7VeHVWBF+ad3I5avc0RVDT4oImXQy9H/AqxA2NSQH8pSxHW6FYbQ== - -punycode@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -1632,13 +1082,6 @@ rimraf@^2.5.2: dependencies: glob "^7.1.3" -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - rollup@^3.18.0, rollup@^3.7.0: version "3.20.0" resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.20.0.tgz#ce7bd88449a776b9f75bf4e35959e25fbd3f51b1" @@ -1675,30 +1118,11 @@ schema-dts@^1.1.2: resolved "https://registry.yarnpkg.com/schema-dts/-/schema-dts-1.1.2.tgz#82ccf71b5dcb80065a1cc5941888507a4ce1e44b" integrity sha512-MpNwH0dZJHinVxk9bT8XUdjKTxMYrA5bLtrrGmFA6PTLwlOKnhi67XoRd6/ty+Djt6ZC0slR57qFhZDNMI6DhQ== -semver@^7.3.7: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== - dependencies: - lru-cache "^6.0.0" - set-cookie-parser@^2.5.1: version "2.6.0" resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz#131921e50f62ff1a66a461d7d62d7b21d5d15a51" integrity sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ== -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -1715,11 +1139,6 @@ sirv@^2.0.2: mrmime "^1.0.0" totalist "^3.0.0" -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - sorcery@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/sorcery/-/sorcery-0.11.0.tgz#310c80ee993433854bb55bb9aa4003acd147fca8" @@ -1740,13 +1159,6 @@ streamsearch@^1.1.0: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -1754,18 +1166,6 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -1850,11 +1250,6 @@ tailwindcss@^3, tailwindcss@^3.2.7: quick-lru "^5.1.1" resolve "^1.22.1" -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - tiny-glob@^0.2.9: version "0.2.9" resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" @@ -1875,35 +1270,11 @@ totalist@^3.0.0: resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.0.tgz#4ef9c58c5f095255cdc3ff2a0a55091c57a3a1bd" integrity sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw== -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - tslib@^2.4.1: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - typescript@^4.9.3, typescript@^4.9.4: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" @@ -1924,13 +1295,6 @@ update-browserslist-db@^1.0.10: escalade "^3.1.1" picocolors "^1.0.0" -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -1953,18 +1317,6 @@ vitefu@^0.2.4: resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.4.tgz#212dc1a9d0254afe65e579351bed4e25d81e0b35" integrity sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g== -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -1975,17 +1327,7 @@ xtend@^4.0.2: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - yaml@^1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/package.json b/package.json index 5f9f99a0..ccfadaac 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,10 @@ "dev:backend": "nodemon -- src/server/start.ts", "dev:bot": "nodemon -- ./src/bot/start.ts", "typecheck": "tsc --noEmit", - "lint": "eslint ./src/ --ext .js,.ts", - "lint:fix": "eslint ./src/ --ext .js,.ts --fix", - "format": "prettier ./src --check", - "format:fix": "prettier ./src --write", + "lint": "eslint ./src/ ./client/src ./client/tests --ext .js,.ts,.svelte", + "lint:fix": "eslint ./src/ ./client/src ./client/tests --ext .js,.ts,.svelte --fix", + "format": "prettier ./src ./client/src ./client/tests --check", + "format:fix": "prettier ./src ./client/src ./client/tests --write", "fix": "yarn lint:fix && yarn format:fix", "test": "jest", "test:e2e": "NODE_OPTIONS='--experimental-vm-modules --es-module-specifier-resolution=node' jest -c jest.e2e.config.js --runInBand" @@ -26,13 +26,21 @@ "author": "", "license": "ISC", "lint-staged": { - "*.js": [ + "./src/**/*.js": [ "prettier --write", "eslint --fix" ], - "*.ts": [ + "./src/**/*.ts": [ "prettier --write", "eslint --fix" + ], + "./client/**/*.js": [ + "cd client && prettier --write", + "cd client && eslint --fix" + ], + "./client/**/*.ts": [ + "cd client && prettier --write", + "cd client && eslint --fix" ] }, "simple-git-hooks": { @@ -62,7 +70,7 @@ }, "devDependencies": { "@eng-automation/js": "^0.0.22", - "@eng-automation/js-style": "^2.0.0", + "@eng-automation/js-style": "^2.1.0", "@types/body-parser": "^1.19.2", "@types/express": "^4.17.13", "@types/jest": "^29.4.0", diff --git a/src/config.ts b/src/config.ts index 13d8d742..53efef98 100644 --- a/src/config.ts +++ b/src/config.ts @@ -13,19 +13,19 @@ type SpecType = T extends { type: "string" } function faucetBotConfig() { const config = faucetConfig(); - type BotConfigSpec = typeof faucetConfigSpec["SMF"]["BOT"]; + type BotConfigSpec = (typeof faucetConfigSpec)["SMF"]["BOT"]; return { Get: (key: K): SpecType => config.Get("BOT", key) }; } function faucetServerConfig() { const config = faucetConfig(); - type ServerConfigSpec = typeof faucetConfigSpec["SMF"]["BACKEND"]; + type ServerConfigSpec = (typeof faucetConfigSpec)["SMF"]["BACKEND"]; return { Get: (key: K): SpecType => config.Get("BACKEND", key), }; } -export function validateConfig(appName: keyof typeof faucetConfigSpec["SMF"]) { +export function validateConfig(appName: keyof (typeof faucetConfigSpec)["SMF"]) { if (process.env.NODE_ENV == "test") { return; } diff --git a/yarn.lock b/yarn.lock index 217c4ca6..ac801259 100644 --- a/yarn.lock +++ b/yarn.lock @@ -492,23 +492,26 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@eng-automation/js-style@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@eng-automation/js-style/-/js-style-2.0.0.tgz#62aee05d0d3ff6d5cf809188d1f226c35f93d569" - integrity sha512-6AqIVIpcLD9lg3zh9J+BqCwWRki7R20AHdhgUjfqYjUcC0JR1ZWk61rvusOAwASeSL77xMTTxVG/PExHVT5Dbg== +"@eng-automation/js-style@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@eng-automation/js-style/-/js-style-2.1.0.tgz#9f6b9ce911e65ef103ed03ed7c92dac06d5abe7f" + integrity sha512-jAMAE7SzIjVMNXkkNwuyJKEHcge47xXaIvg/z6K0pr9ImIp8ZcAoP/B5v58Zy5FOSyweNQISrnYVZjGfw7ZatQ== dependencies: - "@typescript-eslint/eslint-plugin" "^5.21.0" - "@typescript-eslint/parser" "^5.21.0" + "@typescript-eslint/eslint-plugin" "^5.59.1" + "@typescript-eslint/parser" "^5.59.1" eslint "^8.14.0" eslint-config-prettier "^8.5.0" eslint-plugin-import "^2.27.5" eslint-plugin-jest "^26.5.3" - eslint-plugin-prettier "^4.0.0" + eslint-plugin-prettier "^4.2.1" eslint-plugin-simple-import-sort "^7.0.0" eslint-plugin-sonarjs "^0.13.0" + eslint-plugin-svelte "^2.30.0" eslint-plugin-unused-imports "^2.0.0" - prettier "^2.6.2" + prettier "^2.8.0" prettier-plugin-compactify "^0.1.6" + prettier-plugin-svelte "^2.8.1" + svelte "^3.54.0" "@eng-automation/js@^0.0.22": version "0.0.22" @@ -523,6 +526,18 @@ lodash "^4.17.21" node-fetch "^2.6.7" +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.5.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" + integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== + "@eslint/eslintrc@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" @@ -829,6 +844,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@0.3.9", "@jridgewell/trace-mapping@^0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -1558,29 +1578,30 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.21.0": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz#deee67e399f2cb6b4608c935777110e509d8018c" - integrity sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ== +"@typescript-eslint/eslint-plugin@^5.59.1": + version "5.59.9" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz#2604cfaf2b306e120044f901e20c8ed926debf15" + integrity sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA== dependencies: - "@typescript-eslint/scope-manager" "5.48.1" - "@typescript-eslint/type-utils" "5.48.1" - "@typescript-eslint/utils" "5.48.1" + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.59.9" + "@typescript-eslint/type-utils" "5.59.9" + "@typescript-eslint/utils" "5.59.9" debug "^4.3.4" + grapheme-splitter "^1.0.4" ignore "^5.2.0" natural-compare-lite "^1.4.0" - regexpp "^3.2.0" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/parser@^5.21.0": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.48.1.tgz#d0125792dab7e232035434ab8ef0658154db2f10" - integrity sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA== +"@typescript-eslint/parser@^5.59.1": + version "5.59.9" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.9.tgz#a85c47ccdd7e285697463da15200f9a8561dd5fa" + integrity sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ== dependencies: - "@typescript-eslint/scope-manager" "5.48.1" - "@typescript-eslint/types" "5.48.1" - "@typescript-eslint/typescript-estree" "5.48.1" + "@typescript-eslint/scope-manager" "5.59.9" + "@typescript-eslint/types" "5.59.9" + "@typescript-eslint/typescript-estree" "5.59.9" debug "^4.3.4" "@typescript-eslint/scope-manager@5.48.1": @@ -1591,13 +1612,21 @@ "@typescript-eslint/types" "5.48.1" "@typescript-eslint/visitor-keys" "5.48.1" -"@typescript-eslint/type-utils@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz#5d94ac0c269a81a91ad77c03407cea2caf481412" - integrity sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ== +"@typescript-eslint/scope-manager@5.59.9": + version "5.59.9" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz#eadce1f2733389cdb58c49770192c0f95470d2f4" + integrity sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ== dependencies: - "@typescript-eslint/typescript-estree" "5.48.1" - "@typescript-eslint/utils" "5.48.1" + "@typescript-eslint/types" "5.59.9" + "@typescript-eslint/visitor-keys" "5.59.9" + +"@typescript-eslint/type-utils@5.59.9": + version "5.59.9" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz#53bfaae2e901e6ac637ab0536d1754dfef4dafc2" + integrity sha512-ksEsT0/mEHg9e3qZu98AlSrONAQtrSTljL3ow9CGej8eRo7pe+yaC/mvTjptp23Xo/xIf2mLZKC6KPv4Sji26Q== + dependencies: + "@typescript-eslint/typescript-estree" "5.59.9" + "@typescript-eslint/utils" "5.59.9" debug "^4.3.4" tsutils "^3.21.0" @@ -1606,6 +1635,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.1.tgz#efd1913a9aaf67caf8a6e6779fd53e14e8587e14" integrity sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg== +"@typescript-eslint/types@5.59.9": + version "5.59.9" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.9.tgz#3b4e7ae63718ce1b966e0ae620adc4099a6dcc52" + integrity sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw== + "@typescript-eslint/typescript-estree@5.48.1": version "5.48.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz#9efa8ee2aa471c6ab62e649f6e64d8d121bc2056" @@ -1619,7 +1653,34 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.48.1", "@typescript-eslint/utils@^5.10.0": +"@typescript-eslint/typescript-estree@5.59.9": + version "5.59.9" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz#6bfea844e468427b5e72034d33c9fffc9557392b" + integrity sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA== + dependencies: + "@typescript-eslint/types" "5.59.9" + "@typescript-eslint/visitor-keys" "5.59.9" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.59.9": + version "5.59.9" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.9.tgz#adee890107b5ffe02cd46fdaa6c2125fb3c6c7c4" + integrity sha512-1PuMYsju/38I5Ggblaeb98TOoUvjhRvLpLa1DoTOFaLWqaXl/1iQ1eGurTXgBY58NUdtfTXKP5xBq7q9NDaLKg== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.59.9" + "@typescript-eslint/types" "5.59.9" + "@typescript-eslint/typescript-estree" "5.59.9" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/utils@^5.10.0": version "5.48.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.48.1.tgz#20f2f4e88e9e2a0961cbebcb47a1f0f7da7ba7f9" integrity sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA== @@ -1641,6 +1702,14 @@ "@typescript-eslint/types" "5.48.1" eslint-visitor-keys "^3.3.0" +"@typescript-eslint/visitor-keys@5.59.9": + version "5.59.9" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz#9f86ef8e95aca30fb5a705bb7430f95fc58b146d" + integrity sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q== + dependencies: + "@typescript-eslint/types" "5.59.9" + eslint-visitor-keys "^3.3.0" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -2438,7 +2507,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -2825,10 +2894,10 @@ eslint-plugin-jest@^26.5.3: dependencies: "@typescript-eslint/utils" "^5.10.0" -eslint-plugin-prettier@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz#8b99d1e4b8b24a762472b4567992023619cb98e0" - integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ== +eslint-plugin-prettier@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== dependencies: prettier-linter-helpers "^1.0.0" @@ -2849,6 +2918,21 @@ eslint-plugin-sonarjs@^0.13.0: resolved "https://registry.yarnpkg.com/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.13.0.tgz#c34d140cc90abaaed38f5a5201a2ccdebe398862" integrity sha512-t3m7ta0EspzDxSOZh3cEOJIJVZgN/TlJYaBGnQlK6W/PZNbWep8q4RQskkJkA7/zwNpX0BaoEOSUUrqaADVoqA== +eslint-plugin-svelte@^2.30.0: + version "2.30.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-svelte/-/eslint-plugin-svelte-2.30.0.tgz#548f77441c5418cc0063d36cf139970d6a2cefad" + integrity sha512-2/qj0BJsfM0U2j4EjGb7iC/0nbUvXx1Gn78CdtyuXpi/rSomLPCPwnsZsloXMzlt6Xwe8LBlpRvZObSKEHLP5A== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + debug "^4.3.1" + esutils "^2.0.3" + known-css-properties "^0.27.0" + postcss "^8.4.5" + postcss-load-config "^3.1.4" + postcss-safe-parser "^6.0.0" + svelte-eslint-parser "^0.30.0" + eslint-plugin-unused-imports@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-2.0.0.tgz#d8db8c4d0cfa0637a8b51ce3fd7d1b6bc3f08520" @@ -2869,6 +2953,14 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" +eslint-scope@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" + integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-scope@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" @@ -2889,6 +2981,11 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== +eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" + integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== + eslint-visitor-keys@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" @@ -2939,6 +3036,15 @@ eslint@^8.14.0: strip-json-comments "^3.1.0" text-table "^0.2.0" +espree@^9.0.0: + version "9.5.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.2.tgz#e994e7dc33a082a7a82dceaf12883a829353215b" + integrity sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw== + dependencies: + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + espree@^9.4.0: version "9.4.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd" @@ -2977,7 +3083,7 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -esutils@^2.0.2: +esutils@^2.0.2, esutils@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== @@ -3085,9 +3191,9 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-diff@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" - integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== fast-glob@^3.2.9: version "3.2.11" @@ -4425,6 +4531,11 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +known-css-properties@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.27.0.tgz#82a9358dda5fe7f7bd12b5e7142c0a205393c0c5" + integrity sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg== + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -4443,6 +4554,11 @@ lilconfig@2.0.4: resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== +lilconfig@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -4782,6 +4898,11 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + natural-compare-lite@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" @@ -5157,6 +5278,28 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +postcss-load-config@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" + integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== + dependencies: + lilconfig "^2.0.5" + yaml "^1.10.2" + +postcss-safe-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1" + integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ== + +postcss@^8.4.5: + version "8.4.24" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df" + integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -5181,15 +5324,20 @@ prettier-plugin-compactify@^0.1.6: lodash.merge "^4.6.2" prettier "^2.3.2" +prettier-plugin-svelte@^2.8.1: + version "2.10.1" + resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.10.1.tgz#e1abbe5689e8a926c60b8bc42e61233556ca90d1" + integrity sha512-Wlq7Z5v2ueCubWo0TZzKc9XHcm7TDxqcuzRuGd0gcENfzfT4JZ9yDlCbEgxWgiPmLHkBjfOtpAWkcT28MCDpUQ== + prettier@^2.3.2: version "2.8.3" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.3.tgz#ab697b1d3dd46fb4626fbe2f543afe0cc98d8632" integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== -prettier@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" - integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== +prettier@^2.8.0: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== pretty-format@^29.0.0, pretty-format@^29.4.2: version "29.4.2" @@ -5690,6 +5838,11 @@ socks@^2.6.2: ip "^2.0.0" smart-buffer "^4.2.0" +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -5942,6 +6095,20 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +svelte-eslint-parser@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/svelte-eslint-parser/-/svelte-eslint-parser-0.30.0.tgz#ba9f482c369a7ac0501127a8e801cdcd0758a3d3" + integrity sha512-H0Cn2TKr70DU9p/Gb04CfwtS7eK28MYumrHYPaDNkIFbfwGDLADpbERBn7u8G1Rcm2RMr2/mL6mq0J2e8iKFlA== + dependencies: + eslint-scope "^7.0.0" + eslint-visitor-keys "^3.0.0" + espree "^9.0.0" + +svelte@^3.54.0: + version "3.59.1" + resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.59.1.tgz#3de3d56b9165748f32f3131589b8d183cabe7449" + integrity sha512-pKj8fEBmqf6mq3/NfrB9SLtcJcUvjYSWyePlfCqN9gujLB25RitWK8PvFzlwim6hD/We35KbPlRteuA6rnPGcQ== + tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: version "6.1.11" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" From fcf771c2b154cec908bc1b095841fe64580461d7 Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Fri, 16 Jun 2023 17:39:03 +0200 Subject: [PATCH 11/94] Change teleportAssets to limitedTeleportAssets (#304) --- src/dripper/polkadot/PolkadotActions.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/dripper/polkadot/PolkadotActions.ts b/src/dripper/polkadot/PolkadotActions.ts index e3c57e5d..bf8339a8 100644 --- a/src/dripper/polkadot/PolkadotActions.ts +++ b/src/dripper/polkadot/PolkadotActions.ts @@ -143,9 +143,17 @@ export class PolkadotActions { }), ); + const weightLimit = polkadotApi.createType("XcmV3WeightLimit", { Unlimited: null }); + const feeAssetItem = 0; - const transfer = polkadotApi.tx.xcmPallet.teleportAssets(dest, beneficiary, assets, feeAssetItem); + const transfer = polkadotApi.tx.xcmPallet.limitedTeleportAssets( + dest, + beneficiary, + assets, + feeAssetItem, + weightLimit, + ); if (!this.account) throw new Error("account not ready"); const hash = await transfer.signAndSend(this.account, { nonce: -1 }); From 2938a1ab52204a8118754db390125d44085ec49c Mon Sep 17 00:00:00 2001 From: Przemek Rzad Date: Mon, 19 Jun 2023 14:03:23 +0200 Subject: [PATCH 12/94] Remove unnecessary code construct (#306) --- src/dripper/polkadot/PolkadotActions.ts | 58 +++++++++++-------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/src/dripper/polkadot/PolkadotActions.ts b/src/dripper/polkadot/PolkadotActions.ts index bf8339a8..1bf4d049 100644 --- a/src/dripper/polkadot/PolkadotActions.ts +++ b/src/dripper/polkadot/PolkadotActions.ts @@ -101,47 +101,39 @@ export class PolkadotActions { async teleportTokens(dripAmount: bigint, address: string, parachain_id: string): Promise { logger.info("💸 teleporting tokens"); - const dest = await Promise.resolve( - polkadotApi.createType("XcmVersionedMultiLocation", { - V3: polkadotApi.createType("MultiLocationV2", { - interior: polkadotApi.createType("JunctionsV2", { - X1: polkadotApi.createType("JunctionV2", { - Parachain: polkadotApi.createType("Compact", parachain_id), - }), - }), - parents: 0, + const dest = polkadotApi.createType("XcmVersionedMultiLocation", { + V3: polkadotApi.createType("MultiLocationV2", { + interior: polkadotApi.createType("JunctionsV2", { + X1: polkadotApi.createType("JunctionV2", { Parachain: polkadotApi.createType("Compact", parachain_id) }), }), + parents: 0, }), - ); + }); - const beneficiary = await Promise.resolve( - polkadotApi.createType("XcmVersionedMultiLocation", { - V3: polkadotApi.createType("MultiLocationV2", { - interior: polkadotApi.createType("JunctionsV2", { - X1: polkadotApi.createType("JunctionV2", { - AccountId32: { id: address, network: polkadotApi.createType("NetworkId", "Any") }, - }), + const beneficiary = polkadotApi.createType("XcmVersionedMultiLocation", { + V3: polkadotApi.createType("MultiLocationV2", { + interior: polkadotApi.createType("JunctionsV2", { + X1: polkadotApi.createType("JunctionV2", { + AccountId32: { id: address, network: polkadotApi.createType("NetworkId", "Any") }, }), - parents: 0, }), + parents: 0, }), - ); - - const assets = await Promise.resolve( - polkadotApi.createType("XcmVersionedMultiAssets", { - V3: [ - polkadotApi.createType("XcmV2MultiAsset", { - fun: polkadotApi.createType("FungibilityV2", { Fungible: dripAmount }), - id: polkadotApi.createType("XcmAssetId", { - Concrete: polkadotApi.createType("MultiLocationV2", { - interior: polkadotApi.createType("JunctionsV2", "Here"), - parents: 0, - }), + }); + + const assets = polkadotApi.createType("XcmVersionedMultiAssets", { + V3: [ + polkadotApi.createType("XcmV2MultiAsset", { + fun: polkadotApi.createType("FungibilityV2", { Fungible: dripAmount }), + id: polkadotApi.createType("XcmAssetId", { + Concrete: polkadotApi.createType("MultiLocationV2", { + interior: polkadotApi.createType("JunctionsV2", "Here"), + parents: 0, }), }), - ], - }), - ); + }), + ], + }); const weightLimit = polkadotApi.createType("XcmV3WeightLimit", { Unlimited: null }); From 31510be4e26a4c652ef7bddbada3da19cf2abbe0 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Fri, 16 Jun 2023 16:31:52 +0200 Subject: [PATCH 13/94] Configs and environment rework 1. Merging configs for BOT and SERVER together. As the faucet is now only one application, the division brings more trouble than it solves 2. Network configuration data now lives in src/networkData.ts. This allows us to add new configuration options without updating whole deployment pipeline in three places. 3. Dropped explicit `validateConfig` invocation. Now config is validated when module is imported during startup. 4. As I traced "dripAmount" configuration down to its usages, I've discovered double conversions like "bigint->float->bigint", which cancels whole idea of using bigint in order to escape rounding errors. Reworked it to operate only on bigint, and use conversion only for output. 5. For some reason, e2e tests didn't want to work on HTTP endpoints anymore (probably, local environment issues). Changed it to WS as it should've been done a long time ago. IMPORTANT: this PR will have its counterparts in helm-charts and cloud-infra. Do not merge unless changes there are merged first Fixes #298 --- .env.example | 40 ++++------ e2e/bootstrap.sh | 42 ++++------ e2e/docker-compose.deployment.yml | 2 +- env.faucet.config.json | 71 +++-------------- jest.config.js | 9 ++- src/bot/index.ts | 50 +++++++----- src/config.ts | 53 ++++--------- src/dripper/DripRequestHandler.spec.ts | 5 +- src/dripper/Recaptcha.ts | 6 +- src/dripper/polkadot/PolkadotActions.ts | 33 +++----- src/dripper/polkadot/polkadotApi.ts | 12 +-- src/dripper/polkadot/utils.ts | 12 ++- src/faucet.e2e.ts | 11 ++- src/networkData.ts | 101 ++++++++++++++++++++++++ src/server/index.ts | 3 +- src/server/routes/actions.spec.ts | 48 ++++++----- src/server/routes/actions.ts | 9 ++- src/server/routes/healthcheck.ts | 2 +- src/server/routes/metrics.ts | 6 +- src/test/globalSetup.unit.ts | 3 + src/types.ts | 2 +- 21 files changed, 281 insertions(+), 239 deletions(-) create mode 100644 src/networkData.ts create mode 100644 src/test/globalSetup.unit.ts diff --git a/.env.example b/.env.example index 47589de8..d571df72 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,10 @@ -# BOT -SMF_BOT_BACKEND_URL="http://localhost:5555" -SMF_BOT_DRIP_AMOUNT=10 +SMF_CONFIG_NETWORK="rococo" + +SMF_CONFIG_PORT=5555 +SMF_CONFIG_DEPLOYED_REF=local +SMF_CONFIG_DEPLOYED_TIME=local + +SMF_CONFIG_BACKEND_URL="http://localhost:5555" # Steps to retrieve this token: # 1. Create a bot account (see instructions below for SMF_BOT_MATRIX_BOT_USER_ID) @@ -8,33 +12,21 @@ SMF_BOT_DRIP_AMOUNT=10 # 3. Click the "Help & About" tab (left side of the dialog). # 4. Scroll to the bottom and click on part of Access Token. # Note: Keep the browser login active as the token will be invalidated once you logout -SMF_BOT_MATRIX_ACCESS_TOKEN="NotValidExampleToken" +SMF_CONFIG_MATRIX_ACCESS_TOKEN="NotValidExampleToken" # See https://github.com/paritytech/devops/wiki/Matrix%3A-Registering-a-Matrix-Bot for how to register a bot # Note: The bot account needs to be created in SMF_BOT_MATRIX_SERVER -SMF_BOT_MATRIX_BOT_USER_ID="@NotValidExampleAccount:matrix.parity.io" -SMF_BOT_NETWORK_DECIMALS=12 -SMF_BOT_NETWORK_UNIT="UNIT" -SMF_BOT_FAUCET_IGNORE_LIST="" -SMF_BOT_MATRIX_SERVER=https://matrix.parity.io -SMF_BOT_DEPLOYED_REF=local -SMF_BOT_DEPLOYED_TIME=local - -# BACKEND +SMF_CONFIG_MATRIX_BOT_USER_ID="@NotValidExampleAccount:matrix.parity.io" +SMF_CONFIG_FAUCET_IGNORE_LIST="" +SMF_CONFIG_MATRIX_SERVER=https://m.parity.io # To get account mnemonic - create an account in https://polkadot.js.org/apps/#/accounts (save details in password manager) # Prior creating, make sure to switch to according network (in case of westend - TEST WESTEND & PARACHAINS => Westend) # To feed new account with test tokens, go to https://matrix.to/#/#westend_faucet:matrix.org and drip to new address # some convenient for tests amount "!drip
" -SMF_BACKEND_FAUCET_ACCOUNT_MNEMONIC="this is a fake mnemonic" -SMF_BACKEND_FAUCET_BALANCE_CAP=100 -SMF_BACKEND_INJECTED_TYPES="{ "Address": "AccountId", "LookupSource": "AccountId" }" -SMF_BACKEND_NETWORK_DECIMALS=12 -SMF_BACKEND_PORT=5555 -SMF_BACKEND_RPC_ENDPOINT="wss://westend-rpc.polkadot.io/" -SMF_BACKEND_DEPLOYED_REF=local -SMF_BACKEND_DEPLOYED_TIME=local -SMF_BACKEND_DRIP_AMOUNT="0.5" -SMF_BACKEND_EXTERNAL_ACCESS=false +SMF_CONFIG_FAUCET_ACCOUNT_MNEMONIC="this is a fake mnemonic" +SMF_CONFIG_PORT=5555 + +SMF_CONFIG_EXTERNAL_ACCESS=false # Only used with external access -SMF_BACKEND_RECAPTCHA_SECRET="6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" # Public testing secret, will accept all tokens. +SMF_CONFIG_RECAPTCHA_SECRET="6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" # Public testing secret, will accept all tokens. diff --git a/e2e/bootstrap.sh b/e2e/bootstrap.sh index 8200b40f..e99954d1 100755 --- a/e2e/bootstrap.sh +++ b/e2e/bootstrap.sh @@ -48,30 +48,22 @@ curl -s -X POST -d '{}' "$MATRIX_URL/_matrix/client/v3/rooms/$ROOM_ID/join?acces # Prepare the .env that will be used to run the faucet bot and server. cat << EOF > ./.env -# BOT -SMF_BOT_BACKEND_URL="http://127.0.0.1:5555" -SMF_BOT_DRIP_AMOUNT=10 - -SMF_BOT_MATRIX_ACCESS_TOKEN="$BOT_ACCESS_TOKEN" -SMF_BOT_MATRIX_BOT_USER_ID="@bot:parity.io" -SMF_BOT_NETWORK_DECIMALS=12 -SMF_BOT_NETWORK_UNIT="UNIT" -SMF_BOT_FAUCET_IGNORE_LIST="" -SMF_BOT_MATRIX_SERVER="http://matrix:8008" -SMF_BOT_DEPLOYED_REF=local -SMF_BOT_DEPLOYED_TIME=local - -# BACKEND - -SMF_BACKEND_FAUCET_ACCOUNT_MNEMONIC="//Alice" -SMF_BACKEND_FAUCET_BALANCE_CAP=100 -SMF_BACKEND_INJECTED_TYPES="{ \"Address\": \"AccountId\", \"LookupSource\": \"AccountId\" }" -SMF_BACKEND_NETWORK_DECIMALS=12 -SMF_BACKEND_PORT=5555 +SMF_CONFIG_NETWORK=e2e +SMF_CONFIG_BACKEND_URL="http://127.0.0.1:5555" + +SMF_CONFIG_MATRIX_ACCESS_TOKEN="$BOT_ACCESS_TOKEN" +SMF_CONFIG_MATRIX_BOT_USER_ID="@bot:parity.io" +SMF_CONFIG_FAUCET_IGNORE_LIST="" +SMF_CONFIG_MATRIX_SERVER="http://matrix:8008" +SMF_CONFIG_DEPLOYED_REF=local +SMF_CONFIG_DEPLOYED_TIME=local + +SMF_CONFIG_FAUCET_ACCOUNT_MNEMONIC="//Alice" +SMF_CONFIG_PORT=5555 + # Local Zombienet relaychain node. -SMF_BACKEND_RPC_ENDPOINT="ws://host.docker.internal:9933/" -SMF_BACKEND_DEPLOYED_REF=local -SMF_BACKEND_DEPLOYED_TIME=local -SMF_BACKEND_EXTERNAL_ACCESS=true -SMF_BACKEND_RECAPTCHA_SECRET="6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" # Public testing secret, will accept all tokens. +SMF_CONFIG_DEPLOYED_REF=local +SMF_CONFIG_DEPLOYED_TIME=local +SMF_CONFIG_EXTERNAL_ACCESS=true +SMF_CONFIG_RECAPTCHA_SECRET="6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" # Public testing secret, will accept all tokens. EOF diff --git a/e2e/docker-compose.deployment.yml b/e2e/docker-compose.deployment.yml index 8f29f963..81d8186a 100644 --- a/e2e/docker-compose.deployment.yml +++ b/e2e/docker-compose.deployment.yml @@ -7,7 +7,7 @@ services: dockerfile: Dockerfile context: ../ ports: - - "${SMF_BACKEND_PORT}:${SMF_BACKEND_PORT}" + - "${SMF_CONFIG_PORT}:${SMF_CONFIG_PORT}" env_file: - ./.env networks: diff --git a/env.faucet.config.json b/env.faucet.config.json index f1a7e16a..a9940025 100644 --- a/env.faucet.config.json +++ b/env.faucet.config.json @@ -1,37 +1,17 @@ { "SMF": { - "BACKEND": { + "CONFIG": { + "NETWORK": { + "description": "name of the network: westend, rococo, etc.", + "mandatory": true, + "type": "string" + }, "FAUCET_ACCOUNT_MNEMONIC": { "description": "mnemonic seed from faucet account (create via polkadot.js.org)", "mandatory": true, "masked": true, "type": "string" }, - "FAUCET_BALANCE_CAP": { - "description": "Upper limit cap on whether or not the account can receive more tokens. Defaults to 100.", - "default": 100, - "type": "number" - }, - "INJECTED_TYPES": { - "description": "if any type must be overridden", - "default": {}, - "type": "string" - }, - "NETWORK_DECIMALS": { - "description": "decimal amount for the network", - "default": 12, - "type": "number" - }, - "PORT": { - "description": "the port you want the server to listen on (should be part of the bot's `SMF_BOT_BACKEND_URL`.)", - "default": 5555, - "type": "number" - }, - "RPC_ENDPOINT": { - "description": "ws rpc node endpoint", - "default": "https://westend-rpc.polkadot.io/", - "type": "string" - }, "DEPLOYED_REF": { "description": "git ref which of deployed app", "default": "unset", @@ -42,34 +22,27 @@ "default": "unset", "type": "string" }, + "PORT": { + "description": "the port you want the server to listen on (should be part of the bot's `SMF_CONFIG_BACKEND_URL`.)", + "default": 5555, + "type": "number" + }, "EXTERNAL_ACCESS": { "description": "Whether the backend should serve external drip requests", "default": "false", "type": "boolean" }, - "DRIP_AMOUNT": { - "description": "default amount of token to send", - "default": "0.5", - "type": "string" - }, "RECAPTCHA_SECRET": { "description": "A secret recaptcha token used to validate external requests", "default": "", "masked": true, "type": "string" - } - }, - "BOT": { + }, "BACKEND_URL": { "description": "full url for the bot to reach the backend", "default": "http://localhost:5555", "type": "string" }, - "DRIP_AMOUNT": { - "description": "default amount of token to send", - "default": 0.5, - "type": "number" - }, "MATRIX_ACCESS_TOKEN": { "description": "your bot access token here is how to find it https://t2bot.io/docs/access_tokens/.", "required": true, @@ -87,30 +60,10 @@ "default": "https://matrix.org", "type": "string" }, - "NETWORK_DECIMALS": { - "description": "decimal amount for the network", - "default": 12, - "type": "number" - }, - "NETWORK_UNIT": { - "description": "token unit for the network", - "default": "UNIT", - "type": "string" - }, "FAUCET_IGNORE_LIST": { "description": "A list of Matrix accounts that will be silently (but logged) ignored, comma separated. Example: \"@alice:matrix.org,@bob:domain.com\"", "default": "", "type": "string" - }, - "DEPLOYED_REF": { - "description": "git ref which of deployed bot", - "default": "unset", - "type": "string" - }, - "DEPLOYED_TIME": { - "description": "time when we deployed bot", - "default": "unset", - "type": "string" } } } diff --git a/jest.config.js b/jest.config.js index f1729636..4d2a4bbd 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,8 @@ -/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +/** @type {import("ts-jest/dist/types").InitialOptionsTsJest} */ module.exports = { - moduleNameMapper: { '^src/(.*)': `${__dirname}/src/$1` }, - preset: 'ts-jest', - testEnvironment: 'node', + moduleNameMapper: { "^src/(.*)": `${__dirname}/src/$1` }, + preset: "ts-jest", + testEnvironment: "node", roots: ["./src"], + globalSetup: "./src/test/globalSetup.unit.ts", }; diff --git a/src/bot/index.ts b/src/bot/index.ts index f4930fef..78177988 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -4,11 +4,13 @@ import dotenv from "dotenv"; import * as mSDK from "matrix-js-sdk"; import request from "request"; -import { botConfig as config, validateConfig } from "../config"; +import { config } from "../config"; import { getDripRequestHandlerInstance } from "../dripper/DripRequestHandler"; import polkadotActions from "../dripper/polkadot/PolkadotActions"; +import { convertAmountToBn, convertBnAmountToNumber } from "../dripper/polkadot/utils"; import { isDripSuccessResponse } from "../guards"; import { logger } from "../logger"; +import { getNetworkData } from "../networkData"; import { APIVersionResponse } from "../server/routes/healthcheck"; import type { BalanceResponse } from "../types"; import { isAccountPrivileged } from "../utils"; @@ -20,9 +22,10 @@ const dripRequestHandler = getDripRequestHandlerInstance(polkadotActions); const botUserId = config.Get("MATRIX_BOT_USER_ID"); const accessToken = config.Get("MATRIX_ACCESS_TOKEN"); const baseURL = config.Get("BACKEND_URL"); -const decimals = config.Get("NETWORK_DECIMALS"); -const networkUnit = config.Get("NETWORK_UNIT"); -const defaultDripAmount = config.Get("DRIP_AMOUNT"); + +const networkName = config.Get("NETWORK"); +const networkData = getNetworkData(networkName); + const ignoreList = config .Get("FAUCET_IGNORE_LIST") .split(",") @@ -59,7 +62,9 @@ const printHelpMessage = (roomId: string, message = "") => roomId, `${message ? `${message} - ` : ""}The following commands are supported: !balance - Get the faucet's balance. -!drip
[:ParachainId] - Send ${networkUnit}s to
, if the optional suffix \`:SomeParachainId\` is given a teleport will be issued. +!drip
[:ParachainId] - Send ${ + networkData.currency + }s to
, if the optional suffix \`:SomeParachainId\` is given a teleport will be issued. !help - Print this message`, ); @@ -97,7 +102,7 @@ bot.on("Room.timeline", (event: mSDK.MatrixEvent) => { logger.debug(`Processing request from ${sender}`); - let dripAmount = defaultDripAmount; + let dripAmount: bigint = convertAmountToBn(networkData.dripAmount); const [action, arg0, arg1] = body.split(" "); if (action === "!version") { @@ -122,7 +127,10 @@ bot.on("Room.timeline", (event: mSDK.MatrixEvent) => { .then((res) => { const balance = Number(res.data.balance); - sendMessage(roomId, `The faucet has ${balance / 10 ** decimals} ${networkUnit}s remaining.`); + sendMessage( + roomId, + `The faucet has ${balance / 10 ** networkData.decimals} ${networkData.currency}s remaining.`, + ); }) .catch((e) => { sendMessage(roomId, "An error occurred, please check the server logs."); @@ -148,33 +156,40 @@ bot.on("Room.timeline", (event: mSDK.MatrixEvent) => { // Parity users can override the drip amount by using a 3rd argument if (arg1 && isAccountPrivileged(sender)) { - dripAmount = Number(arg1); + dripAmount = convertAmountToBn(arg1); // not sending these messages to matrix room, since this feature only for internal users // who have access to loki logs if (Number.isNaN(dripAmount)) { logger.error( - `⭕ Failed to convert drip amount: "${arg1}" to number, defaulting to ${defaultDripAmount} ${networkUnit}s`, + `⭕ Failed to convert drip amount: "${arg1}" to number, defaulting to ${networkData.dripAmount} ${networkData.currency}s`, ); - dripAmount = defaultDripAmount; + dripAmount = convertAmountToBn(networkData.dripAmount); } if (dripAmount <= 0) { logger.error( - `⭕ Drip amount can't be less than 0, got ${dripAmount}, defaulting to ${defaultDripAmount} ${networkUnit}s`, + `⭕ Drip amount can't be less than 0, got ${dripAmount}, defaulting to ${networkData.dripAmount} ${networkData.currency}s`, ); - dripAmount = defaultDripAmount; + dripAmount = convertAmountToBn(networkData.dripAmount); } } dripRequestHandler - .handleRequest({ external: false, address, parachain_id, amount: dripAmount.toString(), sender }) + .handleRequest({ external: false, address, parachain_id, amount: dripAmount, sender }) .then((res) => { // if hash is null or empty, something went wrong - const message = isDripSuccessResponse(res) - ? `Sent ${sender} ${dripAmount} ${networkUnit}s. Extrinsic hash: ${res.hash}` - : res.error || "An unexpected error occurred, please check the server logs"; - sendMessage(roomId, message); + if (!isDripSuccessResponse(res)) { + sendMessage(roomId, res.error || "An unexpected error occurred, please check the server logs"); + return; + } + + sendMessage( + roomId, + `Sent ${sender} ${convertBnAmountToNumber(dripAmount)} ${networkData.currency}s. Extrinsic hash: [${ + res.hash + }]()`, + ); }) .catch((e) => { sendMessage(roomId, "An unexpected error occurred, please check the server logs"); @@ -188,6 +203,5 @@ bot.on("Room.timeline", (event: mSDK.MatrixEvent) => { }); export const startBot = () => { - validateConfig("BOT"); bot.startClient({ initialSyncLimit: 0 }).catch((e) => logger.error(e)); }; diff --git a/src/config.ts b/src/config.ts index 53efef98..4b8a5623 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,9 +1,9 @@ -import { ConfigManager, ModuleDictionnary } from "confmgr/lib"; +import { ConfigManager, ConfigObject } from "confmgr/lib"; import faucetConfigSpec from "../env.faucet.config.json"; import { logger } from "./logger"; -type SpecType = T extends { type: "string" } +export type SpecType = T extends { type: "string" } ? string : T extends { type: "number" } ? number @@ -11,53 +11,28 @@ type SpecType = T extends { type: "string" } ? boolean : never; -function faucetBotConfig() { - const config = faucetConfig(); - type BotConfigSpec = (typeof faucetConfigSpec)["SMF"]["BOT"]; - return { Get: (key: K): SpecType => config.Get("BOT", key) }; -} - -function faucetServerConfig() { - const config = faucetConfig(); - type ServerConfigSpec = (typeof faucetConfigSpec)["SMF"]["BACKEND"]; - return { - Get: (key: K): SpecType => config.Get("BACKEND", key), - }; -} +function resolveConfig(): ConfigObject { + const specs = ConfigManager.loadSpecsFromYaml(`env.faucet.config.json`); -export function validateConfig(appName: keyof (typeof faucetConfigSpec)["SMF"]) { + const configInstance = ConfigManager.getInstance(specs).getConfig(); if (process.env.NODE_ENV == "test") { - return; + return configInstance; } - const specs = ConfigManager.loadSpecsFromYaml(`env.faucet.config.json`); - // Delete all keys but the app in question that is being validated. - for (const key of Object.keys(specs.config)) { - if (key !== appName) { - // In this case we only delete properties, no injection is possible - // eslint-disable-next-line security/detect-object-injection - delete (specs.config as ModuleDictionnary)[key]; - } - } - - ConfigManager.clearInstance(); - const configInstance = ConfigManager.getInstance(specs).getConfig(); configInstance.Print({ compact: true }); if (!configInstance.Validate()) { - console.error(`⭕ - Invalid environment configuration for "${appName}" app`); + throw new Error(`⭕ - Invalid environment configuration`); } else { - logger.info(`✅ ${appName} config validated`); + logger.info(`✅ Config validated`); } - // Reload the proper singleton instance. - ConfigManager.clearInstance(); - faucetConfig(); + return configInstance; } -function faucetConfig() { - return ConfigManager.getInstance(`env.faucet.config.json`).getConfig(); -} +const configInstance = resolveConfig(); +export type ConfigSpec = (typeof faucetConfigSpec)["SMF"]["CONFIG"]; -export const botConfig = faucetBotConfig(); -export const serverConfig = faucetServerConfig(); +export const config = { + Get: (key: K): SpecType => configInstance.Get("CONFIG", key), +}; diff --git a/src/dripper/DripRequestHandler.spec.ts b/src/dripper/DripRequestHandler.spec.ts index ba4a525c..3e02c39e 100644 --- a/src/dripper/DripRequestHandler.spec.ts +++ b/src/dripper/DripRequestHandler.spec.ts @@ -3,6 +3,7 @@ import fs from "fs"; import DripperStorage from "./DripperStorage"; import { DripRequestHandler } from "./DripRequestHandler"; import type { PolkadotActions } from "./polkadot/PolkadotActions"; +import { convertAmountToBn } from "./polkadot/utils"; import type { Recaptcha } from "./Recaptcha"; const actionsMock: PolkadotActions = { @@ -35,7 +36,7 @@ describe("DripRequestHandler", () => { describe("Without external access", () => { const defaultRequest = { external: false, - amount: "0.5", + amount: convertAmountToBn("0.5"), parachain_id: "1002", address: "123", sender: "someone", @@ -97,7 +98,7 @@ describe("DripRequestHandler", () => { describe("With external access", () => { const defaultRequest = { external: true, - amount: "0.5", + amount: convertAmountToBn("0.5"), parachain_id: "1002", address: "123", recaptcha: "valid", diff --git a/src/dripper/Recaptcha.ts b/src/dripper/Recaptcha.ts index bf8800d1..b4c8b5bb 100644 --- a/src/dripper/Recaptcha.ts +++ b/src/dripper/Recaptcha.ts @@ -2,12 +2,12 @@ import axios from "axios"; import { URLSearchParams } from "url"; import errorCounter from "../common/ErrorCounter"; -import { serverConfig } from "../config"; +import { config } from "../config"; import { logger } from "../logger"; export class Recaptcha { - constructor(private secret: string = serverConfig.Get("RECAPTCHA_SECRET")) { - if (serverConfig.Get("EXTERNAL_ACCESS") && !this.secret) { + constructor(private secret: string = config.Get("RECAPTCHA_SECRET")) { + if (config.Get("EXTERNAL_ACCESS") && !this.secret) { throw new Error(`⭕ Recaptcha is not configured. Check the RECAPTCHA_SECRET variable.`); } } diff --git a/src/dripper/polkadot/PolkadotActions.ts b/src/dripper/polkadot/PolkadotActions.ts index 1bf4d049..8624c458 100644 --- a/src/dripper/polkadot/PolkadotActions.ts +++ b/src/dripper/polkadot/PolkadotActions.ts @@ -4,18 +4,19 @@ import { waitReady } from "@polkadot/wasm-crypto"; import BN from "bn.js"; import errorCounter from "../../common/ErrorCounter"; -import { serverConfig as config } from "../../config"; +import { config } from "../../config"; import { isDripSuccessResponse } from "../../guards"; import { logger } from "../../logger"; +import { getNetworkData } from "../../networkData"; import { DripResponse } from "../../types"; import polkadotApi from "./polkadotApi"; -import { convertAmountToBn } from "./utils"; const mnemonic = config.Get("FAUCET_ACCOUNT_MNEMONIC"); -const decimals = config.Get("NETWORK_DECIMALS"); -const balanceCap = config.Get("FAUCET_BALANCE_CAP"); const balancePollIntervalMs = 60000; // 1 minute +const networkName = config.Get("NETWORK"); +const networkData = getNetworkData(networkName); + const rpcTimeout = (service: string) => { const timeout = 10000; return setTimeout(() => { @@ -27,7 +28,7 @@ const rpcTimeout = (service: string) => { export class PolkadotActions { account: KeyringPair | undefined; - #faucetBalance: number | undefined; + #faucetBalance: bigint | undefined; constructor() { logger.info("🚰 Plip plop - Creating the faucets's account"); @@ -66,20 +67,14 @@ export class PolkadotActions { try { await polkadotApi.isReady; const { data: balances } = await polkadotApi.query.system.account(this.account.address); - const precision = 5; - this.#faucetBalance = - balances.free - .toBn() - .div(new BN(10 ** (decimals - precision))) - .toNumber() / - 10 ** precision; + this.#faucetBalance = balances.free.toBigInt(); } catch (e) { logger.error(e); errorCounter.plusOne("other"); } } - public getFaucetBalance(): number | undefined { + public getFaucetBalance(): bigint | undefined { return this.#faucetBalance; } @@ -90,12 +85,12 @@ export class PolkadotActions { return balanceFree .toBn() - .div(new BN(10 ** decimals)) + .div(new BN(10 ** networkData.decimals)) .toNumber(); } public async isAccountOverBalanceCap(address: string): Promise { - return (await this.getAccountBalance(address)) > balanceCap; + return (await this.getAccountBalance(address)) > networkData.balanceCap; } async teleportTokens(dripAmount: bigint, address: string, parachain_id: string): Promise { @@ -154,7 +149,7 @@ export class PolkadotActions { return result; } - async sendTokens(address: string, parachain_id: string, amount: string): Promise { + async sendTokens(address: string, parachain_id: string, amount: bigint): Promise { let dripTimeout: ReturnType | null = null; let result: DripResponse; const parsedAmount = Number(amount); @@ -167,15 +162,13 @@ export class PolkadotActions { throw new Error(`Can't send "${parsedAmount}", as balance is smaller "${faucetBalance}"`); } - const dripAmount = convertAmountToBn(amount); - // start a counter and log a timeout error if we didn't get an answer in time dripTimeout = rpcTimeout("drip"); if (parachain_id != "") { - result = await this.teleportTokens(dripAmount, address, parachain_id); + result = await this.teleportTokens(amount, address, parachain_id); } else { logger.info("💸 sending tokens"); - const transfer = polkadotApi.tx.balances.transfer(address, dripAmount); + const transfer = polkadotApi.tx.balances.transfer(address, amount); const hash = await transfer.signAndSend(this.account, { nonce: -1 }); result = { hash: hash.toHex() }; } diff --git a/src/dripper/polkadot/polkadotApi.ts b/src/dripper/polkadot/polkadotApi.ts index 60e4451c..dea81cd8 100644 --- a/src/dripper/polkadot/polkadotApi.ts +++ b/src/dripper/polkadot/polkadotApi.ts @@ -2,13 +2,13 @@ import "@polkadot/api-augment"; import { ApiPromise } from "@polkadot/api"; import { WsProvider } from "@polkadot/rpc-provider"; -import { serverConfig } from "../../config"; +import { config } from "../../config"; +import { getNetworkData } from "../../networkData"; -const rpcEndpointUrl = serverConfig.Get("RPC_ENDPOINT"); -const injectedTypes = JSON.parse(serverConfig.Get("INJECTED_TYPES")) as Record; +const networkName = config.Get("NETWORK"); +const networkData = getNetworkData(networkName); -const provider = new WsProvider(rpcEndpointUrl); -const types = injectedTypes; -const polkadotApi = new ApiPromise({ provider, types }); +const provider = new WsProvider(networkData.rpcEndpoint); +const polkadotApi = new ApiPromise({ provider, types: networkData.injectedTypes }); export default polkadotApi; diff --git a/src/dripper/polkadot/utils.ts b/src/dripper/polkadot/utils.ts index 357fc156..30cca3a0 100644 --- a/src/dripper/polkadot/utils.ts +++ b/src/dripper/polkadot/utils.ts @@ -1,11 +1,17 @@ import { BigFloat } from "bigfloat.js"; -import { serverConfig as config } from "../../config"; +import { config } from "../../config"; +import { getNetworkData } from "../../networkData"; -const decimals = config.Get("NETWORK_DECIMALS"); +const networkName = config.Get("NETWORK"); +const networkData = getNetworkData(networkName); export function convertAmountToBn(amount: string): bigint { const parsedAmount = new BigFloat(amount); - return BigInt(parsedAmount.mul(new BigFloat(10).pow(new BigFloat(decimals))).toString()); + return BigInt(parsedAmount.mul(new BigFloat(10).pow(new BigFloat(networkData.decimals))).toString()); +} + +export function convertBnAmountToNumber(amount: bigint): number { + return Number(amount) / 10 ** networkData.decimals; } diff --git a/src/faucet.e2e.ts b/src/faucet.e2e.ts index 49ed1e6d..7b4a4b3a 100644 --- a/src/faucet.e2e.ts +++ b/src/faucet.e2e.ts @@ -1,7 +1,7 @@ import { until } from "@eng-automation/js"; import { ApiPromise } from "@polkadot/api"; import { createTestKeyring } from "@polkadot/keyring"; -import { HttpProvider } from "@polkadot/rpc-provider"; +import { WsProvider } from "@polkadot/rpc-provider"; import { BN } from "@polkadot/util"; import { randomAsU8a } from "@polkadot/util-crypto"; import axios from "axios"; @@ -17,13 +17,13 @@ describe("Faucet E2E", () => { const polkadotApi = new ApiPromise({ // Zombienet relaychain node. - provider: new HttpProvider("http://localhost:9933"), + provider: new WsProvider("ws://127.0.0.1:9933"), types: { Address: "AccountId", LookupSource: "AccountId" }, }); const parachainApi = new ApiPromise({ // Zombienet parachain node. - provider: new HttpProvider("http://localhost:9934"), + provider: new WsProvider("ws://127.0.0.1:9934"), types: { Address: "AccountId", LookupSource: "AccountId" }, }); @@ -64,6 +64,11 @@ describe("Faucet E2E", () => { roomId = room.data.room_id; }); + afterAll(async () => { + await polkadotApi.disconnect(); + await parachainApi.disconnect(); + }); + test("The bot responds to the !balance message", async () => { await postMessage("!balance"); diff --git a/src/networkData.ts b/src/networkData.ts new file mode 100644 index 00000000..1320232e --- /dev/null +++ b/src/networkData.ts @@ -0,0 +1,101 @@ +// TODO: merge this with networkData.ts from client/ + +export interface ChainData { + name: string; + id: number; +} + +export interface NetworkData { + networkName: string; + currency: string; + chains: ChainData[]; + explorer: string | null; + rpcEndpoint: string; + injectedTypes: Record; + decimals: number; + dripAmount: string; + balanceCap: number; +} + +const rococo: NetworkData = { + balanceCap: 100, + chains: [ + { name: "Rococo Relay Chain", id: -1 }, + { name: "Rockmine", id: 1000 }, + { name: "Contracts", id: 1002 }, + { name: "Encointer Lietaer", id: 1003 }, + { name: "Bridgehub", id: 1013 }, + ], + currency: "ROC", + decimals: 12, + dripAmount: "100", + explorer: "https://rococo.subscan.io", + injectedTypes: {}, + networkName: "Rococo", + rpcEndpoint: "wss://rococo-rpc.polkadot.io/", +}; + +const wococo: NetworkData = { + balanceCap: 100, + chains: [], + currency: "UNIT", + decimals: 12, + dripAmount: "10", + explorer: null, + injectedTypes: {}, + networkName: "Wococo", + rpcEndpoint: "wss://wococo-rpc-node-0.parity-testnet.parity.io/", +}; + +const westend: NetworkData = { + balanceCap: 100, + chains: [ + { name: "Westend Relay Chain", id: -1 }, + { name: "Westmint", id: 1000 }, + { name: "Collectives", id: 1001 }, + ], + currency: "WND", + decimals: 12, + dripAmount: "1", + explorer: "https://westend.subscan.io", + injectedTypes: {}, + networkName: "Westend", + rpcEndpoint: "wss://westend-rpc.polkadot.io/", +}; + +const versi: NetworkData = { + balanceCap: 100, + chains: [], + currency: "VRS", + decimals: 12, + dripAmount: "100", + explorer: null, + injectedTypes: {}, + networkName: "Versi", + rpcEndpoint: "wss://versi-rpc-node-0.parity-versi.parity.io/", +}; + +const e2e: NetworkData = { + balanceCap: 100, + chains: [], + currency: "UNIT", + decimals: 12, + dripAmount: "10", + explorer: "", + injectedTypes: {}, + networkName: "Rococo", + rpcEndpoint: "ws://host.docker.internal:9933/", +}; + +export const networks: Record = { rococo, versi, westend, wococo, e2e }; + +export function getNetworkData(networkName: string): NetworkData { + if (!Object.keys(networks).includes(networkName)) { + throw new Error( + `Unknown NETWORK in env: ${networkName}; valid networks are: [${Object.keys(networks).join(", ")}]`, + ); + } + // networkName value is valdated one line before, safe to use as index + // eslint-disable-next-line security/detect-object-injection + return networks[networkName] as NetworkData; +} diff --git a/src/server/index.ts b/src/server/index.ts index fe2b5e67..ba16146a 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -4,14 +4,13 @@ import bodyParser from "body-parser"; import express from "express"; import * as pkg from "../../package.json"; -import { serverConfig as config, validateConfig } from "../config"; +import { config } from "../config"; import { logger } from "../logger"; import router from "./router"; const PORT = config.Get("PORT"); export const startServer = () => { - validateConfig("BACKEND"); const app = express(); app.use(bodyParser.json()); diff --git a/src/server/routes/actions.spec.ts b/src/server/routes/actions.spec.ts index 76d52079..81cdae3d 100644 --- a/src/server/routes/actions.spec.ts +++ b/src/server/routes/actions.spec.ts @@ -29,24 +29,17 @@ jest.mock("../../dripper/DripRequestHandler", () => { }; }); -const mockConfigImplementation = (type: string) => { - switch (type) { - case "RPC_ENDPOINT": - return "http://localhost"; - case "INJECTED_TYPES": - return "{}"; - case "FAUCET_ACCOUNT_MNEMONIC": - // random seed phrase - return "scrub inquiry adapt lounge voice current manage chief build shoot drip liar head season inside"; - case "EXTERNAL_ACCESS": - return true; - default: - return "generic"; - } -}; - +const mockConfigValue: { config: Record } = { config: {} }; jest.mock("../../config", () => { - return { serverConfig: { Get: mockConfig.mockImplementation((type: string) => mockConfigImplementation(type)) } }; + return { + config: { + Get: mockConfig.mockImplementation( + (key: string) => + // eslint-disable-next-line security/detect-object-injection + ({ NETWORK: "rococo" }[key]), // minimal viable config on the initial import + ), + }, + }; }); let app: Express; @@ -55,6 +48,11 @@ const parameterError = (parameter: keyof BotRequestType | keyof FaucetRequestTyp `Missing parameter: '${parameter}'`; describe("/drip/web tests", () => { + beforeAll(() => { + // eslint-disable-next-line security/detect-object-injection + mockConfig.mockImplementation((key: string) => mockConfigValue.config[key]); + }); + beforeEach(() => { jest.clearAllMocks(); app = express(); @@ -62,6 +60,13 @@ describe("/drip/web tests", () => { const routers = express.Router(); routers.use("/", router); app.use(router); + + mockConfigValue.config = { + NETWORK: "rococo", + EXTERNAL_ACCESS: true, + FAUCET_ACCOUNT_MNEMONIC: + "scrub inquiry adapt lounge voice current manage chief build shoot drip liar head season inside", + }; }); test("should fail with no address", async () => { @@ -77,18 +82,11 @@ describe("/drip/web tests", () => { }); test("should fail if external access is not enabled", async () => { - mockConfig.mockImplementation((type: string) => { - if (type === "EXTERNAL_ACCESS") { - return false; - } - return mockConfigImplementation(type); - }); + mockConfigValue.config.EXTERNAL_ACCESS = false; const res = await request(app).post("/drip/web").send({ address: "example" }); expect(res.body.error).toBe("Endpoint unavailable"); expect(res.status).toBe(503); - - mockConfig.mockImplementation((type: string) => mockConfigImplementation(type)); }); test("should request drip", async () => { diff --git a/src/server/routes/actions.ts b/src/server/routes/actions.ts index 46cb27ba..467b5101 100644 --- a/src/server/routes/actions.ts +++ b/src/server/routes/actions.ts @@ -2,10 +2,12 @@ import cors from "cors"; import express, { NextFunction, Request, Response } from "express"; import errorCounter from "../../common/ErrorCounter"; -import { serverConfig as config } from "../../config"; +import { config } from "../../config"; import { getDripRequestHandlerInstance } from "../../dripper/DripRequestHandler"; import polkadotActions from "../../dripper/polkadot/PolkadotActions"; +import { convertAmountToBn } from "../../dripper/polkadot/utils"; import { logger } from "../../logger"; +import { getNetworkData } from "../../networkData"; import { BalanceResponse, BotRequestType, @@ -15,6 +17,9 @@ import { FaucetRequestType, } from "../../types"; +const networkName = config.Get("NETWORK"); +const networkData = getNetworkData(networkName); + const router = express.Router(); router.use(cors()); const dripRequestHandler = getDripRequestHandlerInstance(polkadotActions); @@ -63,7 +68,7 @@ router.post>("/drip/web", external: true, address, parachain_id: parachain_id ?? "", - amount: config.Get("DRIP_AMOUNT"), + amount: convertAmountToBn(networkData.dripAmount), recaptcha, }); diff --git a/src/server/routes/healthcheck.ts b/src/server/routes/healthcheck.ts index ff43767c..853c7943 100644 --- a/src/server/routes/healthcheck.ts +++ b/src/server/routes/healthcheck.ts @@ -1,6 +1,6 @@ import express, { Request, Response } from "express"; -import { serverConfig as config } from "../../config"; +import { config } from "../../config"; import polkadotApi from "../../dripper/polkadot/polkadotApi"; import { logger } from "../../logger"; diff --git a/src/server/routes/metrics.ts b/src/server/routes/metrics.ts index b7e0667a..b9c581b9 100644 --- a/src/server/routes/metrics.ts +++ b/src/server/routes/metrics.ts @@ -4,6 +4,7 @@ import express from "express"; import ErrorCounter from "../../common/ErrorCounter"; import { metricsDefinition } from "../../common/metricsDefinition"; import actions from "../../dripper/polkadot/PolkadotActions"; +import { convertBnAmountToNumber } from "../../dripper/polkadot/utils"; const router = express.Router(); @@ -21,7 +22,10 @@ router.get("/metrics", (_, res) => { "The total amount of timeout errors between the faucet backend and the rpc node", ); - const balance = getMetrics("balance", "gauge", actions.getFaucetBalance(), "Current balance of the faucet", true); + const balanceBigint = actions.getFaucetBalance(); + const balanceMaybeNumber = balanceBigint === undefined ? undefined : convertBnAmountToNumber(balanceBigint); + + const balance = getMetrics("balance", "gauge", balanceMaybeNumber, "Current balance of the faucet", true); const total_requests = getMetrics( "total_requests", diff --git a/src/test/globalSetup.unit.ts b/src/test/globalSetup.unit.ts new file mode 100644 index 00000000..b63d9c43 --- /dev/null +++ b/src/test/globalSetup.unit.ts @@ -0,0 +1,3 @@ +export default function() { + process.env.SMF_CONFIG_NETWORK = "rococo" +} diff --git a/src/types.ts b/src/types.ts index 9ffeeaf7..15e14c9e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -37,7 +37,7 @@ export type DripResponse = DripErrorResponse | DripSuccessResponse; export interface DripRequestType { address: string; - amount: string; + amount: bigint; parachain_id: string; sender?: string; recaptcha?: string; From 2b329079990df8eb928c99a108a52cd0c160518d Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Mon, 19 Jun 2023 13:59:11 +0200 Subject: [PATCH 14/94] Removed unused injectedTypes --- src/dripper/polkadot/polkadotApi.ts | 2 +- src/networkData.ts | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/dripper/polkadot/polkadotApi.ts b/src/dripper/polkadot/polkadotApi.ts index dea81cd8..2a922e57 100644 --- a/src/dripper/polkadot/polkadotApi.ts +++ b/src/dripper/polkadot/polkadotApi.ts @@ -9,6 +9,6 @@ const networkName = config.Get("NETWORK"); const networkData = getNetworkData(networkName); const provider = new WsProvider(networkData.rpcEndpoint); -const polkadotApi = new ApiPromise({ provider, types: networkData.injectedTypes }); +const polkadotApi = new ApiPromise({ provider }); export default polkadotApi; diff --git a/src/networkData.ts b/src/networkData.ts index 1320232e..2775dac0 100644 --- a/src/networkData.ts +++ b/src/networkData.ts @@ -11,7 +11,6 @@ export interface NetworkData { chains: ChainData[]; explorer: string | null; rpcEndpoint: string; - injectedTypes: Record; decimals: number; dripAmount: string; balanceCap: number; @@ -30,7 +29,6 @@ const rococo: NetworkData = { decimals: 12, dripAmount: "100", explorer: "https://rococo.subscan.io", - injectedTypes: {}, networkName: "Rococo", rpcEndpoint: "wss://rococo-rpc.polkadot.io/", }; @@ -42,7 +40,6 @@ const wococo: NetworkData = { decimals: 12, dripAmount: "10", explorer: null, - injectedTypes: {}, networkName: "Wococo", rpcEndpoint: "wss://wococo-rpc-node-0.parity-testnet.parity.io/", }; @@ -58,7 +55,6 @@ const westend: NetworkData = { decimals: 12, dripAmount: "1", explorer: "https://westend.subscan.io", - injectedTypes: {}, networkName: "Westend", rpcEndpoint: "wss://westend-rpc.polkadot.io/", }; @@ -70,7 +66,6 @@ const versi: NetworkData = { decimals: 12, dripAmount: "100", explorer: null, - injectedTypes: {}, networkName: "Versi", rpcEndpoint: "wss://versi-rpc-node-0.parity-versi.parity.io/", }; @@ -82,7 +77,6 @@ const e2e: NetworkData = { decimals: 12, dripAmount: "10", explorer: "", - injectedTypes: {}, networkName: "Rococo", rpcEndpoint: "ws://host.docker.internal:9933/", }; From b655f99de8e1c12820937f12fb62e66fbccac64b Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Mon, 19 Jun 2023 14:34:14 +0200 Subject: [PATCH 15/94] Removed incorrect "required" option In the config file, we used both `required` and `mandatory` properties. `mandatory` is the right one. But instead of fixing it in this case, we're relaxing requirements for `MATRIX_ACCESS_TOKEN` and `MATRIX_BOT_USER_ID` as it's currently consciously unset for rococo web faucet --- env.faucet.config.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/env.faucet.config.json b/env.faucet.config.json index a9940025..833ace2b 100644 --- a/env.faucet.config.json +++ b/env.faucet.config.json @@ -45,13 +45,11 @@ }, "MATRIX_ACCESS_TOKEN": { "description": "your bot access token here is how to find it https://t2bot.io/docs/access_tokens/.", - "required": true, "masked": true, "type": "string" }, "MATRIX_BOT_USER_ID": { "description": "your bot user id", - "required": true, "regexp": "^@.*:.*$", "type": "string" }, From ef23c76fd084c208dea847492b58ae17fc86ea64 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Mon, 19 Jun 2023 15:32:39 +0200 Subject: [PATCH 16/94] Fixed wococo currency name It's shoudln't be "UNIT", it actually has a name: "WOOK" --- src/networkData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networkData.ts b/src/networkData.ts index 2775dac0..c6c32999 100644 --- a/src/networkData.ts +++ b/src/networkData.ts @@ -36,7 +36,7 @@ const rococo: NetworkData = { const wococo: NetworkData = { balanceCap: 100, chains: [], - currency: "UNIT", + currency: "WOOK", decimals: 12, dripAmount: "10", explorer: null, From 5d85ac6e1a27a9bbbcad68abd0fbcad66713b08e Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Tue, 20 Jun 2023 14:37:25 +0200 Subject: [PATCH 17/94] Reintroducing subscan links, for supported networks --- src/bot/index.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/bot/index.ts b/src/bot/index.ts index 78177988..a2fb737c 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -184,12 +184,14 @@ bot.on("Room.timeline", (event: mSDK.MatrixEvent) => { return; } - sendMessage( - roomId, - `Sent ${sender} ${convertBnAmountToNumber(dripAmount)} ${networkData.currency}s. Extrinsic hash: [${ - res.hash - }]()`, - ); + let message = `Sent ${sender} ${convertBnAmountToNumber(dripAmount)} ${networkData.currency}s. `; + if (networkData.explorer !== null) { + message += `Extrinsic hash: [${res.hash}](${networkData.explorer}/${res.hash})`; + } else { + message += `Extrinsic hash: ${res.hash}`; + } + + sendMessage(roomId, message); }) .catch((e) => { sendMessage(roomId, "An unexpected error occurred, please check the server logs"); From 54256d181e4759e065c63616d1dc086e274b0441 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Tue, 20 Jun 2023 15:50:07 +0200 Subject: [PATCH 18/94] Removing http requests between bot and server These parts are in the same process now, so the data can be queried directly --- .env.example | 2 -- README.md | 6 ++--- e2e/bootstrap.sh | 1 - env.faucet.config.json | 7 +----- src/bot/index.ts | 52 ++++++++++++------------------------------ 5 files changed, 19 insertions(+), 49 deletions(-) diff --git a/.env.example b/.env.example index d571df72..2525acaa 100644 --- a/.env.example +++ b/.env.example @@ -4,8 +4,6 @@ SMF_CONFIG_PORT=5555 SMF_CONFIG_DEPLOYED_REF=local SMF_CONFIG_DEPLOYED_TIME=local -SMF_CONFIG_BACKEND_URL="http://localhost:5555" - # Steps to retrieve this token: # 1. Create a bot account (see instructions below for SMF_BOT_MATRIX_BOT_USER_ID) # 2. Log in to the bots matrix account. Click on the name in the top left corner, then "Settings". diff --git a/README.md b/README.md index 6ae062a3..13f5d01d 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,6 @@ An official [substrate-faucet helm chart](https://github.com/paritytech/helm-cha ### Misc: * Bump API: `yarn upgrade @polkadot/util@latest @polkadot/wasm-crypto@latest @polkadot/keyring@latest @polkadot/x-randomvalues@latest @polkadot/api@latest @polkadot/keyring@latest @polkadot/util-crypto@latest` -* Server can be queried for Prometheus metrics via http://$SMF_BOT_BACKEND_URL/metrics -* Readiness check URL via http://$SMF_BOT_BACKEND_URL/ready -* Health check URL via http://$SMF_BOT_BACKEND_URL/health +* Server can be queried for Prometheus metrics via `/metrics` +* Readiness check URL via `ready` +* Health check URL via `/health` diff --git a/e2e/bootstrap.sh b/e2e/bootstrap.sh index e99954d1..718d5a3f 100755 --- a/e2e/bootstrap.sh +++ b/e2e/bootstrap.sh @@ -49,7 +49,6 @@ curl -s -X POST -d '{}' "$MATRIX_URL/_matrix/client/v3/rooms/$ROOM_ID/join?acces # Prepare the .env that will be used to run the faucet bot and server. cat << EOF > ./.env SMF_CONFIG_NETWORK=e2e -SMF_CONFIG_BACKEND_URL="http://127.0.0.1:5555" SMF_CONFIG_MATRIX_ACCESS_TOKEN="$BOT_ACCESS_TOKEN" SMF_CONFIG_MATRIX_BOT_USER_ID="@bot:parity.io" diff --git a/env.faucet.config.json b/env.faucet.config.json index 833ace2b..feea3b40 100644 --- a/env.faucet.config.json +++ b/env.faucet.config.json @@ -23,7 +23,7 @@ "type": "string" }, "PORT": { - "description": "the port you want the server to listen on (should be part of the bot's `SMF_CONFIG_BACKEND_URL`.)", + "description": "the port you want the server to listen on", "default": 5555, "type": "number" }, @@ -38,11 +38,6 @@ "masked": true, "type": "string" }, - "BACKEND_URL": { - "description": "full url for the bot to reach the backend", - "default": "http://localhost:5555", - "type": "string" - }, "MATRIX_ACCESS_TOKEN": { "description": "your bot access token here is how to find it https://t2bot.io/docs/access_tokens/.", "masked": true, diff --git a/src/bot/index.ts b/src/bot/index.ts index a2fb737c..63b634b0 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -1,5 +1,4 @@ import { decodeAddress } from "@polkadot/keyring"; -import axios from "axios"; import dotenv from "dotenv"; import * as mSDK from "matrix-js-sdk"; import request from "request"; @@ -11,8 +10,6 @@ import { convertAmountToBn, convertBnAmountToNumber } from "../dripper/polkadot/ import { isDripSuccessResponse } from "../guards"; import { logger } from "../logger"; import { getNetworkData } from "../networkData"; -import { APIVersionResponse } from "../server/routes/healthcheck"; -import type { BalanceResponse } from "../types"; import { isAccountPrivileged } from "../utils"; dotenv.config(); @@ -21,7 +18,9 @@ const dripRequestHandler = getDripRequestHandlerInstance(polkadotActions); const botUserId = config.Get("MATRIX_BOT_USER_ID"); const accessToken = config.Get("MATRIX_ACCESS_TOKEN"); -const baseURL = config.Get("BACKEND_URL"); + +const deployedRef = config.Get("DEPLOYED_REF"); +const deployedTime = config.Get("DEPLOYED_TIME"); const networkName = config.Get("NETWORK"); const networkData = getNetworkData(networkName); @@ -30,8 +29,6 @@ const ignoreList = config .Get("FAUCET_IGNORE_LIST") .split(",") .map((item) => item.replace('"', "")); -const botDeployedRef = config.Get("DEPLOYED_REF"); -const botDeployedTime = config.Get("DEPLOYED_TIME"); // Show the ignore list at start if any if (ignoreList.length > 0) { @@ -47,8 +44,6 @@ const bot = mSDK.createClient({ userId: botUserId, }); -const ax = axios.create({ baseURL, timeout: 10000 }); - const sendMessage = (roomId: string, msg: string) => { bot .sendEvent(roomId, "m.room.message", { body: msg, msgtype: "m.text" }, "", (err) => { @@ -106,36 +101,19 @@ bot.on("Room.timeline", (event: mSDK.MatrixEvent) => { const [action, arg0, arg1] = body.split(" "); if (action === "!version") { - ax.get("/version") - .then((res) => { - const { data } = res; - const { version, time } = data; - - sendMessage( - roomId, - `Versions: - Bot: ${botDeployedRef}; ${botDeployedTime} - Server: ${version}; ${time}`, - ); - }) - .catch((e) => { - sendMessage(roomId, "An error occurred, please check the server logs."); - logger.error("⭕ An error occurred when checking the balance", e); - }); + sendMessage(roomId, `Current version: ${deployedRef}; ${deployedTime}`); } else if (action === "!balance") { - ax.get("/balance") - .then((res) => { - const balance = Number(res.data.balance); - - sendMessage( - roomId, - `The faucet has ${balance / 10 ** networkData.decimals} ${networkData.currency}s remaining.`, - ); - }) - .catch((e) => { - sendMessage(roomId, "An error occurred, please check the server logs."); - logger.error("⭕ An error occurred when checking the balance", e); - }); + (async () => { + const balance = await polkadotActions.getBalance(); + + sendMessage( + roomId, + `The faucet has ${Number(balance) / 10 ** networkData.decimals} ${networkData.currency}s remaining.`, + ); + })().catch((e) => { + sendMessage(roomId, "An error occurred, please check the server logs."); + logger.error("⭕ An error occurred when checking the balance", e); + }); } else if (action === "!drip") { if (!arg0) { logger.warn("Address not provided, skipping"); From f6f8cf8b7fed04bbce69064fba56aae980573dcc Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Tue, 20 Jun 2023 18:37:50 +0200 Subject: [PATCH 19/94] Subscan url fix --- src/bot/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/index.ts b/src/bot/index.ts index 63b634b0..303fcc18 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -164,7 +164,7 @@ bot.on("Room.timeline", (event: mSDK.MatrixEvent) => { let message = `Sent ${sender} ${convertBnAmountToNumber(dripAmount)} ${networkData.currency}s. `; if (networkData.explorer !== null) { - message += `Extrinsic hash: [${res.hash}](${networkData.explorer}/${res.hash})`; + message += `Extrinsic hash: [${res.hash}](${networkData.explorer}/extrinsic/${res.hash})`; } else { message += `Extrinsic hash: ${res.hash}`; } From 3924b9108738d5b7caf916ba17927b93e1bc252a Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Wed, 21 Jun 2023 11:16:35 +0200 Subject: [PATCH 20/94] Subscan URL fix [2] This time, actually tested locally --- src/bot/index.ts | 46 +++++++++++++++++++++++++++++++++++++--------- src/networkData.ts | 2 +- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/bot/index.ts b/src/bot/index.ts index 303fcc18..8eebba14 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -44,9 +44,19 @@ const bot = mSDK.createClient({ userId: botUserId, }); -const sendMessage = (roomId: string, msg: string) => { +const sendMessage = (roomId: string, msg: string, formattedMsg?: string) => { + const msgObject: { body: string; msgtype: string; format?: string; formatted_body?: string } = { + body: msg, + msgtype: "m.text", + }; + + if (formattedMsg !== undefined) { + msgObject.format = "org.matrix.custom.html"; + msgObject.formatted_body = formattedMsg; + } + bot - .sendEvent(roomId, "m.room.message", { body: msg, msgtype: "m.text" }, "", (err) => { + .sendEvent(roomId, "m.room.message", msgObject, "", (err) => { if (err) logger.error(err); }) .catch((e) => logger.error(e)); @@ -162,14 +172,9 @@ bot.on("Room.timeline", (event: mSDK.MatrixEvent) => { return; } - let message = `Sent ${sender} ${convertBnAmountToNumber(dripAmount)} ${networkData.currency}s. `; - if (networkData.explorer !== null) { - message += `Extrinsic hash: [${res.hash}](${networkData.explorer}/extrinsic/${res.hash})`; - } else { - message += `Extrinsic hash: ${res.hash}`; - } + const message = formattedSuccessfulDripResponse(dripAmount, sender, res.hash); - sendMessage(roomId, message); + sendMessage(roomId, message.plain, message.formatted); }) .catch((e) => { sendMessage(roomId, "An unexpected error occurred, please check the server logs"); @@ -185,3 +190,26 @@ bot.on("Room.timeline", (event: mSDK.MatrixEvent) => { export const startBot = () => { bot.startClient({ initialSyncLimit: 0 }).catch((e) => logger.error(e)); }; + +function formattedSuccessfulDripResponse( + dripAmount: bigint, + sender: string, + extrinsicHash: string, +): { + plain: string; + formatted: string; +} { + const numberDripAmount = convertBnAmountToNumber(dripAmount); + const extrinsicLink = networkData.explorer !== null ? `${networkData.explorer}/extrinsic/${extrinsicHash}` : null; + + const messagePlain = `Sent ${sender} ${numberDripAmount} ${networkData.currency}s. +Extrinsic hash: ${extrinsicHash}`; + let messageHtml = `Sent ${sender} ${numberDripAmount} ${networkData.currency}s.`; + if (extrinsicLink !== null) { + messageHtml += `
Extrinsic hash: ${extrinsicHash}`; + } else { + messageHtml += `
Extrinsic hash: ${extrinsicHash}`; + } + + return { plain: messagePlain, formatted: messageHtml }; +} diff --git a/src/networkData.ts b/src/networkData.ts index c6c32999..5cfc66e3 100644 --- a/src/networkData.ts +++ b/src/networkData.ts @@ -76,7 +76,7 @@ const e2e: NetworkData = { currency: "UNIT", decimals: 12, dripAmount: "10", - explorer: "", + explorer: null, networkName: "Rococo", rpcEndpoint: "ws://host.docker.internal:9933/", }; From 16e8277907cc271646d10fa78b2a97aba7c46848 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Wed, 21 Jun 2023 13:47:54 +0200 Subject: [PATCH 21/94] Upgrading matrix-js-sdk package --- package.json | 5 +-- src/bot/index.ts | 30 ++++++-------- tsconfig.json | 100 +++++++++-------------------------------------- yarn.lock | 78 +++++++++++++++++++++++------------- 4 files changed, 85 insertions(+), 128 deletions(-) diff --git a/package.json b/package.json index ccfadaac..90df1b73 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "dotenv": "^16.0.0", "express": "4.17.3", "log4js": "6.4.5", - "matrix-js-sdk": "^19.7.0", + "matrix-js-sdk": "^26.1.0", "request": "^2.88.2", "sqlite3": "^5.1.2" }, @@ -74,8 +74,7 @@ "@types/body-parser": "^1.19.2", "@types/express": "^4.17.13", "@types/jest": "^29.4.0", - "@types/matrix-js-sdk": "^11.0.1", - "@types/node": "16.10", + "@types/node": "^18.16", "@types/request": "^2.48.8", "@types/sqlite3": "^3.1.8", "@types/supertest": "^2.0.12", diff --git a/src/bot/index.ts b/src/bot/index.ts index 8eebba14..175478b7 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -1,7 +1,6 @@ import { decodeAddress } from "@polkadot/keyring"; import dotenv from "dotenv"; import * as mSDK from "matrix-js-sdk"; -import request from "request"; import { config } from "../config"; import { getDripRequestHandlerInstance } from "../dripper/DripRequestHandler"; @@ -40,26 +39,18 @@ const bot = mSDK.createClient({ accessToken, baseUrl: config.Get("MATRIX_SERVER"), localTimeoutMs: 10000, - request, // workaround for failed syncs - https://github.com/matrix-org/matrix-js-sdk/issues/2415#issuecomment-1255755056 userId: botUserId, }); const sendMessage = (roomId: string, msg: string, formattedMsg?: string) => { - const msgObject: { body: string; msgtype: string; format?: string; formatted_body?: string } = { - body: msg, - msgtype: "m.text", - }; + const msgObject: mSDK.IContent = { body: msg, msgtype: "m.text" }; if (formattedMsg !== undefined) { msgObject.format = "org.matrix.custom.html"; msgObject.formatted_body = formattedMsg; } - bot - .sendEvent(roomId, "m.room.message", msgObject, "", (err) => { - if (err) logger.error(err); - }) - .catch((e) => logger.error(e)); + bot.sendEvent(roomId, null, "m.room.message", msgObject, "").catch((e) => logger.error(e)); }; const printHelpMessage = (roomId: string, message = "") => @@ -73,21 +64,26 @@ const printHelpMessage = (roomId: string, message = "") => !help - Print this message`, ); -bot.on("RoomMember.membership", (_, member: Record) => { - if (member.membership === "invite" && member.userId === botUserId) { +bot.on(mSDK.RoomEvent.MyMembership, (room: mSDK.Room, membership: string) => { + if (membership === "invite") { bot - .joinRoom(member.roomId) + .joinRoom(room.roomId) .then(() => { - logger.info(`Auto-joined ${member.roomId}.`); + logger.info(`Auto-joined ${room.roomId}.`); }) .catch((e) => logger.error("⭕ Auto-join error", e)); } }); -bot.on("Room.timeline", (event: mSDK.MatrixEvent) => { +bot.on(mSDK.RoomEvent.Timeline, (event: mSDK.MatrixEvent) => { const sender = event.getSender(); const roomId = event.getRoomId(); - const { body } = event.getContent(); + const { body } = event.getContent<{ body: string }>(); + + if (roomId === undefined) { + // Should never happen for a "Room.timeline" event + throw new Error("roomId is not defined"); + } // only act on messages if (event.getType() !== "m.room.message") { diff --git a/tsconfig.json b/tsconfig.json index 01b682f7..7acafc29 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,82 +1,20 @@ { - "compilerOptions": { - "paths": { "matrix-js-sdk": ["node_modules/@types/matrix-js-sdk/index.d.ts"] }, - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "es2021", /* Node.js LTS 16. Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - "lib": [ - "es2021", /* Node.js LTS 16. */ - ], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./build", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - "typeRoots": [ - "./node_modules/@types", - "./src/types" - ], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - - /* Advanced Options */ - "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ - "skipLibCheck": true, /* Skip checking .d.ts files for errors */ - "pretty": true, - - "resolveJsonModule": true, - }, - "include": [ - "./src/**/*" - ], - "exclude": [ - "node_modules" - ] - } + "compilerOptions": { + "baseUrl": ".", + "declaration": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "lib": ["es2021"], + "module": "commonjs", + "moduleResolution": "node", + "outDir": "./build", + "pretty": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "es2021", + "typeRoots": ["./node_modules/@types", "./src/types"] + }, + "include": ["src/**/*"], + "exclude": ["node_modules/**/*"] +} diff --git a/yarn.lock b/yarn.lock index ac801259..2a2ba24d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -880,6 +880,11 @@ semver "^7.3.5" tar "^6.1.11" +"@matrix-org/matrix-sdk-crypto-js@^0.1.0-alpha.10": + version "0.1.0-alpha.11" + resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.11.tgz#24d705318c3159ef7dbe43bca464ac2bdd11e45d" + integrity sha512-HD3rskPkqrUUSaKzGLg97k/bN+OZrkcX7ODB/pNBs/jqq+/A0wDKqsszJotzFwsQcDPpWn78BmMyvBo4tLxKjw== + "@noble/hashes@1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" @@ -1408,6 +1413,11 @@ dependencies: "@types/node" "*" +"@types/events@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + "@types/express-serve-static-core@^4.17.18": version "4.17.28" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" @@ -1471,11 +1481,6 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/matrix-js-sdk@^11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@types/matrix-js-sdk/-/matrix-js-sdk-11.0.1.tgz#6766819b345003858e52b3ee0b8cdf6ce0447b27" - integrity sha512-0cI+IlIf96mxsTgtS9x7Cw2tZKhhkqwAmodKXGW+rf7THmbiM7rvOsRmp3eDik16mRlWrluq2egVFtQ8NGjgPw== - "@types/mime@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" @@ -1486,10 +1491,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.13.tgz#5ed7ed7c662948335fcad6c412bb42d99ea754e3" integrity sha512-Y86MAxASe25hNzlDbsviXl8jQHb0RDvKt4c40ZJQ1Don0AAL0STLZSs4N+6gLEO55pedy7r2cLwS+ZDxPm/2Bw== -"@types/node@16.10": - version "16.10.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.9.tgz#8f1cdd517972f76a3b928298f4c0747cd6fef25a" - integrity sha512-H9ReOt+yqIJPCutkTYjFjlyK6WEMQYT9hLZMlWtOjFQY2ItppsWZ6RJf8Aw+jz5qTYceuHvFgPIaKOHtLAEWBw== +"@types/node@^18.16": + version "18.16.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.18.tgz#85da09bafb66d4bc14f7c899185336d0c1736390" + integrity sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw== "@types/prettier@^2.1.5": version "2.6.0" @@ -2117,11 +2122,6 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browser-request@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/browser-request/-/browser-request-0.3.3.tgz#9ece5b5aca89a29932242e18bf933def9876cc17" - integrity sha1-ns5bWsqJopkyJC4Yv5M975h2zBc= - browserslist@^4.17.5: version "4.20.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf" @@ -3103,6 +3103,11 @@ eventemitter3@^5.0.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.0.tgz#084eb7f5b5388df1451e63f4c2aafd71b217ccb3" integrity sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg== +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + execa@^5.0.0, execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -4713,27 +4718,36 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" -matrix-events-sdk@^0.0.1-beta.7: - version "0.0.1-beta.7" - resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1-beta.7.tgz#5ffe45eba1f67cc8d7c2377736c728b322524934" - integrity sha512-9jl4wtWanUFSy2sr2lCjErN/oC8KTAtaeaozJtrgot1JiQcEI4Rda9OLgQ7nLKaqb4Z/QUx/fR3XpDzm5Jy1JA== +matrix-events-sdk@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz#c8c38911e2cb29023b0bbac8d6f32e0de2c957dd" + integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA== -matrix-js-sdk@^19.7.0: - version "19.7.0" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-19.7.0.tgz#ccadae630c56032b040c87b163475a04601409ce" - integrity sha512-mFN1LBmEpYHCH6II1F8o7y8zJr0kn1yX7ga7tRXHbLJAlBS4bAXRsEoAzdv6OrV8/dS325JlVUYQLHFHQWjYxg== +matrix-js-sdk@^26.1.0: + version "26.1.0" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-26.1.0.tgz#d09f2892ca8263a92413cf61a66c2b2d8c4f545d" + integrity sha512-2isNpMFCR0hV8wNub4GJ/+8lIbz/jag12gf1P8rU++PfLFpeObooIDI3nnffBGLV5biZ3iLkaLOKAl33dSxchA== dependencies: "@babel/runtime" "^7.12.5" + "@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.10" another-json "^0.2.0" - browser-request "^0.3.3" bs58 "^5.0.0" content-type "^1.0.4" loglevel "^1.7.1" - matrix-events-sdk "^0.0.1-beta.7" + matrix-events-sdk "0.0.1" + matrix-widget-api "^1.3.1" p-retry "4" - qs "^6.9.6" - request "^2.88.2" + sdp-transform "^2.14.1" unhomoglyph "^1.0.6" + uuid "9" + +matrix-widget-api@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.4.0.tgz#e426ec16a013897f3a4a9c2bff423f54ab0ba745" + integrity sha512-dw0dRylGQzDUoiaY/g5xx1tBbS7aoov31PRtFMAvG58/4uerYllV9Gfou7w+I1aglwB6hihTREzKltVjARWV6A== + dependencies: + "@types/events" "^3.0.0" + events "^3.2.0" media-typer@0.3.0: version "0.3.0" @@ -5397,7 +5411,7 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qs@6.10.3, qs@^6.9.6: +qs@6.10.3: version "6.10.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== @@ -5654,6 +5668,11 @@ safe-regex@^2.1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sdp-transform@^2.14.1: + version "2.14.1" + resolved "https://registry.yarnpkg.com/sdp-transform/-/sdp-transform-2.14.1.tgz#2bb443583d478dee217df4caa284c46b870d5827" + integrity sha512-RjZyX3nVwJyCuTo5tGPx+PZWkDMCg7oOLpSlhjDdZfwUoNqG1mM8nyj31IGHyaPWXhjbP7cdK3qZ2bmkJ1GzRw== + semver@7.x: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" @@ -6387,6 +6406,11 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= +uuid@9: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" From ab6872378884fc1eaac366335c8e605f4ed5e0b6 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Fri, 23 Jun 2023 11:35:27 +0200 Subject: [PATCH 22/94] Node.js and dependencies upgrade --- .github/workflows/E2E.yml | 4 +- .gitlab-ci.yml | 2 +- Dockerfile | 2 +- package.json | 12 +- yarn.lock | 635 +++++++++++++++++++------------------- 5 files changed, 328 insertions(+), 327 deletions(-) diff --git a/.github/workflows/E2E.yml b/.github/workflows/E2E.yml index ef647895..269e5adf 100644 --- a/.github/workflows/E2E.yml +++ b/.github/workflows/E2E.yml @@ -14,8 +14,8 @@ jobs: - name: Setup Node.js for use with actions uses: actions/setup-node@v3.5.1 with: - node-version: "16.10" - - run: yarn install --network-concurrency 1 --frozen-lockfile + node-version: "18.16" + - run: yarn install --frozen-lockfile - name: Download Polkadot and parachain binaries run: | wget --no-verbose https://github.com/paritytech/cumulus/releases/download/v0.9.420/polkadot-parachain diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cc7d8f80..75600c63 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,7 +18,7 @@ variables: KUBE_NAMESPACE: "faucetbots" CI_REGISTRY: "docker.io/paritytech" GIT_STRATEGY: fetch - CI_IMAGE: node:16.20-alpine + CI_IMAGE: node:18.16-alpine BUILDAH_IMAGE: "quay.io/buildah/stable:v1.29" BUILDAH_COMMAND: "buildah --storage-driver overlay2" ARGOCD_IMAGE: argoproj/argocd:v2.5.5 diff --git a/Dockerfile b/Dockerfile index e615b68b..ad43b916 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ LABEL io.parity.image.authors="cicd-team@parity.io" \ WORKDIR /faucet COPY ./package.json ./yarn.lock ./ -RUN yarn --network-concurrency 1 --frozen-lockfile +RUN yarn --frozen-lockfile COPY . . RUN yarn build diff --git a/package.json b/package.json index 90df1b73..04f26d89 100644 --- a/package.json +++ b/package.json @@ -47,12 +47,12 @@ "pre-commit": "yarn lint-staged" }, "dependencies": { - "@polkadot/api": "^10.0.1", - "@polkadot/keyring": "^11.0.1", - "@polkadot/util": "^11.0.1", - "@polkadot/util-crypto": "^11.0.1", - "@polkadot/wasm-crypto": "^7.0.2", - "@polkadot/x-randomvalues": "^11.0.1", + "@polkadot/api": "^10.9.1", + "@polkadot/keyring": "^12.3.2", + "@polkadot/util": "^12.3.2", + "@polkadot/util-crypto": "^12.3.2", + "@polkadot/wasm-crypto": "^7.2.1", + "@polkadot/x-randomvalues": "^12.3.2", "@types/cors": "^2.8.13", "axios": "^0.26.1", "bigfloat.js": "^3.0.1", diff --git a/yarn.lock b/yarn.lock index 2a2ba24d..141c978a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -885,15 +885,17 @@ resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.11.tgz#24d705318c3159ef7dbe43bca464ac2bdd11e45d" integrity sha512-HD3rskPkqrUUSaKzGLg97k/bN+OZrkcX7ODB/pNBs/jqq+/A0wDKqsszJotzFwsQcDPpWn78BmMyvBo4tLxKjw== -"@noble/hashes@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" - integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== +"@noble/curves@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d" + integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== + dependencies: + "@noble/hashes" "1.3.1" -"@noble/secp256k1@1.7.1": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" - integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== +"@noble/hashes@1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" + integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -932,329 +934,330 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@polkadot/api-augment@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-10.0.1.tgz#b8e7766e1d89e3ee753ae053e3edd21ddc0c780c" - integrity sha512-VOMkUurEZ/r27Sx5zeGACApm4wLZx5bsxo8sWxaVE1enZvob1JpzGuN12rTlMr0ej4Az8BxvlGbcT3fQYw275Q== - dependencies: - "@polkadot/api-base" "10.0.1" - "@polkadot/rpc-augment" "10.0.1" - "@polkadot/types" "10.0.1" - "@polkadot/types-augment" "10.0.1" - "@polkadot/types-codec" "10.0.1" - "@polkadot/util" "^11.0.1" - tslib "^2.5.0" - -"@polkadot/api-base@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-10.0.1.tgz#c3f19b5ea483ba642e98724364b58c9e16ba26fe" - integrity sha512-yuCgHYQU7Tn32I4sNk5Qb/OwB85ICXCfWja95watbEP6os601IllI6s7JhFx3G4fjvfI94DzewOnOhhBHt+2SA== - dependencies: - "@polkadot/rpc-core" "10.0.1" - "@polkadot/types" "10.0.1" - "@polkadot/util" "^11.0.1" - rxjs "^7.8.0" - tslib "^2.5.0" - -"@polkadot/api-derive@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-10.0.1.tgz#fa62a5dc9301167fc628483a6bce65fd5f5a8adc" - integrity sha512-btiE/ATJybKqBBYQvjujXZ+WrMfzwNvKRGI84cbEYnX4OHIo47O/v+zGQ2nUhbOfcJFa8FBU6dB9fMTBRl2R5g== - dependencies: - "@polkadot/api" "10.0.1" - "@polkadot/api-augment" "10.0.1" - "@polkadot/api-base" "10.0.1" - "@polkadot/rpc-core" "10.0.1" - "@polkadot/types" "10.0.1" - "@polkadot/types-codec" "10.0.1" - "@polkadot/util" "^11.0.1" - "@polkadot/util-crypto" "^11.0.1" - rxjs "^7.8.0" - tslib "^2.5.0" - -"@polkadot/api@10.0.1", "@polkadot/api@^10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-10.0.1.tgz#ff3272e9f3d5d8c6d7c9e842f0a11c1c6a462265" - integrity sha512-XDJwGqtnFKWlY2kEGOCOAFhczgUxwNZ553zVbmkR65eK4gVlCwIMHLkU/rPlPf/QShrTCZXQhaS/HwIXeFHIvw== - dependencies: - "@polkadot/api-augment" "10.0.1" - "@polkadot/api-base" "10.0.1" - "@polkadot/api-derive" "10.0.1" - "@polkadot/keyring" "^11.0.1" - "@polkadot/rpc-augment" "10.0.1" - "@polkadot/rpc-core" "10.0.1" - "@polkadot/rpc-provider" "10.0.1" - "@polkadot/types" "10.0.1" - "@polkadot/types-augment" "10.0.1" - "@polkadot/types-codec" "10.0.1" - "@polkadot/types-create" "10.0.1" - "@polkadot/types-known" "10.0.1" - "@polkadot/util" "^11.0.1" - "@polkadot/util-crypto" "^11.0.1" - eventemitter3 "^5.0.0" - rxjs "^7.8.0" - tslib "^2.5.0" - -"@polkadot/keyring@^11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-11.0.1.tgz#e03de854f15ae68b5797137604d5055e55412bac" - integrity sha512-ypQs9cYp/WsmHPvnv4RowbVyZTdOg8rIvcHj6Ols3sqJbQXVn9rfWZTS2l341d9z4kJtmqwbSdKAVV0GT+Mj1A== - dependencies: - "@polkadot/util" "11.0.1" - "@polkadot/util-crypto" "11.0.1" - tslib "^2.5.0" - -"@polkadot/networks@11.0.1", "@polkadot/networks@^11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-11.0.1.tgz#c97d8eebc8c415bb6966d9d0cf46eb4eb40b7732" - integrity sha512-el1qzqFVhZQry/m9Qriq/AcksXOKGwgl6Aq5RsjpRLLMJyxHjATTICOPdKoY5gxSnVayku/fd46eak31/O/MnA== +"@polkadot/api-augment@10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-10.9.1.tgz#9fc81b81903229bb23b0b16783e97ec52a5d4f1b" + integrity sha512-kRZZvCFVcN4hAH4dJ+Qzfdy27/4EEq3oLDf3ihj0LTVrAezSWcKPGE3EVFy+Mn6Lo4SUc7RVyoKvIUhSk2l4Dg== + dependencies: + "@polkadot/api-base" "10.9.1" + "@polkadot/rpc-augment" "10.9.1" + "@polkadot/types" "10.9.1" + "@polkadot/types-augment" "10.9.1" + "@polkadot/types-codec" "10.9.1" + "@polkadot/util" "^12.3.1" + tslib "^2.5.3" + +"@polkadot/api-base@10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-10.9.1.tgz#27f63c4950814c2f10535f794121fa1384dc2207" + integrity sha512-Q3m2KzlceMK2kX8bhnUZWk3RT6emmijeeFZZQgCePpEcrSeNjnqG4qjuTPgkveaOkUT8MAoDc5Avuzcc2jlW9g== dependencies: - "@polkadot/util" "11.0.1" - "@substrate/ss58-registry" "^1.39.0" - tslib "^2.5.0" + "@polkadot/rpc-core" "10.9.1" + "@polkadot/types" "10.9.1" + "@polkadot/util" "^12.3.1" + rxjs "^7.8.1" + tslib "^2.5.3" -"@polkadot/rpc-augment@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-10.0.1.tgz#f46a97543310837fd2feabc9419b9e8a6c68f9c0" - integrity sha512-DZK4V99qIhtSS9gaYL5BjsFoa5DxIunO3emxvc5V0jm3o5ZNejGDwRCZNL/atIt5tGyjosU6cYMmVvvgLuQbzg== +"@polkadot/api-derive@10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-10.9.1.tgz#04a4ca3285fd215c4cd50cfb3f4791d38dd90050" + integrity sha512-mRud1UZCFIc4Z63qAoGSIHh/foyUYADfy1RQYCmPpeFKfIdCIrHpd7xFdJXTOMYOS0BwlM6u4qli/ZT4XigezQ== + dependencies: + "@polkadot/api" "10.9.1" + "@polkadot/api-augment" "10.9.1" + "@polkadot/api-base" "10.9.1" + "@polkadot/rpc-core" "10.9.1" + "@polkadot/types" "10.9.1" + "@polkadot/types-codec" "10.9.1" + "@polkadot/util" "^12.3.1" + "@polkadot/util-crypto" "^12.3.1" + rxjs "^7.8.1" + tslib "^2.5.3" + +"@polkadot/api@10.9.1", "@polkadot/api@^10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-10.9.1.tgz#156b3436f45ef18218960804988c1f552d2c4e46" + integrity sha512-ND/2UqZBWvtt4PfV03OStTKg0mxmPk4UpMAgJKutdgsz/wP9CYJ1KbjwFgPNekL9JnzbKQsWyQNPVrcw7kQk8A== + dependencies: + "@polkadot/api-augment" "10.9.1" + "@polkadot/api-base" "10.9.1" + "@polkadot/api-derive" "10.9.1" + "@polkadot/keyring" "^12.3.1" + "@polkadot/rpc-augment" "10.9.1" + "@polkadot/rpc-core" "10.9.1" + "@polkadot/rpc-provider" "10.9.1" + "@polkadot/types" "10.9.1" + "@polkadot/types-augment" "10.9.1" + "@polkadot/types-codec" "10.9.1" + "@polkadot/types-create" "10.9.1" + "@polkadot/types-known" "10.9.1" + "@polkadot/util" "^12.3.1" + "@polkadot/util-crypto" "^12.3.1" + eventemitter3 "^5.0.1" + rxjs "^7.8.1" + tslib "^2.5.3" + +"@polkadot/keyring@^12.3.1", "@polkadot/keyring@^12.3.2": + version "12.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-12.3.2.tgz#112a0c28816a1f47edad6260dc94222c29465a54" + integrity sha512-NTdtDeI0DP9l/45hXynNABeP5VB8piw5YR+CbUxK2e36xpJWVXwbcOepzslg5ghE9rs8UKJb30Z/HqTU4sBY0Q== + dependencies: + "@polkadot/util" "12.3.2" + "@polkadot/util-crypto" "12.3.2" + tslib "^2.5.3" + +"@polkadot/networks@12.3.2", "@polkadot/networks@^12.3.1": + version "12.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-12.3.2.tgz#131b0439c481add159814dd2cf0286c6c3fe5b3b" + integrity sha512-uCkyybKoeEm1daKr0uT/9oNDHDDzCy2/ZdVl346hQqfdR1Ct3BaxMjxqvdmb5N8aCw0cBWSfgsxAYtw8ESmllQ== + dependencies: + "@polkadot/util" "12.3.2" + "@substrate/ss58-registry" "^1.40.0" + tslib "^2.5.3" + +"@polkadot/rpc-augment@10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-10.9.1.tgz#214ec3ee145d20caa61ea204041a3aadb89c6b0f" + integrity sha512-MaLHkNlyqN20ZRYr6uNd1BZr1OsrnX9qLAmsl0mcrri1vPGRH6VHjfFH1RBLkikpWD82v17g0l2hLwdV1ZHMcw== dependencies: - "@polkadot/rpc-core" "10.0.1" - "@polkadot/types" "10.0.1" - "@polkadot/types-codec" "10.0.1" - "@polkadot/util" "^11.0.1" - tslib "^2.5.0" + "@polkadot/rpc-core" "10.9.1" + "@polkadot/types" "10.9.1" + "@polkadot/types-codec" "10.9.1" + "@polkadot/util" "^12.3.1" + tslib "^2.5.3" -"@polkadot/rpc-core@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-10.0.1.tgz#f6644c9e61c4001c76db2cc5cf80443d83cef26a" - integrity sha512-HuWFttfQknSfB0Xff+svDP1rba5cwLyOhJ4EDPxz2QcyChTdOCzHBymD9GLKZJEaGp+IT4VOcUPwLDMml1TG1A== +"@polkadot/rpc-core@10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-10.9.1.tgz#798c514dbed6f6c2e43098a494c9f51fb144dc31" + integrity sha512-ZtA8B8SfXSAwVkBlCcKRHw0eSM7ec/sbiNOM5GasXPeRujUgT7lOwSH2GbUZSqe9RfRDMp6DvO9c2JoGc3LLWw== dependencies: - "@polkadot/rpc-augment" "10.0.1" - "@polkadot/rpc-provider" "10.0.1" - "@polkadot/types" "10.0.1" - "@polkadot/util" "^11.0.1" - rxjs "^7.8.0" - tslib "^2.5.0" + "@polkadot/rpc-augment" "10.9.1" + "@polkadot/rpc-provider" "10.9.1" + "@polkadot/types" "10.9.1" + "@polkadot/util" "^12.3.1" + rxjs "^7.8.1" + tslib "^2.5.3" -"@polkadot/rpc-provider@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-10.0.1.tgz#ad94e2f0c357263a7ea5e1b6566f6e772ad0b38a" - integrity sha512-kv6uShbKgBZtoRcsxTVxpzkjRUqcd/cctG0lEqpy2BZU8koCnSu3XhooifcTm8jO17EUuC4Mm/wfM0DQKmojmQ== - dependencies: - "@polkadot/keyring" "^11.0.1" - "@polkadot/types" "10.0.1" - "@polkadot/types-support" "10.0.1" - "@polkadot/util" "^11.0.1" - "@polkadot/util-crypto" "^11.0.1" - "@polkadot/x-fetch" "^11.0.1" - "@polkadot/x-global" "^11.0.1" - "@polkadot/x-ws" "^11.0.1" - eventemitter3 "^5.0.0" +"@polkadot/rpc-provider@10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-10.9.1.tgz#de3a474bbcd26d28d9cd3134acdb3b5ce92b680b" + integrity sha512-4QzT2QzD+320+eT6b79sGAA85Tt3Bb8fQvse4r5Mom2iiBd2SO81vOhxSAOaIe4GUsw25VzFJmsbe7+OObItdg== + dependencies: + "@polkadot/keyring" "^12.3.1" + "@polkadot/types" "10.9.1" + "@polkadot/types-support" "10.9.1" + "@polkadot/util" "^12.3.1" + "@polkadot/util-crypto" "^12.3.1" + "@polkadot/x-fetch" "^12.3.1" + "@polkadot/x-global" "^12.3.1" + "@polkadot/x-ws" "^12.3.1" + eventemitter3 "^5.0.1" mock-socket "^9.2.1" - nock "^13.3.0" - tslib "^2.5.0" + nock "^13.3.1" + tslib "^2.5.3" optionalDependencies: - "@substrate/connect" "0.7.20" + "@substrate/connect" "0.7.26" -"@polkadot/types-augment@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-10.0.1.tgz#5c3d40c2976510f893e11f6acde02d8fe0c40a8b" - integrity sha512-PK7CmZwamJiqIIuyeEfV2a1KsEKAuviTH7DkDZWb1aH8495hNkKx88JeTwotjTG6xrkaFZcEqF7UbhXCQs2zOA== +"@polkadot/types-augment@10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-10.9.1.tgz#5f1c1225c04ffbfe243629a46087c9c9de25a6b3" + integrity sha512-OY9/jTMFRFqYdkUnfcGwqMLC64A0Q25bjvCuVQCVjsPFKE3wl0Kt5rNT01eV2UmLXrR6fY0xWbR2w80bLA7CIQ== dependencies: - "@polkadot/types" "10.0.1" - "@polkadot/types-codec" "10.0.1" - "@polkadot/util" "^11.0.1" - tslib "^2.5.0" + "@polkadot/types" "10.9.1" + "@polkadot/types-codec" "10.9.1" + "@polkadot/util" "^12.3.1" + tslib "^2.5.3" -"@polkadot/types-codec@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-10.0.1.tgz#c9f4fd0142800fc5339de3c0d6c1611d7d0947d8" - integrity sha512-RrrEuc6PZID/VvIH+eZ6aqvpx7kjbFD58nsb/8ZQR57352EP4tVvR3arHsqh6j2WiM62uJ3zKT/rL8bCYVHjIw== +"@polkadot/types-codec@10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-10.9.1.tgz#f30026d3dfeaa69c07c45fa66d1c39318fd232cc" + integrity sha512-mJ5OegKGraY1FLvEa8FopRCr3pQrhDkcn5RNOjmgJQozENVeRaxhk0NwxYz7IojFvSDnKnc6lNQfKaaSe5pLHg== dependencies: - "@polkadot/util" "^11.0.1" - "@polkadot/x-bigint" "^11.0.1" - tslib "^2.5.0" + "@polkadot/util" "^12.3.1" + "@polkadot/x-bigint" "^12.3.1" + tslib "^2.5.3" -"@polkadot/types-create@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-10.0.1.tgz#c94edbd181abc5b589a7807b20ccd16788eae2cd" - integrity sha512-Sr4BmswhFGj09e727XeS4nOnrvkWwWSSaXAwLenwVOCK9UaevYw+jmc28HcYypL5+i8kT4jKyU+1av7UtJyOzg== +"@polkadot/types-create@10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-10.9.1.tgz#087d7e2af51cce558b67e3859613b932a3bdc0a3" + integrity sha512-OVz50MGTTuiuVnRP/zAx4CTuLioc0hsiwNwqN2lNhmIJGtnQ4Vy/7mQRsIWehiYz6g0Vzzm5B3qWkTXO1NSN5w== dependencies: - "@polkadot/types-codec" "10.0.1" - "@polkadot/util" "^11.0.1" - tslib "^2.5.0" + "@polkadot/types-codec" "10.9.1" + "@polkadot/util" "^12.3.1" + tslib "^2.5.3" -"@polkadot/types-known@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-10.0.1.tgz#0bbee8b8556d202549b2cacd7336122da9796492" - integrity sha512-GoHnDS1yKwLmsEQX7xjcMNR5SvaszxGV7E5Jkgl16VOF3QmO13Vs19jz1bdyv4Dw6soKFI5XAUEJY9PoA0DDMg== +"@polkadot/types-known@10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-10.9.1.tgz#fe0c7e55191aa843119edcaf9abb5d2471463a7d" + integrity sha512-zCMVWc4pJtkbMFPu72bD4IhvV/gkHXPX3C5uu92WdmCfnn0vEIEsMKWlVXVVvQQZKAqvs/awpqIfrUtEViOGEA== dependencies: - "@polkadot/networks" "^11.0.1" - "@polkadot/types" "10.0.1" - "@polkadot/types-codec" "10.0.1" - "@polkadot/types-create" "10.0.1" - "@polkadot/util" "^11.0.1" - tslib "^2.5.0" + "@polkadot/networks" "^12.3.1" + "@polkadot/types" "10.9.1" + "@polkadot/types-codec" "10.9.1" + "@polkadot/types-create" "10.9.1" + "@polkadot/util" "^12.3.1" + tslib "^2.5.3" -"@polkadot/types-support@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-10.0.1.tgz#301125e2e2840801e575cb969fe0cc2bb36df86a" - integrity sha512-J5i4BM08/HZGBNQhN2X29eWPS8+Ie7n6O8L0y8IZ3rS0hkXU1V2SFd9X4LO8ADPGvT3JvPpQKESsq0f/Z5UbYQ== +"@polkadot/types-support@10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-10.9.1.tgz#17a861aab8e5a225a4e20cefa2d16076ddd51baf" + integrity sha512-XsieuLDsszvMZQlleacQBfx07i/JkwQV/UxH9q8Hz7Okmaz9pEVEW1h3ka2/cPuC7a4l32JhaORBUYshBZNdJg== dependencies: - "@polkadot/util" "^11.0.1" - tslib "^2.5.0" + "@polkadot/util" "^12.3.1" + tslib "^2.5.3" -"@polkadot/types@10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-10.0.1.tgz#54c636bbe6f151c5e4bc2c9220bb98bbb1fc428d" - integrity sha512-/ALKLIWulXJrK/nNEY8iXByZRwaq1uQiRzzFqwWgfGpnLVCHYljV5ZEi3QqeDjGJzywQYxqJB+bJSiUe0+iNvg== - dependencies: - "@polkadot/keyring" "^11.0.1" - "@polkadot/types-augment" "10.0.1" - "@polkadot/types-codec" "10.0.1" - "@polkadot/types-create" "10.0.1" - "@polkadot/util" "^11.0.1" - "@polkadot/util-crypto" "^11.0.1" - rxjs "^7.8.0" - tslib "^2.5.0" - -"@polkadot/util-crypto@11.0.1", "@polkadot/util-crypto@^11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-11.0.1.tgz#ec17ee499bf78262d1cc23963fb365aeb4ede34b" - integrity sha512-IEircl43g6ZT9IqjzB1ttR7vK+/IaUar6l8yeU+Bn6x0TSS6tXyOJmXfVsXsqfLM58GIXY18mBZCTfhF/LwuKg== - dependencies: - "@noble/hashes" "1.2.0" - "@noble/secp256k1" "1.7.1" - "@polkadot/networks" "11.0.1" - "@polkadot/util" "11.0.1" - "@polkadot/wasm-crypto" "^7.0.2" - "@polkadot/x-bigint" "11.0.1" - "@polkadot/x-randomvalues" "11.0.1" +"@polkadot/types@10.9.1": + version "10.9.1" + resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-10.9.1.tgz#f111d00f7278ad3be95deba3d701fafefe080cb2" + integrity sha512-AG33i2ZGGfq7u+5rkAdGrXAQHHl844/Yv+junH5ZzX69xiCoWO1bH/yzDUNBdpki2GlACWvF9nLYh3F2tVF93w== + dependencies: + "@polkadot/keyring" "^12.3.1" + "@polkadot/types-augment" "10.9.1" + "@polkadot/types-codec" "10.9.1" + "@polkadot/types-create" "10.9.1" + "@polkadot/util" "^12.3.1" + "@polkadot/util-crypto" "^12.3.1" + rxjs "^7.8.1" + tslib "^2.5.3" + +"@polkadot/util-crypto@12.3.2", "@polkadot/util-crypto@^12.3.1", "@polkadot/util-crypto@^12.3.2": + version "12.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-12.3.2.tgz#42d810886904e06fa6e5db254c15f6ef80f4ab72" + integrity sha512-pTpx+YxolY0BDT4RcGmgeKbHHD/dI6Ll9xRsqmVdIjpcVVY20uDNTyXs81ZNtfKgyod1y9JQkfNv2Dz9iEpTkQ== + dependencies: + "@noble/curves" "1.1.0" + "@noble/hashes" "1.3.1" + "@polkadot/networks" "12.3.2" + "@polkadot/util" "12.3.2" + "@polkadot/wasm-crypto" "^7.2.1" + "@polkadot/wasm-util" "^7.2.1" + "@polkadot/x-bigint" "12.3.2" + "@polkadot/x-randomvalues" "12.3.2" "@scure/base" "1.1.1" - ed2curve "^0.3.0" - tslib "^2.5.0" - tweetnacl "^1.0.3" + tslib "^2.5.3" -"@polkadot/util@11.0.1", "@polkadot/util@^11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-11.0.1.tgz#04ce76339a7bd6ea2746173cbac119905330b47a" - integrity sha512-IMk3hPxIGzlAW6fhOigVPMvaW0E+dTMzO1IKnEATdhAJFKjaqU4K9Pwj79fj93xgM5Y8PkHV5sUPJKuce+u+4A== +"@polkadot/util@12.3.2", "@polkadot/util@^12.3.1", "@polkadot/util@^12.3.2": + version "12.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-12.3.2.tgz#f46e147b0e6a426da5ba59df4ce65de1a3effe4a" + integrity sha512-y/JShcGyOamCUiSIg++XZuLHt1ktSKBaSH2K5Nw5NXlgP0+7am+GZzqPB8fQ4qhYLruEOv+YRiz0GC1Zr9S+wg== dependencies: - "@polkadot/x-bigint" "11.0.1" - "@polkadot/x-global" "11.0.1" - "@polkadot/x-textdecoder" "11.0.1" - "@polkadot/x-textencoder" "11.0.1" + "@polkadot/x-bigint" "12.3.2" + "@polkadot/x-global" "12.3.2" + "@polkadot/x-textdecoder" "12.3.2" + "@polkadot/x-textencoder" "12.3.2" "@types/bn.js" "^5.1.1" bn.js "^5.2.1" - tslib "^2.5.0" + tslib "^2.5.3" -"@polkadot/wasm-bridge@7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-bridge/-/wasm-bridge-7.0.2.tgz#c906c12fa5a2ce40515c37d88a17637f467f6199" - integrity sha512-towgTgPG3FRLBRGPi4rLgdklouZxpedbwzUJ2s2CPrpCrbBxCsVn7Z36Wmr0qgLUTqzQEed3DFtluu5Od3EJLQ== +"@polkadot/wasm-bridge@7.2.1": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-bridge/-/wasm-bridge-7.2.1.tgz#8464a96552207d2b49c6f32137b24132534b91ee" + integrity sha512-uV/LHREDBGBbHrrv7HTki+Klw0PYZzFomagFWII4lp6Toj/VCvRh5WMzooVC+g/XsBGosAwrvBhoModabyHx+A== dependencies: + "@polkadot/wasm-util" "7.2.1" tslib "^2.5.0" -"@polkadot/wasm-crypto-asmjs@7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.0.2.tgz#c7f807ee4f290c98d6b17b629f0b2cdcc4f8b186" - integrity sha512-oSNp+vrnBlPf2CpXBjq+jxfzaCuem+hGgyNxEDlwkON1yyopHXxyHhChmFt6zbDXkcUa6+YEaH0XT94ZC94Qrg== +"@polkadot/wasm-crypto-asmjs@7.2.1": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.2.1.tgz#3e7a91e2905ab7354bc37b82f3e151a62bb024db" + integrity sha512-z/d21bmxyVfkzGsKef/FWswKX02x5lK97f4NPBZ9XBeiFkmzlXhdSnu58/+b1sKsRAGdW/Rn/rTNRDhW0GqCAg== dependencies: tslib "^2.5.0" -"@polkadot/wasm-crypto-init@7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.0.2.tgz#e707c34ba6be9ee1419453eeceab2b441da7fef3" - integrity sha512-OQLelialR0swKp5iEisYJSP0nvs8FmchPt5juI6cRhn9k2jO+88c/s9Igh0Q+77wU5dhCT+tT4HqgilhJP4UHQ== +"@polkadot/wasm-crypto-init@7.2.1": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.2.1.tgz#9dbba41ed7d382575240f1483cf5a139ff2787bd" + integrity sha512-GcEXtwN9LcSf32V9zSaYjHImFw16hCyo2Xzg4GLLDPPeaAAfbFr2oQMgwyDbvBrBjLKHVHjsPZyGhXae831amw== dependencies: - "@polkadot/wasm-bridge" "7.0.2" - "@polkadot/wasm-crypto-asmjs" "7.0.2" - "@polkadot/wasm-crypto-wasm" "7.0.2" + "@polkadot/wasm-bridge" "7.2.1" + "@polkadot/wasm-crypto-asmjs" "7.2.1" + "@polkadot/wasm-crypto-wasm" "7.2.1" + "@polkadot/wasm-util" "7.2.1" tslib "^2.5.0" -"@polkadot/wasm-crypto-wasm@7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.0.2.tgz#6adecfd7ac7dec7967c427adcc18c9b6be09e566" - integrity sha512-e1UztCUZ9s9LRfu8AhnWZDrxNhsuPBOJdDmOqoqe1dHaBQpB/R4+v1NhT9nLRSOM/JmV6B1PIaehcRbkb++8KA== +"@polkadot/wasm-crypto-wasm@7.2.1": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.2.1.tgz#d2486322c725f6e5d2cc2d6abcb77ecbbaedc738" + integrity sha512-DqyXE4rSD0CVlLIw88B58+HHNyrvm+JAnYyuEDYZwCvzUWOCNos/DDg9wi/K39VAIsCCKDmwKqkkfIofuOj/lA== dependencies: - "@polkadot/wasm-util" "7.0.2" + "@polkadot/wasm-util" "7.2.1" tslib "^2.5.0" -"@polkadot/wasm-crypto@^7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-7.0.2.tgz#44aa5f5322f3e8bdcacb1806e3e1a6543d20cbb4" - integrity sha512-ETOl4OSAZMI8fzMR9f6uH8vid81CUIgJFZYLVkCwNS7kttdWHKO1EOGnlmuH0dzsEZ4YQOcfV4jx6fZ+2yS+YQ== +"@polkadot/wasm-crypto@^7.2.1": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-7.2.1.tgz#db671dcb73f1646dc13478b5ffc3be18c64babe1" + integrity sha512-SA2+33S9TAwGhniKgztVN6pxUKpGfN4Tre/eUZGUfpgRkT92wIUT2GpGWQE+fCCqGQgADrNiBcwt6XwdPqMQ4Q== dependencies: - "@polkadot/wasm-bridge" "7.0.2" - "@polkadot/wasm-crypto-asmjs" "7.0.2" - "@polkadot/wasm-crypto-init" "7.0.2" - "@polkadot/wasm-crypto-wasm" "7.0.2" - "@polkadot/wasm-util" "7.0.2" + "@polkadot/wasm-bridge" "7.2.1" + "@polkadot/wasm-crypto-asmjs" "7.2.1" + "@polkadot/wasm-crypto-init" "7.2.1" + "@polkadot/wasm-crypto-wasm" "7.2.1" + "@polkadot/wasm-util" "7.2.1" tslib "^2.5.0" -"@polkadot/wasm-util@7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-util/-/wasm-util-7.0.2.tgz#1f3eaf048bd9134f08857e74325084ce129cd50c" - integrity sha512-N7bnZDNwUkjVO2WiJ6VvB0y8JS+CgiFTz6ofLxxSa/0HtBW1boCLrLxvp9EA0rh1+Ca212spLNM1GNkD/msMeg== +"@polkadot/wasm-util@7.2.1", "@polkadot/wasm-util@^7.2.1": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-util/-/wasm-util-7.2.1.tgz#fda233120ec02f77f0d14e4d3c7ad9ce06535fb8" + integrity sha512-FBSn/3aYJzhN0sYAYhHB8y9JL8mVgxLy4M1kUXYbyo+8GLRQEN5rns8Vcb8TAlIzBWgVTOOptYBvxo0oj0h7Og== dependencies: tslib "^2.5.0" -"@polkadot/x-bigint@11.0.1", "@polkadot/x-bigint@^11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-11.0.1.tgz#0a2c21f8fa76d1299a02111f8391d16593f456ae" - integrity sha512-/Sbl5seEG5F7og6UsjYi+V7/LrVw5cRoXhs3oEv8MFh5yIzCXaTO8vLd06PHLkNwBUvEBtfrbhuVYi+FDTAP0g== +"@polkadot/x-bigint@12.3.2", "@polkadot/x-bigint@^12.3.1": + version "12.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-12.3.2.tgz#0e99489cc7938bed40762aaaed58ded6850ab54b" + integrity sha512-JLqLgfGXe/x+hZJETd5ZqfpVsbwyMsH5Nn1Q20ineMMjXN/ig+kVR8Mc15LXBMuw4g7LldFW6UUrotWnuMI8Yw== dependencies: - "@polkadot/x-global" "11.0.1" - tslib "^2.5.0" + "@polkadot/x-global" "12.3.2" + tslib "^2.5.3" -"@polkadot/x-fetch@^11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-11.0.1.tgz#fc77042c1f226a6235ea55799f2dd632462d1ba2" - integrity sha512-P1Dxof3F1y7vH44akgfaVk2+5bRtyxONxsX1hmcNH/QaFEXF49bDj2+asScxXXbDZyBfBdy847ec39w7tj2HIQ== +"@polkadot/x-fetch@^12.3.1": + version "12.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-12.3.2.tgz#7e8d2113268e792dd5d1b259ef13839c6aa77996" + integrity sha512-3IEuZ5S+RI/t33NsdPLIIa5COfDCfpUW2sbaByEczn75aD1jLqJZSEDwiBniJ2osyNd4uUxBf6e5jw7LAZeZJg== dependencies: - "@polkadot/x-global" "11.0.1" - node-fetch "^3.3.0" - tslib "^2.5.0" + "@polkadot/x-global" "12.3.2" + node-fetch "^3.3.1" + tslib "^2.5.3" -"@polkadot/x-global@11.0.1", "@polkadot/x-global@^11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-11.0.1.tgz#c52f0d6790d3d1c8a8dfc0b503809652c95ff459" - integrity sha512-K14xREFEH1OqFcoD8cabByuqarX0NRz/iUlYcUv2Xgs+CCk6xfgdjCbFFoTHH5bzNqIJrrEjAbOn3zL4Xm1W4g== +"@polkadot/x-global@12.3.2", "@polkadot/x-global@^12.3.1": + version "12.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-12.3.2.tgz#04ac0b0e559a35107f0b95ff7889fcade3796aa3" + integrity sha512-yVZq6oIegjlyh5rUZiTklgu+fL+W/DG1ypEa02683tUCB3avV5cA3PAHKptMSlb6FpweHu37lKKrqfAWrraDxg== dependencies: - tslib "^2.5.0" + tslib "^2.5.3" -"@polkadot/x-randomvalues@11.0.1", "@polkadot/x-randomvalues@^11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-11.0.1.tgz#2eefd528620a8bb25a2ad68756b094928737b8ef" - integrity sha512-4WwmZ+uO2vyNWOB1EPUi47enuX7sftJi8maGkHVvJy0ZWWPvf4VzXkburVA8PX1if8uPJDL1/3SVdMjwPALWHQ== +"@polkadot/x-randomvalues@12.3.2", "@polkadot/x-randomvalues@^12.3.2": + version "12.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-12.3.2.tgz#43ac489a998098bdd40b3f82f28adb5b542db2a5" + integrity sha512-ywjIs8CWpvOGmq+3cGCNPOHxAjPHdBUiXyDccftx5BRVdmtbt36gK/V84bKr6Xs73FGu0jprUAOSRRsLZX/3dg== dependencies: - "@polkadot/x-global" "11.0.1" - tslib "^2.5.0" + "@polkadot/x-global" "12.3.2" + tslib "^2.5.3" -"@polkadot/x-textdecoder@11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-11.0.1.tgz#4f42d41150ebf5c36a40adb0dfe7b7da530a21a5" - integrity sha512-uadRuBPGeZNDknOk9FQWgnzTiVE4tHmSl5cBO9g/UWurqbovme01rQGhgARp3x9jrRC0/3xxsAAR96VhPh1ftA== +"@polkadot/x-textdecoder@12.3.2": + version "12.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-12.3.2.tgz#bbd5682744f3552ce5d4d792ff48a3ca525eafcf" + integrity sha512-lY5bfA5xArJRWEJlYOlQQMJeTjWD8s0yMhchirVgf5xj8Id9vPGeUoneH+VFDEwgXxrqBvDFJ4smN4T/r6a/fg== dependencies: - "@polkadot/x-global" "11.0.1" - tslib "^2.5.0" + "@polkadot/x-global" "12.3.2" + tslib "^2.5.3" -"@polkadot/x-textencoder@11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-11.0.1.tgz#f95c7c3e06065862e3a2624ad873bbf0329c7d3b" - integrity sha512-uzol/LRVxDQZNY/FLu0NpCsZLungIPdLG4FQjB4nBzJ1wW+z+AEuUHIVRJ87ohwikEB6yr20Prmz7g9kF4eB9g== +"@polkadot/x-textencoder@12.3.2": + version "12.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-12.3.2.tgz#223e6f6dd78e2d81c6dcc6f244c76ceae7b08e32" + integrity sha512-iP3qEBiHzBckQ9zeY7ZHRWuu7mCEg5SMpOugs6UODRk8sx6KHzGQYlghBbWLit0uppPDVE0ifEwZ2n73djJHWQ== dependencies: - "@polkadot/x-global" "11.0.1" - tslib "^2.5.0" + "@polkadot/x-global" "12.3.2" + tslib "^2.5.3" -"@polkadot/x-ws@^11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-11.0.1.tgz#4c588a9abb4649dbae561503f816f98c844cb260" - integrity sha512-L72dBHW6l6oLwixNL61kdGNmcaxgQNvrNL1sVTbG7ERZtUjH3oi7OdfiBvM7OKqwlDd5Ql+8lpKWtjInyXnfOA== +"@polkadot/x-ws@^12.3.1": + version "12.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-12.3.2.tgz#422559dfbdaac4c965d5e1b406b6cc4529214f94" + integrity sha512-yM9Z64pLNlHpJE43+Xtr+iUXmYpFFY5u5hrke2PJt13O48H8f9Vb9cRaIh94appLyICoS0aekGhDkGH+MCspBA== dependencies: - "@polkadot/x-global" "11.0.1" - tslib "^2.5.0" - ws "^8.12.1" + "@polkadot/x-global" "12.3.2" + tslib "^2.5.3" + ws "^8.13.0" "@scure/base@1.1.1": version "1.1.1" @@ -1302,19 +1305,19 @@ resolved "https://registry.yarnpkg.com/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz#fa5738039586c648013caa6a0c95c43265dbe77d" integrity sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg== -"@substrate/connect@0.7.20": - version "0.7.20" - resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.20.tgz#ce5647368be21199d608715bbd77bcb7c25a4227" - integrity sha512-f/sMgGUikJxDaNMkQXCU/1WaMy0MLJB+KS+P+CpsIhWyxj2dOcph5YXjAJiIlgrZqHImV28RJnraxXBD3AlmLQ== +"@substrate/connect@0.7.26": + version "0.7.26" + resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.26.tgz#a0ee5180c9cb2f29250d1219a32f7b7e7dea1196" + integrity sha512-uuGSiroGuKWj1+38n1kY5HReer5iL9bRwPCzuoLtqAOmI1fGI0hsSI2LlNQMAbfRgr7VRHXOk5MTuQf5ulsFRw== dependencies: "@substrate/connect-extension-protocol" "^1.0.1" eventemitter3 "^4.0.7" - smoldot "0.7.11" + smoldot "1.0.4" -"@substrate/ss58-registry@^1.39.0": - version "1.39.0" - resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.39.0.tgz#eb916ff5fea7fa02e77745823fde21af979273d2" - integrity sha512-qZYpuE6n+mwew+X71dOur/CbMXj6rNW27o63JeJwdQH/GvcSKm3JLNhd+bGzwUKg0D/zD30Qc6p4JykArzM+tA== +"@substrate/ss58-registry@^1.40.0": + version "1.40.0" + resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.40.0.tgz#2223409c496271df786c1ca8496898896595441e" + integrity sha512-QuU2nBql3J4KCnOWtWDw4n1K4JU0T79j54ZZvm/9nhsX6AIar13FyhsaBfs6QkJ2ixTQAnd7TocJIoJRWbqMZA== "@tootallnate/once@1": version "1.1.2" @@ -2663,13 +2666,6 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -ed2curve@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/ed2curve/-/ed2curve-0.3.0.tgz#322b575152a45305429d546b071823a93129a05d" - integrity sha512-8w2fmmq3hv9rCrcI7g9hms2pMunQr1JINfcjwR9tAyZqhtyaMN991lF/ZfHfr5tzZQ8c7y7aBgZbjfbd0fjFwQ== - dependencies: - tweetnacl "1.x.x" - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -3098,10 +3094,10 @@ eventemitter3@^4.0.7: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -eventemitter3@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.0.tgz#084eb7f5b5388df1451e63f4c2aafd71b217ccb3" - integrity sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg== +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== events@^3.2.0: version "3.3.0" @@ -4932,10 +4928,10 @@ negotiator@0.6.3, negotiator@^0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -nock@^13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.3.0.tgz#b13069c1a03f1ad63120f994b04bfd2556925768" - integrity sha512-HHqYQ6mBeiMc+N038w8LkMpDCRquCHWeNmN3v6645P3NhN2+qXOBqvPqo7Rt1VyCMzKhJ733wZqw5B7cQVFNPg== +nock@^13.3.1: + version "13.3.1" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.3.1.tgz#f22d4d661f7a05ebd9368edae1b5dc0a62d758fc" + integrity sha512-vHnopocZuI93p2ccivFyGuUfzjq2fxNyNurp7816mlT5V5HF4SzXu8lvLrVzBbNqzs+ODooZ6OksuSUNM7Njkw== dependencies: debug "^4.1.0" json-stringify-safe "^5.0.1" @@ -4959,10 +4955,10 @@ node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" -node-fetch@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.0.tgz#37e71db4ecc257057af828d523a7243d651d91e4" - integrity sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA== +node-fetch@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.1.tgz#b3eea7b54b3a48020e46f4f88b9c5a7430d20b2e" + integrity sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow== dependencies: data-uri-to-buffer "^4.0.0" fetch-blob "^3.1.4" @@ -5630,10 +5626,10 @@ rxjs@^7.5.5: dependencies: tslib "^2.1.0" -rxjs@^7.8.0: - version "7.8.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" - integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== +rxjs@^7.8.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" @@ -5832,10 +5828,10 @@ smart-buffer@^4.2.0: resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== -smoldot@0.7.11: - version "0.7.11" - resolved "https://registry.yarnpkg.com/smoldot/-/smoldot-0.7.11.tgz#8e39464f2cf7736eacff5f2a87819dd9f688b352" - integrity sha512-aE1led154FJ2/jrXKv2HLKdNIyvYJG6H2ZmKYFS++kW1OAcTQ6idDy3fzAI1VdydLDYK0YbKUsj7SJDmrjsS3g== +smoldot@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/smoldot/-/smoldot-1.0.4.tgz#e4c38cedad68d699a11b5b9ce72bb75c891bfd98" + integrity sha512-N3TazI1C4GGrseFH/piWyZCCCRJTRx2QhDfrUKRT4SzILlW5m8ayZ3QTKICcz1C/536T9cbHHJyP7afxI6Mi1A== dependencies: pako "^2.0.4" ws "^8.8.1" @@ -6259,6 +6255,11 @@ tslib@^2.3.1, tslib@^2.5.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== +tslib@^2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -6273,11 +6274,6 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tweetnacl@1.x.x, tweetnacl@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" - integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== - tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -6542,7 +6538,12 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@^8.12.1, ws@^8.8.1: +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + +ws@^8.8.1: version "8.12.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.1.tgz#c51e583d79140b5e42e39be48c934131942d4a8f" integrity sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew== From f4cb960e20fdb74b66f4005bd1a27ac81ff8b113 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Mon, 3 Jul 2023 12:35:02 +0200 Subject: [PATCH 23/94] 10 WND drip amount for westend, fixed balance cap for other networks Seems that balance cap being 10 times drip amount is an established rule of thumb, so aligned it for all networks --- src/networkData.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/networkData.ts b/src/networkData.ts index 5cfc66e3..cb7c1c61 100644 --- a/src/networkData.ts +++ b/src/networkData.ts @@ -17,7 +17,7 @@ export interface NetworkData { } const rococo: NetworkData = { - balanceCap: 100, + balanceCap: 1000, chains: [ { name: "Rococo Relay Chain", id: -1 }, { name: "Rockmine", id: 1000 }, @@ -53,14 +53,14 @@ const westend: NetworkData = { ], currency: "WND", decimals: 12, - dripAmount: "1", + dripAmount: "10", explorer: "https://westend.subscan.io", networkName: "Westend", rpcEndpoint: "wss://westend-rpc.polkadot.io/", }; const versi: NetworkData = { - balanceCap: 100, + balanceCap: 1000, chains: [], currency: "VRS", decimals: 12, From 1d0738e34846774243d60d66b813721e4065bd13 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Mon, 10 Jul 2023 15:59:31 +0200 Subject: [PATCH 24/94] Merging rococo faucets * Removing old ink-docs faucet deployment * Used new merged faucet endpoint for the client --- .github/workflows/deploy-site.yml | 2 +- .gitlab-ci.yml | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index 04967163..468ce956 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -31,7 +31,7 @@ jobs: - run: yarn run build env: PUBLIC_CAPTCHA_KEY: 6LdU5kckAAAAANktvvAKJ0auYUBRP0su94G7WXwe - PUBLIC_FAUCET_ROCOCO_URL: https://ink-docs-rococo-faucet.parity-testnet.parity.io/drip/web + PUBLIC_FAUCET_ROCOCO_URL: https://rococo-faucet.parity-testnet.parity.io/drip/web PUBLIC_FAUCET_WESTEND_URL: "https://westend-faucet.polkadot.io/drip/web" GITHUB_PAGES: "/${{ github.event.repository.name }}" STATIC: true diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 75600c63..a2ff326b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -171,15 +171,6 @@ deploy-rococo: DOMAIN: parity-testnet APP: substrate-faucet-rococo -deploy-rococo-ink-docs: - stage: deploy - <<: *deploy-prod-refs - extends: .deploy-with-argocd - environment: rococo-ink-docs - variables: - DOMAIN: parity-testnet - APP: substrate-faucet-rococo-ink-docs - deploy-westend: stage: deploy <<: *deploy-prod-refs From 2feda758948ea9c8e41d664cd7d504bb4b9e1c2e Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Mon, 10 Jul 2023 12:21:33 +0200 Subject: [PATCH 25/94] Use testcontainers for e2e tests Key points here: * application container is built manually, in order to reuse cache * changed startup, so web part would be last to start, and listening to web port might be considered as "ready" event. This change will be essential for proper service replication. Things that are now being waited for are matrix sync and `isReady` on polkadotjs api * had to create additional Dockerfile for synapse, as I couldn't solve permissions issue through testcontainers. However, building that is super fast, fine to do every time Fixes #202 --- .github/workflows/E2E.yml | 15 +- .gitignore | 1 + e2e/README.md | 23 +- e2e/bootstrap.sh | 68 ---- e2e/docker-compose.deployment.yml | 16 - e2e/docker-compose.infrastructure.yml | 17 - e2e/matrix_container.Dockerfile | 7 + e2e/matrix_data/.gitignore | 1 - jest.e2e.config.js | 10 +- package.json | 3 + src/bot/index.ts | 26 +- src/bot/start.ts | 3 - src/dripper/polkadot/PolkadotActions.ts | 11 +- src/faucet.e2e.ts | 149 +++++--- src/start.ts | 13 +- src/test/matrixHelpers.ts | 117 ++++++ src/test/setupE2E.ts | 141 +++++++ yarn.lock | 481 +++++++++++++++++++++++- 18 files changed, 887 insertions(+), 215 deletions(-) delete mode 100755 e2e/bootstrap.sh delete mode 100644 e2e/docker-compose.deployment.yml delete mode 100644 e2e/docker-compose.infrastructure.yml create mode 100644 e2e/matrix_container.Dockerfile delete mode 100644 e2e/matrix_data/.gitignore delete mode 100644 src/bot/start.ts create mode 100644 src/test/matrixHelpers.ts create mode 100644 src/test/setupE2E.ts diff --git a/.github/workflows/E2E.yml b/.github/workflows/E2E.yml index 269e5adf..df2981d9 100644 --- a/.github/workflows/E2E.yml +++ b/.github/workflows/E2E.yml @@ -4,6 +4,7 @@ on: push: branches: - main + jobs: e2e: timeout-minutes: 15 @@ -31,14 +32,8 @@ jobs: source wait_until.sh 'curl -s "127.0.0.1:9933"' source wait_until.sh 'curl -s "127.0.0.1:9934"' working-directory: e2e - - name: Bootstrap Matrix - run: | - chmod -R o+rwx ./e2e/matrix_data - ./e2e/bootstrap.sh - - name: Run the faucet bot and backend - run: | - docker-compose -f e2e/docker-compose.deployment.yml up --build -d - ./e2e/wait_until.sh 'curl -s "127.0.0.1:5555"' + - name: Build faucet + run: yarn build:docker - name: Run the E2E tests run: yarn test:e2e - name: Debug Polkadot logs @@ -46,7 +41,7 @@ jobs: run: cat e2e/polkadot.txt - name: Debug Matrix logs if: failure() - run: docker-compose -f e2e/docker-compose.infrastructure.yml logs matrix + run: cat e2e/containter_logs/faucet-test-matrix.log - name: Debug faucet logs if: failure() - run: docker-compose -f e2e/docker-compose.deployment.yml logs faucet + run: cat e2e/containter_logs/faucet-test-app.log diff --git a/.gitignore b/.gitignore index aabe2197..08cd09fb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ node_modules storage.db sqlite.db build +e2e/containter_logs # Autogenerated env.*.config.json.d.ts diff --git a/e2e/README.md b/e2e/README.md index df1ffe5b..5f1b4197 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -104,31 +104,20 @@ curl localhost:9934 # Expecting: Used HTTP Method is not allowed. POST or OPTIONS is required ``` -3. Bootstrap the infrastructure and configuration - -The next step is to run the scripts that will perform the following: - -- Start the Synapse (Matrix) server -- Create Matrix users, rooms, invitations -- Prepare a `.env` file with necessary configuration for the fuacet - -```bash -./e2e/bootstrap.sh -``` - -4. Start the faucet - -Finally, we start the faucet which is the code that's being tested. +3. Build the faucet ```bash -docker-compose -f e2e/docker-compose.deployment.yml up --build +yarn build:docker ``` -5. Run the tests +4. Run the tests ```bash yarn test:e2e ``` +Logs of the application container will be avaiable at `e2e/containter_logs/faucet-test-app.log` +Logs of matrix container will be avaiable at `e2e/containter_logs/faucet-test-matrix.log` + The whole suite of tests can take tens of seconds, because it depends on the blockchain to mine blocks and execute the XCM teleportation process. diff --git a/e2e/bootstrap.sh b/e2e/bootstrap.sh deleted file mode 100755 index 718d5a3f..00000000 --- a/e2e/bootstrap.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -cd $(dirname $0) - -# Clean up potential data from previous runs, -# in order to start with a clean state. -docker-compose -f docker-compose.infrastructure.yml down -v -rm -rf ./matrix_data/homeserver.db* ../sqlite.db -docker network rm faucet-e2e || true - - -# Make sure Polkadot with a parachain are up. -# They should be started before running this script. -source wait_until.sh 'curl -s "127.0.0.1:9933"' -source wait_until.sh 'curl -s "127.0.0.1:9934"' - - -# Start Matrix and wait until it is up. -docker network create faucet-e2e -docker-compose -f docker-compose.infrastructure.yml up -d -source wait_until.sh 'curl -s "127.0.0.1:8008"' - - -# Generate users: -# one admin to create rooms, one faucet bot, one user that will be requesting funds. -docker exec e2e-matrix register_new_matrix_user \ - --user admin --password admin -c /data/homeserver.yaml --admin -docker exec e2e-matrix register_new_matrix_user \ - --user bot --password bot -c /data/homeserver.yaml --no-admin -docker exec e2e-matrix register_new_matrix_user \ - --user user --password user -c /data/homeserver.yaml --no-admin - - -# Retrieve access tokens (by logging in). -MATRIX_URL="http://localhost:8008" -ADMIN_ACCESS_TOKEN=$(curl -s -X POST $MATRIX_URL/_matrix/client/v3/login -H "Content-Type: application/json" -d '{"type":"m.login.password", "user":"admin", "password":"admin"}' | jq -r .access_token) -BOT_ACCESS_TOKEN=$(curl -s -X POST $MATRIX_URL/_matrix/client/v3/login -H "Content-Type: application/json" -d '{"type":"m.login.password", "user":"bot", "password":"bot"}' | jq -r .access_token) -USER_ACCESS_TOKEN=$(curl -s -X POST $MATRIX_URL/_matrix/client/v3/login -H "Content-Type: application/json" -d '{"type":"m.login.password", "user":"user", "password":"user"}' | jq -r .access_token) - - -# Create the faucet room and invite interested parties. -ROOM_ID=$(curl -s -X POST -d '{"room_alias_name":"faucet"}' "$MATRIX_URL/_matrix/client/v3/createRoom?access_token=$ADMIN_ACCESS_TOKEN" | jq -r .room_id) -curl -s -X POST -d '{"user_id":"@bot:parity.io"}' "$MATRIX_URL/_matrix/client/v3/rooms/$ROOM_ID/invite?access_token=$ADMIN_ACCESS_TOKEN" -curl -s -X POST -d '{"user_id":"@user:parity.io"}' "$MATRIX_URL/_matrix/client/v3/rooms/$ROOM_ID/invite?access_token=$ADMIN_ACCESS_TOKEN" -curl -s -X POST -d '{}' "$MATRIX_URL/_matrix/client/v3/rooms/$ROOM_ID/join?access_token=$BOT_ACCESS_TOKEN" -curl -s -X POST -d '{}' "$MATRIX_URL/_matrix/client/v3/rooms/$ROOM_ID/join?access_token=$USER_ACCESS_TOKEN" - - -# Prepare the .env that will be used to run the faucet bot and server. -cat << EOF > ./.env -SMF_CONFIG_NETWORK=e2e - -SMF_CONFIG_MATRIX_ACCESS_TOKEN="$BOT_ACCESS_TOKEN" -SMF_CONFIG_MATRIX_BOT_USER_ID="@bot:parity.io" -SMF_CONFIG_FAUCET_IGNORE_LIST="" -SMF_CONFIG_MATRIX_SERVER="http://matrix:8008" -SMF_CONFIG_DEPLOYED_REF=local -SMF_CONFIG_DEPLOYED_TIME=local - -SMF_CONFIG_FAUCET_ACCOUNT_MNEMONIC="//Alice" -SMF_CONFIG_PORT=5555 - -# Local Zombienet relaychain node. -SMF_CONFIG_DEPLOYED_REF=local -SMF_CONFIG_DEPLOYED_TIME=local -SMF_CONFIG_EXTERNAL_ACCESS=true -SMF_CONFIG_RECAPTCHA_SECRET="6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" # Public testing secret, will accept all tokens. -EOF diff --git a/e2e/docker-compose.deployment.yml b/e2e/docker-compose.deployment.yml deleted file mode 100644 index 81d8186a..00000000 --- a/e2e/docker-compose.deployment.yml +++ /dev/null @@ -1,16 +0,0 @@ -networks: - faucet-e2e: - external: true -services: - faucet: - build: - dockerfile: Dockerfile - context: ../ - ports: - - "${SMF_CONFIG_PORT}:${SMF_CONFIG_PORT}" - env_file: - - ./.env - networks: - - faucet-e2e - extra_hosts: - - "host.docker.internal:host-gateway" diff --git a/e2e/docker-compose.infrastructure.yml b/e2e/docker-compose.infrastructure.yml deleted file mode 100644 index 0af732c0..00000000 --- a/e2e/docker-compose.infrastructure.yml +++ /dev/null @@ -1,17 +0,0 @@ -networks: - faucet-e2e: - external: true -services: - matrix: - container_name: e2e-matrix - image: matrixdotorg/synapse:v1.76.0 - ports: - - "8008:8008" - networks: - - faucet-e2e - volumes: - - "./matrix_data:/data" - environment: - SYNAPSE_SERVER_NAME: "parity.io" - SYNAPSE_REPORT_STATS: "no" - command: "run" diff --git a/e2e/matrix_container.Dockerfile b/e2e/matrix_container.Dockerfile new file mode 100644 index 00000000..fe67dbfa --- /dev/null +++ b/e2e/matrix_container.Dockerfile @@ -0,0 +1,7 @@ +FROM matrixdotorg/synapse:v1.87.0 + +# Tried to solve this with testcontainers, failed +COPY matrix_data /data +RUN chown 991:991 -R /data + +ENTRYPOINT ["/start.py"] diff --git a/e2e/matrix_data/.gitignore b/e2e/matrix_data/.gitignore deleted file mode 100644 index 7ab33b72..00000000 --- a/e2e/matrix_data/.gitignore +++ /dev/null @@ -1 +0,0 @@ -homeserver.db* diff --git a/jest.e2e.config.js b/jest.e2e.config.js index 9f0542e6..a440e561 100644 --- a/jest.e2e.config.js +++ b/jest.e2e.config.js @@ -1,8 +1,4 @@ -const commonConfig = require("./jest.config") +const commonConfig = require("./jest.config"); -/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ -module.exports = { - ...commonConfig, - testMatch: [ "**/?(*.)+(e2e).[jt]s?(x)" ], - testTimeout: 60_000 -}; +/** @type {import("ts-jest/dist/types").InitialOptionsTsJest} */ +module.exports = { ...commonConfig, testMatch: ["**/?(*.)+(e2e).[jt]s?(x)"], testTimeout: 60_000 }; diff --git a/package.json b/package.json index 04f26d89..cf539bfe 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "prebuild": "yarn generate:types", "generate:types": "echo \"declare const schema: $(cat env.faucet.config.json); export default schema;\" > env.faucet.config.json.d.ts", "build": "tsc", + "build:docker": "docker build -t polkadot-testnet-faucet .", "start": "node ./build/src/start.js", "start:backend": "node ./build/src/server/start.js", "start:bot": "node ./build/src/bot/start.js", @@ -80,10 +81,12 @@ "@types/supertest": "^2.0.12", "eslint-plugin-security": "^1.5.0", "jest": "^29.4.2", + "joi": "^17.6.4", "lint-staged": "^12.3.8", "nodemon": "^2.0.19", "simple-git-hooks": "^2.7.0", "supertest": "^6.3.3", + "testcontainers": "^9.9.1", "ts-jest": "^29.0.5", "ts-node": "^10.9.1", "typescript": "^4.6.3" diff --git a/src/bot/index.ts b/src/bot/index.ts index 175478b7..d44744ec 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -183,9 +183,29 @@ bot.on(mSDK.RoomEvent.Timeline, (event: mSDK.MatrixEvent) => { } }); -export const startBot = () => { - bot.startClient({ initialSyncLimit: 0 }).catch((e) => logger.error(e)); -}; +export async function startBot(): Promise { + // Resolving on error allows web server to start even if matrix is imparied + await new Promise((resolve) => { + bot + .startClient({ initialSyncLimit: 0 }) + .then(() => { + bot.once(mSDK.ClientEvent.Sync, (state) => { + if (state === "PREPARED") { + resolve(); + } + }); + + bot.once(mSDK.ClientEvent.SyncUnexpectedError, (e) => { + logger.error("Matrix bot SyncUnexpectedError: ", e); + resolve(); + }); + }) + .catch((e) => { + logger.error("Matrix bot did not start: ", e); + resolve(); + }); + }); +} function formattedSuccessfulDripResponse( dripAmount: bigint, diff --git a/src/bot/start.ts b/src/bot/start.ts deleted file mode 100644 index 8d11e333..00000000 --- a/src/bot/start.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { startBot } from "./index"; - -startBot(); diff --git a/src/dripper/polkadot/PolkadotActions.ts b/src/dripper/polkadot/PolkadotActions.ts index 8624c458..af706b97 100644 --- a/src/dripper/polkadot/PolkadotActions.ts +++ b/src/dripper/polkadot/PolkadotActions.ts @@ -29,9 +29,15 @@ const rpcTimeout = (service: string) => { export class PolkadotActions { account: KeyringPair | undefined; #faucetBalance: bigint | undefined; + isReady: Promise; constructor() { logger.info("🚰 Plip plop - Creating the faucets's account"); + let makeReady: () => void; + + this.isReady = new Promise((resolve) => { + makeReady = resolve; + }); try { const keyring = new Keyring({ type: "sr25519" }); @@ -41,13 +47,12 @@ export class PolkadotActions { // We do want the following to just start and run // TODO: Adding a subscription would be better but the server supports on http for now - const updateFaucetBalance = (log = false) => { + const updateFaucetBalance = (log = false) => this.updateFaucetBalance().then(() => { if (log) logger.info("Fetched faucet balance 💰"); setTimeout(updateFaucetBalance, balancePollIntervalMs); }); - }; - updateFaucetBalance(true); + updateFaucetBalance(true).then(makeReady); }); } catch (error) { logger.error(error); diff --git a/src/faucet.e2e.ts b/src/faucet.e2e.ts index 7b4a4b3a..8216250b 100644 --- a/src/faucet.e2e.ts +++ b/src/faucet.e2e.ts @@ -1,19 +1,23 @@ -import { until } from "@eng-automation/js"; +import { until, validatedFetch } from "@eng-automation/js"; import { ApiPromise } from "@polkadot/api"; import { createTestKeyring } from "@polkadot/keyring"; import { WsProvider } from "@polkadot/rpc-provider"; import { BN } from "@polkadot/util"; import { randomAsU8a } from "@polkadot/util-crypto"; -import axios from "axios"; +import Joi from "joi"; + +import { getLatestMessage, postMessage } from "./test/matrixHelpers"; +import { E2ESetup, setup, teardown } from "./test/setupE2E"; const randomAddress = () => createTestKeyring().addFromSeed(randomAsU8a(32)).address; describe("Faucet E2E", () => { - const matrix = axios.create({ baseURL: "http://localhost:8008" }); - const webEndpoint = axios.create({ baseURL: "http://localhost:5555" }); const PARACHAIN_ID = 1000; // From the zombienet config. let roomId: string; let userAccessToken: string; + let matrixUrl: string; + let webEndpoint: string; + let e2eSetup: E2ESetup; const polkadotApi = new ApiPromise({ // Zombienet relaychain node. @@ -27,53 +31,39 @@ describe("Faucet E2E", () => { types: { Address: "AccountId", LookupSource: "AccountId" }, }); - const login = async (user: string, password: string): Promise => { - const result = await matrix.post("_matrix/client/v3/login", { type: "m.login.password", user, password }); - return result.data.access_token; - }; - - const postMessage = async (body: string) => { - await matrix.post(`/_matrix/client/v3/rooms/${roomId}/send/m.room.message?access_token=${userAccessToken}`, { - msgtype: "m.text", - body, - }); - }; - - const getLatestMessage = async (): Promise<{ sender: string; body: string }> => { - const latestMessage = await matrix.get( - `/_matrix/client/v3/rooms/${roomId}/messages?dir=b&limit=1&access_token=${userAccessToken}`, - ); - const chunk = latestMessage.data.chunk[0]; - return { sender: chunk.sender, body: chunk.content.body }; - }; - const getUserBalance = async (userAddress: string, api: ApiPromise = polkadotApi) => { const { data } = await api.query.system.account(userAddress); return data.free.toBn(); }; beforeAll(async () => { - userAccessToken = await login("user", "user"); + e2eSetup = await setup(); + roomId = e2eSetup.matrixSetup.roomId; + userAccessToken = e2eSetup.matrixSetup.userAccessToken; + matrixUrl = e2eSetup.matrixSetup.matrixUrl; + webEndpoint = e2eSetup.webEndpoint; + await polkadotApi.isReady; await parachainApi.isReady; - /* - We should have already joined the room, but we repeat it to retrieve the room id. - We cannot send a message to a room via alias. - */ - const room = await matrix.post(`/_matrix/client/v3/join/%23faucet:parity.io?access_token=${userAccessToken}`, {}); - roomId = room.data.room_id; }); afterAll(async () => { await polkadotApi.disconnect(); await parachainApi.disconnect(); + if (e2eSetup) teardown(e2eSetup); }); test("The bot responds to the !balance message", async () => { - await postMessage("!balance"); + await postMessage(matrixUrl, { roomId, accessToken: userAccessToken, body: "!balance" }); - await until(async () => (await getLatestMessage()).sender === "@bot:parity.io", 500, 10, "Bot did not reply."); - const botMessage = await getLatestMessage(); + await until( + async () => + (await getLatestMessage(matrixUrl, { roomId, accessToken: userAccessToken })).sender === "@bot:parity.io", + 500, + 20, + "Bot did not reply.", + ); + const botMessage = await getLatestMessage(matrixUrl, { roomId, accessToken: userAccessToken }); expect(botMessage.body).toMatch(/^The faucet has (999.*|100.*) UNITs remaining.$/); }); @@ -81,10 +71,16 @@ describe("Faucet E2E", () => { const userAddress = randomAddress(); const initialBalance = await getUserBalance(userAddress); - await postMessage(`!drip ${userAddress}`); + await postMessage(matrixUrl, { roomId, accessToken: userAccessToken, body: `!drip ${userAddress}` }); - await until(async () => (await getLatestMessage()).sender === "@bot:parity.io", 500, 10, "Bot did not reply."); - const botMessage = await getLatestMessage(); + await until( + async () => + (await getLatestMessage(matrixUrl, { roomId, accessToken: userAccessToken })).sender === "@bot:parity.io", + 500, + 10, + "Bot did not reply.", + ); + const botMessage = await getLatestMessage(matrixUrl, { roomId, accessToken: userAccessToken }); expect(botMessage.body).toContain("Sent @user:parity.io 10 UNITs."); await until( async () => (await getUserBalance(userAddress)).gt(initialBalance), @@ -98,10 +94,20 @@ describe("Faucet E2E", () => { const userAddress = randomAddress(); const initialBalance = await getUserBalance(userAddress, parachainApi); - await postMessage(`!drip ${userAddress}:${PARACHAIN_ID}`); + await postMessage(matrixUrl, { + roomId, + accessToken: userAccessToken, + body: `!drip ${userAddress}:${PARACHAIN_ID}`, + }); - await until(async () => (await getLatestMessage()).sender === "@bot:parity.io", 500, 10, "Bot did not reply."); - const botMessage = await getLatestMessage(); + await until( + async () => + (await getLatestMessage(matrixUrl, { roomId, accessToken: userAccessToken })).sender === "@bot:parity.io", + 500, + 10, + "Bot did not reply.", + ); + const botMessage = await getLatestMessage(matrixUrl, { roomId, accessToken: userAccessToken }); expect(botMessage.body).toContain("Sent @user:parity.io 10 UNITs."); await until( @@ -115,29 +121,43 @@ describe("Faucet E2E", () => { test("The bot fails on invalid chain id", async () => { const userAddress = randomAddress(); - await postMessage(`!drip ${userAddress}:123`); + await postMessage(matrixUrl, { roomId, accessToken: userAccessToken, body: `!drip ${userAddress}:123` }); - await until(async () => (await getLatestMessage()).sender === "@bot:parity.io", 500, 10, "Bot did not reply."); - const botMessage = await getLatestMessage(); + await until( + async () => + (await getLatestMessage(matrixUrl, { roomId, accessToken: userAccessToken })).sender === "@bot:parity.io", + 500, + 10, + "Bot did not reply.", + ); + const botMessage = await getLatestMessage(matrixUrl, { roomId, accessToken: userAccessToken }); expect(botMessage.body).toContain("Parachain invalid. Be sure to set a value between 1000 and 9999"); }); test("The web endpoint responds to a balance query", async () => { - const result = await webEndpoint.get("/balance"); + const result = await validatedFetch<{ + balance: string; + }>(`${webEndpoint}/balance`, Joi.object({ balance: Joi.string() }), {}); - expect(result.status).toEqual(200); - expect("balance" in result.data).toBeTruthy(); - expect(new BN(result.data.balance).gtn(0)).toBeTruthy(); + expect("balance" in result).toBeTruthy(); + expect(new BN(result.balance).gtn(0)).toBeTruthy(); }); test("The web endpoint drips to a given address", async () => { const userAddress = randomAddress(); const initialBalance = await getUserBalance(userAddress); - const result = await webEndpoint.post("/drip/web", { address: userAddress, recaptcha: "anything goes" }); + const result = await validatedFetch<{ + hash: string; + }>(`${webEndpoint}/drip/web`, Joi.object({ hash: Joi.string() }), { + init: { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ address: userAddress, recaptcha: "anything goes" }), + }, + }); - expect(result.status).toEqual(200); - expect("hash" in result.data).toBeTruthy(); + expect(result.hash).toBeTruthy(); await until( async () => (await getUserBalance(userAddress)).gt(initialBalance), 500, @@ -150,14 +170,17 @@ describe("Faucet E2E", () => { const userAddress = randomAddress(); const initialBalance = await getUserBalance(userAddress, parachainApi); - const result = await webEndpoint.post("/drip/web", { - address: userAddress, - recaptcha: "anything goes", - parachain_id: "1000", + const result = await validatedFetch<{ + hash: string; + }>(`${webEndpoint}/drip/web`, Joi.object({ hash: Joi.string() }), { + init: { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ address: userAddress, recaptcha: "anything goes", parachain_id: "1000" }), + }, }); - expect(result.status).toEqual(200); - expect("hash" in result.data).toBeTruthy(); + expect(result.hash).toBeTruthy(); await until( async () => (await getUserBalance(userAddress, parachainApi)).gt(initialBalance), 1000, @@ -169,14 +192,18 @@ describe("Faucet E2E", () => { test("The web endpoint fails on wrong parachain", async () => { const userAddress = randomAddress(); - const promise = webEndpoint.post("/drip/web", { - address: userAddress, - recaptcha: "anything goes", - parachain_id: "100", + const promise = validatedFetch<{ + hash: string; + }>(`${webEndpoint}/drip/web`, Joi.object({ hash: Joi.string() }), { + init: { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ address: userAddress, recaptcha: "anything goes", parachain_id: "100" }), + }, }); await expect(promise).rejects.toThrow(); await expect(promise).rejects.toMatchObject({ - response: { data: { error: "Parachain invalid. Be sure to set a value between 1000 and 9999" } }, + message: expect.stringMatching("Parachain invalid. Be sure to set a value between 1000 and 9999"), }); }); }); diff --git a/src/start.ts b/src/start.ts index 7d1cfcbf..362590ba 100644 --- a/src/start.ts +++ b/src/start.ts @@ -1,5 +1,14 @@ import { startBot } from "./bot"; +import polkadotActions from "./dripper/polkadot/PolkadotActions"; import { startServer } from "./server"; -startBot(); -startServer(); +(async () => { + // Waiting for bot to start first. + // Thus, listening to port on the server side can be treated as "ready" signal. + await startBot(); + await polkadotActions.isReady; + startServer(); +})().catch((e) => { + console.error("Start failed:", e); + process.exit(1); +}); diff --git a/src/test/matrixHelpers.ts b/src/test/matrixHelpers.ts new file mode 100644 index 00000000..f07026f9 --- /dev/null +++ b/src/test/matrixHelpers.ts @@ -0,0 +1,117 @@ +import { validatedFetch } from "@eng-automation/js"; +import Joi from "joi"; + + +type AccessTokenResponse = { access_token: string }; + +export async function getAccessToken(matrixUrl: string, params: { user: string, password: string }): Promise { + const response = await validatedFetch( + `${matrixUrl}/_matrix/client/v3/login` + , Joi.object({ access_token: Joi.string().required() }), { + init: { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ "type": "m.login.password", "user": params.user, "password": params.password }) + } + }); + + return response.access_token; +} + +type RoomResponse = { room_id: string }; + +export async function createRoom(matrixUrl: string, params: { + roomAliasName: string, + accessToken: string +}): Promise { + const { room_id: roomId } = await validatedFetch( + `${matrixUrl}/_matrix/client/v3/createRoom?access_token=${params.accessToken}` + , Joi.object({ room_id: Joi.string().required() }), { + init: { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ "room_alias_name": "faucet" }) + } + }); + + return roomId; +} + +export async function inviteUser(matrixUrl: string, params: { + roomId: string, + accessToken: string, + userId: string +}): Promise { + await validatedFetch<{}>( + `${matrixUrl}/_matrix/client/v3/rooms/${params.roomId}/invite?access_token=${params.accessToken}` + , Joi.any(), { + init: { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ "user_id": params.userId }) + } + }); +} + +export async function joinRoom(matrixUrl: string, params: { + roomId: string, + accessToken: string, +}): Promise { + await validatedFetch<{}>( + `${matrixUrl}/_matrix/client/v3/rooms/${params.roomId}/join?access_token=${params.accessToken}` + , Joi.any(), { + init: { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({}) + } + }); +} + +export async function postMessage(matrixUrl: string, params: { + roomId: string, + accessToken: string, + body: string +}): Promise { + await validatedFetch<{}>( + `${matrixUrl}/_matrix/client/v3/rooms/${params.roomId}/send/m.room.message?access_token=${params.accessToken}`, + Joi.any(), { + init: { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + msgtype: "m.text", + body: params.body + }) + } + }); +} + +type LatestMessageResponse = { + chunk: [{ + sender: string, + content: { body: string } + }] +} + +export async function getLatestMessage(matrixUrl: string, params: { + roomId: string, + accessToken: string, +}): Promise<{ + sender: string, + body: string +}> { + const res = await validatedFetch( + `${matrixUrl}/_matrix/client/v3/rooms/${params.roomId}/messages?dir=b&limit=1&access_token=${params.accessToken}`, + Joi.object({ + chunk: Joi.array().items(Joi.object({ + sender: Joi.string().required(), + content: Joi.object({ body: Joi.string().required() }) + }).required()) + }), { init: { headers: { "Content-Type": "application/json" } } }); + + return { + sender: res.chunk[0].sender, + body: res.chunk[0].content.body + }; +} diff --git a/src/test/setupE2E.ts b/src/test/setupE2E.ts new file mode 100644 index 00000000..8521138b --- /dev/null +++ b/src/test/setupE2E.ts @@ -0,0 +1,141 @@ +import { + GenericContainer, + Wait, + StartedTestContainer +} from "testcontainers"; + +import path from "path"; +import { promises as fs } from "fs"; +import { createRoom, getAccessToken, inviteUser, joinRoom } from "./matrixHelpers"; +import { Readable } from "stream"; + +export type E2ESetup = { + matrixContainer: StartedTestContainer; + appContainer: StartedTestContainer; + matrixSetup: MatrixSetup; + webEndpoint: string; +} +export type MatrixSetup = { + botAccessToken: string, + userAccessToken: string, + roomId: string; + matrixUrl: string; + matrixPort: number; +}; + +const matrixDataDir = path.join(process.cwd(), "e2e", "matrix_data"); +const containterLogsDir = path.join(process.cwd(), "e2e", "containter_logs"); +const start = Date.now(); + +// Taking all output to e2e/*.container.log +function logConsumer(name: string): (stream: Readable) => Promise { + return async (stream: Readable) => { + const logsfile = await fs.open(path.join(containterLogsDir, `${name}.log`), "w"); + stream.on("data", line => logsfile.write(`[${Date.now() - start}ms] ${line}`)); + stream.on("err", line => logsfile.write(`[${Date.now() - start}ms] ${line}`)); + stream.on("end", () => { + logsfile.write("Stream closed\n"); + logsfile.close(); + }); + }; +} + +export async function setup(): Promise { + await fs.mkdir(containterLogsDir, { recursive: true }); + + const matrixContainer = await setupMatrixContainer(); + const matrixSetup = await setupMatrix(matrixContainer); + + const appContainer = await setupAppContainer({ + botAccessToken: matrixSetup.botAccessToken, + matrixPort: matrixSetup.matrixPort + }); + + const webEndpoint = `http://localhost:${appContainer.getFirstMappedPort()}`; + + return { + matrixContainer, + appContainer, + matrixSetup, + webEndpoint + }; +} + +export async function teardown(setup: E2ESetup): Promise { + await setup.appContainer.stop(); + await setup.matrixContainer.stop(); +} + +async function setupMatrixContainer(): Promise { + const image = await GenericContainer.fromDockerfile("e2e", "matrix_container.Dockerfile").build(); + + const matrixContainer = image + .withExposedPorts(8008) + .withEnvironment({ + SYNAPSE_SERVER_NAME: "parity.io", + SYNAPSE_REPORT_STATS: "no" + }) + .withCommand(["run"]) + .withWaitStrategy(Wait.forHealthCheck()) + .withLogConsumer(logConsumer("faucet-test-matrix")) + .start(); + + return matrixContainer; +} + +async function setupMatrix(matrixContainer: StartedTestContainer): Promise { + // Generate users: + // one admin to create rooms, one faucet bot, one user that will be requesting funds. + await matrixContainer.exec(["register_new_matrix_user", "--user", "admin", "--password", "admin", "-c", "/data/homeserver.yaml", "--admin"]); + await matrixContainer.exec(["register_new_matrix_user", "--user", "bot", "--password", "bot", "-c", "/data/homeserver.yaml", "--no-admin"]); + await matrixContainer.exec(["register_new_matrix_user", "--user", "user", "--password", "user", "-c", "/data/homeserver.yaml", "--no-admin"]); + + const matrixPort = matrixContainer.getFirstMappedPort(); + const matrixUrl = `http://localhost:${matrixPort}`; + + // Retrieve access tokens (by logging in). + const adminAccessToken = await getAccessToken(matrixUrl, { user: "admin", password: "admin" }); + const botAccessToken = await getAccessToken(matrixUrl, { user: "bot", password: "bot" }); + const userAccessToken = await getAccessToken(matrixUrl, { user: "user", password: "user" }); + + // Create the faucet room and invite interested parties. + const roomId = await createRoom(matrixUrl, { roomAliasName: "faucet", accessToken: adminAccessToken }); + + await inviteUser(matrixUrl, { roomId, userId: "@bot:parity.io", accessToken: adminAccessToken }); + await inviteUser(matrixUrl, { roomId, userId: "@user:parity.io", accessToken: adminAccessToken }); + + await joinRoom(matrixUrl, { roomId, accessToken: botAccessToken }); + await joinRoom(matrixUrl, { roomId, accessToken: userAccessToken }); + + return { botAccessToken, userAccessToken, roomId, matrixUrl, matrixPort }; +} + +async function setupAppContainer(params: { + botAccessToken: string, + matrixPort: number +}): Promise { + const appContainer = await new GenericContainer("polkadot-testnet-faucet") + .withExposedPorts(5555) + .withEnvironment({ + SMF_CONFIG_NETWORK: "e2e", + + SMF_CONFIG_MATRIX_ACCESS_TOKEN: params.botAccessToken, + SMF_CONFIG_MATRIX_BOT_USER_ID: "@bot:parity.io", + SMF_CONFIG_FAUCET_IGNORE_LIST: "", + SMF_CONFIG_MATRIX_SERVER: `http://host.docker.internal:${params.matrixPort}`, + SMF_CONFIG_DEPLOYED_REF: "local", + SMF_CONFIG_DEPLOYED_TIME: "local", + SMF_CONFIG_FAUCET_ACCOUNT_MNEMONIC: "//Alice", + SMF_CONFIG_PORT: "5555", + + // Local Zombienet relaychain node. + SMF_CONFIG_EXTERNAL_ACCESS: "true", + // Public testing secret, will accept all tokens. + SMF_CONFIG_RECAPTCHA_SECRET: "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" + }) + .withWaitStrategy(Wait.forListeningPorts()) + .withExtraHosts([{ host: "host.docker.internal", ipAddress: "host-gateway" }]) + .withLogConsumer(logConsumer("faucet-test-app")); + + return await appContainer.start(); +} diff --git a/yarn.lock b/yarn.lock index 141c978a..878ac192 100644 --- a/yarn.lock +++ b/yarn.lock @@ -480,6 +480,11 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@balena/dockerignore@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz#9ffe4726915251e8eb69f44ef3547e0da2c03e0d" + integrity sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q== + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1344,6 +1349,13 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== +"@types/archiver@^5.3.2": + version "5.3.2" + resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-5.3.2.tgz#a9f0bcb0f0b991400e7766d35f6e19d163bdadcc" + integrity sha512-IctHreBuWE5dvBDz/0WeKtyVKVRs4h75IblxOACL92wU66v+HGAfEYAOyXkOFphvRJMhuXdI9huDXpX0FC6lCw== + dependencies: + "@types/readdir-glob" "*" + "@types/babel__core@^7.1.14": version "7.1.19" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" @@ -1416,6 +1428,22 @@ dependencies: "@types/node" "*" +"@types/docker-modem@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/docker-modem/-/docker-modem-3.0.2.tgz#c49c902e17364fc724e050db5c1d2b298c6379d4" + integrity sha512-qC7prjoEYR2QEe6SmCVfB1x3rfcQtUr1n4x89+3e0wSTMQ/KYCyf+/RAA9n2tllkkNc6//JMUZePdFRiGIWfaQ== + dependencies: + "@types/node" "*" + "@types/ssh2" "*" + +"@types/dockerode@^3.3.19": + version "3.3.19" + resolved "https://registry.yarnpkg.com/@types/dockerode/-/dockerode-3.3.19.tgz#59eb07550a102b397a9504083a6c50d811eed04c" + integrity sha512-7CC5yIpQi+bHXwDK43b/deYXteP3Lem9gdocVVHJPSRJJLMfbiOchQV3rDmAPkMw+n3GIVj7m1six3JW+VcwwA== + dependencies: + "@types/docker-modem" "*" + "@types/node" "*" + "@types/events@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -1494,6 +1522,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.13.tgz#5ed7ed7c662948335fcad6c412bb42d99ea754e3" integrity sha512-Y86MAxASe25hNzlDbsviXl8jQHb0RDvKt4c40ZJQ1Don0AAL0STLZSs4N+6gLEO55pedy7r2cLwS+ZDxPm/2Bw== +"@types/node@^18.11.18": + version "18.16.19" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.19.tgz#cb03fca8910fdeb7595b755126a8a78144714eea" + integrity sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA== + "@types/node@^18.16": version "18.16.18" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.18.tgz#85da09bafb66d4bc14f7c899185336d0c1736390" @@ -1514,6 +1547,13 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== +"@types/readdir-glob@*": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/readdir-glob/-/readdir-glob-1.1.1.tgz#27ac2db283e6aa3d110b14ff9da44fcd1a5c38b1" + integrity sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ== + dependencies: + "@types/node" "*" + "@types/request@^2.48.8": version "2.48.8" resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.8.tgz#0b90fde3b655ab50976cb8c5ac00faca22f5a82c" @@ -1549,6 +1589,28 @@ dependencies: "@types/node" "*" +"@types/ssh2-streams@*": + version "0.1.9" + resolved "https://registry.yarnpkg.com/@types/ssh2-streams/-/ssh2-streams-0.1.9.tgz#8ca51b26f08750a780f82ee75ff18d7160c07a87" + integrity sha512-I2J9jKqfmvXLR5GomDiCoHrEJ58hAOmFrekfFqmCFd+A6gaEStvWnPykoWUwld1PNg4G5ag1LwdA+Lz1doRJqg== + dependencies: + "@types/node" "*" + +"@types/ssh2@*": + version "1.11.12" + resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-1.11.12.tgz#f506e8a4dbacd3189391d36d283fcbfeb31ef57b" + integrity sha512-uvrvY6oN+3LiFiQmDCnuP65Qlryqvw5sbpKP7oce5cI/Ib3u3yY/DIkxIegT87MaVpnchUfSkEo2g9HeC7mUng== + dependencies: + "@types/node" "^18.11.18" + +"@types/ssh2@^0.5.48": + version "0.5.52" + resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-0.5.52.tgz#9dbd8084e2a976e551d5e5e70b978ed8b5965741" + integrity sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg== + dependencies: + "@types/node" "*" + "@types/ssh2-streams" "*" + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" @@ -1844,6 +1906,35 @@ anymatch@^3.0.3, anymatch@~3.1.2: resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== +archiver-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" + integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== + dependencies: + glob "^7.1.4" + graceful-fs "^4.2.0" + lazystream "^1.0.0" + lodash.defaults "^4.2.0" + lodash.difference "^4.5.0" + lodash.flatten "^4.4.0" + lodash.isplainobject "^4.0.6" + lodash.union "^4.6.0" + normalize-path "^3.0.0" + readable-stream "^2.0.0" + +archiver@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.1.tgz#21e92811d6f09ecfce649fbefefe8c79e57cbbb6" + integrity sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w== + dependencies: + archiver-utils "^2.1.0" + async "^3.2.3" + buffer-crc32 "^0.2.1" + readable-stream "^3.6.0" + readdir-glob "^1.0.0" + tar-stream "^2.2.0" + zip-stream "^4.1.0" + are-we-there-yet@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" @@ -1931,7 +2022,7 @@ asap@^2.0.0: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== -asn1@~0.2.3: +asn1@^0.2.6, asn1@~0.2.3: version "0.2.6" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== @@ -1948,6 +2039,11 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +async-lock@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.4.0.tgz#c8b6630eff68fbbdd8a5b6eb763dac3bfbb8bf02" + integrity sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ== + async-mutex@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df" @@ -1955,6 +2051,11 @@ async-mutex@^0.3.2: dependencies: tslib "^2.3.1" +async@^3.2.3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2052,7 +2153,12 @@ base-x@^4.0.0: resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== -bcrypt-pbkdf@^1.0.0: +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= @@ -2071,6 +2177,15 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" @@ -2118,6 +2233,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -2167,11 +2289,41 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +buildcheck@~0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238" + integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== + +busboy@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + +byline@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== + bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -2276,6 +2428,11 @@ chokidar@^3.5.2: optionalDependencies: fsevents "~2.3.2" +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + chownr@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" @@ -2394,6 +2551,16 @@ component-emitter@^1.3.0: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +compress-commons@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d" + integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ== + dependencies: + buffer-crc32 "^0.2.13" + crc32-stream "^4.0.2" + normalize-path "^3.0.0" + readable-stream "^3.6.0" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -2459,6 +2626,11 @@ core-util-is@1.0.2: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + cors@^2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" @@ -2467,6 +2639,27 @@ cors@^2.8.5: object-assign "^4" vary "^1" +cpu-features@~0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.8.tgz#a2d464b023b8ad09004c8cdca23b33f192f63546" + integrity sha512-BbHBvtYhUhksqTjr6bhNOjGgMnhwhGTQmOoZGD+K7BCaQDCuZl/Ve1ZxUSMRwVC4D/rkCPQ2MAIeYzrWyK7eEg== + dependencies: + buildcheck "~0.0.6" + nan "^2.17.0" + +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +crc32-stream@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007" + integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w== + dependencies: + crc-32 "^1.2.0" + readable-stream "^3.4.0" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -2634,6 +2827,32 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +docker-compose@^0.24.1: + version "0.24.1" + resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.24.1.tgz#09f9a2408395b8adf80feaacf65f6e2a4b119866" + integrity sha512-CVphzCi0Hmw/0CHlAzgiwhLsJjFRqnvpBYMYbf63bz6MON69ElgrfrgQTmgPtEjbifjgaptu3+Gea62vI+9jiA== + dependencies: + yaml "^2.2.2" + +docker-modem@^3.0.0: + version "3.0.8" + resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-3.0.8.tgz#ef62c8bdff6e8a7d12f0160988c295ea8705e77a" + integrity sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ== + dependencies: + debug "^4.1.1" + readable-stream "^3.5.0" + split-ca "^1.0.1" + ssh2 "^1.11.0" + +dockerode@^3.3.5: + version "3.3.5" + resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-3.3.5.tgz#7ae3f40f2bec53ae5e9a741ce655fff459745629" + integrity sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA== + dependencies: + "@balena/dockerignore" "^1.0.2" + docker-modem "^3.0.0" + tar-fs "~2.0.1" + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -2708,6 +2927,13 @@ encoding@^0.1.12: dependencies: iconv-lite "^0.6.2" +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + env-paths@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" @@ -3371,6 +3597,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + fs-extra@^10.0.1: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" @@ -3479,6 +3710,11 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-port@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== + get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -3736,6 +3972,11 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + ignore-by-default@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" @@ -3785,7 +4026,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4001,6 +4242,11 @@ is-weakref@^1.0.1, is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -4537,6 +4783,13 @@ known-css-properties@^0.27.0: resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.27.0.tgz#82a9358dda5fe7f7bd12b5e7142c0a205393c0c5" integrity sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg== +lazystream@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" + integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== + dependencies: + readable-stream "^2.0.5" + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -4618,6 +4871,26 @@ lodash-es@^4.17.21: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== + +lodash.difference@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" + integrity sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA== + +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + lodash.memoize@4.x: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -4628,6 +4901,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.union@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" + integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== + lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -4824,6 +5102,13 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^5.1.0: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" @@ -4883,6 +5168,11 @@ minizlib@^2.0.0, minizlib@^2.1.1: minipass "^3.0.0" yallist "^4.0.0" +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" @@ -4908,6 +5198,11 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +nan@^2.17.0: + version "2.17.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" + integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== + nanoid@^3.3.6: version "3.3.6" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" @@ -4948,6 +5243,13 @@ node-domexception@^1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== +node-fetch@^2.6.11: + version "2.6.12" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" + integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== + dependencies: + whatwg-url "^5.0.0" + node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -5125,7 +5427,7 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -once@^1.3.0, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -5358,6 +5660,11 @@ pretty-format@^29.0.0, pretty-format@^29.4.2: ansi-styles "^5.0.0" react-is "^18.0.0" +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -5384,6 +5691,13 @@ propagate@^2.0.0: resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== +properties-reader@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/properties-reader/-/properties-reader-2.2.0.tgz#41d837fe143d8d5f2386b6a869a1975c0b2c595c" + integrity sha512-CgVcr8MwGoBKK24r9TwHfZkLLaNFHQ6y4wgT9w/XzdpacOOi5ciH4hcuLechSDAwXsfrGQtI2JTutY2djOx2Ow== + dependencies: + mkdirp "^1.0.4" + proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -5402,6 +5716,14 @@ pstree.remy@^1.1.8: resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -5466,6 +5788,28 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +readable-stream@^2.0.0, readable-stream@^2.0.5: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" @@ -5475,6 +5819,13 @@ readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readdir-glob@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" + integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== + dependencies: + minimatch "^5.1.0" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -5605,7 +5956,7 @@ rfdc@^1.3.0: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== -rimraf@^3.0.2: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -5638,7 +5989,7 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.1: +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -5876,6 +6227,11 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +split-ca@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" + integrity sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -5892,6 +6248,25 @@ sqlite3@^5.1.2: optionalDependencies: node-gyp "8.x" +ssh-remote-port-forward@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz#72b0c5df8ec27ca300c75805cc6b266dee07e298" + integrity sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ== + dependencies: + "@types/ssh2" "^0.5.48" + ssh2 "^1.4.0" + +ssh2@^1.11.0, ssh2@^1.4.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.14.0.tgz#8f68440e1b768b66942c9e4e4620b2725b3555bb" + integrity sha512-AqzD1UCqit8tbOKoj6ztDDi1ffJZ2rV2SwlgrVVrHPkV5vWqGJOVp5pmtj18PunkPJAuKQsnInyKV+/Nb2bUnA== + dependencies: + asn1 "^0.2.6" + bcrypt-pbkdf "^1.0.2" + optionalDependencies: + cpu-features "~0.0.8" + nan "^2.17.0" + sshpk@^1.7.0: version "1.17.0" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" @@ -5940,6 +6315,11 @@ streamroller@^3.0.7: debug "^4.3.4" fs-extra "^10.0.1" +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + string-argv@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" @@ -6021,6 +6401,13 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -6124,6 +6511,37 @@ svelte@^3.54.0: resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.59.1.tgz#3de3d56b9165748f32f3131589b8d183cabe7449" integrity sha512-pKj8fEBmqf6mq3/NfrB9SLtcJcUvjYSWyePlfCqN9gujLB25RitWK8PvFzlwim6hD/We35KbPlRteuA6rnPGcQ== +tar-fs@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-fs@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2" + integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.0.0" + +tar-stream@^2.0.0, tar-stream@^2.1.4, tar-stream@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: version "6.1.11" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" @@ -6145,6 +6563,27 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +testcontainers@^9.9.1: + version "9.9.1" + resolved "https://registry.yarnpkg.com/testcontainers/-/testcontainers-9.9.1.tgz#0af23e56c52eb7cd0e3bc63cc3e059abf3f1705a" + integrity sha512-44iDiyK0JzK5FbqdrKjmE/8QSZhvCFxh0d/OfG+R9fkznU5KVCYeeUsPULkVRIi210X4FAitSXra1ZU591Zlqw== + dependencies: + "@balena/dockerignore" "^1.0.2" + "@types/archiver" "^5.3.2" + "@types/dockerode" "^3.3.19" + archiver "^5.3.1" + async-lock "^1.4.0" + byline "^5.0.0" + debug "^4.3.4" + docker-compose "^0.24.1" + dockerode "^3.3.5" + get-port "^5.1.1" + node-fetch "^2.6.11" + properties-reader "^2.2.0" + ssh-remote-port-forward "^1.0.4" + tar-fs "^2.1.1" + tmp "^0.2.1" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -6155,6 +6594,13 @@ through@^2.3.8: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +tmp@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -6348,6 +6794,13 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== +undici@^5.22.1: + version "5.22.1" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.22.1.tgz#877d512effef2ac8be65e695f3586922e1a57d7b" + integrity sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw== + dependencies: + busboy "^1.6.0" + unhomoglyph@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/unhomoglyph/-/unhomoglyph-1.0.6.tgz#ea41f926d0fcf598e3b8bb2980c2ddac66b081d3" @@ -6392,7 +6845,7 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -util-deprecate@^1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== @@ -6573,6 +7026,11 @@ yaml@^1.10.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.2.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" + integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== + yargs-parser@^21.0.1, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" @@ -6600,3 +7058,12 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zip-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" + integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A== + dependencies: + archiver-utils "^2.1.0" + compress-commons "^4.1.0" + readable-stream "^3.6.0" From 702905036066c68172576230b224a6a69d4eccad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jul 2023 10:39:17 +0000 Subject: [PATCH 26/94] Bump semver from 5.7.1 to 5.7.2 (#319) --- yarn.lock | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/yarn.lock b/yarn.lock index 878ac192..36e39a89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6020,36 +6020,22 @@ sdp-transform@^2.14.1: resolved "https://registry.yarnpkg.com/sdp-transform/-/sdp-transform-2.14.1.tgz#2bb443583d478dee217df4caa284c46b870d5827" integrity sha512-RjZyX3nVwJyCuTo5tGPx+PZWkDMCg7oOLpSlhjDdZfwUoNqG1mM8nyj31IGHyaPWXhjbP7cdK3qZ2bmkJ1GzRw== -semver@7.x: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== +semver@7.x, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== - dependencies: - lru-cache "^6.0.0" - -semver@^7.3.7, semver@^7.3.8: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== - dependencies: - lru-cache "^6.0.0" + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@~7.0.0: version "7.0.0" From df251f8be4907a4b7ea643e1779788fc15e25384 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Wed, 12 Jul 2023 17:53:37 +0200 Subject: [PATCH 27/94] Removed SMF_CONFIG_DEPLOYED_TIME variable This seems to be causing re-deployments every time argoCD configuration updates. --- .env.example | 1 - env.faucet.config.json | 5 ----- src/bot/index.ts | 3 +-- src/server/routes/healthcheck.ts | 5 ++--- src/test/setupE2E.ts | 1 - yarn.lock | 19 ------------------- 6 files changed, 3 insertions(+), 31 deletions(-) diff --git a/.env.example b/.env.example index 2525acaa..e453275d 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,6 @@ SMF_CONFIG_NETWORK="rococo" SMF_CONFIG_PORT=5555 SMF_CONFIG_DEPLOYED_REF=local -SMF_CONFIG_DEPLOYED_TIME=local # Steps to retrieve this token: # 1. Create a bot account (see instructions below for SMF_BOT_MATRIX_BOT_USER_ID) diff --git a/env.faucet.config.json b/env.faucet.config.json index feea3b40..34fb0f4c 100644 --- a/env.faucet.config.json +++ b/env.faucet.config.json @@ -17,11 +17,6 @@ "default": "unset", "type": "string" }, - "DEPLOYED_TIME": { - "description": "time when we deployed app backend", - "default": "unset", - "type": "string" - }, "PORT": { "description": "the port you want the server to listen on", "default": 5555, diff --git a/src/bot/index.ts b/src/bot/index.ts index d44744ec..b5e80f87 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -19,7 +19,6 @@ const botUserId = config.Get("MATRIX_BOT_USER_ID"); const accessToken = config.Get("MATRIX_ACCESS_TOKEN"); const deployedRef = config.Get("DEPLOYED_REF"); -const deployedTime = config.Get("DEPLOYED_TIME"); const networkName = config.Get("NETWORK"); const networkData = getNetworkData(networkName); @@ -107,7 +106,7 @@ bot.on(mSDK.RoomEvent.Timeline, (event: mSDK.MatrixEvent) => { const [action, arg0, arg1] = body.split(" "); if (action === "!version") { - sendMessage(roomId, `Current version: ${deployedRef}; ${deployedTime}`); + sendMessage(roomId, `Current version: ${deployedRef}`); } else if (action === "!balance") { (async () => { const balance = await polkadotActions.getBalance(); diff --git a/src/server/routes/healthcheck.ts b/src/server/routes/healthcheck.ts index 853c7943..98bf2e47 100644 --- a/src/server/routes/healthcheck.ts +++ b/src/server/routes/healthcheck.ts @@ -16,12 +16,11 @@ const checkHealth = async (_req: Request, res: Response): Promise => { } }; -export type APIVersionResponse = { version: string; time: string }; +export type APIVersionResponse = { version: string }; const version = async (req: Request, res: Response) => { try { const appDeployedRef = config.Get("DEPLOYED_REF"); - const appDeployedTime = config.Get("DEPLOYED_TIME"); - res.status(200).send({ time: appDeployedTime, version: appDeployedRef } as APIVersionResponse); + res.status(200).send({ version: appDeployedRef } as APIVersionResponse); } catch (e) { logger.error(`⭕ Api error: ${(e as Error).message}`); res.status(503).send({ msg: "Faucet backend is NOT healthy." }); diff --git a/src/test/setupE2E.ts b/src/test/setupE2E.ts index 8521138b..af65782f 100644 --- a/src/test/setupE2E.ts +++ b/src/test/setupE2E.ts @@ -124,7 +124,6 @@ async function setupAppContainer(params: { SMF_CONFIG_FAUCET_IGNORE_LIST: "", SMF_CONFIG_MATRIX_SERVER: `http://host.docker.internal:${params.matrixPort}`, SMF_CONFIG_DEPLOYED_REF: "local", - SMF_CONFIG_DEPLOYED_TIME: "local", SMF_CONFIG_FAUCET_ACCOUNT_MNEMONIC: "//Alice", SMF_CONFIG_PORT: "5555", diff --git a/yarn.lock b/yarn.lock index 36e39a89..bd692146 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2312,13 +2312,6 @@ buildcheck@~0.0.6: resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238" integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== -busboy@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" - integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== - dependencies: - streamsearch "^1.1.0" - byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" @@ -6301,11 +6294,6 @@ streamroller@^3.0.7: debug "^4.3.4" fs-extra "^10.0.1" -streamsearch@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" - integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== - string-argv@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" @@ -6780,13 +6768,6 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== -undici@^5.22.1: - version "5.22.1" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.22.1.tgz#877d512effef2ac8be65e695f3586922e1a57d7b" - integrity sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw== - dependencies: - busboy "^1.6.0" - unhomoglyph@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/unhomoglyph/-/unhomoglyph-1.0.6.tgz#ea41f926d0fcf598e3b8bb2980c2ddac66b081d3" From 90af40d3986d83a6b549ad9c9c514da2034f76c8 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Tue, 18 Jul 2023 11:37:34 +0200 Subject: [PATCH 28/94] Using @eng-automation/js logger and metrics * Using our logger instead of log4js * Using prom-client instead of constructing metric responses by hand * Added default prom-client node.js metrics. These are mainly focused on performance, and can shed light on many related problems * Capturing metrics on log entries --- package.json | 3 +- src/common/ErrorCounter.ts | 31 ---------- src/common/metricsDefinition.ts | 6 -- src/dripper/DripRequestHandler.ts | 8 +-- src/dripper/Recaptcha.ts | 2 - src/dripper/polkadot/PolkadotActions.ts | 6 -- src/logger.ts | 13 +++- src/metrics.ts | 23 +++++++ src/server/routes/actions.ts | 3 - src/server/routes/metrics.ts | 70 +++------------------- yarn.lock | 80 ++++++++----------------- 11 files changed, 71 insertions(+), 174 deletions(-) delete mode 100644 src/common/ErrorCounter.ts delete mode 100644 src/common/metricsDefinition.ts create mode 100644 src/metrics.ts diff --git a/package.json b/package.json index cf539bfe..d4c70a58 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "pre-commit": "yarn lint-staged" }, "dependencies": { + "@eng-automation/js": "^0.0.23", "@polkadot/api": "^10.9.1", "@polkadot/keyring": "^12.3.2", "@polkadot/util": "^12.3.2", @@ -64,13 +65,11 @@ "crypto": "^1.0.1", "dotenv": "^16.0.0", "express": "4.17.3", - "log4js": "6.4.5", "matrix-js-sdk": "^26.1.0", "request": "^2.88.2", "sqlite3": "^5.1.2" }, "devDependencies": { - "@eng-automation/js": "^0.0.22", "@eng-automation/js-style": "^2.1.0", "@types/body-parser": "^1.19.2", "@types/express": "^4.17.13", diff --git a/src/common/ErrorCounter.ts b/src/common/ErrorCounter.ts deleted file mode 100644 index 565fdcf9..00000000 --- a/src/common/ErrorCounter.ts +++ /dev/null @@ -1,31 +0,0 @@ -type CounterType = "rpcTimeout" | "other"; - -class ErrorCounter { - private static instance: ErrorCounter; - #counter: Record; - - private constructor() { - this.#counter = { other: 0, rpcTimeout: 0 }; - } - - public getValue = (type: CounterType) => - // eslint-disable-next-line security/detect-object-injection - this.#counter[type]; - - public plusOne = (type: CounterType) => { - // eslint-disable-next-line security/detect-object-injection - this.#counter[type] += 1; - }; - - public total = (): number => Object.values(this.#counter).reduce((prev, curr) => prev + curr, 0); - - public static getInstance(): ErrorCounter { - if (!ErrorCounter.instance) { - ErrorCounter.instance = new ErrorCounter(); - } - - return ErrorCounter.instance; - } -} - -export default ErrorCounter.getInstance(); diff --git a/src/common/metricsDefinition.ts b/src/common/metricsDefinition.ts deleted file mode 100644 index 346e871a..00000000 --- a/src/common/metricsDefinition.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { MetricsDefinition } from "../types"; - -export const metricsDefinition: MetricsDefinition = { - data: { errors_rpc_timeout: 0, errors_total: 0, success_requests: 0, total_requests: 0 }, - meta: { prefix: "faucet" }, -}; diff --git a/src/dripper/DripRequestHandler.ts b/src/dripper/DripRequestHandler.ts index 805cc8d5..340f6596 100644 --- a/src/dripper/DripRequestHandler.ts +++ b/src/dripper/DripRequestHandler.ts @@ -1,8 +1,7 @@ -import errorCounter from "../common/ErrorCounter"; -import { metricsDefinition } from "../common/metricsDefinition"; import DripperStorage from "../dripper/DripperStorage"; import { isDripSuccessResponse } from "../guards"; import { logger } from "../logger"; +import { counters } from "../metrics"; import { DripRequestType, DripResponse } from "../types"; import { isAccountPrivileged } from "../utils"; import type { PolkadotActions } from "./polkadot/PolkadotActions"; @@ -29,7 +28,7 @@ export class DripRequestHandler { | ({ external: false; sender: string } & Omit), ): Promise { const { external, address: addr, parachain_id, amount } = opts; - metricsDefinition.data.total_requests++; + counters.totalRequests.inc(); if (external && !(await this.recaptcha.validate(opts.recaptcha))) return { error: "Captcha validation was unsuccessful" }; @@ -50,10 +49,9 @@ export class DripRequestHandler { // hash is null if something wrong happened if (isDripSuccessResponse(sendTokensResult)) { - metricsDefinition.data.success_requests++; + counters.successfulRequests.inc(); this.storage.saveData(external ? { addr } : { username: opts.sender, addr }).catch((e) => { logger.error(e); - errorCounter.plusOne("other"); }); } diff --git a/src/dripper/Recaptcha.ts b/src/dripper/Recaptcha.ts index b4c8b5bb..9fc523e0 100644 --- a/src/dripper/Recaptcha.ts +++ b/src/dripper/Recaptcha.ts @@ -1,7 +1,6 @@ import axios from "axios"; import { URLSearchParams } from "url"; -import errorCounter from "../common/ErrorCounter"; import { config } from "../config"; import { logger } from "../logger"; @@ -23,7 +22,6 @@ export class Recaptcha { return false; } catch (e) { logger.error(`⭕ An error occurred when validating captcha`, e); - errorCounter.plusOne("other"); return false; } } diff --git a/src/dripper/polkadot/PolkadotActions.ts b/src/dripper/polkadot/PolkadotActions.ts index af706b97..05a77180 100644 --- a/src/dripper/polkadot/PolkadotActions.ts +++ b/src/dripper/polkadot/PolkadotActions.ts @@ -3,7 +3,6 @@ import { KeyringPair } from "@polkadot/keyring/types"; import { waitReady } from "@polkadot/wasm-crypto"; import BN from "bn.js"; -import errorCounter from "../../common/ErrorCounter"; import { config } from "../../config"; import { isDripSuccessResponse } from "../../guards"; import { logger } from "../../logger"; @@ -22,7 +21,6 @@ const rpcTimeout = (service: string) => { return setTimeout(() => { // log an error in console and in prometheus if the timeout is reached logger.error(`⭕ Oops, ${service} took more than ${timeout}ms to answer`); - errorCounter.plusOne("rpcTimeout"); }, timeout); }; @@ -56,7 +54,6 @@ export class PolkadotActions { }); } catch (error) { logger.error(error); - errorCounter.plusOne("other"); } } @@ -75,7 +72,6 @@ export class PolkadotActions { this.#faucetBalance = balances.free.toBigInt(); } catch (e) { logger.error(e); - errorCounter.plusOne("other"); } } @@ -180,7 +176,6 @@ export class PolkadotActions { } catch (e) { result = { error: (e as Error).message || "An error occured when sending tokens" }; logger.error("⭕ An error occured when sending tokens", e); - errorCounter.plusOne("other"); } // we got and answer reset the timeout @@ -212,7 +207,6 @@ export class PolkadotActions { return balances.free.toString(); } catch (e) { logger.error("⭕ An error occured when querying the balance", e); - errorCounter.plusOne("other"); return "0"; } } diff --git a/src/logger.ts b/src/logger.ts index 20d6b979..96b8f65d 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,4 +1,11 @@ -import log4js from "log4js"; +import { Logger } from "@eng-automation/js"; -export const logger = log4js.getLogger(); -logger.level = "debug"; +import { counters } from "./metrics"; + +export const logger = new Logger({ + impl: console, + logFormat: null, + metricsCounter: counters.logEntries, + minLogLevel: "debug", + name: "app", +}); diff --git a/src/metrics.ts b/src/metrics.ts new file mode 100644 index 00000000..92e42d44 --- /dev/null +++ b/src/metrics.ts @@ -0,0 +1,23 @@ +import promClient from "prom-client"; + +export const counters: { [key: string]: promClient.Counter } = { + totalRequests: new promClient.Counter({ + name: "faucet_total_requests", + help: "Total number of requests to the faucet", + }), + successfulRequests: new promClient.Counter({ + name: "faucet_successful_requests", + help: "Total number of successful requests to the faucet", + }), + logEntries: new promClient.Counter({ + name: "faucet_log_entries", + help: "Log entries", + labelNames: ["level"] as const, + }), +}; + +export const gauges: { [key: string]: promClient.Gauge } = { + balance: new promClient.Gauge({ name: "faucet_balance", help: "Current balance of the faucet" }), +}; + +promClient.collectDefaultMetrics({ prefix: "faucet_" }); diff --git a/src/server/routes/actions.ts b/src/server/routes/actions.ts index 467b5101..849a8595 100644 --- a/src/server/routes/actions.ts +++ b/src/server/routes/actions.ts @@ -1,7 +1,6 @@ import cors from "cors"; import express, { NextFunction, Request, Response } from "express"; -import errorCounter from "../../common/ErrorCounter"; import { config } from "../../config"; import { getDripRequestHandlerInstance } from "../../dripper/DripRequestHandler"; import polkadotActions from "../../dripper/polkadot/PolkadotActions"; @@ -30,7 +29,6 @@ router.get("/balance", (_, res) => { .then((balance) => res.send({ balance })) .catch((e) => { logger.error(e); - errorCounter.plusOne("other"); res.send({ balance: "0" }); }); }); @@ -79,7 +77,6 @@ router.post>("/drip/web", } } catch (e) { logger.error(e); - errorCounter.plusOne("other"); res.status(500).send({ error: "Operation failed." }); } }); diff --git a/src/server/routes/metrics.ts b/src/server/routes/metrics.ts index b9c581b9..30733713 100644 --- a/src/server/routes/metrics.ts +++ b/src/server/routes/metrics.ts @@ -1,74 +1,20 @@ // prometheus metrics import express from "express"; +import promClient from "prom-client"; -import ErrorCounter from "../../common/ErrorCounter"; -import { metricsDefinition } from "../../common/metricsDefinition"; import actions from "../../dripper/polkadot/PolkadotActions"; import { convertBnAmountToNumber } from "../../dripper/polkadot/utils"; +import { gauges } from "../../metrics"; const router = express.Router(); - -router.get("/metrics", (_, res) => { - const errors_total = getMetrics( - "errors_total", - "counter", - ErrorCounter.total(), - "The total amount of errors logged on the faucet backend", - ); - const errors_rpc_timeout = getMetrics( - "errors_rpc_timeout", - "counter", - ErrorCounter.getValue("rpcTimeout"), - "The total amount of timeout errors between the faucet backend and the rpc node", - ); - +router.get("/metrics", async (_, res) => { const balanceBigint = actions.getFaucetBalance(); - const balanceMaybeNumber = balanceBigint === undefined ? undefined : convertBnAmountToNumber(balanceBigint); - - const balance = getMetrics("balance", "gauge", balanceMaybeNumber, "Current balance of the faucet", true); - - const total_requests = getMetrics( - "total_requests", - "gauge", - metricsDefinition.data.total_requests, - "Total number of requests to the faucet", - ); - const successful_requests = getMetrics( - "successful_requests", - "gauge", - metricsDefinition.data.success_requests, - "The total number of successful requests to the faucet", - ); + if (balanceBigint !== undefined) { + gauges.balance.set(convertBnAmountToNumber(balanceBigint)); + } - res.end(`${errors_total}${errors_rpc_timeout}${balance}${total_requests}${successful_requests}`); + res.contentType("text/plain"); + res.end(await promClient.register.metrics()); }); -/** - * Simplistic function to generate a prometheus metrics. - * TODO: Switch to prom-client - * @param name Name of the metric - * @param type Type - * @param value Value - * @param help Help test - * @param voidIfUndefined Whether we render it even if null/undefined - * @returns string - */ -export function getMetrics( - name: string, - type: string, - value: number | string | undefined, - help = "", - voidIfUndefined = false, -): string { - if (!value && voidIfUndefined) return ""; - const metrics_name = `${metricsDefinition.meta.prefix}_${name}`; - - let result = ""; - if (help && help.length) result += `# HELP ${metrics_name} ${help}\n`; - result += `# TYPE ${metrics_name} ${type}\n`; - result += `${metrics_name} ${value || 0}\n`; - - return result; -} - export default router; diff --git a/yarn.lock b/yarn.lock index bd692146..2a4727c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -518,10 +518,10 @@ prettier-plugin-svelte "^2.8.1" svelte "^3.54.0" -"@eng-automation/js@^0.0.22": - version "0.0.22" - resolved "https://registry.yarnpkg.com/@eng-automation/js/-/js-0.0.22.tgz#6abb457e767874179a349f86b26e78a66b7048c2" - integrity sha512-m2p+c/w1jyTMmc1jaEQleuibb0EUXgaxyJXmGx9tDG3EDQ12JJuls9vziUTsdJPRP6aMDrBtAaOz4VteGZiI0w== +"@eng-automation/js@^0.0.23": + version "0.0.23" + resolved "https://registry.yarnpkg.com/@eng-automation/js/-/js-0.0.23.tgz#4cc8775844242a3935cdc539b87a420a53e92868" + integrity sha512-bg3Tih/KvLQKOVCgMsBnmgi1c/BNQZYBgbnPuvx83pTKJ5DrIRIFIwWKvKlmqb7EIJC5PJKkVvC1Zqm/D6FKjg== dependencies: async-mutex "^0.3.2" commander "^9.3.0" @@ -530,6 +530,7 @@ joi-to-typescript "^4.0.5" lodash "^4.17.21" node-fetch "^2.6.7" + prom-client "^14.2.0" "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" @@ -2177,6 +2178,11 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bintrees@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" + integrity sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw== + bl@^4.0.3: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -2684,11 +2690,6 @@ data-uri-to-buffer@^4.0.0: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== -date-format@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.7.tgz#aa1cf4400badfe693c8462bbfcba43ab821d7d14" - integrity sha512-k5xqlzDGIfv2N/DHR/BR8Kc4N9CRy9ReuDkmdxeX/jNfit94QXd36emWMm40ZOEDKNm/c91yV9EO3uGPkR7wWQ== - debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -3514,7 +3515,7 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" -flatted@^3.1.0, flatted@^3.2.5: +flatted@^3.1.0: version "3.2.5" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== @@ -3595,15 +3596,6 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@^10.0.1: - version "10.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -3792,7 +3784,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.2.0, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -4747,15 +4739,6 @@ json5@^2.2.1, json5@^2.2.2, json5@^2.2.3: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - jsprim@^1.2.2: version "1.4.2" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" @@ -4914,17 +4897,6 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" -log4js@6.4.5: - version "6.4.5" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.4.5.tgz#5cca31b29ece65a625efbc3df6fcbd9cecb9ee7b" - integrity sha512-43RJcYZ7nfUxpPO2woTl8CJ0t5+gucLJZ43mtp2PlInT+LygCp/bl6hNJtKulCJ+++fQsjIv4EO3Mp611PfeLQ== - dependencies: - date-format "^4.0.7" - debug "^4.3.4" - flatted "^3.2.5" - rfdc "^1.3.0" - streamroller "^3.0.7" - loglevel@^1.7.1: version "1.8.0" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114" @@ -5658,6 +5630,13 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +prom-client@^14.2.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-14.2.0.tgz#ca94504e64156f6506574c25fb1c34df7812cf11" + integrity sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA== + dependencies: + tdigest "^0.1.1" + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -6285,15 +6264,6 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -streamroller@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.0.7.tgz#d566353d3d8b5d2f3d813d2df557c08083b414cf" - integrity sha512-kh68kwiDGuIPiPDWwRbEC5us+kfARP1e9AsQiaLaSqGrctOvMn0mtL8iNY3r4/o5nIoYi3gPI1jexguZsXDlxw== - dependencies: - date-format "^4.0.7" - debug "^4.3.4" - fs-extra "^10.0.1" - string-argv@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" @@ -6528,6 +6498,13 @@ tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" +tdigest@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.2.tgz#96c64bac4ff10746b910b0e23b515794e12faced" + integrity sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA== + dependencies: + bintrees "1.0.2" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -6787,11 +6764,6 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" -universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== - unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" From fd77df976885b6d3dc22f1085f4e3b24280f3477 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Wed, 19 Jul 2023 14:21:07 +0200 Subject: [PATCH 29/94] Removing EXTERNAL_ACCESS + Updated README + Removed redundant `dotenv.config()` usage. `confmgr` does the same thing internally --- .env.example | 1 - README.md | 29 +++-------------------------- env.faucet.config.json | 5 ----- package.json | 1 - src/bot/index.ts | 3 --- src/dripper/Recaptcha.ts | 2 +- src/server/routes/actions.spec.ts | 9 --------- src/server/routes/actions.ts | 3 --- src/test/setupE2E.ts | 2 -- yarn.lock | 2 +- 10 files changed, 5 insertions(+), 52 deletions(-) diff --git a/.env.example b/.env.example index e453275d..d056526f 100644 --- a/.env.example +++ b/.env.example @@ -24,6 +24,5 @@ SMF_CONFIG_MATRIX_SERVER=https://m.parity.io SMF_CONFIG_FAUCET_ACCOUNT_MNEMONIC="this is a fake mnemonic" SMF_CONFIG_PORT=5555 -SMF_CONFIG_EXTERNAL_ACCESS=false # Only used with external access SMF_CONFIG_RECAPTCHA_SECRET="6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" # Public testing secret, will accept all tokens. diff --git a/README.md b/README.md index 13f5d01d..ff0efe04 100644 --- a/README.md +++ b/README.md @@ -14,45 +14,22 @@ yarn simple-git-hooks To launch a hot-reloading dev environment ```bash -yarn dev:backend -yarn dev:bot +yarn dev ``` -## Server and Bot environment variables +## Environment variables -Definition with explanation is in `./env.bot.config.json` and `./env.server.config.json` +Definition with explanation is in `./env.faucet.config.json` Copy example file to real env and change its values: ```bash $ cp example.env .env ``` -## Run the faucet locally for troubleshooting - -Use the following commands to run a local instance of the faucet built directly from sources: - -```bash - cd docker/ - export SMF_BACKEND_FAUCET_ACCOUNT_MNEMONIC=*** - export SMF_BOT_MATRIX_BOT_USER_ID=*** - export SMF_BOT_MATRIX_ACCESS_TOKEN=*** - docker-compose -f docker-compose..yml up -``` - -Note: You will need a valid funded account mnemonic and matrix user ID / access token. - ## End-to-end tests Please refer to the [E2E Readme](./E2E/README.md). -## Allowing external access to the faucet - -The default mode of operation of the faucet is to handle trusted requests from the matrix bot, -and to not allow any direct external access. - -With the `SMF_BACKEND_EXTERNAL_ACCESS` variable (and by exposing the faucet port to the Internet) -you can allow the faucet to handle external requests, which are protected by ReCAPTCHA. - Example requests: ```bash diff --git a/env.faucet.config.json b/env.faucet.config.json index 34fb0f4c..1d19282e 100644 --- a/env.faucet.config.json +++ b/env.faucet.config.json @@ -22,11 +22,6 @@ "default": 5555, "type": "number" }, - "EXTERNAL_ACCESS": { - "description": "Whether the backend should serve external drip requests", - "default": "false", - "type": "boolean" - }, "RECAPTCHA_SECRET": { "description": "A secret recaptcha token used to validate external requests", "default": "", diff --git a/package.json b/package.json index d4c70a58..fad04bca 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,6 @@ "confmgr": "^1.0.8", "cors": "^2.8.5", "crypto": "^1.0.1", - "dotenv": "^16.0.0", "express": "4.17.3", "matrix-js-sdk": "^26.1.0", "request": "^2.88.2", diff --git a/src/bot/index.ts b/src/bot/index.ts index b5e80f87..2c30f7ea 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -1,5 +1,4 @@ import { decodeAddress } from "@polkadot/keyring"; -import dotenv from "dotenv"; import * as mSDK from "matrix-js-sdk"; import { config } from "../config"; @@ -11,8 +10,6 @@ import { logger } from "../logger"; import { getNetworkData } from "../networkData"; import { isAccountPrivileged } from "../utils"; -dotenv.config(); - const dripRequestHandler = getDripRequestHandlerInstance(polkadotActions); const botUserId = config.Get("MATRIX_BOT_USER_ID"); diff --git a/src/dripper/Recaptcha.ts b/src/dripper/Recaptcha.ts index 9fc523e0..ce1af0ad 100644 --- a/src/dripper/Recaptcha.ts +++ b/src/dripper/Recaptcha.ts @@ -6,7 +6,7 @@ import { logger } from "../logger"; export class Recaptcha { constructor(private secret: string = config.Get("RECAPTCHA_SECRET")) { - if (config.Get("EXTERNAL_ACCESS") && !this.secret) { + if (!this.secret) { throw new Error(`⭕ Recaptcha is not configured. Check the RECAPTCHA_SECRET variable.`); } } diff --git a/src/server/routes/actions.spec.ts b/src/server/routes/actions.spec.ts index 81cdae3d..c473b627 100644 --- a/src/server/routes/actions.spec.ts +++ b/src/server/routes/actions.spec.ts @@ -63,7 +63,6 @@ describe("/drip/web tests", () => { mockConfigValue.config = { NETWORK: "rococo", - EXTERNAL_ACCESS: true, FAUCET_ACCOUNT_MNEMONIC: "scrub inquiry adapt lounge voice current manage chief build shoot drip liar head season inside", }; @@ -81,14 +80,6 @@ describe("/drip/web tests", () => { expect(res.status).toBe(400); }); - test("should fail if external access is not enabled", async () => { - mockConfigValue.config.EXTERNAL_ACCESS = false; - - const res = await request(app).post("/drip/web").send({ address: "example" }); - expect(res.body.error).toBe("Endpoint unavailable"); - expect(res.status).toBe(503); - }); - test("should request drip", async () => { mockHandleRequest.mockImplementation(() => { return {}; diff --git a/src/server/routes/actions.ts b/src/server/routes/actions.ts index 849a8595..1acc0666 100644 --- a/src/server/routes/actions.ts +++ b/src/server/routes/actions.ts @@ -54,9 +54,6 @@ const addressMiddleware = ( type PartialDrip = Partial & Pick; router.post>("/drip/web", addressMiddleware, async (req, res) => { - if (!config.Get("EXTERNAL_ACCESS")) { - return res.status(503).send({ error: "Endpoint unavailable" }); - } const { address, parachain_id, recaptcha } = req.body; if (!recaptcha) { return missingParameterError(res, "recaptcha"); diff --git a/src/test/setupE2E.ts b/src/test/setupE2E.ts index af65782f..fbdc5e69 100644 --- a/src/test/setupE2E.ts +++ b/src/test/setupE2E.ts @@ -127,8 +127,6 @@ async function setupAppContainer(params: { SMF_CONFIG_FAUCET_ACCOUNT_MNEMONIC: "//Alice", SMF_CONFIG_PORT: "5555", - // Local Zombienet relaychain node. - SMF_CONFIG_EXTERNAL_ACCESS: "true", // Public testing secret, will accept all tokens. SMF_CONFIG_RECAPTCHA_SECRET: "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" }) diff --git a/yarn.lock b/yarn.lock index 2a4727c5..12d1c068 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2861,7 +2861,7 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dotenv@16.0.0, dotenv@^16.0.0: +dotenv@16.0.0: version "16.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411" integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q== From 3586a6d45639e19952880b77f24330db98c40757 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Thu, 20 Jul 2023 14:48:04 +0200 Subject: [PATCH 30/94] Masking secrets in logs --- package.json | 2 +- src/config.ts | 11 +++++++++++ yarn.lock | 8 ++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index fad04bca..d4c0bb3a 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "pre-commit": "yarn lint-staged" }, "dependencies": { - "@eng-automation/js": "^0.0.23", + "@eng-automation/js": "^1.0.0", "@polkadot/api": "^10.9.1", "@polkadot/keyring": "^12.3.2", "@polkadot/util": "^12.3.2", diff --git a/src/config.ts b/src/config.ts index 4b8a5623..8e56626c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -19,6 +19,17 @@ function resolveConfig(): ConfigObject { return configInstance; } + for (const config of Object.values(specs.config)) { + for (const item of Object.values(config)) { + if (item.options.masked) { + const value = configInstance.Get("CONFIG", item.name); + if (value !== undefined) { + logger.addSecretsToMask(value); + } + } + } + } + configInstance.Print({ compact: true }); if (!configInstance.Validate()) { diff --git a/yarn.lock b/yarn.lock index 12d1c068..ff57a00d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -518,10 +518,10 @@ prettier-plugin-svelte "^2.8.1" svelte "^3.54.0" -"@eng-automation/js@^0.0.23": - version "0.0.23" - resolved "https://registry.yarnpkg.com/@eng-automation/js/-/js-0.0.23.tgz#4cc8775844242a3935cdc539b87a420a53e92868" - integrity sha512-bg3Tih/KvLQKOVCgMsBnmgi1c/BNQZYBgbnPuvx83pTKJ5DrIRIFIwWKvKlmqb7EIJC5PJKkVvC1Zqm/D6FKjg== +"@eng-automation/js@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@eng-automation/js/-/js-1.0.0.tgz#f6bd5e36f1ab047cdf185be913283788432003b0" + integrity sha512-LHRJYdug9zhCHGUBJc4KdPIlAKLFMC4sTgIT2fqrM2+v61e/y51fijrxRQ86gzcNG+ODrYJf0+9pCH5bNbaFTA== dependencies: async-mutex "^0.3.2" commander "^9.3.0" From edb83a99ceb19bf29af1b0490fcb04aa407ad0a8 Mon Sep 17 00:00:00 2001 From: cornholio <0@mcornholio.ru> Date: Fri, 21 Jul 2023 16:56:40 +0200 Subject: [PATCH 31/94] Trappist faucet + updates Stopped copying known static urls like PUBLIC_FAUCET_ROCOCO_URL, storing it in NetworkData now. Setting PUBLIC_FAUCET_URL to anything non-empty will override all requests to it. Trappist doesn't exist on subscan yet, so making it optional. --- .github/workflows/deploy-site.yml | 2 - client/.env | 7 +++- client/.eslintrc.cjs | 2 - client/README.md | 2 +- client/playwright.config.ts | 3 +- client/src/lib/components/Form.svelte | 2 +- .../src/lib/components/screens/Success.svelte | 10 +++-- client/src/lib/utils/networkData.ts | 29 ++++++++++--- client/src/routes/+page.svelte | 2 +- client/src/routes/trappist/+page.svelte | 12 ++++++ client/src/routes/westend/+page.svelte | 2 +- client/tests/faucet.ts | 42 +++++++++++++------ client/tests/rococo.test.ts | 4 +- client/tests/trappist.test.ts | 11 +++++ client/tests/westend.test.ts | 2 +- package.json | 2 +- src/networkData.ts | 13 +++++- yarn.lock | 8 ++-- 18 files changed, 111 insertions(+), 44 deletions(-) create mode 100644 client/src/routes/trappist/+page.svelte create mode 100644 client/tests/trappist.test.ts diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index 468ce956..dfdaa567 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -31,8 +31,6 @@ jobs: - run: yarn run build env: PUBLIC_CAPTCHA_KEY: 6LdU5kckAAAAANktvvAKJ0auYUBRP0su94G7WXwe - PUBLIC_FAUCET_ROCOCO_URL: https://rococo-faucet.parity-testnet.parity.io/drip/web - PUBLIC_FAUCET_WESTEND_URL: "https://westend-faucet.polkadot.io/drip/web" GITHUB_PAGES: "/${{ github.event.repository.name }}" STATIC: true BASE: "/polkadot-testnet-faucet" diff --git a/client/.env b/client/.env index 7aff1a12..dafeac9e 100644 --- a/client/.env +++ b/client/.env @@ -1,6 +1,9 @@ PUBLIC_DEMO_MODE= PUBLIC_CAPTCHA_KEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI -PUBLIC_FAUCET_ROCOCO_URL= -PUBLIC_FAUCET_WESTEND_URL= + +PUBLIC_FAUCET_URL= +# uncomment to direct requests to local instance +# PUBLIC_FAUCET_URL=http://localhost:5555 + PUBLIC_ISSUE_LINK=https://github.com/paritytech/polkadot-testnet-faucet/issues/new/choose PUBLIC_FORUM="https://forum.polkadot.network/t/experiencing-trouble-accessing-our-rococo-faucet-please-post-here/2952" diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs index 7ecff701..d6fbafc0 100644 --- a/client/.eslintrc.cjs +++ b/client/.eslintrc.cjs @@ -10,6 +10,4 @@ conf.overrides.push(tsConfOverride); // and it doesn't live well with Tailwind CSSª conf.rules["svelte/valid-compile"] = ["error", { ignoreWarnings: true }]; -conf.rules["no-restricted-imports"] = "off"; - module.exports = conf; diff --git a/client/README.md b/client/README.md index a7d29bfe..316cd3be 100644 --- a/client/README.md +++ b/client/README.md @@ -13,7 +13,7 @@ Two current options are to [access Matrix and contact a bot](https://wiki.polkad To develop you need two env variables: - `PUBLIC_CAPTCHA_KEY`: The [reCaptcha v2 site key](https://www.google.com/u/0/recaptcha/admin). -- `PUBLIC_FAUCET_URL`: The endpoint to contact the faucet. +- `PUBLIC_FAUCET_URL`: The endpoint to contact the faucet backend. Keep unset to run client-side code with production backend. The reason for which these variables have `PUBLIC_` as a prefix is a security measure to not upload any unnecessary data. [More info here](https://kit.svelte.dev/docs/modules#$env-static-public) diff --git a/client/playwright.config.ts b/client/playwright.config.ts index b098203c..35383889 100644 --- a/client/playwright.config.ts +++ b/client/playwright.config.ts @@ -6,9 +6,8 @@ const config: PlaywrightTestConfig = { port: 4173, env: { PUBLIC_CAPTCHA_KEY: "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI", - PUBLIC_FAUCET_WESTEND_URL: "https://example.com/test", - PUBLIC_FAUCET_ROCOCO_URL: "https://test.com/example", PUBLIC_DEMO_MODE: "", + PUBLIC_FAUCET_URL: "https://example.com/test", }, }, testDir: "tests", diff --git a/client/src/lib/components/Form.svelte b/client/src/lib/components/Form.svelte index 21a6d8c9..ab4f5051 100644 --- a/client/src/lib/components/Form.svelte +++ b/client/src/lib/components/Form.svelte @@ -55,7 +55,7 @@
{:else} diff --git a/client/src/lib/components/screens/Success.svelte b/client/src/lib/components/screens/Success.svelte index 4bcae9ba..f231972b 100644 --- a/client/src/lib/components/screens/Success.svelte +++ b/client/src/lib/components/screens/Success.svelte @@ -9,11 +9,13 @@
- Successfully sent {$testnet.currency} to your address. + Successfully sent {$testnet.currency}s to your address.
- - - +{#if $testnet.explorer} + + + +{/if} diff --git a/client/src/lib/components/NetworkInput.svelte b/client/src/lib/components/NetworkInput.svelte index 2e5a2ed7..73bbd245 100644 --- a/client/src/lib/components/NetworkInput.svelte +++ b/client/src/lib/components/NetworkInput.svelte @@ -50,7 +50,7 @@ class:hidden={!customValue} /> {#if !customValue} -