From 441272767c51be090b3fb18aa9b0a271fb6b6bc7 Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Fri, 8 Aug 2025 18:24:10 +0700 Subject: [PATCH 1/5] Add TikTok as auth method for In-App and Ecosystem Wallets Introduces TikTok as a new authentication provider across In-App and Ecosystem Wallets. Updates types, UI components, icon sets, documentation, and connector logic to support TikTok login. --- .changeset/slick-cups-joke.md | 5 + apps/dashboard/src/@/api/team/ecosystems.ts | 1 + .../client/auth-options-form.client.tsx | 1 + .../InAppWalletUsersChartCard.stories.tsx | 1 + .../components/InAppWalletFormGroup.tsx | 1 + .../providers/ecosystem-wallet/page.mdx | 2 +- .../wallets/providers/in-app-wallet/page.mdx | 2 +- .../v5/wallets/ecosystem-wallet/page.mdx | 2 +- .../unity/v5/wallets/in-app-wallet/page.mdx | 2 +- .../src/components/Document/AuthList.tsx | 1 + .../write/createDelayedRevealBatch.ts | 170 ------- .../erc721/lazyMinting/write/reveal.test.ts | 29 -- .../src/extensions/erc721/write/sigMint.ts | 414 ------------------ .../read/accountImplementation.ts | 69 --- .../erc7702/account/sessionkey.test.ts | 132 ------ .../IIdGateway/write/registerFor.ts | 195 --------- .../read/CHANGE_RECOVERY_ADDRESS_TYPEHASH.ts | 73 --- .../src/react/core/utils/walletIcon.test.ts | 4 + .../src/react/core/utils/walletIcon.ts | 5 + .../native/ui/components/WalletImage.tsx | 3 + .../react/native/ui/connect/InAppWalletUI.tsx | 2 + .../src/react/native/ui/icons/svgs.ts | 10 + .../shared/ConnectWalletSocialOptions.tsx | 1 + .../react/web/wallets/shared/oauthSignIn.ts | 1 + .../in-app/core/authentication/types.ts | 1 + .../wallets/in-app/native/native-connector.ts | 1 + .../wallets/in-app/web/lib/web-connector.ts | 2 + packages/thirdweb/src/wallets/types.ts | 1 + 28 files changed, 45 insertions(+), 1086 deletions(-) create mode 100644 .changeset/slick-cups-joke.md diff --git a/.changeset/slick-cups-joke.md b/.changeset/slick-cups-joke.md new file mode 100644 index 00000000000..05cf067bfb7 --- /dev/null +++ b/.changeset/slick-cups-joke.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +Adds TikTok as a new authentication method for In-App and Ecosystem Wallets. diff --git a/apps/dashboard/src/@/api/team/ecosystems.ts b/apps/dashboard/src/@/api/team/ecosystems.ts index e1365ce8cb0..5220e51d1a1 100644 --- a/apps/dashboard/src/@/api/team/ecosystems.ts +++ b/apps/dashboard/src/@/api/team/ecosystems.ts @@ -12,6 +12,7 @@ export type AuthOption = | "google" | "facebook" | "x" + | "tiktok" | "discord" | "farcaster" | "telegram" diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/auth-options-form.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/auth-options-form.client.tsx index 2ec8e7b714f..7907f9ce133 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/auth-options-form.client.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/auth-options-form.client.tsx @@ -48,6 +48,7 @@ const authOptions = [ "google", "facebook", "x", + "tiktok", "discord", "farcaster", "telegram", diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/analytics/chart/InAppWalletUsersChartCard.stories.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/analytics/chart/InAppWalletUsersChartCard.stories.tsx index f22155c6f46..9b397aafd92 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/analytics/chart/InAppWalletUsersChartCard.stories.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/analytics/chart/InAppWalletUsersChartCard.stories.tsx @@ -86,6 +86,7 @@ const authMethodsToPickFrom: InAppWalletAuth[] = [ "discord", "line", "x", + "tiktok", "coinbase", "farcaster", "telegram", diff --git a/apps/playground-web/src/app/wallets/sign-in/components/InAppWalletFormGroup.tsx b/apps/playground-web/src/app/wallets/sign-in/components/InAppWalletFormGroup.tsx index a7f1e6a7aa1..c4bdc1834ed 100644 --- a/apps/playground-web/src/app/wallets/sign-in/components/InAppWalletFormGroup.tsx +++ b/apps/playground-web/src/app/wallets/sign-in/components/InAppWalletFormGroup.tsx @@ -19,6 +19,7 @@ const allInAppWalletLoginMethods: InAppWalletAuth[] = [ "phone", "line", "x", + "tiktok", "facebook", "apple", "coinbase", diff --git a/apps/portal/src/app/dotnet/wallets/providers/ecosystem-wallet/page.mdx b/apps/portal/src/app/dotnet/wallets/providers/ecosystem-wallet/page.mdx index 93708372bf2..aeebe7b9c0b 100644 --- a/apps/portal/src/app/dotnet/wallets/providers/ecosystem-wallet/page.mdx +++ b/apps/portal/src/app/dotnet/wallets/providers/ecosystem-wallet/page.mdx @@ -15,7 +15,7 @@ Create an instance of `EcosystemWallet` using a user's email, phone number or OA Ecosystem Wallets support a variety of login methods: - Email (OTP Login) - Phone (OTP Login) -- Socials (Google, Apple, Facebook, Telegram, Farcaster, Line, Github, Twitch, Steam etc.) +- Socials (Google, Apple, Facebook, Telegram, Farcaster, Line, Github, Twitch, Steam, TikTok etc.) - Custom Auth (OIDC Compatible) - Custom Auth (Generic Auth Endpoint) - Guest (Onboard easily, link other accounts later) diff --git a/apps/portal/src/app/dotnet/wallets/providers/in-app-wallet/page.mdx b/apps/portal/src/app/dotnet/wallets/providers/in-app-wallet/page.mdx index cf0124772b1..26455d9bd69 100644 --- a/apps/portal/src/app/dotnet/wallets/providers/in-app-wallet/page.mdx +++ b/apps/portal/src/app/dotnet/wallets/providers/in-app-wallet/page.mdx @@ -15,7 +15,7 @@ Create an instance of `InAppWallet` using a user's email, phone number or OAuth. In-App Wallets support a variety of login methods: - Email (OTP Login) - Phone (OTP Login) -- Socials (Google, Apple, Facebook, Telegram, Farcaster, Line, Github, Twitch, Steam etc.) +- Socials (Google, Apple, Facebook, Telegram, Farcaster, Line, Github, Twitch, Steam, TikTok etc.) - Custom Auth (OIDC Compatible) - Custom Auth (Generic Auth Endpoint) - Guest (Onboard easily, link other accounts later) diff --git a/apps/portal/src/app/unity/v5/wallets/ecosystem-wallet/page.mdx b/apps/portal/src/app/unity/v5/wallets/ecosystem-wallet/page.mdx index 5078695f65b..32cf6ee1b3a 100644 --- a/apps/portal/src/app/unity/v5/wallets/ecosystem-wallet/page.mdx +++ b/apps/portal/src/app/unity/v5/wallets/ecosystem-wallet/page.mdx @@ -20,7 +20,7 @@ Ecosystem Wallets have a very similar API to the [In-App Wallet](/unity/v5/walle Ecosystem Wallets support a variety of login methods: - Email (OTP Login) - Phone (OTP Login) -- Socials (Google, Apple, Facebook, Telegram, Farcaster, Line, Github, Twitch, Steam etc.) +- Socials (Google, Apple, Facebook, Telegram, Farcaster, Line, Github, Twitch, Steam, TikTok etc.) - Custom Auth (OIDC Compatible) - Custom Auth (Generic Auth Endpoint) - Guest (Onboard easily, link other accounts later) diff --git a/apps/portal/src/app/unity/v5/wallets/in-app-wallet/page.mdx b/apps/portal/src/app/unity/v5/wallets/in-app-wallet/page.mdx index bd485377a9c..23f00bfce14 100644 --- a/apps/portal/src/app/unity/v5/wallets/in-app-wallet/page.mdx +++ b/apps/portal/src/app/unity/v5/wallets/in-app-wallet/page.mdx @@ -18,7 +18,7 @@ It makes for a fantastic [SmartWallet](/unity/v5/wallets/account-abstraction) ad In-App Wallets support a variety of login methods: - Email (OTP Login) - Phone (OTP Login) -- Socials (Google, Apple, Facebook, Telegram, Farcaster, Line, Github, Twitch, Steam etc.) +- Socials (Google, Apple, Facebook, Telegram, Farcaster, Line, Github, Twitch, Steam, TikTok etc.) - Custom Auth (OIDC Compatible) - Custom Auth (Generic Auth Endpoint) - Guest (Onboard easily, link other accounts later) diff --git a/apps/portal/src/components/Document/AuthList.tsx b/apps/portal/src/components/Document/AuthList.tsx index bb0895a95b6..bed6db4802b 100644 --- a/apps/portal/src/components/Document/AuthList.tsx +++ b/apps/portal/src/components/Document/AuthList.tsx @@ -14,6 +14,7 @@ const authOptions: InAppWalletAuth[] = [ "apple", "facebook", "x", + "tiktok", "discord", "telegram", "twitch", diff --git a/packages/thirdweb/src/extensions/erc721/lazyMinting/write/createDelayedRevealBatch.ts b/packages/thirdweb/src/extensions/erc721/lazyMinting/write/createDelayedRevealBatch.ts index 7e777a18175..e69de29bb2d 100644 --- a/packages/thirdweb/src/extensions/erc721/lazyMinting/write/createDelayedRevealBatch.ts +++ b/packages/thirdweb/src/extensions/erc721/lazyMinting/write/createDelayedRevealBatch.ts @@ -1,170 +0,0 @@ -import { encodePacked } from "viem/utils"; -import { upload } from "../../../../storage/upload.js"; -import type { BaseTransactionOptions } from "../../../../transaction/types.js"; -import { encodeAbiParameters } from "../../../../utils/abi/encodeAbiParameters.js"; -import { toHex } from "../../../../utils/encoding/hex.js"; -import { keccak256 } from "../../../../utils/hashing/keccak256.js"; -import { getBaseUriFromBatch } from "../../../../utils/ipfs.js"; -import type { NFTInput } from "../../../../utils/nft/parseNft.js"; -import { - getBaseURICount, - isGetBaseURICountSupported, -} from "../../__generated__/IBatchMintMetadata/read/getBaseURICount.js"; -import { - encryptDecrypt, - isEncryptDecryptSupported, -} from "../../__generated__/IDelayedReveal/read/encryptDecrypt.js"; -import { nextTokenIdToMint } from "../../__generated__/IERC721Enumerable/read/nextTokenIdToMint.js"; -import { - lazyMint as generatedLazyMint, - isLazyMintSupported, -} from "../../__generated__/ILazyMint/write/lazyMint.js"; -import { hashDelayedRevealPassword } from "../helpers/hashDelayedRevealBatch.js"; - -/** - * @extension ERC721 - */ -export type CreateDelayedRevealBatchParams = { - placeholderMetadata: NFTInput; - metadata: NFTInput[]; - password: string; -}; - -/** - * Creates a batch of encrypted NFTs that can be revealed at a later time. - * This method is only available on the `DropERC721` contract. - * - * @param options {CreateDelayedRevealBatchParams} - The delayed reveal options. - * @param options.placeholderMetadata {@link NFTInput} - The placeholder metadata for the batch. - * @param options.metadata {@link NFTInput} - An array of NFT metadata to be revealed at a later time. - * @param options.password {string} - The password for the reveal. - * @param options.contract {@link ThirdwebContract} - The NFT contract instance. - * - * @returns The prepared transaction to send. - * - * @extension ERC721 - * @example - * ```ts - * import { createDelayedRevealBatch } from "thirdweb/extensions/erc721"; - * - * const placeholderNFT = { - * name: "Hidden NFT", - * description: "Will be revealed next week!" - * }; - * - * const realNFTs = [{ - * name: "Common NFT #1", - * description: "Common NFT, one of many.", - * image: ipfs://..., - * }, { - * name: "Super Rare NFT #2", - * description: "You got a Super Rare NFT!", - * image: ipfs://..., - * }]; - * - * const transaction = createDelayedRevealBatch({ - * contract, - * placeholderMetadata: placeholderNFT, - * metadata: realNFTs, - * password: "password123", - * }); - * - * const { transactionHash } = await sendTransaction({ transaction, account }); - * ``` - */ -export function createDelayedRevealBatch( - options: BaseTransactionOptions, -) { - if (!options.password) { - throw new Error("Password is required"); - } - - return generatedLazyMint({ - asyncParams: async () => { - const [placeholderUris, startFileNumber] = await Promise.all([ - upload({ - client: options.contract.client, - files: Array(options.metadata.length).fill( - options.placeholderMetadata, - ), - }), - nextTokenIdToMint({ - contract: options.contract, - }), - ]); - const placeholderUri = getBaseUriFromBatch(placeholderUris); - - const uris = await upload({ - client: options.contract.client, - files: options.metadata, - // IMPORTANT: File number has to be calculated properly otherwise the whole batch will break - // e.g: If you are uploading a second batch, the file name should never start from `0` - rewriteFileNames: { - fileStartNumber: Number(startFileNumber), - }, - }); - - const baseUri = getBaseUriFromBatch(uris); - const baseUriId = await getBaseURICount({ - contract: options.contract, - }); - - const hashedPassword = await hashDelayedRevealPassword( - baseUriId, - options.password, - options.contract, - ); - const encryptedBaseURI = await encryptDecrypt({ - contract: options.contract, - data: toHex(baseUri), - key: hashedPassword, - }); - - const chainId = BigInt(options.contract.chain.id); - const provenanceHash = keccak256( - encodePacked( - ["bytes", "bytes", "uint256"], - [toHex(baseUri), hashedPassword, chainId], - ), - ); - const data = encodeAbiParameters( - [ - { name: "baseUri", type: "bytes" }, - { name: "provenanceHash", type: "bytes32" }, - ], - [encryptedBaseURI, provenanceHash], - ); - - return { - amount: BigInt(options.metadata.length), - baseURIForTokens: - placeholderUri.slice(-1) === "/" - ? placeholderUri - : `${placeholderUri}/`, - extraData: data, - } as const; - }, - contract: options.contract, - }); -} - -/** - * Checks if the `createDelayedRevealBatch` method is supported by the given contract. - * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. - * @returns A boolean indicating if the `createDelayedRevealBatch` method is supported. - * @extension ERC721 - * @example - * ```ts - * import { isCreateDelayedRevealBatchSupported } from "thirdweb/extensions/erc721"; - * const supported = isCreateDelayedRevealBatchSupported(["0x..."]); - * ``` - */ -export function isCreateDelayedRevealBatchSupported( - availableSelectors: string[], -) { - return [ - isGetBaseURICountSupported(availableSelectors), - isEncryptDecryptSupported(availableSelectors), - isLazyMintSupported(availableSelectors), - ].every(Boolean); -} diff --git a/packages/thirdweb/src/extensions/erc721/lazyMinting/write/reveal.test.ts b/packages/thirdweb/src/extensions/erc721/lazyMinting/write/reveal.test.ts index 75563d6c1f6..e69de29bb2d 100644 --- a/packages/thirdweb/src/extensions/erc721/lazyMinting/write/reveal.test.ts +++ b/packages/thirdweb/src/extensions/erc721/lazyMinting/write/reveal.test.ts @@ -1,29 +0,0 @@ -import { beforeAll, describe, expect, it } from "vitest"; -import { ANVIL_CHAIN } from "../../../../../test/src/chains.js"; -import { TEST_CLIENT } from "../../../../../test/src/test-clients.js"; -import { - getContract, - type ThirdwebContract, -} from "../../../../contract/contract.js"; -import { reveal } from "./reveal.js"; - -describe("reveal", () => { - let contract: ThirdwebContract; - beforeAll(() => { - contract = getContract({ - address: "0x708781BAE850faA490cB5b5b16b4687Ec0A8D65D", - chain: ANVIL_CHAIN, - client: TEST_CLIENT, - }); - }); - - it("should throw an error if password is missing", async () => { - const options = { - batchId: 1n, - contract, - password: "", - }; - - expect(() => reveal(options)).toThrowError("Password is required"); - }); -}); diff --git a/packages/thirdweb/src/extensions/erc721/write/sigMint.ts b/packages/thirdweb/src/extensions/erc721/write/sigMint.ts index a65173a230f..e69de29bb2d 100644 --- a/packages/thirdweb/src/extensions/erc721/write/sigMint.ts +++ b/packages/thirdweb/src/extensions/erc721/write/sigMint.ts @@ -1,414 +0,0 @@ -import type { AbiParameterToPrimitiveType, Address } from "abitype"; -import { type Hex, isHex, stringToHex } from "viem"; -import { - isNativeTokenAddress, - NATIVE_TOKEN_ADDRESS, -} from "../../../constants/addresses.js"; -import type { ThirdwebContract } from "../../../contract/contract.js"; -import type { BaseTransactionOptions } from "../../../transaction/types.js"; -import { toBigInt } from "../../../utils/bigint.js"; -import { dateToSeconds, tenYearsFromNow } from "../../../utils/date.js"; -import type { NFTInput } from "../../../utils/nft/parseNft.js"; -import { randomBytesHex } from "../../../utils/random.js"; -import type { Account } from "../../../wallets/interfaces/wallet.js"; -import { - mintWithSignature as generatedMintWithSignature, - type MintWithSignatureParams, -} from "../__generated__/ISignatureMintERC721/write/mintWithSignature.js"; -import { - mintWithSignature as generatedMintWithSignatureV2, - type MintWithSignatureParams as MintWithSignatureParamsV2, -} from "../__generated__/ISignatureMintERC721_v2/write/mintWithSignature.js"; - -/** - * Mints a new ERC721 token with the given minter signature - * This method is only available on the `TokenERC721` contract. - * - * @param options - The transaction options. - * @example - * ```ts - * import { mintWithSignature, generateMintSignature } from "thirdweb/extensions/erc721"; - * import { sendTransaction } from "thirdweb"; - * - * const { payload, signature } = await generateMintSignature(...) - * - * const transaction = mintWithSignature({ - * contract, - * payload, - * signature, - * }); - * - * await sendTransaction({ transaction, account }); - * ``` - * @extension ERC721 - * @returns A promise that resolves to the transaction result. - */ -export function mintWithSignature( - options: BaseTransactionOptions< - | { payload: PayloadTypeV2; signature: Hex } - | { payload: PayloadType; signature: Hex } - >, -) { - const { payload } = options; - if ("quantity" in payload) { - return mintWithSignatureV2( - options as BaseTransactionOptions, - ); - } - return mintWithSignatureV1( - options as BaseTransactionOptions, - ); -} - -function mintWithSignatureV1( - options: BaseTransactionOptions, -) { - const value = isNativeTokenAddress(options.payload.currency) - ? options.payload.price - : 0n; - return generatedMintWithSignature({ - ...options, - overrides: { - value, - }, - }); -} - -function mintWithSignatureV2( - options: BaseTransactionOptions, -) { - const value = isNativeTokenAddress(options.payload.currency) - ? options.payload.pricePerToken * options.payload.quantity - : 0n; - return generatedMintWithSignatureV2({ - ...options, - overrides: { - value, - }, - }); -} - -export type GenerateMintSignatureOptions< - T extends "LoyaltyCard" | "TokenERC721" = "TokenERC721", -> = { - account: Account; - contract: ThirdwebContract; - mintRequest: GeneratePayloadInput; - contractType?: T; -}; - -/** - * Generates the payload and signature for minting an ERC721 token. - * @param options - The options for the minting process. - * @example - * ```ts - * import { mintWithSignature, generateMintSignature } from "thirdweb/extensions/erc721"; - * - * const { payload, signature } = await generateMintSignature({ - * account, - * contract, - * mintRequest: { - * to: "0x...", - * metadata: { - * name: "My NFT", - * description: "This is my NFT", - * image: "https://example.com/image.png", - * }, - * }, - * }); - * - * const transaction = mintWithSignature({ - * contract, - * payload, - * signature, - * }); - * await sendTransaction({ transaction, account }); - * ``` - * @extension ERC721 - * @returns A promise that resolves to the payload and signature. - */ -export async function generateMintSignature< - T extends "LoyaltyCard" | "TokenERC721" = "TokenERC721", ->(options: GenerateMintSignatureOptions): Promise> { - const { mintRequest, account, contract, contractType } = options; - - const currency = mintRequest.currency || NATIVE_TOKEN_ADDRESS; - const [price, uri, uid] = await Promise.all([ - // price per token in wei - (async () => { - // if priceInWei is provided, use it - if ("priceInWei" in mintRequest && mintRequest.priceInWei) { - return mintRequest.priceInWei; - } - // if price is provided, convert it to wei - if ("price" in mintRequest && mintRequest.price) { - const { convertErc20Amount } = await import( - "../../../utils/extensions/convert-erc20-amount.js" - ); - return await convertErc20Amount({ - amount: mintRequest.price, - chain: contract.chain, - client: contract.client, - erc20Address: currency, - }); - } - // if neither price nor priceInWei is provided, default to 0 - return 0n; - })(), - // uri - (async () => { - if ("metadata" in mintRequest) { - if (typeof mintRequest.metadata === "object") { - // async import the upload function because it is not always required - const { upload } = await import("../../../storage/upload.js"); - return await upload({ - client: options.contract.client, - files: [mintRequest.metadata], - }); - } - return mintRequest.metadata; - } - return ""; - })(), - // uid computation - ((): Hex => { - if (mintRequest.uid) { - return isHex(mintRequest.uid) - ? mintRequest.uid - : stringToHex(mintRequest.uid, { size: 32 }); - } - return randomBytesHex(); - })(), - ]); - - const startTime = mintRequest.validityStartTimestamp || new Date(0); - const endTime = mintRequest.validityEndTimestamp || tenYearsFromNow(); - - let saleRecipient: Address; - if ( - mintRequest.primarySaleRecipient?.length === 0 || - !mintRequest.primarySaleRecipient - ) { - const { primarySaleRecipient } = await import( - "../../common/__generated__/IPrimarySale/read/primarySaleRecipient.js" - ); - saleRecipient = await primarySaleRecipient({ - contract, - }); - } else { - saleRecipient = mintRequest.primarySaleRecipient; - } - - let royaltyRecipient: Address; - if ( - mintRequest.royaltyRecipient?.length === 0 || - !mintRequest.royaltyRecipient - ) { - const { getDefaultRoyaltyInfo } = await import( - "../../common/__generated__/IRoyalty/read/getDefaultRoyaltyInfo.js" - ); - const royaltyInfo = await getDefaultRoyaltyInfo({ - contract, - }); - royaltyRecipient = royaltyInfo[0]; - } else { - royaltyRecipient = mintRequest.royaltyRecipient; - } - - if (contractType === "LoyaltyCard") { - return signPayloadV2({ - account, - contract, - currency, - endTime, - mintRequest, - price, - primarySaleRecipient: saleRecipient, - royaltyRecipient, - startTime, - uid, - uri, - }) as Promise>; - } - return signPayloadV1({ - account, - contract, - currency, - endTime, - mintRequest, - price, - primarySaleRecipient: saleRecipient, - royaltyRecipient, - startTime, - uid, - uri, - }) as Promise>; -} - -async function signPayloadV1({ - mintRequest, - account, - contract, - uri, - currency, - uid, - price, - royaltyRecipient, - primarySaleRecipient, - startTime, - endTime, -}: { - mintRequest: GeneratePayloadInput; - account: Account; - contract: ThirdwebContract; - uri: string; - currency: Address; - uid: Hex; - price: bigint; - royaltyRecipient: Address; - primarySaleRecipient: Address; - startTime: Date; - endTime: Date; -}): Promise<{ payload: PayloadType; signature: Hex }> { - const payload: PayloadType = { - currency, - price, - primarySaleRecipient, - royaltyBps: toBigInt(mintRequest.royaltyBps || 0), - royaltyRecipient, - to: mintRequest.to, - uid, - uri, - validityEndTimestamp: dateToSeconds(endTime), - validityStartTimestamp: dateToSeconds(startTime), - }; - - const signature = await account.signTypedData({ - domain: { - chainId: contract.chain.id, - name: "TokenERC721", - verifyingContract: contract.address, - version: "1", - }, - message: payload, - primaryType: "MintRequest", - types: { - MintRequest: MintRequest721, - }, - }); - return { payload, signature }; -} - -async function signPayloadV2({ - mintRequest, - account, - contract, - uri, - currency, - uid, - price, - royaltyRecipient, - primarySaleRecipient, - startTime, - endTime, -}: { - mintRequest: GeneratePayloadInput; - account: Account; - contract: ThirdwebContract; - uri: string; - currency: Address; - uid: Hex; - price: bigint; - royaltyRecipient: Address; - primarySaleRecipient: Address; - startTime: Date; - endTime: Date; -}): Promise<{ payload: PayloadTypeV2; signature: Hex }> { - const payload: PayloadTypeV2 = { - currency, - pricePerToken: price, - primarySaleRecipient, - quantity: toBigInt(1), // always 1 for 721 NFTs - royaltyBps: toBigInt(mintRequest.royaltyBps || 0), - royaltyRecipient, - to: mintRequest.to, - uid, - uri, - validityEndTimestamp: dateToSeconds(endTime), - validityStartTimestamp: dateToSeconds(startTime), - }; - - const signature = await account.signTypedData({ - domain: { - chainId: contract.chain.id, - name: "SignatureMintERC721", - verifyingContract: contract.address, - version: "1", - }, - message: payload, - primaryType: "MintRequest", - types: { - MintRequest: MintRequest721_V2, - }, - }); - return { payload, signature }; -} - -type SignPayloadResult = T extends "LoyaltyCard" - ? Awaited> - : Awaited>; - -type PayloadType = AbiParameterToPrimitiveType<{ - type: "tuple"; - name: "payload"; - components: typeof MintRequest721; -}>; - -type PayloadTypeV2 = AbiParameterToPrimitiveType<{ - type: "tuple"; - name: "payload"; - components: typeof MintRequest721_V2; -}>; - -type GeneratePayloadInput = { - to: string; - metadata: NFTInput | string; - royaltyRecipient?: Address; - royaltyBps?: number; - primarySaleRecipient?: Address; - price?: string; - priceInWei?: bigint; - currency?: Address; - validityStartTimestamp?: Date; - validityEndTimestamp?: Date; - uid?: string; -}; - -const MintRequest721 = [ - { name: "to", type: "address" }, - { name: "royaltyRecipient", type: "address" }, - { name: "royaltyBps", type: "uint256" }, - { name: "primarySaleRecipient", type: "address" }, - { name: "uri", type: "string" }, - { name: "price", type: "uint256" }, - { name: "currency", type: "address" }, - { name: "validityStartTimestamp", type: "uint128" }, - { name: "validityEndTimestamp", type: "uint128" }, - { name: "uid", type: "bytes32" }, -] as const; - -// used for LoyaltyCard contract and base sigmint contracts -// adds quantity to the payload so its the same as 1155 -const MintRequest721_V2 = [ - { name: "to", type: "address" }, - { name: "royaltyRecipient", type: "address" }, - { name: "royaltyBps", type: "uint256" }, - { name: "primarySaleRecipient", type: "address" }, - { name: "uri", type: "string" }, - { name: "quantity", type: "uint256" }, - { name: "pricePerToken", type: "uint256" }, - { name: "currency", type: "address" }, - { name: "validityStartTimestamp", type: "uint128" }, - { name: "validityEndTimestamp", type: "uint128" }, - { name: "uid", type: "bytes32" }, -] as const; diff --git a/packages/thirdweb/src/extensions/erc7579/__generated__/ModularAccountFactory/read/accountImplementation.ts b/packages/thirdweb/src/extensions/erc7579/__generated__/ModularAccountFactory/read/accountImplementation.ts index 5f54e325971..e69de29bb2d 100644 --- a/packages/thirdweb/src/extensions/erc7579/__generated__/ModularAccountFactory/read/accountImplementation.ts +++ b/packages/thirdweb/src/extensions/erc7579/__generated__/ModularAccountFactory/read/accountImplementation.ts @@ -1,69 +0,0 @@ -import { decodeAbiParameters } from "viem"; -import { readContract } from "../../../../../transaction/read-contract.js"; -import type { BaseTransactionOptions } from "../../../../../transaction/types.js"; -import { detectMethod } from "../../../../../utils/bytecode/detectExtension.js"; -import type { Hex } from "../../../../../utils/encoding/hex.js"; - -export const FN_SELECTOR = "0x11464fbe" as const; -const FN_INPUTS = [] as const; -const FN_OUTPUTS = [ - { - type: "address", - }, -] as const; - -/** - * Checks if the `accountImplementation` method is supported by the given contract. - * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. - * @returns A boolean indicating if the `accountImplementation` method is supported. - * @extension ERC7579 - * @example - * ```ts - * import { isAccountImplementationSupported } from "thirdweb/extensions/erc7579"; - * const supported = isAccountImplementationSupported(["0x..."]); - * ``` - */ -export function isAccountImplementationSupported(availableSelectors: string[]) { - return detectMethod({ - availableSelectors, - method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, - }); -} - -/** - * Decodes the result of the accountImplementation function call. - * @param result - The hexadecimal result to decode. - * @returns The decoded result as per the FN_OUTPUTS definition. - * @extension ERC7579 - * @example - * ```ts - * import { decodeAccountImplementationResult } from "thirdweb/extensions/erc7579"; - * const result = decodeAccountImplementationResultResult("..."); - * ``` - */ -export function decodeAccountImplementationResult(result: Hex) { - return decodeAbiParameters(FN_OUTPUTS, result)[0]; -} - -/** - * Calls the "accountImplementation" function on the contract. - * @param options - The options for the accountImplementation function. - * @returns The parsed result of the function call. - * @extension ERC7579 - * @example - * ```ts - * import { accountImplementation } from "thirdweb/extensions/erc7579"; - * - * const result = await accountImplementation({ - * contract, - * }); - * - * ``` - */ -export async function accountImplementation(options: BaseTransactionOptions) { - return readContract({ - contract: options.contract, - method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, - params: [], - }); -} diff --git a/packages/thirdweb/src/extensions/erc7702/account/sessionkey.test.ts b/packages/thirdweb/src/extensions/erc7702/account/sessionkey.test.ts index 156fcc8ee6c..e69de29bb2d 100644 --- a/packages/thirdweb/src/extensions/erc7702/account/sessionkey.test.ts +++ b/packages/thirdweb/src/extensions/erc7702/account/sessionkey.test.ts @@ -1,132 +0,0 @@ -import { defineChain } from "src/chains/utils.js"; -import { prepareTransaction } from "src/transaction/prepare-transaction.js"; -import { inAppWallet } from "src/wallets/in-app/web/in-app.js"; -import type { Account } from "src/wallets/interfaces/wallet.js"; -import { beforeAll, describe, expect, it } from "vitest"; -import { TEST_CLIENT } from "../../../../test/src/test-clients.js"; -import { TEST_ACCOUNT_A } from "../../../../test/src/test-wallets.js"; -import { ZERO_ADDRESS } from "../../../constants/addresses.js"; -import { - getContract, - type ThirdwebContract, -} from "../../../contract/contract.js"; -import { parseEventLogs } from "../../../event/actions/parse-logs.js"; -import { sendAndConfirmTransaction } from "../../../transaction/actions/send-and-confirm-transaction.js"; -import { sessionCreatedEvent } from "../__generated__/MinimalAccount/events/SessionCreated.js"; -import { createSessionKey } from "./createSessionKey.js"; -import { Condition, LimitType } from "./types.js"; - -describe.runIf(process.env.TW_SECRET_KEY)( - "Session Key Behavior", - { - retry: 0, - timeout: 240_000, - }, - () => { - const chainId = 11155111; - let account: Account; - let accountContract: ThirdwebContract; - - beforeAll(async () => { - // Create 7702 Smart EOA - const wallet = inAppWallet({ - executionMode: { - mode: "EIP7702", - sponsorGas: true, - }, - }); - account = await wallet.connect({ - chain: defineChain(chainId), - client: TEST_CLIENT, - strategy: "guest", - }); - - // Send a null tx to trigger deploy/upgrade - await sendAndConfirmTransaction({ - account: account, - transaction: prepareTransaction({ - chain: defineChain(chainId), - client: TEST_CLIENT, - to: account.address, - value: 0n, - }), - }); - - // Will auto resolve abi since it's deployed - accountContract = getContract({ - address: account.address, - chain: defineChain(chainId), - client: TEST_CLIENT, - }); - }, 120_000); - - it("should allow adding adminlike session keys", async () => { - const receipt = await sendAndConfirmTransaction({ - account: account, - transaction: createSessionKey({ - account: account, - contract: accountContract, - durationInSeconds: 86400, - grantFullPermissions: true, // 1 day - sessionKeyAddress: TEST_ACCOUNT_A.address, - }), - }); - const logs = parseEventLogs({ - events: [sessionCreatedEvent()], - logs: receipt.logs, - }); - expect(logs[0]?.args.signer).toBe(TEST_ACCOUNT_A.address); - }); - - it("should allow adding granular session keys", async () => { - const receipt = await sendAndConfirmTransaction({ - account: account, - transaction: createSessionKey({ - account: account, - callPolicies: [ - { - constraints: [ - { - condition: Condition.Unconstrained, - index: 0n, - refValue: - "0x0000000000000000000000000000000000000000000000000000000000000000", - }, - ], - maxValuePerUse: 0n, - selector: "0x00000000", - target: ZERO_ADDRESS, - valueLimit: { - limit: 0n, - limitType: LimitType.Unlimited, - period: 0n, - }, - }, - ], - contract: accountContract, - durationInSeconds: 86400, // 1 day - grantFullPermissions: false, - sessionKeyAddress: TEST_ACCOUNT_A.address, - transferPolicies: [ - { - maxValuePerUse: 0n, - target: ZERO_ADDRESS, - valueLimit: { - limit: 0n, - limitType: 0, - period: 0n, - }, - }, - ], - }), - }); - const logs = parseEventLogs({ - events: [sessionCreatedEvent()], - logs: receipt.logs, - }); - expect(logs[0]?.args.signer).toBe(TEST_ACCOUNT_A.address); - expect(logs[0]?.args.sessionSpec.callPolicies).toHaveLength(1); - expect(logs[0]?.args.sessionSpec.transferPolicies).toHaveLength(1); - }); - }, -); diff --git a/packages/thirdweb/src/extensions/farcaster/__generated__/IIdGateway/write/registerFor.ts b/packages/thirdweb/src/extensions/farcaster/__generated__/IIdGateway/write/registerFor.ts index 17e541df5d2..e69de29bb2d 100644 --- a/packages/thirdweb/src/extensions/farcaster/__generated__/IIdGateway/write/registerFor.ts +++ b/packages/thirdweb/src/extensions/farcaster/__generated__/IIdGateway/write/registerFor.ts @@ -1,195 +0,0 @@ -import type { AbiParameterToPrimitiveType } from "abitype"; -import { prepareContractCall } from "../../../../../transaction/prepare-contract-call.js"; -import type { - BaseTransactionOptions, - WithOverrides, -} from "../../../../../transaction/types.js"; -import { encodeAbiParameters } from "../../../../../utils/abi/encodeAbiParameters.js"; -import { detectMethod } from "../../../../../utils/bytecode/detectExtension.js"; -import { once } from "../../../../../utils/promise/once.js"; - -/** - * Represents the parameters for the "registerFor" function. - */ -export type RegisterForParams = WithOverrides<{ - to: AbiParameterToPrimitiveType<{ type: "address"; name: "to" }>; - recovery: AbiParameterToPrimitiveType<{ type: "address"; name: "recovery" }>; - deadline: AbiParameterToPrimitiveType<{ type: "uint256"; name: "deadline" }>; - sig: AbiParameterToPrimitiveType<{ type: "bytes"; name: "sig" }>; - extraStorage: AbiParameterToPrimitiveType<{ - type: "uint256"; - name: "extraStorage"; - }>; -}>; - -export const FN_SELECTOR = "0xa0c7529c" as const; -const FN_INPUTS = [ - { - name: "to", - type: "address", - }, - { - name: "recovery", - type: "address", - }, - { - name: "deadline", - type: "uint256", - }, - { - name: "sig", - type: "bytes", - }, - { - name: "extraStorage", - type: "uint256", - }, -] as const; -const FN_OUTPUTS = [ - { - name: "fid", - type: "uint256", - }, - { - name: "overpayment", - type: "uint256", - }, -] as const; - -/** - * Checks if the `registerFor` method is supported by the given contract. - * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. - * @returns A boolean indicating if the `registerFor` method is supported. - * @extension FARCASTER - * @example - * ```ts - * import { isRegisterForSupported } from "thirdweb/extensions/farcaster"; - * - * const supported = isRegisterForSupported(["0x..."]); - * ``` - */ -export function isRegisterForSupported(availableSelectors: string[]) { - return detectMethod({ - availableSelectors, - method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, - }); -} - -/** - * Encodes the parameters for the "registerFor" function. - * @param options - The options for the registerFor function. - * @returns The encoded ABI parameters. - * @extension FARCASTER - * @example - * ```ts - * import { encodeRegisterForParams } from "thirdweb/extensions/farcaster"; - * const result = encodeRegisterForParams({ - * to: ..., - * recovery: ..., - * deadline: ..., - * sig: ..., - * extraStorage: ..., - * }); - * ``` - */ -export function encodeRegisterForParams(options: RegisterForParams) { - return encodeAbiParameters(FN_INPUTS, [ - options.to, - options.recovery, - options.deadline, - options.sig, - options.extraStorage, - ]); -} - -/** - * Encodes the "registerFor" function into a Hex string with its parameters. - * @param options - The options for the registerFor function. - * @returns The encoded hexadecimal string. - * @extension FARCASTER - * @example - * ```ts - * import { encodeRegisterFor } from "thirdweb/extensions/farcaster"; - * const result = encodeRegisterFor({ - * to: ..., - * recovery: ..., - * deadline: ..., - * sig: ..., - * extraStorage: ..., - * }); - * ``` - */ -export function encodeRegisterFor(options: RegisterForParams) { - // we do a "manual" concat here to avoid the overhead of the "concatHex" function - // we can do this because we know the specific formats of the values - return (FN_SELECTOR + - encodeRegisterForParams(options).slice( - 2, - )) as `${typeof FN_SELECTOR}${string}`; -} - -/** - * Prepares a transaction to call the "registerFor" function on the contract. - * @param options - The options for the "registerFor" function. - * @returns A prepared transaction object. - * @extension FARCASTER - * @example - * ```ts - * import { sendTransaction } from "thirdweb"; - * import { registerFor } from "thirdweb/extensions/farcaster"; - * - * const transaction = registerFor({ - * contract, - * to: ..., - * recovery: ..., - * deadline: ..., - * sig: ..., - * extraStorage: ..., - * overrides: { - * ... - * } - * }); - * - * // Send the transaction - * await sendTransaction({ transaction, account }); - * ``` - */ -export function registerFor( - options: BaseTransactionOptions< - | RegisterForParams - | { - asyncParams: () => Promise; - } - >, -) { - const asyncOptions = once(async () => { - return "asyncParams" in options ? await options.asyncParams() : options; - }); - - return prepareContractCall({ - accessList: async () => (await asyncOptions()).overrides?.accessList, - authorizationList: async () => - (await asyncOptions()).overrides?.authorizationList, - contract: options.contract, - erc20Value: async () => (await asyncOptions()).overrides?.erc20Value, - extraGas: async () => (await asyncOptions()).overrides?.extraGas, - gas: async () => (await asyncOptions()).overrides?.gas, - gasPrice: async () => (await asyncOptions()).overrides?.gasPrice, - maxFeePerGas: async () => (await asyncOptions()).overrides?.maxFeePerGas, - maxPriorityFeePerGas: async () => - (await asyncOptions()).overrides?.maxPriorityFeePerGas, - method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, - nonce: async () => (await asyncOptions()).overrides?.nonce, - params: async () => { - const resolvedOptions = await asyncOptions(); - return [ - resolvedOptions.to, - resolvedOptions.recovery, - resolvedOptions.deadline, - resolvedOptions.sig, - resolvedOptions.extraStorage, - ] as const; - }, - value: async () => (await asyncOptions()).overrides?.value, - }); -} diff --git a/packages/thirdweb/src/extensions/farcaster/__generated__/IIdRegistry/read/CHANGE_RECOVERY_ADDRESS_TYPEHASH.ts b/packages/thirdweb/src/extensions/farcaster/__generated__/IIdRegistry/read/CHANGE_RECOVERY_ADDRESS_TYPEHASH.ts index 8cf023ced5f..e69de29bb2d 100644 --- a/packages/thirdweb/src/extensions/farcaster/__generated__/IIdRegistry/read/CHANGE_RECOVERY_ADDRESS_TYPEHASH.ts +++ b/packages/thirdweb/src/extensions/farcaster/__generated__/IIdRegistry/read/CHANGE_RECOVERY_ADDRESS_TYPEHASH.ts @@ -1,73 +0,0 @@ -import { decodeAbiParameters } from "viem"; -import { readContract } from "../../../../../transaction/read-contract.js"; -import type { BaseTransactionOptions } from "../../../../../transaction/types.js"; -import { detectMethod } from "../../../../../utils/bytecode/detectExtension.js"; -import type { Hex } from "../../../../../utils/encoding/hex.js"; - -export const FN_SELECTOR = "0xd5bac7f3" as const; -const FN_INPUTS = [] as const; -const FN_OUTPUTS = [ - { - type: "bytes32", - }, -] as const; - -/** - * Checks if the `CHANGE_RECOVERY_ADDRESS_TYPEHASH` method is supported by the given contract. - * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. - * @returns A boolean indicating if the `CHANGE_RECOVERY_ADDRESS_TYPEHASH` method is supported. - * @extension FARCASTER - * @example - * ```ts - * import { isCHANGE_RECOVERY_ADDRESS_TYPEHASHSupported } from "thirdweb/extensions/farcaster"; - * const supported = isCHANGE_RECOVERY_ADDRESS_TYPEHASHSupported(["0x..."]); - * ``` - */ -export function isCHANGE_RECOVERY_ADDRESS_TYPEHASHSupported( - availableSelectors: string[], -) { - return detectMethod({ - availableSelectors, - method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, - }); -} - -/** - * Decodes the result of the CHANGE_RECOVERY_ADDRESS_TYPEHASH function call. - * @param result - The hexadecimal result to decode. - * @returns The decoded result as per the FN_OUTPUTS definition. - * @extension FARCASTER - * @example - * ```ts - * import { decodeCHANGE_RECOVERY_ADDRESS_TYPEHASHResult } from "thirdweb/extensions/farcaster"; - * const result = decodeCHANGE_RECOVERY_ADDRESS_TYPEHASHResultResult("..."); - * ``` - */ -export function decodeCHANGE_RECOVERY_ADDRESS_TYPEHASHResult(result: Hex) { - return decodeAbiParameters(FN_OUTPUTS, result)[0]; -} - -/** - * Calls the "CHANGE_RECOVERY_ADDRESS_TYPEHASH" function on the contract. - * @param options - The options for the CHANGE_RECOVERY_ADDRESS_TYPEHASH function. - * @returns The parsed result of the function call. - * @extension FARCASTER - * @example - * ```ts - * import { CHANGE_RECOVERY_ADDRESS_TYPEHASH } from "thirdweb/extensions/farcaster"; - * - * const result = await CHANGE_RECOVERY_ADDRESS_TYPEHASH({ - * contract, - * }); - * - * ``` - */ -export async function CHANGE_RECOVERY_ADDRESS_TYPEHASH( - options: BaseTransactionOptions, -) { - return readContract({ - contract: options.contract, - method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, - params: [], - }); -} diff --git a/packages/thirdweb/src/react/core/utils/walletIcon.test.ts b/packages/thirdweb/src/react/core/utils/walletIcon.test.ts index 6c1ea80d667..239470dec27 100644 --- a/packages/thirdweb/src/react/core/utils/walletIcon.test.ts +++ b/packages/thirdweb/src/react/core/utils/walletIcon.test.ts @@ -51,6 +51,10 @@ describe("walletIcon", () => { expect(getSocialIcon("x")).toBe(socialIcons.x); }); + it("should return tiktok icon", () => { + expect(getSocialIcon("tiktok")).toBe(socialIcons.tiktok); + }); + it("should return farcaster icon", () => { expect(getSocialIcon("farcaster")).toBe(socialIcons.farcaster); }); diff --git a/packages/thirdweb/src/react/core/utils/walletIcon.ts b/packages/thirdweb/src/react/core/utils/walletIcon.ts index 25b43435c60..c4518d48409 100644 --- a/packages/thirdweb/src/react/core/utils/walletIcon.ts +++ b/packages/thirdweb/src/react/core/utils/walletIcon.ts @@ -29,6 +29,8 @@ const githubIconUri = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTA2IiBoZWlnaHQ9IjEwNiIgdmlld0JveD0iMCAwIDEwNiAxMDYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxjaXJjbGUgY3g9IjUzIiBjeT0iNTMiIHI9IjUzIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUyLjg1NCA0QzI1LjgzOSA0IDQgMjYgNCA1My4yMTdDNCA3NC45NzMgMTcuOTkzIDkzLjM4OSAzNy40MDUgOTkuOTA3QzM5LjgzMiAxMDAuMzk3IDQwLjcyMSA5OC44NDggNDAuNzIxIDk3LjU0NUM0MC43MjEgOTYuNDA0IDQwLjY0MSA5Mi40OTMgNDAuNjQxIDg4LjQxOEMyNy4wNTEgOTEuMzUyIDI0LjIyMSA4Mi41NTEgMjQuMjIxIDgyLjU1MUMyMi4wMzcgNzYuODQ3IDE4LjgwMSA3NS4zODEgMTguODAxIDc1LjM4MUMxNC4zNTMgNzIuMzY2IDE5LjEyNSA3Mi4zNjYgMTkuMTI1IDcyLjM2NkMyNC4wNTkgNzIuNjkyIDI2LjY0OCA3Ny40MTggMjYuNjQ4IDc3LjQxOEMzMS4wMTUgODQuOTE0IDM4LjA1MiA4Mi43OTYgNDAuODgzIDgxLjQ5MkM0MS4yODcgNzguMzE0IDQyLjU4MiA3Ni4xMTQgNDMuOTU3IDc0Ljg5MkMzMy4xMTggNzMuNzUxIDIxLjcxNCA2OS41MTQgMjEuNzE0IDUwLjYwOUMyMS43MTQgNDUuMjMxIDIzLjY1NCA0MC44MzEgMjYuNzI4IDM3LjQwOUMyNi4yNDMgMzYuMTg3IDI0LjU0NCAzMS4xMzQgMjcuMjE0IDI0LjM3MUMyNy4yMTQgMjQuMzcxIDMxLjMzOSAyMy4wNjcgNDAuNjQgMjkuNDIzQzQ0LjYyMjEgMjguMzQ1NyA0OC43Mjg4IDI3Ljc5NzYgNTIuODU0IDI3Ljc5M0M1Ni45NzkgMjcuNzkzIDYxLjE4NCAyOC4zNjQgNjUuMDY3IDI5LjQyM0M3NC4zNjkgMjMuMDY3IDc4LjQ5NCAyNC4zNzEgNzguNDk0IDI0LjM3MUM4MS4xNjQgMzEuMTM0IDc5LjQ2NCAzNi4xODcgNzguOTc5IDM3LjQwOUM4Mi4xMzQgNDAuODMxIDgzLjk5NCA0NS4yMzEgODMuOTk0IDUwLjYwOUM4My45OTQgNjkuNTE0IDcyLjU5IDczLjY2OSA2MS42NyA3NC44OTJDNjMuNDUgNzYuNDQgNjQuOTg2IDc5LjM3MyA2NC45ODYgODQuMDE4QzY0Ljk4NiA5MC42MTggNjQuOTA2IDk1LjkxNSA2NC45MDYgOTcuNTQ0QzY0LjkwNiA5OC44NDggNjUuNzk2IDEwMC4zOTcgNjguMjIyIDk5LjkwOEM4Ny42MzQgOTMuMzg4IDEwMS42MjcgNzQuOTczIDEwMS42MjcgNTMuMjE3QzEwMS43MDcgMjYgNzkuNzg4IDQgNTIuODU0IDRaIiBmaWxsPSIjMjQyOTJGIi8+Cjwvc3ZnPgo="; const xIcon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIwIiBoZWlnaHQ9IjMyMCIgdmlld0JveD0iMCAwIDMyMCAzMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIzMjAiIGhlaWdodD0iMzIwIiByeD0iMjQiIGZpbGw9ImJsYWNrIi8+CjxwYXRoIGQ9Ik0xNzcuMzE1IDE0NS4zMzVMMjQ1LjA2OCA2N0gyMjkuMDEzTDE3MC4xODIgMTM1LjAxN0wxMjMuMTk1IDY3SDY5TDE0MC4wNTUgMTY5Ljg1NEw2OSAyNTJIODUuMDU2M0wxNDcuMTgzIDE4MC4xNzJMMTk2LjgwNSAyNTJIMjUxTDE3Ny4zMTEgMTQ1LjMzNUgxNzcuMzE1Wk0xNTUuMzIzIDE3MC43NkwxNDguMTI0IDE2MC41MThMOTAuODQxNyA3OS4wMjJIMTE1LjUwM0wxNjEuNzMxIDE0NC43OTJMMTY4LjkzIDE1NS4wMzRMMjI5LjAyIDI0MC41MjVIMjA0LjM1OUwxNTUuMzIzIDE3MC43NjRWMTcwLjc2WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg=="; +const tiktokIconUri = + "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyNS4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4wIiBpZD0iQ2FwYV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgdmlld0JveD0iMCAwIDg0OCA1OTQiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDg0OCA1OTQ7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGQ9Ik00MjQsNDFjLTE0MS40LDAtMjU2LDExNC42LTI1NiwyNTZzMTE0LjYsMjU2LDI1NiwyNTZzMjU2LTExNC42LDI1Ni0yNTZTNTY1LjQsNDEsNDI0LDQxeiBNNTUyLjQsMjM2Ljl2MzQuNw0KCWMtMTYuMywwLTMyLjItMy4yLTQ3LjItOS41Yy05LjYtNC4xLTE4LjYtOS4zLTI2LjgtMTUuNmwwLjIsMTA2LjdjLTAuMSwyNC05LjYsNDYuNi0yNi44LDYzLjZjLTE0LDEzLjgtMzEuNywyMi42LTUxLDI1LjUNCgljLTQuNSwwLjctOS4xLDEtMTMuOCwxYy0yMC42LDAtNDAuMS02LjctNTYuMS0xOWMtMy0yLjMtNS45LTQuOC04LjYtNy41Yy0xOC42LTE4LjQtMjguMy00My40LTI2LjYtNjkuN2MxLjItMjAsOS4yLTM5LjEsMjIuNi01NA0KCWMxNy43LTE5LjgsNDIuNC0zMC44LDY4LjgtMzAuOGM0LjYsMCw5LjIsMC4zLDEzLjgsMXYxMi44djM1LjdjLTQuMy0xLjQtOC45LTIuMi0xMy42LTIuMmMtMjQuMSwwLTQzLjcsMTkuNy00My4zLDQzLjgNCgljMC4yLDE1LjUsOC43LDI5LDIxLjEsMzYuNGM1LjksMy41LDEyLjYsNS43LDE5LjgsNi4xYzUuNiwwLjMsMTEtMC41LDE2LTIuMWMxNy4yLTUuNywyOS43LTIxLjksMjkuNy00MWwwLjEtNzEuNFYxNTAuOGg0Ny43DQoJYzAsNC43LDAuNSw5LjMsMS40LDEzLjhjMy42LDE4LjEsMTMuOCwzMy44LDI4LDQ0LjVjMTIuNCw5LjMsMjcuOCwxNC45LDQ0LjUsMTQuOWMwLDAsMC4xLDAsMC4xLDBMNTUyLjQsMjM2LjlMNTUyLjQsMjM2Ljl6Ii8+DQo8L3N2Zz4NCg=="; const emailIcon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEzLjMzMzUgMi42NjY1SDIuNjY2ODNDMS45MzA0NSAyLjY2NjUgMS4zMzM1IDMuMjYzNDYgMS4zMzM1IDMuOTk5ODRWMTEuOTk5OEMxLjMzMzUgMTIuNzM2MiAxLjkzMDQ1IDEzLjMzMzIgMi42NjY4MyAxMy4zMzMySDEzLjMzMzVDMTQuMDY5OSAxMy4zMzMyIDE0LjY2NjggMTIuNzM2MiAxNC42NjY4IDExLjk5OThWMy45OTk4NEMxNC42NjY4IDMuMjYzNDYgMTQuMDY5OSAyLjY2NjUgMTMuMzMzNSAyLjY2NjVaIiBzdHJva2U9IiMzMzg1RkYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTQuNjY2OCA0LjY2NjVMOC42ODY4MyA4LjQ2NjVDOC40ODEwMSA4LjU5NTQ1IDguMjQzMDQgOC42NjM4NCA4LjAwMDE2IDguNjYzODRDNy43NTcyOCA4LjY2Mzg0IDcuNTE5MzEgOC41OTU0NSA3LjMxMzUgOC40NjY1TDEuMzMzNSA0LjY2NjUiIHN0cm9rZT0iIzMzODVGRiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPgo="; const phoneIcon = @@ -58,6 +60,7 @@ export const socialIcons = { telegram: telegramIconUri, twitch: twitchIconUri, x: xIcon, + tiktok: tiktokIconUri, }; /** @@ -94,6 +97,8 @@ export function getSocialIcon(provider: AuthOption | ({} & string)): string { return lineIconUri; case "x": return xIcon; + case "tiktok": + return tiktokIconUri; case "farcaster": return farcasterIconUri; case "telegram": diff --git a/packages/thirdweb/src/react/native/ui/components/WalletImage.tsx b/packages/thirdweb/src/react/native/ui/components/WalletImage.tsx index 7dd8945e0bb..51983797e59 100644 --- a/packages/thirdweb/src/react/native/ui/components/WalletImage.tsx +++ b/packages/thirdweb/src/react/native/ui/components/WalletImage.tsx @@ -21,6 +21,7 @@ import { PHONE_ICON, STEAM_ICON, TELEGRAM_ICON, + TIKTOK_ICON, TWITCH_ICON, WALLET_ICON, X_ICON, @@ -103,6 +104,8 @@ export function getAuthProviderImage(authProvider: string | null): string { return LINE_ICON; case "x": return X_ICON; + case "tiktok": + return TIKTOK_ICON; case "farcaster": return FARCASTER_ICON; case "telegram": diff --git a/packages/thirdweb/src/react/native/ui/connect/InAppWalletUI.tsx b/packages/thirdweb/src/react/native/ui/connect/InAppWalletUI.tsx index e5db683a92f..0c7d919c6c7 100644 --- a/packages/thirdweb/src/react/native/ui/connect/InAppWalletUI.tsx +++ b/packages/thirdweb/src/react/native/ui/connect/InAppWalletUI.tsx @@ -39,6 +39,7 @@ import { LINE_ICON, STEAM_ICON, TELEGRAM_ICON, + TIKTOK_ICON, TWITCH_ICON, X_ICON, } from "../icons/svgs.js"; @@ -67,6 +68,7 @@ const socialIcons = { telegram: TELEGRAM_ICON, twitch: TWITCH_ICON, x: X_ICON, + tiktok: TIKTOK_ICON, }; type InAppWalletFormUIProps = { diff --git a/packages/thirdweb/src/react/native/ui/icons/svgs.ts b/packages/thirdweb/src/react/native/ui/icons/svgs.ts index 7fa8e4c3863..0409d17c85f 100644 --- a/packages/thirdweb/src/react/native/ui/icons/svgs.ts +++ b/packages/thirdweb/src/react/native/ui/icons/svgs.ts @@ -137,6 +137,16 @@ export const X_ICON = ` `; +export const TIKTOK_ICON = ` + +`; + export const GUEST_ICON = ``; export const WALLET_ICON = ` diff --git a/packages/thirdweb/src/react/web/wallets/shared/ConnectWalletSocialOptions.tsx b/packages/thirdweb/src/react/web/wallets/shared/ConnectWalletSocialOptions.tsx index 69124ad4c43..722fc429a39 100644 --- a/packages/thirdweb/src/react/web/wallets/shared/ConnectWalletSocialOptions.tsx +++ b/packages/thirdweb/src/react/web/wallets/shared/ConnectWalletSocialOptions.tsx @@ -118,6 +118,7 @@ export const ConnectWalletSocialOptions = ( telegram: "Telegram", twitch: "Twitch", x: "X", + tiktok: "TikTok", }; const { data: ecosystemAuthOptions, isLoading } = useQuery({ diff --git a/packages/thirdweb/src/react/web/wallets/shared/oauthSignIn.ts b/packages/thirdweb/src/react/web/wallets/shared/oauthSignIn.ts index 9d47f3924ed..57b5fb6448c 100644 --- a/packages/thirdweb/src/react/web/wallets/shared/oauthSignIn.ts +++ b/packages/thirdweb/src/react/web/wallets/shared/oauthSignIn.ts @@ -43,6 +43,7 @@ function getOauthLoginPath( case "steam": case "line": case "x": + case "tiktok": case "guest": case "coinbase": case "discord": diff --git a/packages/thirdweb/src/wallets/in-app/core/authentication/types.ts b/packages/thirdweb/src/wallets/in-app/core/authentication/types.ts index 9b078279671..1311dc573ef 100644 --- a/packages/thirdweb/src/wallets/in-app/core/authentication/types.ts +++ b/packages/thirdweb/src/wallets/in-app/core/authentication/types.ts @@ -102,6 +102,7 @@ export type AuthProvider = | "Discord" | "Coinbase" | "X" + | "TikTok" | "Line" | "Twitch" | "Steam" diff --git a/packages/thirdweb/src/wallets/in-app/native/native-connector.ts b/packages/thirdweb/src/wallets/in-app/native/native-connector.ts index d26dd494fc6..d298dabd9d0 100644 --- a/packages/thirdweb/src/wallets/in-app/native/native-connector.ts +++ b/packages/thirdweb/src/wallets/in-app/native/native-connector.ts @@ -199,6 +199,7 @@ export class InAppNativeConnector implements InAppConnector { case "discord": case "line": case "x": + case "tiktok": case "apple": { const { socialAuth } = await import("./auth/native-auth.js"); const ExpoLinking = require("expo-linking"); diff --git a/packages/thirdweb/src/wallets/in-app/web/lib/web-connector.ts b/packages/thirdweb/src/wallets/in-app/web/lib/web-connector.ts index 68a321891f7..641572d18cb 100644 --- a/packages/thirdweb/src/wallets/in-app/web/lib/web-connector.ts +++ b/packages/thirdweb/src/wallets/in-app/web/lib/web-connector.ts @@ -337,6 +337,7 @@ export class InAppWebConnector implements InAppConnector { case "farcaster": case "line": case "x": + case "tiktok": case "steam": case "coinbase": case "discord": { @@ -410,6 +411,7 @@ export class InAppWebConnector implements InAppConnector { case "github": case "line": case "x": + case "tiktok": case "guest": case "coinbase": case "twitch": diff --git a/packages/thirdweb/src/wallets/types.ts b/packages/thirdweb/src/wallets/types.ts index 4aa870be88a..daf16784ef8 100644 --- a/packages/thirdweb/src/wallets/types.ts +++ b/packages/thirdweb/src/wallets/types.ts @@ -26,6 +26,7 @@ export const socialAuthOptions = [ "discord", "line", "x", + "tiktok", "coinbase", "farcaster", "telegram", From 9142a4ec5e8845e67b8de1ce73927ab5e0da1415 Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Fri, 8 Aug 2025 18:32:19 +0700 Subject: [PATCH 2/5] Revert root pnpm fix side effect --- .../write/createDelayedRevealBatch.ts | 170 +++++++ .../erc721/lazyMinting/write/reveal.test.ts | 29 ++ .../src/extensions/erc721/write/sigMint.ts | 414 ++++++++++++++++++ .../read/accountImplementation.ts | 69 +++ .../erc7702/account/sessionkey.test.ts | 132 ++++++ .../IIdGateway/write/registerFor.ts | 195 +++++++++ .../read/CHANGE_RECOVERY_ADDRESS_TYPEHASH.ts | 73 +++ 7 files changed, 1082 insertions(+) diff --git a/packages/thirdweb/src/extensions/erc721/lazyMinting/write/createDelayedRevealBatch.ts b/packages/thirdweb/src/extensions/erc721/lazyMinting/write/createDelayedRevealBatch.ts index e69de29bb2d..7e777a18175 100644 --- a/packages/thirdweb/src/extensions/erc721/lazyMinting/write/createDelayedRevealBatch.ts +++ b/packages/thirdweb/src/extensions/erc721/lazyMinting/write/createDelayedRevealBatch.ts @@ -0,0 +1,170 @@ +import { encodePacked } from "viem/utils"; +import { upload } from "../../../../storage/upload.js"; +import type { BaseTransactionOptions } from "../../../../transaction/types.js"; +import { encodeAbiParameters } from "../../../../utils/abi/encodeAbiParameters.js"; +import { toHex } from "../../../../utils/encoding/hex.js"; +import { keccak256 } from "../../../../utils/hashing/keccak256.js"; +import { getBaseUriFromBatch } from "../../../../utils/ipfs.js"; +import type { NFTInput } from "../../../../utils/nft/parseNft.js"; +import { + getBaseURICount, + isGetBaseURICountSupported, +} from "../../__generated__/IBatchMintMetadata/read/getBaseURICount.js"; +import { + encryptDecrypt, + isEncryptDecryptSupported, +} from "../../__generated__/IDelayedReveal/read/encryptDecrypt.js"; +import { nextTokenIdToMint } from "../../__generated__/IERC721Enumerable/read/nextTokenIdToMint.js"; +import { + lazyMint as generatedLazyMint, + isLazyMintSupported, +} from "../../__generated__/ILazyMint/write/lazyMint.js"; +import { hashDelayedRevealPassword } from "../helpers/hashDelayedRevealBatch.js"; + +/** + * @extension ERC721 + */ +export type CreateDelayedRevealBatchParams = { + placeholderMetadata: NFTInput; + metadata: NFTInput[]; + password: string; +}; + +/** + * Creates a batch of encrypted NFTs that can be revealed at a later time. + * This method is only available on the `DropERC721` contract. + * + * @param options {CreateDelayedRevealBatchParams} - The delayed reveal options. + * @param options.placeholderMetadata {@link NFTInput} - The placeholder metadata for the batch. + * @param options.metadata {@link NFTInput} - An array of NFT metadata to be revealed at a later time. + * @param options.password {string} - The password for the reveal. + * @param options.contract {@link ThirdwebContract} - The NFT contract instance. + * + * @returns The prepared transaction to send. + * + * @extension ERC721 + * @example + * ```ts + * import { createDelayedRevealBatch } from "thirdweb/extensions/erc721"; + * + * const placeholderNFT = { + * name: "Hidden NFT", + * description: "Will be revealed next week!" + * }; + * + * const realNFTs = [{ + * name: "Common NFT #1", + * description: "Common NFT, one of many.", + * image: ipfs://..., + * }, { + * name: "Super Rare NFT #2", + * description: "You got a Super Rare NFT!", + * image: ipfs://..., + * }]; + * + * const transaction = createDelayedRevealBatch({ + * contract, + * placeholderMetadata: placeholderNFT, + * metadata: realNFTs, + * password: "password123", + * }); + * + * const { transactionHash } = await sendTransaction({ transaction, account }); + * ``` + */ +export function createDelayedRevealBatch( + options: BaseTransactionOptions, +) { + if (!options.password) { + throw new Error("Password is required"); + } + + return generatedLazyMint({ + asyncParams: async () => { + const [placeholderUris, startFileNumber] = await Promise.all([ + upload({ + client: options.contract.client, + files: Array(options.metadata.length).fill( + options.placeholderMetadata, + ), + }), + nextTokenIdToMint({ + contract: options.contract, + }), + ]); + const placeholderUri = getBaseUriFromBatch(placeholderUris); + + const uris = await upload({ + client: options.contract.client, + files: options.metadata, + // IMPORTANT: File number has to be calculated properly otherwise the whole batch will break + // e.g: If you are uploading a second batch, the file name should never start from `0` + rewriteFileNames: { + fileStartNumber: Number(startFileNumber), + }, + }); + + const baseUri = getBaseUriFromBatch(uris); + const baseUriId = await getBaseURICount({ + contract: options.contract, + }); + + const hashedPassword = await hashDelayedRevealPassword( + baseUriId, + options.password, + options.contract, + ); + const encryptedBaseURI = await encryptDecrypt({ + contract: options.contract, + data: toHex(baseUri), + key: hashedPassword, + }); + + const chainId = BigInt(options.contract.chain.id); + const provenanceHash = keccak256( + encodePacked( + ["bytes", "bytes", "uint256"], + [toHex(baseUri), hashedPassword, chainId], + ), + ); + const data = encodeAbiParameters( + [ + { name: "baseUri", type: "bytes" }, + { name: "provenanceHash", type: "bytes32" }, + ], + [encryptedBaseURI, provenanceHash], + ); + + return { + amount: BigInt(options.metadata.length), + baseURIForTokens: + placeholderUri.slice(-1) === "/" + ? placeholderUri + : `${placeholderUri}/`, + extraData: data, + } as const; + }, + contract: options.contract, + }); +} + +/** + * Checks if the `createDelayedRevealBatch` method is supported by the given contract. + * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. + * @returns A boolean indicating if the `createDelayedRevealBatch` method is supported. + * @extension ERC721 + * @example + * ```ts + * import { isCreateDelayedRevealBatchSupported } from "thirdweb/extensions/erc721"; + * const supported = isCreateDelayedRevealBatchSupported(["0x..."]); + * ``` + */ +export function isCreateDelayedRevealBatchSupported( + availableSelectors: string[], +) { + return [ + isGetBaseURICountSupported(availableSelectors), + isEncryptDecryptSupported(availableSelectors), + isLazyMintSupported(availableSelectors), + ].every(Boolean); +} diff --git a/packages/thirdweb/src/extensions/erc721/lazyMinting/write/reveal.test.ts b/packages/thirdweb/src/extensions/erc721/lazyMinting/write/reveal.test.ts index e69de29bb2d..75563d6c1f6 100644 --- a/packages/thirdweb/src/extensions/erc721/lazyMinting/write/reveal.test.ts +++ b/packages/thirdweb/src/extensions/erc721/lazyMinting/write/reveal.test.ts @@ -0,0 +1,29 @@ +import { beforeAll, describe, expect, it } from "vitest"; +import { ANVIL_CHAIN } from "../../../../../test/src/chains.js"; +import { TEST_CLIENT } from "../../../../../test/src/test-clients.js"; +import { + getContract, + type ThirdwebContract, +} from "../../../../contract/contract.js"; +import { reveal } from "./reveal.js"; + +describe("reveal", () => { + let contract: ThirdwebContract; + beforeAll(() => { + contract = getContract({ + address: "0x708781BAE850faA490cB5b5b16b4687Ec0A8D65D", + chain: ANVIL_CHAIN, + client: TEST_CLIENT, + }); + }); + + it("should throw an error if password is missing", async () => { + const options = { + batchId: 1n, + contract, + password: "", + }; + + expect(() => reveal(options)).toThrowError("Password is required"); + }); +}); diff --git a/packages/thirdweb/src/extensions/erc721/write/sigMint.ts b/packages/thirdweb/src/extensions/erc721/write/sigMint.ts index e69de29bb2d..a65173a230f 100644 --- a/packages/thirdweb/src/extensions/erc721/write/sigMint.ts +++ b/packages/thirdweb/src/extensions/erc721/write/sigMint.ts @@ -0,0 +1,414 @@ +import type { AbiParameterToPrimitiveType, Address } from "abitype"; +import { type Hex, isHex, stringToHex } from "viem"; +import { + isNativeTokenAddress, + NATIVE_TOKEN_ADDRESS, +} from "../../../constants/addresses.js"; +import type { ThirdwebContract } from "../../../contract/contract.js"; +import type { BaseTransactionOptions } from "../../../transaction/types.js"; +import { toBigInt } from "../../../utils/bigint.js"; +import { dateToSeconds, tenYearsFromNow } from "../../../utils/date.js"; +import type { NFTInput } from "../../../utils/nft/parseNft.js"; +import { randomBytesHex } from "../../../utils/random.js"; +import type { Account } from "../../../wallets/interfaces/wallet.js"; +import { + mintWithSignature as generatedMintWithSignature, + type MintWithSignatureParams, +} from "../__generated__/ISignatureMintERC721/write/mintWithSignature.js"; +import { + mintWithSignature as generatedMintWithSignatureV2, + type MintWithSignatureParams as MintWithSignatureParamsV2, +} from "../__generated__/ISignatureMintERC721_v2/write/mintWithSignature.js"; + +/** + * Mints a new ERC721 token with the given minter signature + * This method is only available on the `TokenERC721` contract. + * + * @param options - The transaction options. + * @example + * ```ts + * import { mintWithSignature, generateMintSignature } from "thirdweb/extensions/erc721"; + * import { sendTransaction } from "thirdweb"; + * + * const { payload, signature } = await generateMintSignature(...) + * + * const transaction = mintWithSignature({ + * contract, + * payload, + * signature, + * }); + * + * await sendTransaction({ transaction, account }); + * ``` + * @extension ERC721 + * @returns A promise that resolves to the transaction result. + */ +export function mintWithSignature( + options: BaseTransactionOptions< + | { payload: PayloadTypeV2; signature: Hex } + | { payload: PayloadType; signature: Hex } + >, +) { + const { payload } = options; + if ("quantity" in payload) { + return mintWithSignatureV2( + options as BaseTransactionOptions, + ); + } + return mintWithSignatureV1( + options as BaseTransactionOptions, + ); +} + +function mintWithSignatureV1( + options: BaseTransactionOptions, +) { + const value = isNativeTokenAddress(options.payload.currency) + ? options.payload.price + : 0n; + return generatedMintWithSignature({ + ...options, + overrides: { + value, + }, + }); +} + +function mintWithSignatureV2( + options: BaseTransactionOptions, +) { + const value = isNativeTokenAddress(options.payload.currency) + ? options.payload.pricePerToken * options.payload.quantity + : 0n; + return generatedMintWithSignatureV2({ + ...options, + overrides: { + value, + }, + }); +} + +export type GenerateMintSignatureOptions< + T extends "LoyaltyCard" | "TokenERC721" = "TokenERC721", +> = { + account: Account; + contract: ThirdwebContract; + mintRequest: GeneratePayloadInput; + contractType?: T; +}; + +/** + * Generates the payload and signature for minting an ERC721 token. + * @param options - The options for the minting process. + * @example + * ```ts + * import { mintWithSignature, generateMintSignature } from "thirdweb/extensions/erc721"; + * + * const { payload, signature } = await generateMintSignature({ + * account, + * contract, + * mintRequest: { + * to: "0x...", + * metadata: { + * name: "My NFT", + * description: "This is my NFT", + * image: "https://example.com/image.png", + * }, + * }, + * }); + * + * const transaction = mintWithSignature({ + * contract, + * payload, + * signature, + * }); + * await sendTransaction({ transaction, account }); + * ``` + * @extension ERC721 + * @returns A promise that resolves to the payload and signature. + */ +export async function generateMintSignature< + T extends "LoyaltyCard" | "TokenERC721" = "TokenERC721", +>(options: GenerateMintSignatureOptions): Promise> { + const { mintRequest, account, contract, contractType } = options; + + const currency = mintRequest.currency || NATIVE_TOKEN_ADDRESS; + const [price, uri, uid] = await Promise.all([ + // price per token in wei + (async () => { + // if priceInWei is provided, use it + if ("priceInWei" in mintRequest && mintRequest.priceInWei) { + return mintRequest.priceInWei; + } + // if price is provided, convert it to wei + if ("price" in mintRequest && mintRequest.price) { + const { convertErc20Amount } = await import( + "../../../utils/extensions/convert-erc20-amount.js" + ); + return await convertErc20Amount({ + amount: mintRequest.price, + chain: contract.chain, + client: contract.client, + erc20Address: currency, + }); + } + // if neither price nor priceInWei is provided, default to 0 + return 0n; + })(), + // uri + (async () => { + if ("metadata" in mintRequest) { + if (typeof mintRequest.metadata === "object") { + // async import the upload function because it is not always required + const { upload } = await import("../../../storage/upload.js"); + return await upload({ + client: options.contract.client, + files: [mintRequest.metadata], + }); + } + return mintRequest.metadata; + } + return ""; + })(), + // uid computation + ((): Hex => { + if (mintRequest.uid) { + return isHex(mintRequest.uid) + ? mintRequest.uid + : stringToHex(mintRequest.uid, { size: 32 }); + } + return randomBytesHex(); + })(), + ]); + + const startTime = mintRequest.validityStartTimestamp || new Date(0); + const endTime = mintRequest.validityEndTimestamp || tenYearsFromNow(); + + let saleRecipient: Address; + if ( + mintRequest.primarySaleRecipient?.length === 0 || + !mintRequest.primarySaleRecipient + ) { + const { primarySaleRecipient } = await import( + "../../common/__generated__/IPrimarySale/read/primarySaleRecipient.js" + ); + saleRecipient = await primarySaleRecipient({ + contract, + }); + } else { + saleRecipient = mintRequest.primarySaleRecipient; + } + + let royaltyRecipient: Address; + if ( + mintRequest.royaltyRecipient?.length === 0 || + !mintRequest.royaltyRecipient + ) { + const { getDefaultRoyaltyInfo } = await import( + "../../common/__generated__/IRoyalty/read/getDefaultRoyaltyInfo.js" + ); + const royaltyInfo = await getDefaultRoyaltyInfo({ + contract, + }); + royaltyRecipient = royaltyInfo[0]; + } else { + royaltyRecipient = mintRequest.royaltyRecipient; + } + + if (contractType === "LoyaltyCard") { + return signPayloadV2({ + account, + contract, + currency, + endTime, + mintRequest, + price, + primarySaleRecipient: saleRecipient, + royaltyRecipient, + startTime, + uid, + uri, + }) as Promise>; + } + return signPayloadV1({ + account, + contract, + currency, + endTime, + mintRequest, + price, + primarySaleRecipient: saleRecipient, + royaltyRecipient, + startTime, + uid, + uri, + }) as Promise>; +} + +async function signPayloadV1({ + mintRequest, + account, + contract, + uri, + currency, + uid, + price, + royaltyRecipient, + primarySaleRecipient, + startTime, + endTime, +}: { + mintRequest: GeneratePayloadInput; + account: Account; + contract: ThirdwebContract; + uri: string; + currency: Address; + uid: Hex; + price: bigint; + royaltyRecipient: Address; + primarySaleRecipient: Address; + startTime: Date; + endTime: Date; +}): Promise<{ payload: PayloadType; signature: Hex }> { + const payload: PayloadType = { + currency, + price, + primarySaleRecipient, + royaltyBps: toBigInt(mintRequest.royaltyBps || 0), + royaltyRecipient, + to: mintRequest.to, + uid, + uri, + validityEndTimestamp: dateToSeconds(endTime), + validityStartTimestamp: dateToSeconds(startTime), + }; + + const signature = await account.signTypedData({ + domain: { + chainId: contract.chain.id, + name: "TokenERC721", + verifyingContract: contract.address, + version: "1", + }, + message: payload, + primaryType: "MintRequest", + types: { + MintRequest: MintRequest721, + }, + }); + return { payload, signature }; +} + +async function signPayloadV2({ + mintRequest, + account, + contract, + uri, + currency, + uid, + price, + royaltyRecipient, + primarySaleRecipient, + startTime, + endTime, +}: { + mintRequest: GeneratePayloadInput; + account: Account; + contract: ThirdwebContract; + uri: string; + currency: Address; + uid: Hex; + price: bigint; + royaltyRecipient: Address; + primarySaleRecipient: Address; + startTime: Date; + endTime: Date; +}): Promise<{ payload: PayloadTypeV2; signature: Hex }> { + const payload: PayloadTypeV2 = { + currency, + pricePerToken: price, + primarySaleRecipient, + quantity: toBigInt(1), // always 1 for 721 NFTs + royaltyBps: toBigInt(mintRequest.royaltyBps || 0), + royaltyRecipient, + to: mintRequest.to, + uid, + uri, + validityEndTimestamp: dateToSeconds(endTime), + validityStartTimestamp: dateToSeconds(startTime), + }; + + const signature = await account.signTypedData({ + domain: { + chainId: contract.chain.id, + name: "SignatureMintERC721", + verifyingContract: contract.address, + version: "1", + }, + message: payload, + primaryType: "MintRequest", + types: { + MintRequest: MintRequest721_V2, + }, + }); + return { payload, signature }; +} + +type SignPayloadResult = T extends "LoyaltyCard" + ? Awaited> + : Awaited>; + +type PayloadType = AbiParameterToPrimitiveType<{ + type: "tuple"; + name: "payload"; + components: typeof MintRequest721; +}>; + +type PayloadTypeV2 = AbiParameterToPrimitiveType<{ + type: "tuple"; + name: "payload"; + components: typeof MintRequest721_V2; +}>; + +type GeneratePayloadInput = { + to: string; + metadata: NFTInput | string; + royaltyRecipient?: Address; + royaltyBps?: number; + primarySaleRecipient?: Address; + price?: string; + priceInWei?: bigint; + currency?: Address; + validityStartTimestamp?: Date; + validityEndTimestamp?: Date; + uid?: string; +}; + +const MintRequest721 = [ + { name: "to", type: "address" }, + { name: "royaltyRecipient", type: "address" }, + { name: "royaltyBps", type: "uint256" }, + { name: "primarySaleRecipient", type: "address" }, + { name: "uri", type: "string" }, + { name: "price", type: "uint256" }, + { name: "currency", type: "address" }, + { name: "validityStartTimestamp", type: "uint128" }, + { name: "validityEndTimestamp", type: "uint128" }, + { name: "uid", type: "bytes32" }, +] as const; + +// used for LoyaltyCard contract and base sigmint contracts +// adds quantity to the payload so its the same as 1155 +const MintRequest721_V2 = [ + { name: "to", type: "address" }, + { name: "royaltyRecipient", type: "address" }, + { name: "royaltyBps", type: "uint256" }, + { name: "primarySaleRecipient", type: "address" }, + { name: "uri", type: "string" }, + { name: "quantity", type: "uint256" }, + { name: "pricePerToken", type: "uint256" }, + { name: "currency", type: "address" }, + { name: "validityStartTimestamp", type: "uint128" }, + { name: "validityEndTimestamp", type: "uint128" }, + { name: "uid", type: "bytes32" }, +] as const; diff --git a/packages/thirdweb/src/extensions/erc7579/__generated__/ModularAccountFactory/read/accountImplementation.ts b/packages/thirdweb/src/extensions/erc7579/__generated__/ModularAccountFactory/read/accountImplementation.ts index e69de29bb2d..5f54e325971 100644 --- a/packages/thirdweb/src/extensions/erc7579/__generated__/ModularAccountFactory/read/accountImplementation.ts +++ b/packages/thirdweb/src/extensions/erc7579/__generated__/ModularAccountFactory/read/accountImplementation.ts @@ -0,0 +1,69 @@ +import { decodeAbiParameters } from "viem"; +import { readContract } from "../../../../../transaction/read-contract.js"; +import type { BaseTransactionOptions } from "../../../../../transaction/types.js"; +import { detectMethod } from "../../../../../utils/bytecode/detectExtension.js"; +import type { Hex } from "../../../../../utils/encoding/hex.js"; + +export const FN_SELECTOR = "0x11464fbe" as const; +const FN_INPUTS = [] as const; +const FN_OUTPUTS = [ + { + type: "address", + }, +] as const; + +/** + * Checks if the `accountImplementation` method is supported by the given contract. + * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. + * @returns A boolean indicating if the `accountImplementation` method is supported. + * @extension ERC7579 + * @example + * ```ts + * import { isAccountImplementationSupported } from "thirdweb/extensions/erc7579"; + * const supported = isAccountImplementationSupported(["0x..."]); + * ``` + */ +export function isAccountImplementationSupported(availableSelectors: string[]) { + return detectMethod({ + availableSelectors, + method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, + }); +} + +/** + * Decodes the result of the accountImplementation function call. + * @param result - The hexadecimal result to decode. + * @returns The decoded result as per the FN_OUTPUTS definition. + * @extension ERC7579 + * @example + * ```ts + * import { decodeAccountImplementationResult } from "thirdweb/extensions/erc7579"; + * const result = decodeAccountImplementationResultResult("..."); + * ``` + */ +export function decodeAccountImplementationResult(result: Hex) { + return decodeAbiParameters(FN_OUTPUTS, result)[0]; +} + +/** + * Calls the "accountImplementation" function on the contract. + * @param options - The options for the accountImplementation function. + * @returns The parsed result of the function call. + * @extension ERC7579 + * @example + * ```ts + * import { accountImplementation } from "thirdweb/extensions/erc7579"; + * + * const result = await accountImplementation({ + * contract, + * }); + * + * ``` + */ +export async function accountImplementation(options: BaseTransactionOptions) { + return readContract({ + contract: options.contract, + method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, + params: [], + }); +} diff --git a/packages/thirdweb/src/extensions/erc7702/account/sessionkey.test.ts b/packages/thirdweb/src/extensions/erc7702/account/sessionkey.test.ts index e69de29bb2d..156fcc8ee6c 100644 --- a/packages/thirdweb/src/extensions/erc7702/account/sessionkey.test.ts +++ b/packages/thirdweb/src/extensions/erc7702/account/sessionkey.test.ts @@ -0,0 +1,132 @@ +import { defineChain } from "src/chains/utils.js"; +import { prepareTransaction } from "src/transaction/prepare-transaction.js"; +import { inAppWallet } from "src/wallets/in-app/web/in-app.js"; +import type { Account } from "src/wallets/interfaces/wallet.js"; +import { beforeAll, describe, expect, it } from "vitest"; +import { TEST_CLIENT } from "../../../../test/src/test-clients.js"; +import { TEST_ACCOUNT_A } from "../../../../test/src/test-wallets.js"; +import { ZERO_ADDRESS } from "../../../constants/addresses.js"; +import { + getContract, + type ThirdwebContract, +} from "../../../contract/contract.js"; +import { parseEventLogs } from "../../../event/actions/parse-logs.js"; +import { sendAndConfirmTransaction } from "../../../transaction/actions/send-and-confirm-transaction.js"; +import { sessionCreatedEvent } from "../__generated__/MinimalAccount/events/SessionCreated.js"; +import { createSessionKey } from "./createSessionKey.js"; +import { Condition, LimitType } from "./types.js"; + +describe.runIf(process.env.TW_SECRET_KEY)( + "Session Key Behavior", + { + retry: 0, + timeout: 240_000, + }, + () => { + const chainId = 11155111; + let account: Account; + let accountContract: ThirdwebContract; + + beforeAll(async () => { + // Create 7702 Smart EOA + const wallet = inAppWallet({ + executionMode: { + mode: "EIP7702", + sponsorGas: true, + }, + }); + account = await wallet.connect({ + chain: defineChain(chainId), + client: TEST_CLIENT, + strategy: "guest", + }); + + // Send a null tx to trigger deploy/upgrade + await sendAndConfirmTransaction({ + account: account, + transaction: prepareTransaction({ + chain: defineChain(chainId), + client: TEST_CLIENT, + to: account.address, + value: 0n, + }), + }); + + // Will auto resolve abi since it's deployed + accountContract = getContract({ + address: account.address, + chain: defineChain(chainId), + client: TEST_CLIENT, + }); + }, 120_000); + + it("should allow adding adminlike session keys", async () => { + const receipt = await sendAndConfirmTransaction({ + account: account, + transaction: createSessionKey({ + account: account, + contract: accountContract, + durationInSeconds: 86400, + grantFullPermissions: true, // 1 day + sessionKeyAddress: TEST_ACCOUNT_A.address, + }), + }); + const logs = parseEventLogs({ + events: [sessionCreatedEvent()], + logs: receipt.logs, + }); + expect(logs[0]?.args.signer).toBe(TEST_ACCOUNT_A.address); + }); + + it("should allow adding granular session keys", async () => { + const receipt = await sendAndConfirmTransaction({ + account: account, + transaction: createSessionKey({ + account: account, + callPolicies: [ + { + constraints: [ + { + condition: Condition.Unconstrained, + index: 0n, + refValue: + "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + ], + maxValuePerUse: 0n, + selector: "0x00000000", + target: ZERO_ADDRESS, + valueLimit: { + limit: 0n, + limitType: LimitType.Unlimited, + period: 0n, + }, + }, + ], + contract: accountContract, + durationInSeconds: 86400, // 1 day + grantFullPermissions: false, + sessionKeyAddress: TEST_ACCOUNT_A.address, + transferPolicies: [ + { + maxValuePerUse: 0n, + target: ZERO_ADDRESS, + valueLimit: { + limit: 0n, + limitType: 0, + period: 0n, + }, + }, + ], + }), + }); + const logs = parseEventLogs({ + events: [sessionCreatedEvent()], + logs: receipt.logs, + }); + expect(logs[0]?.args.signer).toBe(TEST_ACCOUNT_A.address); + expect(logs[0]?.args.sessionSpec.callPolicies).toHaveLength(1); + expect(logs[0]?.args.sessionSpec.transferPolicies).toHaveLength(1); + }); + }, +); diff --git a/packages/thirdweb/src/extensions/farcaster/__generated__/IIdGateway/write/registerFor.ts b/packages/thirdweb/src/extensions/farcaster/__generated__/IIdGateway/write/registerFor.ts index e69de29bb2d..17e541df5d2 100644 --- a/packages/thirdweb/src/extensions/farcaster/__generated__/IIdGateway/write/registerFor.ts +++ b/packages/thirdweb/src/extensions/farcaster/__generated__/IIdGateway/write/registerFor.ts @@ -0,0 +1,195 @@ +import type { AbiParameterToPrimitiveType } from "abitype"; +import { prepareContractCall } from "../../../../../transaction/prepare-contract-call.js"; +import type { + BaseTransactionOptions, + WithOverrides, +} from "../../../../../transaction/types.js"; +import { encodeAbiParameters } from "../../../../../utils/abi/encodeAbiParameters.js"; +import { detectMethod } from "../../../../../utils/bytecode/detectExtension.js"; +import { once } from "../../../../../utils/promise/once.js"; + +/** + * Represents the parameters for the "registerFor" function. + */ +export type RegisterForParams = WithOverrides<{ + to: AbiParameterToPrimitiveType<{ type: "address"; name: "to" }>; + recovery: AbiParameterToPrimitiveType<{ type: "address"; name: "recovery" }>; + deadline: AbiParameterToPrimitiveType<{ type: "uint256"; name: "deadline" }>; + sig: AbiParameterToPrimitiveType<{ type: "bytes"; name: "sig" }>; + extraStorage: AbiParameterToPrimitiveType<{ + type: "uint256"; + name: "extraStorage"; + }>; +}>; + +export const FN_SELECTOR = "0xa0c7529c" as const; +const FN_INPUTS = [ + { + name: "to", + type: "address", + }, + { + name: "recovery", + type: "address", + }, + { + name: "deadline", + type: "uint256", + }, + { + name: "sig", + type: "bytes", + }, + { + name: "extraStorage", + type: "uint256", + }, +] as const; +const FN_OUTPUTS = [ + { + name: "fid", + type: "uint256", + }, + { + name: "overpayment", + type: "uint256", + }, +] as const; + +/** + * Checks if the `registerFor` method is supported by the given contract. + * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. + * @returns A boolean indicating if the `registerFor` method is supported. + * @extension FARCASTER + * @example + * ```ts + * import { isRegisterForSupported } from "thirdweb/extensions/farcaster"; + * + * const supported = isRegisterForSupported(["0x..."]); + * ``` + */ +export function isRegisterForSupported(availableSelectors: string[]) { + return detectMethod({ + availableSelectors, + method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, + }); +} + +/** + * Encodes the parameters for the "registerFor" function. + * @param options - The options for the registerFor function. + * @returns The encoded ABI parameters. + * @extension FARCASTER + * @example + * ```ts + * import { encodeRegisterForParams } from "thirdweb/extensions/farcaster"; + * const result = encodeRegisterForParams({ + * to: ..., + * recovery: ..., + * deadline: ..., + * sig: ..., + * extraStorage: ..., + * }); + * ``` + */ +export function encodeRegisterForParams(options: RegisterForParams) { + return encodeAbiParameters(FN_INPUTS, [ + options.to, + options.recovery, + options.deadline, + options.sig, + options.extraStorage, + ]); +} + +/** + * Encodes the "registerFor" function into a Hex string with its parameters. + * @param options - The options for the registerFor function. + * @returns The encoded hexadecimal string. + * @extension FARCASTER + * @example + * ```ts + * import { encodeRegisterFor } from "thirdweb/extensions/farcaster"; + * const result = encodeRegisterFor({ + * to: ..., + * recovery: ..., + * deadline: ..., + * sig: ..., + * extraStorage: ..., + * }); + * ``` + */ +export function encodeRegisterFor(options: RegisterForParams) { + // we do a "manual" concat here to avoid the overhead of the "concatHex" function + // we can do this because we know the specific formats of the values + return (FN_SELECTOR + + encodeRegisterForParams(options).slice( + 2, + )) as `${typeof FN_SELECTOR}${string}`; +} + +/** + * Prepares a transaction to call the "registerFor" function on the contract. + * @param options - The options for the "registerFor" function. + * @returns A prepared transaction object. + * @extension FARCASTER + * @example + * ```ts + * import { sendTransaction } from "thirdweb"; + * import { registerFor } from "thirdweb/extensions/farcaster"; + * + * const transaction = registerFor({ + * contract, + * to: ..., + * recovery: ..., + * deadline: ..., + * sig: ..., + * extraStorage: ..., + * overrides: { + * ... + * } + * }); + * + * // Send the transaction + * await sendTransaction({ transaction, account }); + * ``` + */ +export function registerFor( + options: BaseTransactionOptions< + | RegisterForParams + | { + asyncParams: () => Promise; + } + >, +) { + const asyncOptions = once(async () => { + return "asyncParams" in options ? await options.asyncParams() : options; + }); + + return prepareContractCall({ + accessList: async () => (await asyncOptions()).overrides?.accessList, + authorizationList: async () => + (await asyncOptions()).overrides?.authorizationList, + contract: options.contract, + erc20Value: async () => (await asyncOptions()).overrides?.erc20Value, + extraGas: async () => (await asyncOptions()).overrides?.extraGas, + gas: async () => (await asyncOptions()).overrides?.gas, + gasPrice: async () => (await asyncOptions()).overrides?.gasPrice, + maxFeePerGas: async () => (await asyncOptions()).overrides?.maxFeePerGas, + maxPriorityFeePerGas: async () => + (await asyncOptions()).overrides?.maxPriorityFeePerGas, + method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, + nonce: async () => (await asyncOptions()).overrides?.nonce, + params: async () => { + const resolvedOptions = await asyncOptions(); + return [ + resolvedOptions.to, + resolvedOptions.recovery, + resolvedOptions.deadline, + resolvedOptions.sig, + resolvedOptions.extraStorage, + ] as const; + }, + value: async () => (await asyncOptions()).overrides?.value, + }); +} diff --git a/packages/thirdweb/src/extensions/farcaster/__generated__/IIdRegistry/read/CHANGE_RECOVERY_ADDRESS_TYPEHASH.ts b/packages/thirdweb/src/extensions/farcaster/__generated__/IIdRegistry/read/CHANGE_RECOVERY_ADDRESS_TYPEHASH.ts index e69de29bb2d..8cf023ced5f 100644 --- a/packages/thirdweb/src/extensions/farcaster/__generated__/IIdRegistry/read/CHANGE_RECOVERY_ADDRESS_TYPEHASH.ts +++ b/packages/thirdweb/src/extensions/farcaster/__generated__/IIdRegistry/read/CHANGE_RECOVERY_ADDRESS_TYPEHASH.ts @@ -0,0 +1,73 @@ +import { decodeAbiParameters } from "viem"; +import { readContract } from "../../../../../transaction/read-contract.js"; +import type { BaseTransactionOptions } from "../../../../../transaction/types.js"; +import { detectMethod } from "../../../../../utils/bytecode/detectExtension.js"; +import type { Hex } from "../../../../../utils/encoding/hex.js"; + +export const FN_SELECTOR = "0xd5bac7f3" as const; +const FN_INPUTS = [] as const; +const FN_OUTPUTS = [ + { + type: "bytes32", + }, +] as const; + +/** + * Checks if the `CHANGE_RECOVERY_ADDRESS_TYPEHASH` method is supported by the given contract. + * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. + * @returns A boolean indicating if the `CHANGE_RECOVERY_ADDRESS_TYPEHASH` method is supported. + * @extension FARCASTER + * @example + * ```ts + * import { isCHANGE_RECOVERY_ADDRESS_TYPEHASHSupported } from "thirdweb/extensions/farcaster"; + * const supported = isCHANGE_RECOVERY_ADDRESS_TYPEHASHSupported(["0x..."]); + * ``` + */ +export function isCHANGE_RECOVERY_ADDRESS_TYPEHASHSupported( + availableSelectors: string[], +) { + return detectMethod({ + availableSelectors, + method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, + }); +} + +/** + * Decodes the result of the CHANGE_RECOVERY_ADDRESS_TYPEHASH function call. + * @param result - The hexadecimal result to decode. + * @returns The decoded result as per the FN_OUTPUTS definition. + * @extension FARCASTER + * @example + * ```ts + * import { decodeCHANGE_RECOVERY_ADDRESS_TYPEHASHResult } from "thirdweb/extensions/farcaster"; + * const result = decodeCHANGE_RECOVERY_ADDRESS_TYPEHASHResultResult("..."); + * ``` + */ +export function decodeCHANGE_RECOVERY_ADDRESS_TYPEHASHResult(result: Hex) { + return decodeAbiParameters(FN_OUTPUTS, result)[0]; +} + +/** + * Calls the "CHANGE_RECOVERY_ADDRESS_TYPEHASH" function on the contract. + * @param options - The options for the CHANGE_RECOVERY_ADDRESS_TYPEHASH function. + * @returns The parsed result of the function call. + * @extension FARCASTER + * @example + * ```ts + * import { CHANGE_RECOVERY_ADDRESS_TYPEHASH } from "thirdweb/extensions/farcaster"; + * + * const result = await CHANGE_RECOVERY_ADDRESS_TYPEHASH({ + * contract, + * }); + * + * ``` + */ +export async function CHANGE_RECOVERY_ADDRESS_TYPEHASH( + options: BaseTransactionOptions, +) { + return readContract({ + contract: options.contract, + method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, + params: [], + }); +} From 4fbfdbb42a2c5cb4ec4342a2bf671ffdd4f85c48 Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Fri, 8 Aug 2025 19:36:06 +0700 Subject: [PATCH 3/5] Update TikTok icon SVGs for web and native Replaced the TikTok SVG data in both walletIcon.ts and svgs.ts with a new, more detailed SVG. This ensures consistency and improved visual quality for the TikTok icon across web and native platforms. --- .../src/react/core/utils/walletIcon.ts | 3 +-- .../src/react/native/ui/icons/svgs.ts | 22 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/thirdweb/src/react/core/utils/walletIcon.ts b/packages/thirdweb/src/react/core/utils/walletIcon.ts index c4518d48409..d9071b626ce 100644 --- a/packages/thirdweb/src/react/core/utils/walletIcon.ts +++ b/packages/thirdweb/src/react/core/utils/walletIcon.ts @@ -29,8 +29,7 @@ const githubIconUri = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTA2IiBoZWlnaHQ9IjEwNiIgdmlld0JveD0iMCAwIDEwNiAxMDYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxjaXJjbGUgY3g9IjUzIiBjeT0iNTMiIHI9IjUzIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUyLjg1NCA0QzI1LjgzOSA0IDQgMjYgNCA1My4yMTdDNCA3NC45NzMgMTcuOTkzIDkzLjM4OSAzNy40MDUgOTkuOTA3QzM5LjgzMiAxMDAuMzk3IDQwLjcyMSA5OC44NDggNDAuNzIxIDk3LjU0NUM0MC43MjEgOTYuNDA0IDQwLjY0MSA5Mi40OTMgNDAuNjQxIDg4LjQxOEMyNy4wNTEgOTEuMzUyIDI0LjIyMSA4Mi41NTEgMjQuMjIxIDgyLjU1MUMyMi4wMzcgNzYuODQ3IDE4LjgwMSA3NS4zODEgMTguODAxIDc1LjM4MUMxNC4zNTMgNzIuMzY2IDE5LjEyNSA3Mi4zNjYgMTkuMTI1IDcyLjM2NkMyNC4wNTkgNzIuNjkyIDI2LjY0OCA3Ny40MTggMjYuNjQ4IDc3LjQxOEMzMS4wMTUgODQuOTE0IDM4LjA1MiA4Mi43OTYgNDAuODgzIDgxLjQ5MkM0MS4yODcgNzguMzE0IDQyLjU4MiA3Ni4xMTQgNDMuOTU3IDc0Ljg5MkMzMy4xMTggNzMuNzUxIDIxLjcxNCA2OS41MTQgMjEuNzE0IDUwLjYwOUMyMS43MTQgNDUuMjMxIDIzLjY1NCA0MC44MzEgMjYuNzI4IDM3LjQwOUMyNi4yNDMgMzYuMTg3IDI0LjU0NCAzMS4xMzQgMjcuMjE0IDI0LjM3MUMyNy4yMTQgMjQuMzcxIDMxLjMzOSAyMy4wNjcgNDAuNjQgMjkuNDIzQzQ0LjYyMjEgMjguMzQ1NyA0OC43Mjg4IDI3Ljc5NzYgNTIuODU0IDI3Ljc5M0M1Ni45NzkgMjcuNzkzIDYxLjE4NCAyOC4zNjQgNjUuMDY3IDI5LjQyM0M3NC4zNjkgMjMuMDY3IDc4LjQ5NCAyNC4zNzEgNzguNDk0IDI0LjM3MUM4MS4xNjQgMzEuMTM0IDc5LjQ2NCAzNi4xODcgNzguOTc5IDM3LjQwOUM4Mi4xMzQgNDAuODMxIDgzLjk5NCA0NS4yMzEgODMuOTk0IDUwLjYwOUM4My45OTQgNjkuNTE0IDcyLjU5IDczLjY2OSA2MS42NyA3NC44OTJDNjMuNDUgNzYuNDQgNjQuOTg2IDc5LjM3MyA2NC45ODYgODQuMDE4QzY0Ljk4NiA5MC42MTggNjQuOTA2IDk1LjkxNSA2NC45MDYgOTcuNTQ0QzY0LjkwNiA5OC44NDggNjUuNzk2IDEwMC4zOTcgNjguMjIyIDk5LjkwOEM4Ny42MzQgOTMuMzg4IDEwMS42MjcgNzQuOTczIDEwMS42MjcgNTMuMjE3QzEwMS43MDcgMjYgNzkuNzg4IDQgNTIuODU0IDRaIiBmaWxsPSIjMjQyOTJGIi8+Cjwvc3ZnPgo="; const xIcon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIwIiBoZWlnaHQ9IjMyMCIgdmlld0JveD0iMCAwIDMyMCAzMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIzMjAiIGhlaWdodD0iMzIwIiByeD0iMjQiIGZpbGw9ImJsYWNrIi8+CjxwYXRoIGQ9Ik0xNzcuMzE1IDE0NS4zMzVMMjQ1LjA2OCA2N0gyMjkuMDEzTDE3MC4xODIgMTM1LjAxN0wxMjMuMTk1IDY3SDY5TDE0MC4wNTUgMTY5Ljg1NEw2OSAyNTJIODUuMDU2M0wxNDcuMTgzIDE4MC4xNzJMMTk2LjgwNSAyNTJIMjUxTDE3Ny4zMTEgMTQ1LjMzNUgxNzcuMzE1Wk0xNTUuMzIzIDE3MC43NkwxNDguMTI0IDE2MC41MThMOTAuODQxNyA3OS4wMjJIMTE1LjUwM0wxNjEuNzMxIDE0NC43OTJMMTY4LjkzIDE1NS4wMzRMMjI5LjAyIDI0MC41MjVIMjA0LjM1OUwxNTUuMzIzIDE3MC43NjRWMTcwLjc2WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg=="; -const tiktokIconUri = - "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyNS4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4wIiBpZD0iQ2FwYV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgdmlld0JveD0iMCAwIDg0OCA1OTQiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDg0OCA1OTQ7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGQ9Ik00MjQsNDFjLTE0MS40LDAtMjU2LDExNC42LTI1NiwyNTZzMTE0LjYsMjU2LDI1NiwyNTZzMjU2LTExNC42LDI1Ni0yNTZTNTY1LjQsNDEsNDI0LDQxeiBNNTUyLjQsMjM2Ljl2MzQuNw0KCWMtMTYuMywwLTMyLjItMy4yLTQ3LjItOS41Yy05LjYtNC4xLTE4LjYtOS4zLTI2LjgtMTUuNmwwLjIsMTA2LjdjLTAuMSwyNC05LjYsNDYuNi0yNi44LDYzLjZjLTE0LDEzLjgtMzEuNywyMi42LTUxLDI1LjUNCgljLTQuNSwwLjctOS4xLDEtMTMuOCwxYy0yMC42LDAtNDAuMS02LjctNTYuMS0xOWMtMy0yLjMtNS45LTQuOC04LjYtNy41Yy0xOC42LTE4LjQtMjguMy00My40LTI2LjYtNjkuN2MxLjItMjAsOS4yLTM5LjEsMjIuNi01NA0KCWMxNy43LTE5LjgsNDIuNC0zMC44LDY4LjgtMzAuOGM0LjYsMCw5LjIsMC4zLDEzLjgsMXYxMi44djM1LjdjLTQuMy0xLjQtOC45LTIuMi0xMy42LTIuMmMtMjQuMSwwLTQzLjcsMTkuNy00My4zLDQzLjgNCgljMC4yLDE1LjUsOC43LDI5LDIxLjEsMzYuNGM1LjksMy41LDEyLjYsNS43LDE5LjgsNi4xYzUuNiwwLjMsMTEtMC41LDE2LTIuMWMxNy4yLTUuNywyOS43LTIxLjksMjkuNy00MWwwLjEtNzEuNFYxNTAuOGg0Ny43DQoJYzAsNC43LDAuNSw5LjMsMS40LDEzLjhjMy42LDE4LjEsMTMuOCwzMy44LDI4LDQ0LjVjMTIuNCw5LjMsMjcuOCwxNC45LDQ0LjUsMTQuOWMwLDAsMC4xLDAsMC4xLDBMNTUyLjQsMjM2LjlMNTUyLjQsMjM2Ljl6Ii8+DQo8L3N2Zz4NCg=="; +const tiktokIconUri = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjAwIDEyMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxwYXRoIGQ9Ik03NTQgMTIwMUgxVjFoMTIwMHYxMjAwem03MC44Ny04NDguNTkyYy0uNjI0LS4wOC0xLjI0Ny0uMTYxLTIuNTIyLS41My0xLjUwNS0xLjYyNS0zLjAxLTMuMjUtNC41MjUtNS40OTctLjY1OS0uNDM1LTEuMzE4LS44Ny0xLjg4OS0xLjMzIDAgMC0uMDI0LjA4OC4wNC0uNjQyLTEuMjg4LTEuNjctMi42My0zLjMtMy44NTgtNS4wMTQtOS42ODgtMTMuNTE5LTE3Ljk2NS0yNy44Mi0yNC4xMTQtNDMuMzE3LTYuMzMzLTE1Ljk2LTEwLjY0OC0zMi4zOTctMTIuMDA0LTQ5LjU1Ni0uNTA0LTYuMzY3LTEtMTIuNzMzLTEuNTItMTkuMzczLTExLjc1IDAtMjMuNTIgMC0zNS40MzYtLjE4NSAwIDAtLjE4Ni0uMTQ2LS40NjMtLjg4NmwtMy40OTItMzIuODU2SDYwMHY2LjA4NWMwIDE3Ny4wNzktLjAxNyAzNTQuMTU3LjAyOSA1MzEuMjM2LjAwNCAxNS42MDItMy44OTMgMzAuMzE0LTEwLjQ3NiA0NC4xODQtMTEuNjY4IDI0LjU4LTI5LjkxIDQyLjgwNS01NC43NTMgNTQuMzMzLTIzLjU4NiAxMC45NDUtNDcuOTU4IDEzLjY1NC03My4xMzIgNy4xMTMtOC4wNC0yLjA4OS0xNS43ODgtNS4zMDYtMjQuMjY1LTguMjUzLS44LS42OTUtMS42LTEuMzktMi40MjItMi43OS0yNi4xNy00MC43MDctMjguNTk2LTgzLjExMS0yLjI0OC0xMjMuNDc3IDMwLjA1OS00Ni4wNTEgNzQuMjQ5LTYxLjIwOCAxMjcuODktNDcuODc4VjUxNS4zMDdjLTEzLjA1LS4zOTMtMjUuNzQyLS43NzYtMzguNTgtMS4zNDMgMCAwLS4xODctLjE0NS0uMDU4LTEuMDVWNDgxLjQ5Yy0xMS4zODYtLjUwNC0yMi41My0xLjYyMy0zMy42MzYtMS4zMzMtMTQuNDUuMzc2LTI5LjAzOC44NDUtNDMuMjY0IDMuMTQ3LTE3LjE4NiAyLjc4MS0zMy44ODcgNy44NzgtNTAuMTQyIDE0LjU0LTI3LjY5NCAxMS4zNS01Mi42MzkgMjYuODM3LTc0LjQzNCA0Ny4yNzctNy40ODUgNy4wMi0xNC40NzUgMTQuNjA0LTIxLjMwOSAyMi4yNzItMTEuMDY0IDEyLjQxNS0yMC4yMzcgMjYuMTg3LTI4LjI0MiA0MC43NjQtMTEuMzYgMjAuNjg1LTE5Ljk3IDQyLjQ4MS0yNC42NzggNjUuNTU1LTIuNzY1IDEzLjU0OC0zLjgyNyAyNy40NjktNS4xNzMgNDEuMjY5LS42MjcgNi40MzItLjQ0IDEyLjk5Ny0uMDM1IDE5LjQ2NS43NiAxMi4xMTUgMS4wMjkgMjQuMzY3IDMuMTQyIDM2LjI3IDQuODYyIDI3LjM3OCAxMy45ODMgNTMuMzc0IDI3LjggNzcuNjU4IDE0LjY0IDI1LjczIDMzLjAyNCA0OC4zNDMgNTUuNTI1IDY3LjU2MyAzLjkwMSAzLjMzMiA4LjMzMSA2LjA0NiAxMi40ODkgOS4wMzQgMCAwLS4wMTMtLjAyNy4wNC41Ni41OS40OSAxLjE3OC45ODEgMi4wNiAyLjExMyAyLjYyMyAyLjE3NCA1LjI0NiA0LjM0OSA4LjE1NiA3LjEzNC41NS4xMjggMS4xLjI1NSAxLjY1OS4yOTMgMCAwLS4wODkuMDItLjQ2Ni42NzkgMTIuNjIxIDEwLjQzOCAyNC42OCAyMS42NjcgMzcuOTkzIDMxLjEzMiAyMC42OSAxNC43MTIgNDMuNzE1IDI1LjAwNiA2OC4xMzQgMzIuMTc1YTI0OC42IDI0OC42IDAgMCAwIDU3Ljc4NiA5Ljc5MmMyMC4xMDIgMS4wMDIgNDAuMDgtLjc5OSA1OS45MzMtNC42OTQgMjUuMzAxLTQuOTY1IDQ5LjEtMTMuNjExIDcxLjYxOC0yNi4xMDIgMjYuMTM4LTE0LjUgNDguODE0LTMzLjEwNSA2OC4zNC01NS43MzMgMTMuMjAyLTE1LjMwMiAyNC42NTItMzEuODIgMzMuMzU2LTQ5Ljk0NCA2LjQ0Mi0xMy40MTUgMTEuNTU0LTI3LjU1OSAxNi4yMDQtNDEuNzI0IDYuODc3LTIwLjk0OCA5Ljk0LTQyLjc2OCAxMC4wMjEtNjQuNzIxLjMzLTg5LjY0LjEzOC0xNzkuMjg0LjEzOC0yNjguOTI1di02Ljg5MmM1Ni41NDkgMzguODc3IDExNy45NDggNTguOTQgMTg1LjYyNSA1OC45ODhWNDEzLjg5Yy0zLjg2MyAwLTcuMzk0LjM0NC0xMC44MzUtLjA2N2EzOTggMzk4IDAgMCAxLTI3Ljc5LTQuMzI1Yy0uMDU2LS4xNjYtLjExMS0uMzMyLS4wMTgtMS40MjN2LTI3LjQ5M2MtOC4zMDQtLjg5Ny0xNi4wODYtMS43NC0yMy44NjgtMi41NzctMTkuOTU1LTIuMTQ4LTM4LjgyNC03LjkxOC01Ni43My0xNi45NC01LjQ0OC0yLjc0Ni0xMC45NTgtNS4zNjgtMTYuNDE1LTguMDI4IDAgMCAuMDIzLjAyMi0uMS0uNjMiLz4KICA8cGF0aCBmaWxsPSIjRkZGIiBkPSJNOTIxLjgzNCA0MDkuMDAyYy4wNTUuMTY1LjExLjMzMS4xNzggMS4yMDkuMDEzIDM1LjMwMy4wMTMgNjkuODk0LjAxMyAxMDQuOTM1LTY4LjIwNS0uNDY2LTEyOS42NjgtMjAuMTEzLTE4NS43ODYtNTguODktLjExMyAyLjI5Mi0uMjI3IDMuNTY1LS4yMzIgNC44MzktLjM0MSA5My41OC0uNTgzIDE4Ny4xNi0xLjEzNyAyODAuNzM4LS4wNiAxMC4xMjUtMS4xODcgMjAuMzgtMy4xMDcgMzAuMzI2LTMuNTQ2IDE4LjM2LTkuMDUgMzYuMTU2LTE2LjQ2MyA1My40NS0xMS4yMDkgMjYuMTQ4LTI2LjQ2NCA0OS41NzUtNDUuODU1IDcwLjE3Ny05LjI0IDkuODE4LTE5LjM1MiAxOS4wNjQtMzAuMTczIDI3LjA4YTM1MCAzNTAgMCAwIDEtNDAuNjk2IDI1Ljk5NWMtMTcuMjk5IDkuNDMtMzUuOTUxIDE2LjE2Ni01NS4zMTkgMTkuODMtMTYuMjAyIDMuMDY2LTMyLjcwNSA1LjA1LTQ5LjE2NSA2LjEwMy0xOC40MjggMS4xNzgtMzYuNzgtMS4xMzItNTQuOTE0LTQuNzczLTI1Ljg5Mi01LjItNTAuMzYtMTQuMTQtNzMuMi0yNy40NzctNC41OTMtMi42ODItOS40MS00Ljk4LTE0LjEyNC03LjQ1NSAwIDAgLjA4OS0uMDIuMDUyLS4zMS0uNjYtLjQtMS4yODQtLjUwNi0xLjkwOC0uNjEzLTIuNjI0LTIuMTc0LTUuMjQ3LTQuMzQ5LTcuOTY1LTcuMjEtLjczNS0xLjEyOS0xLjM3Ny0xLjU3LTIuMDE4LTIuMDEzIDAgMCAuMDEzLjAyNy4yMDMtLjMwNi0uOTU1LTEuOTM5LTEuOTgyLTMuNjQ0LTMuMjYyLTUuMTMyLTE0Ljg1MS0xNy4yNzUtMjYuNTc2LTM2LjQ2OC0zNS45NDQtNTcuMjItMTEuNzY1LTI2LjA2Ni0xOS4wNzUtNTMuMzk1LTIwLjY4LTgxLjc4OC0xLjA1LTE4LjU5NC0uMDA5LTM3LjQ3OCAyLjAxLTU2LjAyNCAxLjkzOC0xNy44MTggNy4xMTgtMzUuMTMxIDEzLjYtNTEuOTg1IDExLjIwNS0yOS4xMjYgMjcuMjc2LTU1LjI4NSA0OC40NC03OC4wNjQgMTAuMDg0LTEwLjg1NCAyMS4zMjUtMjAuODEyIDMyLjk2OC0zMC4wMDMgMTkuNTA3LTE1LjM5OSA0MS4yMzMtMjcuMDcyIDY0LjY5NS0zNS41NTQgMjUuODI4LTkuMzM3IDUyLjQwMy0xNC4zMDIgNzkuODExLTE1LjA0OCAwIDAgLjE4Ni4xNDUuMTc5LjY5LS4wMDcgMzUuNDU5LS4wMDcgNzAuMzc0LS4wMDcgMTA1LjIyNy05LjE3NC0xLjY0My0xNy43NDYtMy44NDYtMjYuNDQyLTQuNTgtMTMuNjc2LTEuMTU0LTI3LjE3LjcyMy00MC4zMiA0Ljg1NS0yNy44ODggOC43NjQtNDkuNDAxIDI1LjgzLTY0LjEwNiA1MC44OC0xMi43ODkgMjEuNzg2LTE3LjYzNyA0NS40NjktMTQuMjc3IDcwLjgxNSAyLjA3IDE1LjYxOCA3LjAwOCAzMC4wNzYgMTUuMTg5IDQzLjM5NCAxMC42NiAxNy4zNTMgMjQuNzUgMzEuMjM2IDQyLjkzIDQwLjczNC44LjY5NiAxLjYgMS4zOTEgMi43MTIgMi41NTQgNy40NTUgMTAuNTIyIDE2LjMzNiAxOC44MTcgMjYuNTM1IDI1LjYzNyAyNS40MDYgMTYuOTg5IDUzLjMyMiAyMy41MDkgODMuMzk2IDE3Ljc5MyAzMC40NC01Ljc4NSA1NC40OTctMjIuMTg3IDcyLjE5My00Ny41MTYgMTEuMjc1LTE2LjEzOSAxOC44NTEtMzQuMzI4IDE4LjkzOC01My45ODIuNzI4LTE2NC4xODQuODkzLTMyOC4zNyAxLjIyLTQ5Mi41NTYuMDMtMTUuMTU4LjAwNC0zMC4zMTcuMDA0LTQ1LjQ3NlYyMjdjMi41IDAgNC40NTkuMDAzIDYuNDE3IDBsOTIuNDQtLjE4MnMuMTg1LjE0Ni4xNzguNjM4Yy0uMDExLjk5LS4xMTggMS41MTUtLjAwNiAxLjk4NiA0LjUyMyAxOS4wMjcgMTAuNjQyIDM3LjM4IDIwLjMzNCA1NC41NjcgMTEuODY4IDIxLjA0OCAyNy4zMyAzOC45MDcgNDUuNjc2IDU0LjM4NSAzLjIyNSAyLjcyIDcuMjI3IDQuNTIgMTAuODcgNi43NDUgMCAwIC4wMjUtLjA4Ny0uMjI4LjIzNi41NDkuNzU4IDEuMzUxIDEuMTkzIDIuMTUzIDEuNjI4YTM2OCAzNjggMCAwIDAgNSA1LjMyNGMxLjA0NC41NDMgMS42MDEuNjM4IDIuMTU4LjczMyAwIDAtLjAyMy0uMDIyLS4yMi4yOTggNC43OTcgNS4xMTYgOS42OTQgMTAuMDE4IDE0LjggMTQuNjkgMTUuMjY0IDEzLjk2MiAzMi42ODYgMjQuNjAxIDUxLjk0IDMxLjk1MiA5LjgyIDMuNzUgMjAuMTk2IDYuMDQ3IDMwLjMyMiA5LjAwMiIvPgogIDxwYXRoIGZpbGw9IiNGRTJDNTUiIGQ9Ik0zNTEuNjY2IDkzNS40MTljNC45MDIgMi4xNDUgOS43MTkgNC40NDMgMTQuMzEyIDcuMTI1IDIyLjg0IDEzLjMzNyA0Ny4zMDggMjIuMjc4IDczLjIgMjcuNDc3IDE4LjEzNSAzLjY0IDM2LjQ4NiA1Ljk1MSA1NC45MTQgNC43NzMgMTYuNDYtMS4wNTIgMzIuOTYzLTMuMDM3IDQ5LjE2NS02LjEwMyAxOS4zNjgtMy42NjQgMzguMDItMTAuNCA1NS4zMi0xOS44M2EzNTAgMzUwIDAgMCAwIDQwLjY5NS0yNS45OTVjMTAuODIxLTguMDE2IDIwLjkzNC0xNy4yNjIgMzAuMTczLTI3LjA4IDE5LjM5LTIwLjYwMiAzNC42NDYtNDQuMDI5IDQ1Ljg1NS03MC4xNzcgNy40MTMtMTcuMjk0IDEyLjkxNy0zNS4wOSAxNi40NjMtNTMuNDUgMS45Mi05Ljk0NiAzLjA0Ny0yMC4yMDEgMy4xMDctMzAuMzI2LjU1NC05My41NzguNzk2LTE4Ny4xNTkgMS4xMzctMjgwLjczOC4wMDUtMS4yNzQuMTE5LTIuNTQ3LjIzMi00Ljg0IDU2LjExOCAzOC43NzggMTE3LjU4MSA1OC40MjUgMTg1Ljc4NiA1OC44OSAwLTM1LjA0IDAtNjkuNjMxLjA3LTEwNC42ODUgOS4yNzYuODM1IDE4LjQ2NiAyLjI2IDI3LjY5NSAzLjM2NCAzLjQ0LjQxIDYuOTcyLjA2NiAxMC44MzUuMDY2VjU0OS4xYy02Ny42NzctLjA0OC0xMjkuMDc2LTIwLjExLTE4NS42MjUtNTguOTg3djYuODkyYzAgODkuNjQxLjE5MyAxNzkuMjg0LS4xMzggMjY4LjkyNS0uMDggMjEuOTUzLTMuMTQ0IDQzLjc3My0xMC4wMiA2NC43MjEtNC42NSAxNC4xNjUtOS43NjMgMjguMzA5LTE2LjIwNSA0MS43MjQtOC43MDQgMTguMTI1LTIwLjE1NCAzNC42NDItMzMuMzU3IDQ5Ljk0NC0xOS41MjUgMjIuNjI4LTQyLjIwMSA0MS4yMzMtNjguMzQgNTUuNzMzLTIyLjUxNyAxMi40OTEtNDYuMzE2IDIxLjEzNy03MS42MTcgMjYuMTAyLTE5Ljg1MyAzLjg5NS0zOS44MyA1LjY5Ni01OS45MzMgNC42OTRhMjQ4LjYgMjQ4LjYgMCAwIDEtNTcuNzg2LTkuNzkyYy0yNC40MTktNy4xNy00Ny40NDQtMTcuNDYzLTY4LjEzNC0zMi4xNzUtMTMuMzEzLTkuNDY1LTI1LjM3Mi0yMC42OTQtMzcuODA0LTMxLjQ2MiIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik03MzguNzE4IDIyNi40NDhjLTMwLjY3NS40MzEtNjEuNDg4LjQ5Mi05Mi4zMDEuNTUxLTEuOTU4LjAwNC0zLjkxNi4wMDEtNi40MTcuMDAxdjUuMjg4YzAgMTUuMTU5LjAyNiAzMC4zMTgtLjAwNCA0NS40NzYtLjMyNyAxNjQuMTg2LS40OTIgMzI4LjM3Mi0xLjIyIDQ5Mi41NTYtLjA4NyAxOS42NTQtNy42NjMgMzcuODQzLTE4LjkzOCA1My45ODItMTcuNjk2IDI1LjMyOS00MS43NTMgNDEuNzMtNzIuMTkzIDQ3LjUxNi0zMC4wNzQgNS43MTYtNTcuOTktLjgwNC04My4zOTYtMTcuNzkzLTEwLjE5OS02LjgyLTE5LjA4LTE1LjExNS0yNi4yMzctMjUuNTE0IDcuODY4IDIuMzU2IDE1LjYxNiA1LjU3MyAyMy42NTYgNy42NjIgMjUuMTc0IDYuNTQxIDQ5LjU0NiAzLjgzMiA3My4xMzItNy4xMTMgMjQuODQzLTExLjUyOCA0My4wODUtMjkuNzUzIDU0Ljc1My01NC4zMzMgNi41ODMtMTMuODcgMTAuNDgtMjguNTgyIDEwLjQ3Ni00NC4xODQtLjA0Ni0xNzcuMDc5LS4wMjktMzU0LjE1Ny0uMDI5LTUzMS4yMzZ2LTYuMDg1aDEzNS4wODdjMS4xODIgMTEuMTE1IDIuMzM3IDIxLjk4NSAzLjYzIDMzLjIyNiIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik01MjEuOTIgNTEzLjM2N2MtMjcuNDcyIDEuMTk4LTU0LjA0NyA2LjE2My03OS44NzUgMTUuNS0yMy40NjIgOC40ODItNDUuMTg4IDIwLjE1NS02NC42OTUgMzUuNTU0LTExLjY0MyA5LjE5MS0yMi44ODQgMTkuMTQ5LTMyLjk2OSAzMC4wMDMtMjEuMTYzIDIyLjc3OS0zNy4yMzQgNDguOTM4LTQ4LjQzOCA3OC4wNjQtNi40ODMgMTYuODU0LTExLjY2MyAzNC4xNjctMTMuNjAyIDUxLjk4NS0yLjAxOCAxOC41NDYtMy4wNTkgMzcuNDMtMi4wMDggNTYuMDI0IDEuNjA0IDI4LjM5MyA4LjkxNCA1NS43MjIgMjAuNjggODEuNzg4IDkuMzY3IDIwLjc1MiAyMS4wOTIgMzkuOTQ1IDM1Ljk0MyA1Ny4yMiAxLjI4IDEuNDg4IDIuMzA3IDMuMTkzIDMuMjc2IDUuMTM3LTQuMzYyLTIuNjYtOC43OTItNS4zNzQtMTIuNjkzLTguNzA2LTIyLjUwMS0xOS4yMi00MC44ODUtNDEuODMyLTU1LjUyNS02Ny41NjMtMTMuODE3LTI0LjI4NC0yMi45MzgtNTAuMjgtMjcuOC03Ny42NTgtMi4xMTMtMTEuOTAzLTIuMzgyLTI0LjE1NS0zLjE0Mi0zNi4yNy0uNDA2LTYuNDY4LS41OTItMTMuMDMzLjAzNS0xOS40NjUgMS4zNDYtMTMuOCAyLjQwOC0yNy43MjEgNS4xNzMtNDEuMjY5IDQuNzA5LTIzLjA3NCAxMy4zMTktNDQuODcgMjQuNjc4LTY1LjU1NSA4LjAwNS0xNC41NzcgMTcuMTc4LTI4LjM1IDI4LjI0Mi00MC43NjQgNi44MzQtNy42NjggMTMuODI0LTE1LjI1MyAyMS4zMDktMjIuMjcyIDIxLjc5NS0yMC40NCA0Ni43NC0zNS45MjcgNzQuNDM0LTQ3LjI3NyAxNi4yNTUtNi42NjIgMzIuOTU2LTExLjc1OSA1MC4xNDItMTQuNTQgMTQuMjI2LTIuMzAyIDI4LjgxMy0yLjc3IDQzLjI2NC0zLjE0NyAxMS4xMDctLjI5IDIyLjI1LjgyOSAzMy42MzYgMS4zMzMgMCAxMS4xNDUgMCAyMS4yODUtLjA2NCAzMS44NzgiLz4KICA8cGF0aCBmaWxsPSIjRkUyQzU1IiBkPSJNNDM0Ljk5MiA4MjUuNDgyYy0xOC4xNy05LjE0Ni0zMi4yNi0yMy4wMjktNDIuOTItNDAuMzgyLTguMTgtMTMuMzE4LTEzLjEyLTI3Ljc3Ni0xNS4xOS00My4zOTQtMy4zNTktMjUuMzQ2IDEuNDktNDkuMDI5IDE0LjI3OC03MC44MTUgMTQuNzA1LTI1LjA1IDM2LjIxOC00Mi4xMTYgNjQuMTA2LTUwLjg4IDEzLjE1LTQuMTMyIDI2LjY0NC02LjAwOSA0MC4zMi00Ljg1NSA4LjY5Ni43MzQgMTcuMjY4IDIuOTM3IDI2LjQ0MiA0LjU4IDAtMzQuODUzIDAtNjkuNzY4LjA4LTEwNS4xMzUgMTIuNzczLS4wNyAyNS40NjYuMzEzIDM4LjUxNC43MDZ2MTM4LjQ2OGMtNTMuNjQtMTMuMzMtOTcuODMgMS44MjctMTI3Ljg5IDQ3Ljg3OC0yNi4zNDcgNDAuMzY2LTIzLjkyMSA4Mi43NyAyLjI2IDEyMy44MjltMzgwLjk1LTQ4MC43MDhjLTMuNjc2LTEuODYtNy42NzgtMy42Ni0xMC45MDMtNi4zOC0xOC4zNDUtMTUuNDc4LTMzLjgwOC0zMy4zMzctNDUuNjc2LTU0LjM4NS05LjY5Mi0xNy4xODctMTUuODExLTM1LjU0LTIwLjMzNC01NC41NjctLjExMi0uNDcxLS4wMDUtLjk5NS4wOC0xLjg5NCAxMS44NDktLjM5OSAyMy42MTgtLjM5OSAzNS4zNjktLjM5OS41MiA2LjY0IDEuMDE2IDEzLjAwNiAxLjUyIDE5LjM3MyAxLjM1NiAxNy4xNiA1LjY3IDMzLjU5NSAxMi4wMDQgNDkuNTU2IDYuMTUgMTUuNDk4IDE0LjQyNiAyOS43OTggMjQuMTE0IDQzLjMxNyAxLjIyNyAxLjcxMyAyLjU3IDMuMzQ0IDMuODI2IDUuMzc5Ii8+CiAgPHBhdGggZmlsbD0iIzI1RjRFRSIgZD0iTTkyMS45MDggNDA4LjUzOWMtMTAuMi0yLjQ5Mi0yMC41NzUtNC43OS0zMC4zOTctOC41NC0xOS4yNTMtNy4zNS0zNi42NzUtMTcuOTg5LTUxLjkzOS0zMS45NTItNS4xMDYtNC42NzEtMTAuMDAzLTkuNTczLTE0LjgxMi0xNC42OTkgNS42NjYgMi4zNSAxMS4xNzYgNC45NzIgMTYuNjI0IDcuNzE4IDE3LjkwNiA5LjAyMiAzNi43NzUgMTQuNzkyIDU2LjczIDE2Ljk0IDcuNzgyLjgzOCAxNS41NjQgMS42OCAyMy44NjggMi41NzcgMCA4Ljk4OCAwIDE4LjI0LS4wNzQgMjcuOTU2Ii8+CiAgPHBhdGggZmlsbD0iI0ZFMkM1NSIgZD0iTTgxNy44MjkgMzQ2LjY5MmEzLjUgMy41IDAgMCAxLTIuMTkyLTEuMzA1Yy44NjguMTI0IDEuNTI3LjU1OSAyLjE5MiAxLjMwNSIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik0zNDAuMDQyIDkyNS4yMzdjLjYxNC4xNDggMS4yNTYuNTkgMS44NDUgMS4zOTktLjY0LS4xMjUtMS4yMy0uNjE1LTEuODQ1LTEuNE04MjQuOTMgMzUyLjczNGMtLjQ5NS4yMzEtMS4wNTIuMTM2LTEuNzctLjI2My40NjMtLjIyNCAxLjA4Ni0uMTQ0IDEuNzcuMjYzIi8+CiAgPHBhdGggZmlsbD0iI0ZFMkM1NSIgZD0iTTM1MC4xNDEgOTM0LjQ3MmMuNDgtLjIgMS4xMDUtLjA5MiAxLjc2LjM1MS0uNTE3LjIxLTEuMDY3LjA4Mi0xLjc2LS4zNTEiLz4KPC9zdmc+Cg=="; const emailIcon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEzLjMzMzUgMi42NjY1SDIuNjY2ODNDMS45MzA0NSAyLjY2NjUgMS4zMzM1IDMuMjYzNDYgMS4zMzM1IDMuOTk5ODRWMTEuOTk5OEMxLjMzMzUgMTIuNzM2MiAxLjkzMDQ1IDEzLjMzMzIgMi42NjY4MyAxMy4zMzMySDEzLjMzMzVDMTQuMDY5OSAxMy4zMzMyIDE0LjY2NjggMTIuNzM2MiAxNC42NjY4IDExLjk5OThWMy45OTk4NEMxNC42NjY4IDMuMjYzNDYgMTQuMDY5OSAyLjY2NjUgMTMuMzMzNSAyLjY2NjVaIiBzdHJva2U9IiMzMzg1RkYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTQuNjY2OCA0LjY2NjVMOC42ODY4MyA4LjQ2NjVDOC40ODEwMSA4LjU5NTQ1IDguMjQzMDQgOC42NjM4NCA4LjAwMDE2IDguNjYzODRDNy43NTcyOCA4LjY2Mzg0IDcuNTE5MzEgOC41OTU0NSA3LjMxMzUgOC40NjY1TDEuMzMzNSA0LjY2NjUiIHN0cm9rZT0iIzMzODVGRiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPgo="; const phoneIcon = diff --git a/packages/thirdweb/src/react/native/ui/icons/svgs.ts b/packages/thirdweb/src/react/native/ui/icons/svgs.ts index 0409d17c85f..4c11c4a7d59 100644 --- a/packages/thirdweb/src/react/native/ui/icons/svgs.ts +++ b/packages/thirdweb/src/react/native/ui/icons/svgs.ts @@ -137,15 +137,19 @@ export const X_ICON = ` `; -export const TIKTOK_ICON = ` - -`; +export const TIKTOK_ICON = ` + + + + + + + + + + + +`; export const GUEST_ICON = ``; From eceea8389a4399a7939dc4f20a56257b15231170 Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Fri, 8 Aug 2025 19:47:16 +0700 Subject: [PATCH 4/5] use round icon --- .../src/react/core/utils/walletIcon.ts | 2 +- .../src/react/native/ui/icons/svgs.ts | 24 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/thirdweb/src/react/core/utils/walletIcon.ts b/packages/thirdweb/src/react/core/utils/walletIcon.ts index d9071b626ce..708ef216876 100644 --- a/packages/thirdweb/src/react/core/utils/walletIcon.ts +++ b/packages/thirdweb/src/react/core/utils/walletIcon.ts @@ -29,7 +29,7 @@ const githubIconUri = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTA2IiBoZWlnaHQ9IjEwNiIgdmlld0JveD0iMCAwIDEwNiAxMDYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxjaXJjbGUgY3g9IjUzIiBjeT0iNTMiIHI9IjUzIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUyLjg1NCA0QzI1LjgzOSA0IDQgMjYgNCA1My4yMTdDNCA3NC45NzMgMTcuOTkzIDkzLjM4OSAzNy40MDUgOTkuOTA3QzM5LjgzMiAxMDAuMzk3IDQwLjcyMSA5OC44NDggNDAuNzIxIDk3LjU0NUM0MC43MjEgOTYuNDA0IDQwLjY0MSA5Mi40OTMgNDAuNjQxIDg4LjQxOEMyNy4wNTEgOTEuMzUyIDI0LjIyMSA4Mi41NTEgMjQuMjIxIDgyLjU1MUMyMi4wMzcgNzYuODQ3IDE4LjgwMSA3NS4zODEgMTguODAxIDc1LjM4MUMxNC4zNTMgNzIuMzY2IDE5LjEyNSA3Mi4zNjYgMTkuMTI1IDcyLjM2NkMyNC4wNTkgNzIuNjkyIDI2LjY0OCA3Ny40MTggMjYuNjQ4IDc3LjQxOEMzMS4wMTUgODQuOTE0IDM4LjA1MiA4Mi43OTYgNDAuODgzIDgxLjQ5MkM0MS4yODcgNzguMzE0IDQyLjU4MiA3Ni4xMTQgNDMuOTU3IDc0Ljg5MkMzMy4xMTggNzMuNzUxIDIxLjcxNCA2OS41MTQgMjEuNzE0IDUwLjYwOUMyMS43MTQgNDUuMjMxIDIzLjY1NCA0MC44MzEgMjYuNzI4IDM3LjQwOUMyNi4yNDMgMzYuMTg3IDI0LjU0NCAzMS4xMzQgMjcuMjE0IDI0LjM3MUMyNy4yMTQgMjQuMzcxIDMxLjMzOSAyMy4wNjcgNDAuNjQgMjkuNDIzQzQ0LjYyMjEgMjguMzQ1NyA0OC43Mjg4IDI3Ljc5NzYgNTIuODU0IDI3Ljc5M0M1Ni45NzkgMjcuNzkzIDYxLjE4NCAyOC4zNjQgNjUuMDY3IDI5LjQyM0M3NC4zNjkgMjMuMDY3IDc4LjQ5NCAyNC4zNzEgNzguNDk0IDI0LjM3MUM4MS4xNjQgMzEuMTM0IDc5LjQ2NCAzNi4xODcgNzguOTc5IDM3LjQwOUM4Mi4xMzQgNDAuODMxIDgzLjk5NCA0NS4yMzEgODMuOTk0IDUwLjYwOUM4My45OTQgNjkuNTE0IDcyLjU5IDczLjY2OSA2MS42NyA3NC44OTJDNjMuNDUgNzYuNDQgNjQuOTg2IDc5LjM3MyA2NC45ODYgODQuMDE4QzY0Ljk4NiA5MC42MTggNjQuOTA2IDk1LjkxNSA2NC45MDYgOTcuNTQ0QzY0LjkwNiA5OC44NDggNjUuNzk2IDEwMC4zOTcgNjguMjIyIDk5LjkwOEM4Ny42MzQgOTMuMzg4IDEwMS42MjcgNzQuOTczIDEwMS42MjcgNTMuMjE3QzEwMS43MDcgMjYgNzkuNzg4IDQgNTIuODU0IDRaIiBmaWxsPSIjMjQyOTJGIi8+Cjwvc3ZnPgo="; const xIcon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIwIiBoZWlnaHQ9IjMyMCIgdmlld0JveD0iMCAwIDMyMCAzMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIzMjAiIGhlaWdodD0iMzIwIiByeD0iMjQiIGZpbGw9ImJsYWNrIi8+CjxwYXRoIGQ9Ik0xNzcuMzE1IDE0NS4zMzVMMjQ1LjA2OCA2N0gyMjkuMDEzTDE3MC4xODIgMTM1LjAxN0wxMjMuMTk1IDY3SDY5TDE0MC4wNTUgMTY5Ljg1NEw2OSAyNTJIODUuMDU2M0wxNDcuMTgzIDE4MC4xNzJMMTk2LjgwNSAyNTJIMjUxTDE3Ny4zMTEgMTQ1LjMzNUgxNzcuMzE1Wk0xNTUuMzIzIDE3MC43NkwxNDguMTI0IDE2MC41MThMOTAuODQxNyA3OS4wMjJIMTE1LjUwM0wxNjEuNzMxIDE0NC43OTJMMTY4LjkzIDE1NS4wMzRMMjI5LjAyIDI0MC41MjVIMjA0LjM1OUwxNTUuMzIzIDE3MC43NjRWMTcwLjc2WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg=="; -const tiktokIconUri = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjAwIDEyMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxwYXRoIGQ9Ik03NTQgMTIwMUgxVjFoMTIwMHYxMjAwem03MC44Ny04NDguNTkyYy0uNjI0LS4wOC0xLjI0Ny0uMTYxLTIuNTIyLS41My0xLjUwNS0xLjYyNS0zLjAxLTMuMjUtNC41MjUtNS40OTctLjY1OS0uNDM1LTEuMzE4LS44Ny0xLjg4OS0xLjMzIDAgMC0uMDI0LjA4OC4wNC0uNjQyLTEuMjg4LTEuNjctMi42My0zLjMtMy44NTgtNS4wMTQtOS42ODgtMTMuNTE5LTE3Ljk2NS0yNy44Mi0yNC4xMTQtNDMuMzE3LTYuMzMzLTE1Ljk2LTEwLjY0OC0zMi4zOTctMTIuMDA0LTQ5LjU1Ni0uNTA0LTYuMzY3LTEtMTIuNzMzLTEuNTItMTkuMzczLTExLjc1IDAtMjMuNTIgMC0zNS40MzYtLjE4NSAwIDAtLjE4Ni0uMTQ2LS40NjMtLjg4NmwtMy40OTItMzIuODU2SDYwMHY2LjA4NWMwIDE3Ny4wNzktLjAxNyAzNTQuMTU3LjAyOSA1MzEuMjM2LjAwNCAxNS42MDItMy44OTMgMzAuMzE0LTEwLjQ3NiA0NC4xODQtMTEuNjY4IDI0LjU4LTI5LjkxIDQyLjgwNS01NC43NTMgNTQuMzMzLTIzLjU4NiAxMC45NDUtNDcuOTU4IDEzLjY1NC03My4xMzIgNy4xMTMtOC4wNC0yLjA4OS0xNS43ODgtNS4zMDYtMjQuMjY1LTguMjUzLS44LS42OTUtMS42LTEuMzktMi40MjItMi43OS0yNi4xNy00MC43MDctMjguNTk2LTgzLjExMS0yLjI0OC0xMjMuNDc3IDMwLjA1OS00Ni4wNTEgNzQuMjQ5LTYxLjIwOCAxMjcuODktNDcuODc4VjUxNS4zMDdjLTEzLjA1LS4zOTMtMjUuNzQyLS43NzYtMzguNTgtMS4zNDMgMCAwLS4xODctLjE0NS0uMDU4LTEuMDVWNDgxLjQ5Yy0xMS4zODYtLjUwNC0yMi41My0xLjYyMy0zMy42MzYtMS4zMzMtMTQuNDUuMzc2LTI5LjAzOC44NDUtNDMuMjY0IDMuMTQ3LTE3LjE4NiAyLjc4MS0zMy44ODcgNy44NzgtNTAuMTQyIDE0LjU0LTI3LjY5NCAxMS4zNS01Mi42MzkgMjYuODM3LTc0LjQzNCA0Ny4yNzctNy40ODUgNy4wMi0xNC40NzUgMTQuNjA0LTIxLjMwOSAyMi4yNzItMTEuMDY0IDEyLjQxNS0yMC4yMzcgMjYuMTg3LTI4LjI0MiA0MC43NjQtMTEuMzYgMjAuNjg1LTE5Ljk3IDQyLjQ4MS0yNC42NzggNjUuNTU1LTIuNzY1IDEzLjU0OC0zLjgyNyAyNy40NjktNS4xNzMgNDEuMjY5LS42MjcgNi40MzItLjQ0IDEyLjk5Ny0uMDM1IDE5LjQ2NS43NiAxMi4xMTUgMS4wMjkgMjQuMzY3IDMuMTQyIDM2LjI3IDQuODYyIDI3LjM3OCAxMy45ODMgNTMuMzc0IDI3LjggNzcuNjU4IDE0LjY0IDI1LjczIDMzLjAyNCA0OC4zNDMgNTUuNTI1IDY3LjU2MyAzLjkwMSAzLjMzMiA4LjMzMSA2LjA0NiAxMi40ODkgOS4wMzQgMCAwLS4wMTMtLjAyNy4wNC41Ni41OS40OSAxLjE3OC45ODEgMi4wNiAyLjExMyAyLjYyMyAyLjE3NCA1LjI0NiA0LjM0OSA4LjE1NiA3LjEzNC41NS4xMjggMS4xLjI1NSAxLjY1OS4yOTMgMCAwLS4wODkuMDItLjQ2Ni42NzkgMTIuNjIxIDEwLjQzOCAyNC42OCAyMS42NjcgMzcuOTkzIDMxLjEzMiAyMC42OSAxNC43MTIgNDMuNzE1IDI1LjAwNiA2OC4xMzQgMzIuMTc1YTI0OC42IDI0OC42IDAgMCAwIDU3Ljc4NiA5Ljc5MmMyMC4xMDIgMS4wMDIgNDAuMDgtLjc5OSA1OS45MzMtNC42OTQgMjUuMzAxLTQuOTY1IDQ5LjEtMTMuNjExIDcxLjYxOC0yNi4xMDIgMjYuMTM4LTE0LjUgNDguODE0LTMzLjEwNSA2OC4zNC01NS43MzMgMTMuMjAyLTE1LjMwMiAyNC42NTItMzEuODIgMzMuMzU2LTQ5Ljk0NCA2LjQ0Mi0xMy40MTUgMTEuNTU0LTI3LjU1OSAxNi4yMDQtNDEuNzI0IDYuODc3LTIwLjk0OCA5Ljk0LTQyLjc2OCAxMC4wMjEtNjQuNzIxLjMzLTg5LjY0LjEzOC0xNzkuMjg0LjEzOC0yNjguOTI1di02Ljg5MmM1Ni41NDkgMzguODc3IDExNy45NDggNTguOTQgMTg1LjYyNSA1OC45ODhWNDEzLjg5Yy0zLjg2MyAwLTcuMzk0LjM0NC0xMC44MzUtLjA2N2EzOTggMzk4IDAgMCAxLTI3Ljc5LTQuMzI1Yy0uMDU2LS4xNjYtLjExMS0uMzMyLS4wMTgtMS40MjN2LTI3LjQ5M2MtOC4zMDQtLjg5Ny0xNi4wODYtMS43NC0yMy44NjgtMi41NzctMTkuOTU1LTIuMTQ4LTM4LjgyNC03LjkxOC01Ni43My0xNi45NC01LjQ0OC0yLjc0Ni0xMC45NTgtNS4zNjgtMTYuNDE1LTguMDI4IDAgMCAuMDIzLjAyMi0uMS0uNjMiLz4KICA8cGF0aCBmaWxsPSIjRkZGIiBkPSJNOTIxLjgzNCA0MDkuMDAyYy4wNTUuMTY1LjExLjMzMS4xNzggMS4yMDkuMDEzIDM1LjMwMy4wMTMgNjkuODk0LjAxMyAxMDQuOTM1LTY4LjIwNS0uNDY2LTEyOS42NjgtMjAuMTEzLTE4NS43ODYtNTguODktLjExMyAyLjI5Mi0uMjI3IDMuNTY1LS4yMzIgNC44MzktLjM0MSA5My41OC0uNTgzIDE4Ny4xNi0xLjEzNyAyODAuNzM4LS4wNiAxMC4xMjUtMS4xODcgMjAuMzgtMy4xMDcgMzAuMzI2LTMuNTQ2IDE4LjM2LTkuMDUgMzYuMTU2LTE2LjQ2MyA1My40NS0xMS4yMDkgMjYuMTQ4LTI2LjQ2NCA0OS41NzUtNDUuODU1IDcwLjE3Ny05LjI0IDkuODE4LTE5LjM1MiAxOS4wNjQtMzAuMTczIDI3LjA4YTM1MCAzNTAgMCAwIDEtNDAuNjk2IDI1Ljk5NWMtMTcuMjk5IDkuNDMtMzUuOTUxIDE2LjE2Ni01NS4zMTkgMTkuODMtMTYuMjAyIDMuMDY2LTMyLjcwNSA1LjA1LTQ5LjE2NSA2LjEwMy0xOC40MjggMS4xNzgtMzYuNzgtMS4xMzItNTQuOTE0LTQuNzczLTI1Ljg5Mi01LjItNTAuMzYtMTQuMTQtNzMuMi0yNy40NzctNC41OTMtMi42ODItOS40MS00Ljk4LTE0LjEyNC03LjQ1NSAwIDAgLjA4OS0uMDIuMDUyLS4zMS0uNjYtLjQtMS4yODQtLjUwNi0xLjkwOC0uNjEzLTIuNjI0LTIuMTc0LTUuMjQ3LTQuMzQ5LTcuOTY1LTcuMjEtLjczNS0xLjEyOS0xLjM3Ny0xLjU3LTIuMDE4LTIuMDEzIDAgMCAuMDEzLjAyNy4yMDMtLjMwNi0uOTU1LTEuOTM5LTEuOTgyLTMuNjQ0LTMuMjYyLTUuMTMyLTE0Ljg1MS0xNy4yNzUtMjYuNTc2LTM2LjQ2OC0zNS45NDQtNTcuMjItMTEuNzY1LTI2LjA2Ni0xOS4wNzUtNTMuMzk1LTIwLjY4LTgxLjc4OC0xLjA1LTE4LjU5NC0uMDA5LTM3LjQ3OCAyLjAxLTU2LjAyNCAxLjkzOC0xNy44MTggNy4xMTgtMzUuMTMxIDEzLjYtNTEuOTg1IDExLjIwNS0yOS4xMjYgMjcuMjc2LTU1LjI4NSA0OC40NC03OC4wNjQgMTAuMDg0LTEwLjg1NCAyMS4zMjUtMjAuODEyIDMyLjk2OC0zMC4wMDMgMTkuNTA3LTE1LjM5OSA0MS4yMzMtMjcuMDcyIDY0LjY5NS0zNS41NTQgMjUuODI4LTkuMzM3IDUyLjQwMy0xNC4zMDIgNzkuODExLTE1LjA0OCAwIDAgLjE4Ni4xNDUuMTc5LjY5LS4wMDcgMzUuNDU5LS4wMDcgNzAuMzc0LS4wMDcgMTA1LjIyNy05LjE3NC0xLjY0My0xNy43NDYtMy44NDYtMjYuNDQyLTQuNTgtMTMuNjc2LTEuMTU0LTI3LjE3LjcyMy00MC4zMiA0Ljg1NS0yNy44ODggOC43NjQtNDkuNDAxIDI1LjgzLTY0LjEwNiA1MC44OC0xMi43ODkgMjEuNzg2LTE3LjYzNyA0NS40NjktMTQuMjc3IDcwLjgxNSAyLjA3IDE1LjYxOCA3LjAwOCAzMC4wNzYgMTUuMTg5IDQzLjM5NCAxMC42NiAxNy4zNTMgMjQuNzUgMzEuMjM2IDQyLjkzIDQwLjczNC44LjY5NiAxLjYgMS4zOTEgMi43MTIgMi41NTQgNy40NTUgMTAuNTIyIDE2LjMzNiAxOC44MTcgMjYuNTM1IDI1LjYzNyAyNS40MDYgMTYuOTg5IDUzLjMyMiAyMy41MDkgODMuMzk2IDE3Ljc5MyAzMC40NC01Ljc4NSA1NC40OTctMjIuMTg3IDcyLjE5My00Ny41MTYgMTEuMjc1LTE2LjEzOSAxOC44NTEtMzQuMzI4IDE4LjkzOC01My45ODIuNzI4LTE2NC4xODQuODkzLTMyOC4zNyAxLjIyLTQ5Mi41NTYuMDMtMTUuMTU4LjAwNC0zMC4zMTcuMDA0LTQ1LjQ3NlYyMjdjMi41IDAgNC40NTkuMDAzIDYuNDE3IDBsOTIuNDQtLjE4MnMuMTg1LjE0Ni4xNzguNjM4Yy0uMDExLjk5LS4xMTggMS41MTUtLjAwNiAxLjk4NiA0LjUyMyAxOS4wMjcgMTAuNjQyIDM3LjM4IDIwLjMzNCA1NC41NjcgMTEuODY4IDIxLjA0OCAyNy4zMyAzOC45MDcgNDUuNjc2IDU0LjM4NSAzLjIyNSAyLjcyIDcuMjI3IDQuNTIgMTAuODcgNi43NDUgMCAwIC4wMjUtLjA4Ny0uMjI4LjIzNi41NDkuNzU4IDEuMzUxIDEuMTkzIDIuMTUzIDEuNjI4YTM2OCAzNjggMCAwIDAgNSA1LjMyNGMxLjA0NC41NDMgMS42MDEuNjM4IDIuMTU4LjczMyAwIDAtLjAyMy0uMDIyLS4yMi4yOTggNC43OTcgNS4xMTYgOS42OTQgMTAuMDE4IDE0LjggMTQuNjkgMTUuMjY0IDEzLjk2MiAzMi42ODYgMjQuNjAxIDUxLjk0IDMxLjk1MiA5LjgyIDMuNzUgMjAuMTk2IDYuMDQ3IDMwLjMyMiA5LjAwMiIvPgogIDxwYXRoIGZpbGw9IiNGRTJDNTUiIGQ9Ik0zNTEuNjY2IDkzNS40MTljNC45MDIgMi4xNDUgOS43MTkgNC40NDMgMTQuMzEyIDcuMTI1IDIyLjg0IDEzLjMzNyA0Ny4zMDggMjIuMjc4IDczLjIgMjcuNDc3IDE4LjEzNSAzLjY0IDM2LjQ4NiA1Ljk1MSA1NC45MTQgNC43NzMgMTYuNDYtMS4wNTIgMzIuOTYzLTMuMDM3IDQ5LjE2NS02LjEwMyAxOS4zNjgtMy42NjQgMzguMDItMTAuNCA1NS4zMi0xOS44M2EzNTAgMzUwIDAgMCAwIDQwLjY5NS0yNS45OTVjMTAuODIxLTguMDE2IDIwLjkzNC0xNy4yNjIgMzAuMTczLTI3LjA4IDE5LjM5LTIwLjYwMiAzNC42NDYtNDQuMDI5IDQ1Ljg1NS03MC4xNzcgNy40MTMtMTcuMjk0IDEyLjkxNy0zNS4wOSAxNi40NjMtNTMuNDUgMS45Mi05Ljk0NiAzLjA0Ny0yMC4yMDEgMy4xMDctMzAuMzI2LjU1NC05My41NzguNzk2LTE4Ny4xNTkgMS4xMzctMjgwLjczOC4wMDUtMS4yNzQuMTE5LTIuNTQ3LjIzMi00Ljg0IDU2LjExOCAzOC43NzggMTE3LjU4MSA1OC40MjUgMTg1Ljc4NiA1OC44OSAwLTM1LjA0IDAtNjkuNjMxLjA3LTEwNC42ODUgOS4yNzYuODM1IDE4LjQ2NiAyLjI2IDI3LjY5NSAzLjM2NCAzLjQ0LjQxIDYuOTcyLjA2NiAxMC44MzUuMDY2VjU0OS4xYy02Ny42NzctLjA0OC0xMjkuMDc2LTIwLjExLTE4NS42MjUtNTguOTg3djYuODkyYzAgODkuNjQxLjE5MyAxNzkuMjg0LS4xMzggMjY4LjkyNS0uMDggMjEuOTUzLTMuMTQ0IDQzLjc3My0xMC4wMiA2NC43MjEtNC42NSAxNC4xNjUtOS43NjMgMjguMzA5LTE2LjIwNSA0MS43MjQtOC43MDQgMTguMTI1LTIwLjE1NCAzNC42NDItMzMuMzU3IDQ5Ljk0NC0xOS41MjUgMjIuNjI4LTQyLjIwMSA0MS4yMzMtNjguMzQgNTUuNzMzLTIyLjUxNyAxMi40OTEtNDYuMzE2IDIxLjEzNy03MS42MTcgMjYuMTAyLTE5Ljg1MyAzLjg5NS0zOS44MyA1LjY5Ni01OS45MzMgNC42OTRhMjQ4LjYgMjQ4LjYgMCAwIDEtNTcuNzg2LTkuNzkyYy0yNC40MTktNy4xNy00Ny40NDQtMTcuNDYzLTY4LjEzNC0zMi4xNzUtMTMuMzEzLTkuNDY1LTI1LjM3Mi0yMC42OTQtMzcuODA0LTMxLjQ2MiIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik03MzguNzE4IDIyNi40NDhjLTMwLjY3NS40MzEtNjEuNDg4LjQ5Mi05Mi4zMDEuNTUxLTEuOTU4LjAwNC0zLjkxNi4wMDEtNi40MTcuMDAxdjUuMjg4YzAgMTUuMTU5LjAyNiAzMC4zMTgtLjAwNCA0NS40NzYtLjMyNyAxNjQuMTg2LS40OTIgMzI4LjM3Mi0xLjIyIDQ5Mi41NTYtLjA4NyAxOS42NTQtNy42NjMgMzcuODQzLTE4LjkzOCA1My45ODItMTcuNjk2IDI1LjMyOS00MS43NTMgNDEuNzMtNzIuMTkzIDQ3LjUxNi0zMC4wNzQgNS43MTYtNTcuOTktLjgwNC04My4zOTYtMTcuNzkzLTEwLjE5OS02LjgyLTE5LjA4LTE1LjExNS0yNi4yMzctMjUuNTE0IDcuODY4IDIuMzU2IDE1LjYxNiA1LjU3MyAyMy42NTYgNy42NjIgMjUuMTc0IDYuNTQxIDQ5LjU0NiAzLjgzMiA3My4xMzItNy4xMTMgMjQuODQzLTExLjUyOCA0My4wODUtMjkuNzUzIDU0Ljc1My01NC4zMzMgNi41ODMtMTMuODcgMTAuNDgtMjguNTgyIDEwLjQ3Ni00NC4xODQtLjA0Ni0xNzcuMDc5LS4wMjktMzU0LjE1Ny0uMDI5LTUzMS4yMzZ2LTYuMDg1aDEzNS4wODdjMS4xODIgMTEuMTE1IDIuMzM3IDIxLjk4NSAzLjYzIDMzLjIyNiIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik01MjEuOTIgNTEzLjM2N2MtMjcuNDcyIDEuMTk4LTU0LjA0NyA2LjE2My03OS44NzUgMTUuNS0yMy40NjIgOC40ODItNDUuMTg4IDIwLjE1NS02NC42OTUgMzUuNTU0LTExLjY0MyA5LjE5MS0yMi44ODQgMTkuMTQ5LTMyLjk2OSAzMC4wMDMtMjEuMTYzIDIyLjc3OS0zNy4yMzQgNDguOTM4LTQ4LjQzOCA3OC4wNjQtNi40ODMgMTYuODU0LTExLjY2MyAzNC4xNjctMTMuNjAyIDUxLjk4NS0yLjAxOCAxOC41NDYtMy4wNTkgMzcuNDMtMi4wMDggNTYuMDI0IDEuNjA0IDI4LjM5MyA4LjkxNCA1NS43MjIgMjAuNjggODEuNzg4IDkuMzY3IDIwLjc1MiAyMS4wOTIgMzkuOTQ1IDM1Ljk0MyA1Ny4yMiAxLjI4IDEuNDg4IDIuMzA3IDMuMTkzIDMuMjc2IDUuMTM3LTQuMzYyLTIuNjYtOC43OTItNS4zNzQtMTIuNjkzLTguNzA2LTIyLjUwMS0xOS4yMi00MC44ODUtNDEuODMyLTU1LjUyNS02Ny41NjMtMTMuODE3LTI0LjI4NC0yMi45MzgtNTAuMjgtMjcuOC03Ny42NTgtMi4xMTMtMTEuOTAzLTIuMzgyLTI0LjE1NS0zLjE0Mi0zNi4yNy0uNDA2LTYuNDY4LS41OTItMTMuMDMzLjAzNS0xOS40NjUgMS4zNDYtMTMuOCAyLjQwOC0yNy43MjEgNS4xNzMtNDEuMjY5IDQuNzA5LTIzLjA3NCAxMy4zMTktNDQuODcgMjQuNjc4LTY1LjU1NSA4LjAwNS0xNC41NzcgMTcuMTc4LTI4LjM1IDI4LjI0Mi00MC43NjQgNi44MzQtNy42NjggMTMuODI0LTE1LjI1MyAyMS4zMDktMjIuMjcyIDIxLjc5NS0yMC40NCA0Ni43NC0zNS45MjcgNzQuNDM0LTQ3LjI3NyAxNi4yNTUtNi42NjIgMzIuOTU2LTExLjc1OSA1MC4xNDItMTQuNTQgMTQuMjI2LTIuMzAyIDI4LjgxMy0yLjc3IDQzLjI2NC0zLjE0NyAxMS4xMDctLjI5IDIyLjI1LjgyOSAzMy42MzYgMS4zMzMgMCAxMS4xNDUgMCAyMS4yODUtLjA2NCAzMS44NzgiLz4KICA8cGF0aCBmaWxsPSIjRkUyQzU1IiBkPSJNNDM0Ljk5MiA4MjUuNDgyYy0xOC4xNy05LjE0Ni0zMi4yNi0yMy4wMjktNDIuOTItNDAuMzgyLTguMTgtMTMuMzE4LTEzLjEyLTI3Ljc3Ni0xNS4xOS00My4zOTQtMy4zNTktMjUuMzQ2IDEuNDktNDkuMDI5IDE0LjI3OC03MC44MTUgMTQuNzA1LTI1LjA1IDM2LjIxOC00Mi4xMTYgNjQuMTA2LTUwLjg4IDEzLjE1LTQuMTMyIDI2LjY0NC02LjAwOSA0MC4zMi00Ljg1NSA4LjY5Ni43MzQgMTcuMjY4IDIuOTM3IDI2LjQ0MiA0LjU4IDAtMzQuODUzIDAtNjkuNzY4LjA4LTEwNS4xMzUgMTIuNzczLS4wNyAyNS40NjYuMzEzIDM4LjUxNC43MDZ2MTM4LjQ2OGMtNTMuNjQtMTMuMzMtOTcuODMgMS44MjctMTI3Ljg5IDQ3Ljg3OC0yNi4zNDcgNDAuMzY2LTIzLjkyMSA4Mi43NyAyLjI2IDEyMy44MjltMzgwLjk1LTQ4MC43MDhjLTMuNjc2LTEuODYtNy42NzgtMy42Ni0xMC45MDMtNi4zOC0xOC4zNDUtMTUuNDc4LTMzLjgwOC0zMy4zMzctNDUuNjc2LTU0LjM4NS05LjY5Mi0xNy4xODctMTUuODExLTM1LjU0LTIwLjMzNC01NC41NjctLjExMi0uNDcxLS4wMDUtLjk5NS4wOC0xLjg5NCAxMS44NDktLjM5OSAyMy42MTgtLjM5OSAzNS4zNjktLjM5OS41MiA2LjY0IDEuMDE2IDEzLjAwNiAxLjUyIDE5LjM3MyAxLjM1NiAxNy4xNiA1LjY3IDMzLjU5NSAxMi4wMDQgNDkuNTU2IDYuMTUgMTUuNDk4IDE0LjQyNiAyOS43OTggMjQuMTE0IDQzLjMxNyAxLjIyNyAxLjcxMyAyLjU3IDMuMzQ0IDMuODI2IDUuMzc5Ii8+CiAgPHBhdGggZmlsbD0iIzI1RjRFRSIgZD0iTTkyMS45MDggNDA4LjUzOWMtMTAuMi0yLjQ5Mi0yMC41NzUtNC43OS0zMC4zOTctOC41NC0xOS4yNTMtNy4zNS0zNi42NzUtMTcuOTg5LTUxLjkzOS0zMS45NTItNS4xMDYtNC42NzEtMTAuMDAzLTkuNTczLTE0LjgxMi0xNC42OTkgNS42NjYgMi4zNSAxMS4xNzYgNC45NzIgMTYuNjI0IDcuNzE4IDE3LjkwNiA5LjAyMiAzNi43NzUgMTQuNzkyIDU2LjczIDE2Ljk0IDcuNzgyLjgzOCAxNS41NjQgMS42OCAyMy44NjggMi41NzcgMCA4Ljk4OCAwIDE4LjI0LS4wNzQgMjcuOTU2Ii8+CiAgPHBhdGggZmlsbD0iI0ZFMkM1NSIgZD0iTTgxNy44MjkgMzQ2LjY5MmEzLjUgMy41IDAgMCAxLTIuMTkyLTEuMzA1Yy44NjguMTI0IDEuNTI3LjU1OSAyLjE5MiAxLjMwNSIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik0zNDAuMDQyIDkyNS4yMzdjLjYxNC4xNDggMS4yNTYuNTkgMS44NDUgMS4zOTktLjY0LS4xMjUtMS4yMy0uNjE1LTEuODQ1LTEuNE04MjQuOTMgMzUyLjczNGMtLjQ5NS4yMzEtMS4wNTIuMTM2LTEuNzctLjI2My40NjMtLjIyNCAxLjA4Ni0uMTQ0IDEuNzcuMjYzIi8+CiAgPHBhdGggZmlsbD0iI0ZFMkM1NSIgZD0iTTM1MC4xNDEgOTM0LjQ3MmMuNDgtLjIgMS4xMDUtLjA5MiAxLjc2LjM1MS0uNTE3LjIxLTEuMDY3LjA4Mi0xLjc2LS4zNTEiLz4KPC9zdmc+Cg=="; +const tiktokIconUri = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjAwIDEyMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxwYXRoIGQ9Ik02MzYgMTIwMWMtMjMuMDIgMC00Ni4wNDIgMC02OS42OTctLjQwOC05Ljc5NC0xLjEzNS0xOC45OTYtMS41MTgtMjguMTAxLTIuNjY1LTE2LjQyNC0yLjA2Ny0zMi45Ni0zLjc3NS00OS4xNC03LjEyNi0yMS42OTYtNC40OTQtNDMuMjM2LTkuOTA3LTY0LjU4OS0xNS44NDItMjQuMjYtNi43NDMtNDcuNjQ1LTE2LjAyLTcwLjU3Ny0yNi40OC0yNi4yMjYtMTEuOTYzLTUxLjkxMy0yNC45NjQtNzUuNzk4LTQxLjA5OC0xOC43NDYtMTIuNjYzLTM3LjA1Ny0yNi4wNC01NC44OTktMzkuOTQ4LTEzLjU1LTEwLjU2My0yNi43MjktMjEuNzkxLTM4Ljg2Ni0zMy45My0xNi4xOS0xNi4xOS0zMS42NTgtMzMuMTYyLTQ2LjU4Mi01MC41MzItMTAuOTY1LTEyLjc2Mi0yMS4yMS0yNi4yNjUtMzAuNTk3LTQwLjIzLTExLjM3OS0xNi45MjUtMjIuMTc4LTM0LjMyMS0zMi4wMS01Mi4xODUtMTAuMTM2LTE4LjQyLTE5LjYyOC0zNy4yOTUtMjcuOTE3LTU2LjYwOC0xMC43NDEtMjUuMDI1LTE5LjkzMS01MC43MjQtMjYuMjE0LTc3LjMwNy00LjU5Mi0xOS40MjctOS4yOTgtMzguODYzLTEyLjgxMy01OC40OTktMi42NjYtMTQuODkyLTMuNS0zMC4xMS01LjI3Mi00NS4xNjdDMi44MDQgNjUxLjkyMiAxLjY2NiA2NTAuOTg5IDEgNjUwYzAtLjQ0NCAwLS44ODkuMzk5LTEuODIuMTMzLTMuNzE4LS4xMzMtNi45NS0uMzk5LTEwLjE4IDAtMjQuMzU0IDAtNDguNzA4LjM5OS03My42ODUuMTMzLTMuODUzLS4xMzMtNy4wODQtLjM5OS0xMC4zMTUgMC0uNDQ0IDAtLjg4OS4zOTEtMS43OS43OTUtMS4xMzggMS40ODUtMS43ODkgMS41Ni0yLjUwMy43NC03LjE1NiAxLjI0Ni0xNC4zMzggMi4wOC0yMS40ODIgMS4yMjgtMTAuNSAyLjAyOS0yMS4xMDcgNC4xNjMtMzEuNDMgMy44MS0xOC40MjYgOC4zMDMtMzYuNzE3IDEyLjgyNy01NC45ODYgNy44ODUtMzEuODQgMTkuNDA0LTYyLjQ2NyAzMy4yMTMtOTIuMTExIDE1LjM3NS0zMy4wMDcgMzMuMzUzLTY0LjU4NCA1NC41MjMtOTQuMzg1IDE2LjM2NC0yMy4wMzYgMzQuMTc3LTQ0LjggNTMuMjE3LTY1LjU2NSA5LjczNS0xMC42MTcgMjAuNTg5LTIwLjIxMyAzMS4wMS0zMC4xOTUgMTcuMjMtMTYuNTA2IDM1Ljg4Mi0zMS4yNSA1NS4xNDItNDUuMzMgMjEuODk3LTE2LjAwOSA0NC42NDctMzAuNjc4IDY4LjczLTQzLjAxIDIwLjgyOC0xMC42NjggNDIuMTE4LTIwLjY2NSA2My45LTI5LjE3NyAyMS43NDUtOC40OTggNDQuMTcxLTE1LjQyNiA2Ni42MzYtMjEuODYzIDIzLjkyMS02Ljg1NCA0OC4zNTQtMTEuNzI4IDczLjE5LTE0LjE4IDEzLjgyNC0xLjM2NSAyNy42NTgtMi42NSA0MS40ODItNC4wMjQgMS0uMSAxLjk1OC0uNjM2IDIuOTM2LS45NjkgMjMuMDIgMCA0Ni4wNDIgMCA2OS42OTcuNDA4IDkuNzk3IDEuMTM1IDE5LjAwMyAxLjUxMyAyOC4xMTEgMi42NjYgMTYuMjY0IDIuMDYgMzIuNjM0IDMuNzg5IDQ4LjY1NyA3LjExMyAyMS44NiA0LjUzNiA0My41NzYgOS45MzYgNjUuMSAxNS44ODMgMjQuMjggNi43MSA0Ny42NDEgMTYuMDY5IDcwLjYwNyAyNi40NzQgMjUuOTc2IDExLjc3IDUxLjA4MiAyNS4xMTggNzUuMTUgNDAuMzYgMzIuMDkgMjAuMzI0IDYxLjk2NyA0My42MjYgODkuNTUgNjkuNjk4IDEzLjY5NSAxMi45NDUgMjYuNjIgMjYuNzk2IDM5LjAwNCA0MS4wMTIgMTIuNDkgMTQuMzM5IDI0LjAyNyAyOS41MjMgMzUuNzE1IDQ0LjU0NCAxNi4xNzQgMjAuNzg3IDI5LjczNCA0My4zMjQgNDIuMTc3IDY2LjQ0MiAxOS41ODkgMzYuMzk3IDM1LjU1NCA3NC40MTggNDcuMDc2IDExNC4xNTMgNC43NDEgMTYuMzUyIDguNTM4IDMzLjAxIDEyLjAzOSA0OS42OCAyLjkyNSAxMy45MyA1LjE2NCAyOC4wMzMgNy4wMjQgNDIuMTUgMS44MTUgMTMuNzY4IDIuNzUgMjcuNjUyIDQuMTI0IDQxLjQ4LjEgMS4wMDEuNjM2IDEuOTYuOTY5IDIuOTM3IDAgMjMuMDIgMCA0Ni4wNDItLjQwOCA2OS42OTctMS4xMzUgOS43OTctMS41MTMgMTkuMDAzLTIuNjY2IDI4LjExMS0yLjA2IDE2LjI2NC0zLjgxNyAzMi42MjYtNy4xMDcgNDguNjU5LTQuNDU4IDIxLjcyMS05LjYxMyA0My4zNDctMTUuNjE2IDY0LjY5MS03LjE2MyAyNS40NzItMTYuODc1IDUwLjExLTI4IDc0LjEyNy0xMS45NzggMjUuODU4LTI1LjQ3MyA1MC45MjYtNDAuOTE4IDc0Ljg5M2E1OTguNSA1OTguNSAwIDAgMS02Ny44OSA4Ni42OWMtMTIuOTMzIDEzLjcwNS0yNi43OTIgMjYuNjI0LTQxLjAwOCAzOS4wMDctMTQuMzQgMTIuNDkxLTI5LjUyMyAyNC4wMjgtNDQuNTQ1IDM1LjcxNi0yMC43ODYgMTYuMTc0LTQzLjMyMyAyOS43MzQtNjYuNDQyIDQyLjE3Ny0zNi4zOTcgMTkuNTg5LTc0LjQxOCAzNS41NTQtMTE0LjE1MyA0Ny4wNzYtMTYuMzUyIDQuNzQxLTMzLjAxIDguNTM4LTQ5LjY4IDEyLjAzOS0xMy45MyAyLjkyNS0yOC4wMzQgNS4xNjQtNDIuMTUgNy4wMjUtMTMuNzY4IDEuODE0LTI3LjY1MiAyLjc0OC00MS40OCA0LjEyMy0xLjAwMS4xLTEuOTYuNjM2LTIuOTM3Ljk2OW0xNzkuOTM0LTg1NS45NDhzLS4wMjQuMDg3LjA0LS42NDNjLTEuMjg4LTEuNjctMi42My0zLjMtMy44NTgtNS4wMTQtOS42ODgtMTMuNTE5LTE3Ljk2NS0yNy44Mi0yNC4xMTQtNDMuMzE3LTYuMzMzLTE1Ljk2LTEwLjY0OC0zMi4zOTctMTIuMDA0LTQ5LjU1Ni0uNTA0LTYuMzY3LTEtMTIuNzMzLTEuNTItMTkuMzczLTExLjc1IDAtMjMuNTIgMC0zNS40MzYtLjE4NSAwIDAtLjE4Ni0uMTQ2LS40NjMtLjg4NmwtMy40OTItMzIuODU2SDYwMHY2LjA4NWMwIDE3Ny4wNzktLjAxNyAzNTQuMTU3LjAyOSA1MzEuMjM2LjAwNCAxNS42MDItMy44OTMgMzAuMzE0LTEwLjQ3NiA0NC4xODQtMTEuNjY4IDI0LjU4LTI5LjkxIDQyLjgwNS01NC43NTMgNTQuMzMzLTIzLjU4NiAxMC45NDUtNDcuOTU4IDEzLjY1NC03My4xMzIgNy4xMTMtOC4wNC0yLjA4OS0xNS43ODgtNS4zMDYtMjQuMjY1LTguMjUzLS44LS42OTUtMS42LTEuMzktMi40MjItMi43OS0yNi4xNy00MC43MDctMjguNTk2LTgzLjExMS0yLjI0OC0xMjMuNDc3IDMwLjA1OS00Ni4wNTEgNzQuMjQ5LTYxLjIwOCAxMjcuODktNDcuODc4VjUxNS4zMDdjLTEzLjA1LS4zOTMtMjUuNzQyLS43NzYtMzguNTgtMS4zNDMgMCAwLS4xODctLjE0NS0uMDU4LTEuMDVWNDgxLjQ5Yy0xMS4zODYtLjUwNC0yMi41My0xLjYyMy0zMy42MzYtMS4zMzMtMTQuNDUuMzc2LTI5LjAzOC44NDUtNDMuMjY0IDMuMTQ3LTE3LjE4NiAyLjc4MS0zMy44ODcgNy44NzgtNTAuMTQyIDE0LjU0LTI3LjY5NCAxMS4zNS01Mi42MzkgMjYuODM3LTc0LjQzNCA0Ny4yNzctNy40ODUgNy4wMi0xNC40NzUgMTQuNjA0LTIxLjMwOSAyMi4yNzItMTEuMDY0IDEyLjQxNS0yMC4yMzcgMjYuMTg3LTI4LjI0MiA0MC43NjQtMTEuMzYgMjAuNjg1LTE5Ljk3IDQyLjQ4MS0yNC42NzggNjUuNTU1LTIuNzY1IDEzLjU0OC0zLjgyNyAyNy40NjktNS4xNzMgNDEuMjY5LS42MjcgNi40MzItLjQ0IDEyLjk5Ny0uMDM1IDE5LjQ2NS43NiAxMi4xMTUgMS4wMjkgMjQuMzY3IDMuMTQyIDM2LjI3IDQuODYyIDI3LjM3OCAxMy45ODMgNTMuMzc0IDI3LjggNzcuNjU4IDE0LjY0IDI1LjczIDMzLjAyNCA0OC4zNDMgNTUuNTI1IDY3LjU2MyAzLjkwMSAzLjMzMiA4LjMzMSA2LjA0NiAxMi40ODkgOS4wMzQgMCAwLS4wMTMtLjAyNy4wNC41Ni41OS40OSAxLjE3OC45ODEgMi4wNTggMi4xMTEgMi4yOSAxLjg0MiA0LjU4MSAzLjY4MyA3LjA5MiA2LjA4LjY0Mi40IDEuMjg0Ljc5OCAxLjg0MyAxLjIzIDAgMCAuMDEyLS4wODguMDcuNjAxIDEyLjc3NCAxMC41MjEgMjUuMDEgMjEuNzk0IDM4LjQ0MyAzMS4zOSAyMS4wOCAxNS4wNTYgNDQuNjMgMjUuNDM1IDY5LjUzIDMyLjYxIDE4LjQzIDUuMzEyIDM3LjMxMiA4LjM2MyA1Ni40MTUgOS4zMiAxOC40NzEuOTI3IDM2Ljg2Ny0uNTY4IDU1LjE0NS0zLjc5NSAyNy4wMS00Ljc3IDUyLjQ0OS0xMy43MDcgNzYuNDA0LTI3LjA1OSAyNi4xMDMtMTQuNTQ4IDQ4LjgyNS0zMy4xMjYgNjguMjk3LTU1LjgwNyAxMi44MjktMTQuOTQ0IDI0LjIxLTMwLjkxMyAzMi41MjUtNDguNzAyIDYuNTgtMTQuMDggMTIuMDEyLTI4Ljc2MiAxNy4wMy00My40OTEgNi45MDctMjAuMjY1IDkuODA1LTQxLjQ4IDkuOTAzLTYyLjczMS40MTctOTAuMTQzLjE3Ny0xODAuMjg4LjE3Ny0yNzAuNDMzdi02Ljc0NGM1Ni42NSAzOC45MiAxMTguMDM4IDU4Ljk2NiAxODUuNzQzIDU4Ljk4NXYtMTM0LjUyYy04LjU4Ni0uODc2LTE2LjkwNC0xLjY3Ni0yNS4yMDktMi41OTUtNC41MjQtLjUtOS4wMjUtMS4yMDMtMTQuMDgtMi4wMjUtLjIwNi0uMzE3LS40MTMtLjYzNS0uMjI2LTEuNzE5di0yNy43NDVjLTcuODE4LS44Mi0xNS43ODgtMS42Ni0yMy43NTktMi40ODktMTMuOTE4LTEuNDQ2LTI3LjQ0LTQuNzU4LTQwLjMwNi0xMC4xMDUtMTEuMDE3LTQuNTc4LTIxLjYwNS0xMC4xOS0zMi40NjYtMTUuNDc0IDAgMC0uMjM0LS4yLS42NjQtLjgyM2EyMSAyMSAwIDAgMS0yLjY4LS43MzJjLTEuMTczLTEuMjg4LTIuMzQ2LTIuNTc3LTMuNTMtNC40ODctLjY1OS0uNDM1LTEuMzE4LS44Ny0xLjg4OS0xLjMzIiBzdHlsZT0iLS1kYXJrcmVhZGVyLWlubGluZS1maWxsOnZhcigtLWRhcmtyZWFkZXItYmFja2dyb3VuZC0wMDAwMDAsICMwMDAwMDApOy0tZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO29wYWNpdHk6MSIgZGF0YS1pbmRleD0iMCIvPgogIDxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik0xIDU1NC40NTRjLjI2NiAyLjc3Ny41MzIgNi4wMDguMzk5IDkuMzkyQzEgNTYwLjk3IDEgNTU3Ljk0IDEgNTU0LjQ1NCIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLXRleHQtZmZmZmZmLCAjZThlNmUzKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lO3Zpc2liaWxpdHk6dmlzaWJsZSIgZGF0YS1pbmRleD0iNSIvPgogIDxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik0xIDYzOC40NTRjLjI2NiAyLjc3Ny41MzIgNi4wMDguMzk5IDkuMzkyQzEgNjQ0Ljk3IDEgNjQxLjk0IDEgNjM4LjQ1NCIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLXRleHQtZmZmZmZmLCAjZThlNmUzKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lO3Zpc2liaWxpdHk6dmlzaWJsZSIgZGF0YS1pbmRleD0iNiIvPgogIDxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik04MjQuNDYzIDM1Mi4yMjJzLjIzNC4yLjA1NC42MjljNC44OTggNS4zNjIgOS44NTcgMTAuNDI1IDE1LjA3NSAxNS4yMDYgMTUuMjUgMTMuOTcxIDMyLjY5NCAyNC41NTYgNTEuOTIzIDMxLjk2NCA5LjUxNCAzLjY2NiAxOS41MyA2LjAyOSAyOS4zMTkgOC45OC4yMDcuMzE5LjQxNC42MzcuODkzIDEuNTE3LjI3MiAzNS4xMzUuMjcyIDY5LjcwOC4yNzIgMTA0LjU3OS0zMy44NjYtLjA1Ni02Ni4yMDMtNS4wODctOTcuNzc1LTE1LjEyNC0zMS41MTYtMTAuMDItNjAuNzIyLTI0Ljc0NS04OC4yMjQtNDMuOTcyIDAgMS41NDYuMDAzIDIuMzY1IDAgMy4xODQtLjMzMyA5NC4yMzUtLjU3NiAxODguNDctMS4xMzEgMjgyLjcwMy0uMDYgMTAuMTIxLTEuMjA0IDIwLjM3My0zLjEyOCAzMC4zMTctMy41NTMgMTguMzU1LTkuMDQ5IDM2LjE1LTE2LjQ2IDUzLjQ0Mi0xMS4xMzkgMjUuOTkxLTI2LjI5IDQ5LjI4OS00NS41MTMgNjkuODAxLTkuMzMgOS45NTYtMTkuNTczIDE5LjMxLTMwLjUxOCAyNy40MzQtMTIuODk5IDkuNTc1LTI2LjU4OCAxOC4yOS00MC42OTQgMjUuOTg0YTE5MC4zIDE5MC4zIDAgMCAxLTU1LjMxIDE5LjgzOGMtMTUuODY4IDMuMDQxLTMyLjA0OCA0LjY4LTQ4LjE2NCA2LjExOC03LjgzNy42OTgtMTUuODk5LS4wMDgtMjMuNzYtLjkyNi0xMy45MzQtMS42MjctMjcuOTY3LTMuMTAyLTQxLjY0NC02LjA4NS0yMi42OS00Ljk0OC00NC4wNzEtMTMuNjQyLTY0LjExMi0yNS41My00LjY1OS0yLjc2My05LjY1My00Ljk2LTE0LjQ5My03LjQxOCAwIDAtLjAxMi4wODcuMjE0LS4yNjctLjYxMy0uNzEyLTEuNDUtMS4wNzEtMi4yOS0xLjQzLTIuMjktMS44NDItNC41OC0zLjY4My02Ljk2NS02LjIxLS43MzQtMS4xMjktMS4zNzYtMS41Ny0yLjAxNy0yLjAxMyAwIDAgLjAxMy4wMjcuMjAzLS4zMDYtLjk1NS0xLjkzOS0xLjk4Mi0zLjY0NC0zLjI2Mi01LjEzMi0xNC44NTEtMTcuMjc1LTI2LjU3Ni0zNi40NjgtMzUuOTQ0LTU3LjIyLTExLjc2NS0yNi4wNjYtMTkuMDc1LTUzLjM5NS0yMC42OC04MS43ODgtMS4wNS0xOC41OTQtLjAwOS0zNy40NzggMi4wMS01Ni4wMjQgMS45MzgtMTcuODE4IDcuMTE4LTM1LjEzMSAxMy42LTUxLjk4NSAxMS4yMDUtMjkuMTI2IDI3LjI3Ni01NS4yODUgNDguNDQtNzguMDY0IDEwLjA4NC0xMC44NTQgMjEuMzI1LTIwLjgxMiAzMi45NjgtMzAuMDAzIDE5LjUwNy0xNS4zOTkgNDEuMjMzLTI3LjA3MiA2NC42OTUtMzUuNTU0IDI1LjgyOC05LjMzNyA1Mi40MDMtMTQuMzAyIDc5LjgxMS0xNS4wNDggMCAwIC4xODYuMTQ1LjE3OS42OS0uMDA3IDM1LjQ1OS0uMDA3IDcwLjM3NC0uMDA3IDEwNS4yMjctOS4xNzQtMS42NDMtMTcuNzQ2LTMuODQ2LTI2LjQ0Mi00LjU4LTEzLjY3Ni0xLjE1NC0yNy4xNy43MjMtNDAuMzIgNC44NTUtMjcuODg4IDguNzY0LTQ5LjQwMSAyNS44My02NC4xMDYgNTAuODgtMTIuNzg5IDIxLjc4Ni0xNy42MzcgNDUuNDY5LTE0LjI3NyA3MC44MTUgMi4wNyAxNS42MTggNy4wMDggMzAuMDc2IDE1LjE4OSA0My4zOTQgMTAuNjYgMTcuMzUzIDI0Ljc1IDMxLjIzNiA0Mi45MyA0MC43MzQuOC42OTYgMS42IDEuMzkxIDIuNzEyIDIuNTU0IDcuNDU1IDEwLjUyMiAxNi4zMzYgMTguODE3IDI2LjUzNSAyNS42MzcgMjUuNDA2IDE2Ljk4OSA1My4zMjIgMjMuNTA5IDgzLjM5NiAxNy43OTMgMzAuNDQtNS43ODUgNTQuNDk3LTIyLjE4NyA3Mi4xOTMtNDcuNTE2IDExLjI3NS0xNi4xMzkgMTguODUxLTM0LjMyOCAxOC45MzgtNTMuOTgyLjcyOC0xNjQuMTg0Ljg5My0zMjguMzcgMS4yMi00OTIuNTU2LjAzLTE1LjE1OC4wMDQtMzAuMzE3LjAwNC00NS40NzZWMjI3YzIuNSAwIDQuNDU5LjAwMyA2LjQxNyAwbDkyLjQ0LS4xODJzLjE4NS4xNDYuMTc4LjYzOGMtLjAxMS45OS0uMTE4IDEuNTE1LS4wMDYgMS45ODYgNC41MjMgMTkuMDI3IDEwLjY0MiAzNy4zOCAyMC4zMzQgNTQuNTY3IDExLjg2OCAyMS4wNDggMjcuMzMgMzguOTA3IDQ1LjY3NiA1NC4zODUgMy4yMjUgMi43MiA3LjIyNyA0LjUyIDEwLjg3IDYuNzQ1IDAgMCAuMDI1LS4wODctLjIyOC4yMzYuNTQ5Ljc1OCAxLjM1MSAxLjE5MyAyLjE1MyAxLjYyOGEyMTMgMjEzIDAgMCAwIDQuMDUgNC4zNTdjMS4yMTUuNjE1IDEuODk3LjczOSAyLjU3OS44NjIiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci1iYWNrZ3JvdW5kLWZmZmZmZiwgIzE4MWExYik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6bm9uZTtvcGFjaXR5OjEiIGRhdGEtaW5kZXg9IjciLz4KICA8cGF0aCBmaWxsPSIjRkUyQzU1IiBkPSJNMzUxLjEwMiA5MzUuMjA3YzQuODEgMi4xMTMgOS44MDUgNC4zMSAxNC40NjQgNy4wNzQgMjAuMDQgMTEuODg4IDQxLjQyMyAyMC41ODIgNjQuMTEyIDI1LjUzIDEzLjY3NyAyLjk4MyAyNy43MSA0LjQ1OCA0MS42NDUgNi4wODUgNy44Ni45MTggMTUuOTIyIDEuNjI0IDIzLjc1OS45MjYgMTYuMTE2LTEuNDM3IDMyLjI5Ni0zLjA3NyA0OC4xNjQtNi4xMThhMTkwLjMgMTkwLjMgMCAwIDAgNTUuMzEtMTkuODM4YzE0LjEwNi03LjY5NSAyNy43OTUtMTYuNDA5IDQwLjY5NC0yNS45ODQgMTAuOTQ1LTguMTI1IDIxLjE4Ny0xNy40NzggMzAuNTE4LTI3LjQzNCAxOS4yMjMtMjAuNTEyIDM0LjM3NC00My44MSA0NS41MTMtNjkuOCA3LjQxMS0xNy4yOTQgMTIuOTA3LTM1LjA4OCAxNi40Ni01My40NDMgMS45MjQtOS45NDQgMy4wNjgtMjAuMTk2IDMuMTI4LTMwLjMxNy41NTUtOTQuMjMzLjc5OC0xODguNDY4IDEuMTMtMjgyLjcwMy4wMDQtLjgyLjAwMS0xLjYzOC4wMDEtMy4xODQgMjcuNTAyIDE5LjIyNyA1Ni43MDggMzMuOTUzIDg4LjIyNCA0My45NzIgMzEuNTcyIDEwLjAzNyA2My45MSAxNS4wNjggOTcuNzc1IDE1LjEyNGwtLjAwMS0xMDQuNDc0YzQuNTEuMTU0IDkuMDEyLjg1NyAxMy41MzYgMS4zNTcgOC4zMDUuOTE5IDE2LjYyMyAxLjcxOSAyNS4yMDkgMi41OTZ2MTM0LjUxOWMtNjcuNzA1LS4wMi0xMjkuMDkzLTIwLjA2Ni0xODUuNzQzLTU4Ljk4NXY2Ljc0NGMwIDkwLjE0NS4yNCAxODAuMjktLjE3NyAyNzAuNDMzLS4wOTggMjEuMjUxLTIuOTk2IDQyLjQ2Ni05LjkwMiA2Mi43My01LjAyIDE0LjczLTEwLjQ1IDI5LjQxMi0xNy4wMzEgNDMuNDkyLTguMzE0IDE3Ljc4OS0xOS42OTYgMzMuNzU4LTMyLjUyNSA0OC43MDItMTkuNDcyIDIyLjY4LTQyLjE5NCA0MS4yNTktNjguMjk3IDU1LjgwNy0yMy45NTUgMTMuMzUyLTQ5LjM5NSAyMi4yOS03Ni40MDQgMjcuMDU5LTE4LjI3OCAzLjIyNy0zNi42NzQgNC43MjItNTUuMTQ1IDMuNzk2LTE5LjEwMy0uOTU4LTM3Ljk4NC00LjAwOS01Ni40MTUtOS4zMi0yNC45LTcuMTc2LTQ4LjQ1LTE3LjU1NS02OS41My0zMi42MTEtMTMuNDM0LTkuNTk2LTI1LjY3LTIwLjg2OS0zOC40NzItMzEuNzM1IiBzdHlsZT0iLS1kYXJrcmVhZGVyLWlubGluZS1maWxsOnZhcigtLWRhcmtyZWFkZXItYmFja2dyb3VuZC1mZTJjNTUsICNiMTAxMjMpOy0tZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO29wYWNpdHk6MSIgZGF0YS1pbmRleD0iOCIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik03MzguNzE4IDIyNi40NDhjLTMwLjY3NS40MzEtNjEuNDg4LjQ5Mi05Mi4zMDEuNTUxLTEuOTU4LjAwNC0zLjkxNi4wMDEtNi40MTcuMDAxdjUuMjg4YzAgMTUuMTU5LjAyNiAzMC4zMTgtLjAwNCA0NS40NzYtLjMyNyAxNjQuMTg2LS40OTIgMzI4LjM3Mi0xLjIyIDQ5Mi41NTYtLjA4NyAxOS42NTQtNy42NjMgMzcuODQzLTE4LjkzOCA1My45ODItMTcuNjk2IDI1LjMyOS00MS43NTMgNDEuNzMtNzIuMTkzIDQ3LjUxNi0zMC4wNzQgNS43MTYtNTcuOTktLjgwNC04My4zOTYtMTcuNzkzLTEwLjE5OS02LjgyLTE5LjA4LTE1LjExNS0yNi4yMzctMjUuNTE0IDcuODY4IDIuMzU2IDE1LjYxNiA1LjU3MyAyMy42NTYgNy42NjIgMjUuMTc0IDYuNTQxIDQ5LjU0NiAzLjgzMiA3My4xMzItNy4xMTMgMjQuODQzLTExLjUyOCA0My4wODUtMjkuNzUzIDU0Ljc1My01NC4zMzMgNi41ODMtMTMuODcgMTAuNDgtMjguNTgyIDEwLjQ3Ni00NC4xODQtLjA0Ni0xNzcuMDc5LS4wMjktMzU0LjE1Ny0uMDI5LTUzMS4yMzZ2LTYuMDg1aDEzNS4wODdjMS4xODIgMTEuMTE1IDIuMzM3IDIxLjk4NSAzLjYzIDMzLjIyNiIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLWJhY2tncm91bmQtMjVmNGVlLCAjMDliM2IwKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lO29wYWNpdHk6MSIgZGF0YS1pbmRleD0iOSIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik01MjEuOTIgNTEzLjM2N2MtMjcuNDcyIDEuMTk4LTU0LjA0NyA2LjE2My03OS44NzUgMTUuNS0yMy40NjIgOC40ODItNDUuMTg4IDIwLjE1NS02NC42OTUgMzUuNTU0LTExLjY0MyA5LjE5MS0yMi44ODQgMTkuMTQ5LTMyLjk2OSAzMC4wMDMtMjEuMTYzIDIyLjc3OS0zNy4yMzQgNDguOTM4LTQ4LjQzOCA3OC4wNjQtNi40ODMgMTYuODU0LTExLjY2MyAzNC4xNjctMTMuNjAyIDUxLjk4NS0yLjAxOCAxOC41NDYtMy4wNTkgMzcuNDMtMi4wMDggNTYuMDI0IDEuNjA0IDI4LjM5MyA4LjkxNCA1NS43MjIgMjAuNjggODEuNzg4IDkuMzY3IDIwLjc1MiAyMS4wOTIgMzkuOTQ1IDM1Ljk0MyA1Ny4yMiAxLjI4IDEuNDg4IDIuMzA3IDMuMTkzIDMuMjc2IDUuMTM3LTQuMzYyLTIuNjYtOC43OTItNS4zNzQtMTIuNjkzLTguNzA2LTIyLjUwMS0xOS4yMi00MC44ODUtNDEuODMyLTU1LjUyNS02Ny41NjMtMTMuODE3LTI0LjI4NC0yMi45MzgtNTAuMjgtMjcuOC03Ny42NTgtMi4xMTMtMTEuOTAzLTIuMzgyLTI0LjE1NS0zLjE0Mi0zNi4yNy0uNDA2LTYuNDY4LS41OTItMTMuMDMzLjAzNS0xOS40NjUgMS4zNDYtMTMuOCAyLjQwOC0yNy43MjEgNS4xNzMtNDEuMjY5IDQuNzA5LTIzLjA3NCAxMy4zMTktNDQuODcgMjQuNjc4LTY1LjU1NSA4LjAwNS0xNC41NzcgMTcuMTc4LTI4LjM1IDI4LjI0Mi00MC43NjQgNi44MzQtNy42NjggMTMuODI0LTE1LjI1MyAyMS4zMDktMjIuMjcyIDIxLjc5NS0yMC40NCA0Ni43NC0zNS45MjcgNzQuNDM0LTQ3LjI3NyAxNi4yNTUtNi42NjIgMzIuOTU2LTExLjc1OSA1MC4xNDItMTQuNTQgMTQuMjI2LTIuMzAyIDI4LjgxMy0yLjc3IDQzLjI2NC0zLjE0NyAxMS4xMDctLjI5IDIyLjI1LjgyOSAzMy42MzYgMS4zMzMgMCAxMS4xNDUgMCAyMS4yODUtLjA2NCAzMS44NzgiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci1iYWNrZ3JvdW5kLTI1ZjRlZSwgIzA5YjNiMCk7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6dmFyKC0tZGFya3JlYWRlci10ZXh0LTBkOTlmZiwgIzIzYTNmZik7b3BhY2l0eToxIiBkYXRhLWluZGV4PSIxMCIvPgogIDxwYXRoIGZpbGw9IiNGRTJDNTUiIGQ9Ik00MzQuOTkyIDgyNS40ODJjLTE4LjE3LTkuMTQ2LTMyLjI2LTIzLjAyOS00Mi45Mi00MC4zODItOC4xOC0xMy4zMTgtMTMuMTItMjcuNzc2LTE1LjE5LTQzLjM5NC0zLjM1OS0yNS4zNDYgMS40OS00OS4wMjkgMTQuMjc4LTcwLjgxNSAxNC43MDUtMjUuMDUgMzYuMjE4LTQyLjExNiA2NC4xMDYtNTAuODggMTMuMTUtNC4xMzIgMjYuNjQ0LTYuMDA5IDQwLjMyLTQuODU1IDguNjk2LjczNCAxNy4yNjggMi45MzcgMjYuNDQyIDQuNTggMC0zNC44NTMgMC02OS43NjguMDgtMTA1LjEzNSAxMi43NzMtLjA3IDI1LjQ2Ni4zMTMgMzguNTE0LjcwNnYxMzguNDY4Yy01My42NC0xMy4zMy05Ny44MyAxLjgyNy0xMjcuODkgNDcuODc4LTI2LjM0NyA0MC4zNjYtMjMuOTIxIDgyLjc3IDIuMjYgMTIzLjgyOSIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLWJhY2tncm91bmQtZmUyYzU1LCAjYjEwMTIzKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lO29wYWNpdHk6MSIgZGF0YS1pbmRleD0iMTEiLz4KICA8cGF0aCBmaWxsPSIjRkUyQzU1IiBkPSJNODE1Ljk0MiAzNDQuNzc0Yy0zLjY3Ni0xLjg2LTcuNjc4LTMuNjYtMTAuOTAzLTYuMzgtMTguMzQ1LTE1LjQ3OC0zMy44MDgtMzMuMzM3LTQ1LjY3Ni01NC4zODUtOS42OTItMTcuMTg3LTE1LjgxMS0zNS41NC0yMC4zMzQtNTQuNTY3LS4xMTItLjQ3MS0uMDA1LS45OTUuMDgtMS44OTQgMTEuODQ5LS4zOTkgMjMuNjE4LS4zOTkgMzUuMzY5LS4zOTkuNTIgNi42NCAxLjAxNiAxMy4wMDYgMS41MiAxOS4zNzMgMS4zNTYgMTcuMTYgNS42NyAzMy41OTUgMTIuMDA0IDQ5LjU1NiA2LjE1IDE1LjQ5OCAxNC40MjYgMjkuNzk4IDI0LjExNCA0My4zMTcgMS4yMjcgMS43MTMgMi41NyAzLjM0NCAzLjgyNiA1LjM3OSIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLWJhY2tncm91bmQtZmUyYzU1LCAjYjEwMTIzKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lIiBkYXRhLWluZGV4PSIxMiIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik05MjEuMDMgNDA4LjYxOWMtOS45ODUtMi41Ny0yMC00LjkzMi0yOS41MTUtOC41OTgtMTkuMjMtNy40MDgtMzYuNjczLTE3Ljk5My01MS45MjMtMzEuOTY0LTUuMjE4LTQuNzgtMTAuMTc3LTkuODQ0LTE1LjAzMy0xNS4xNDEgMTEgNC43OTIgMjEuNTg3IDEwLjQwMyAzMi42MDQgMTQuOTgxIDEyLjg2NyA1LjM0NyAyNi4zODggOC42NiA0MC4zMDYgMTAuMTA1IDcuOTcxLjgyOCAxNS45NCAxLjY3IDIzLjc1OSAyLjQ5IDAgOS4wODYgMCAxOC40MTUtLjE5NyAyOC4xMjciIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci10ZXh0LTI1ZjRlZSwgIzM2ZjVlZik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6dmFyKC0tZGFya3JlYWRlci10ZXh0LTBkOTlmZiwgIzIzYTNmZik7b3BhY2l0eToxIiBkYXRhLWluZGV4PSIxMyIvPgogIDxwYXRoIGZpbGw9IiNGRTJDNTUiIGQ9Ik04MTcuODI5IDM0Ni42OTJhMy41IDMuNSAwIDAgMS0yLjE5Mi0xLjMwNWMuODY4LjEyNCAxLjUyNy41NTkgMi4xOTIgMS4zMDUiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci10ZXh0LWZlMmM1NSwgI2ZlMzk1Zik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6bm9uZSIgZGF0YS1pbmRleD0iMTQiLz4KICA8cGF0aCBmaWxsPSIjMjVGNEVFIiBkPSJNMzQwLjA0MiA5MjUuMjM3Yy42MTQuMTQ4IDEuMjU2LjU5IDEuODQ1IDEuMzk5LS42NC0uMTI1LTEuMjMtLjYxNS0xLjg0NS0xLjQiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci10ZXh0LTI1ZjRlZSwgIzM2ZjVlZik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6bm9uZTt2aXNpYmlsaXR5OnZpc2libGUiIGRhdGEtaW5kZXg9IjE1Ii8+CiAgPHBhdGggZmlsbD0iI0ZFMkM1NSIgZD0iTTM0OS4xMDggOTMzLjQ0M2EzLjY2IDMuNjYgMCAwIDEgMi4yMiAxLjEzOGMtLjgyNi0uMDYyLTEuNDY4LS40Ni0yLjIyLTEuMTM4IiBzdHlsZT0iLS1kYXJrcmVhZGVyLWlubGluZS1maWxsOnZhcigtLWRhcmtyZWFkZXItdGV4dC1mZTJjNTUsICNmZTM5NWYpOy0tZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlIiBkYXRhLWluZGV4PSIxNiIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik04MjQuMjQ4IDM1MS45MTFjLS40NjcuMTg4LTEuMTUuMDY0LTIuMDQtLjQwMi40Ny0uMTk4IDEuMTQ3LS4wNTMgMi4wNC40MDIiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci10ZXh0LTI1ZjRlZSwgIzM2ZjVlZik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6bm9uZSIgZGF0YS1pbmRleD0iMTciLz4KPC9zdmc+Cg=="; const emailIcon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEzLjMzMzUgMi42NjY1SDIuNjY2ODNDMS45MzA0NSAyLjY2NjUgMS4zMzM1IDMuMjYzNDYgMS4zMzM1IDMuOTk5ODRWMTEuOTk5OEMxLjMzMzUgMTIuNzM2MiAxLjkzMDQ1IDEzLjMzMzIgMi42NjY4MyAxMy4zMzMySDEzLjMzMzVDMTQuMDY5OSAxMy4zMzMyIDE0LjY2NjggMTIuNzM2MiAxNC42NjY4IDExLjk5OThWMy45OTk4NEMxNC42NjY4IDMuMjYzNDYgMTQuMDY5OSAyLjY2NjUgMTMuMzMzNSAyLjY2NjVaIiBzdHJva2U9IiMzMzg1RkYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTQuNjY2OCA0LjY2NjVMOC42ODY4MyA4LjQ2NjVDOC40ODEwMSA4LjU5NTQ1IDguMjQzMDQgOC42NjM4NCA4LjAwMDE2IDguNjYzODRDNy43NTcyOCA4LjY2Mzg0IDcuNTE5MzEgOC41OTU0NSA3LjMxMzUgOC40NjY1TDEuMzMzNSA0LjY2NjUiIHN0cm9rZT0iIzMzODVGRiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPgo="; const phoneIcon = diff --git a/packages/thirdweb/src/react/native/ui/icons/svgs.ts b/packages/thirdweb/src/react/native/ui/icons/svgs.ts index 4c11c4a7d59..9d846dbf379 100644 --- a/packages/thirdweb/src/react/native/ui/icons/svgs.ts +++ b/packages/thirdweb/src/react/native/ui/icons/svgs.ts @@ -138,16 +138,20 @@ export const X_ICON = ``; export const TIKTOK_ICON = ` - - - - - - - - - - + + + + + + + + + + + + + + `; From 015d09695756d8a83c4be01334f90d30d8cee99c Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Fri, 8 Aug 2025 20:10:41 +0700 Subject: [PATCH 5/5] fix lint --- packages/thirdweb/src/react/core/utils/walletIcon.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/thirdweb/src/react/core/utils/walletIcon.ts b/packages/thirdweb/src/react/core/utils/walletIcon.ts index 708ef216876..9ee3e6ff5b3 100644 --- a/packages/thirdweb/src/react/core/utils/walletIcon.ts +++ b/packages/thirdweb/src/react/core/utils/walletIcon.ts @@ -29,7 +29,8 @@ const githubIconUri = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTA2IiBoZWlnaHQ9IjEwNiIgdmlld0JveD0iMCAwIDEwNiAxMDYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxjaXJjbGUgY3g9IjUzIiBjeT0iNTMiIHI9IjUzIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUyLjg1NCA0QzI1LjgzOSA0IDQgMjYgNCA1My4yMTdDNCA3NC45NzMgMTcuOTkzIDkzLjM4OSAzNy40MDUgOTkuOTA3QzM5LjgzMiAxMDAuMzk3IDQwLjcyMSA5OC44NDggNDAuNzIxIDk3LjU0NUM0MC43MjEgOTYuNDA0IDQwLjY0MSA5Mi40OTMgNDAuNjQxIDg4LjQxOEMyNy4wNTEgOTEuMzUyIDI0LjIyMSA4Mi41NTEgMjQuMjIxIDgyLjU1MUMyMi4wMzcgNzYuODQ3IDE4LjgwMSA3NS4zODEgMTguODAxIDc1LjM4MUMxNC4zNTMgNzIuMzY2IDE5LjEyNSA3Mi4zNjYgMTkuMTI1IDcyLjM2NkMyNC4wNTkgNzIuNjkyIDI2LjY0OCA3Ny40MTggMjYuNjQ4IDc3LjQxOEMzMS4wMTUgODQuOTE0IDM4LjA1MiA4Mi43OTYgNDAuODgzIDgxLjQ5MkM0MS4yODcgNzguMzE0IDQyLjU4MiA3Ni4xMTQgNDMuOTU3IDc0Ljg5MkMzMy4xMTggNzMuNzUxIDIxLjcxNCA2OS41MTQgMjEuNzE0IDUwLjYwOUMyMS43MTQgNDUuMjMxIDIzLjY1NCA0MC44MzEgMjYuNzI4IDM3LjQwOUMyNi4yNDMgMzYuMTg3IDI0LjU0NCAzMS4xMzQgMjcuMjE0IDI0LjM3MUMyNy4yMTQgMjQuMzcxIDMxLjMzOSAyMy4wNjcgNDAuNjQgMjkuNDIzQzQ0LjYyMjEgMjguMzQ1NyA0OC43Mjg4IDI3Ljc5NzYgNTIuODU0IDI3Ljc5M0M1Ni45NzkgMjcuNzkzIDYxLjE4NCAyOC4zNjQgNjUuMDY3IDI5LjQyM0M3NC4zNjkgMjMuMDY3IDc4LjQ5NCAyNC4zNzEgNzguNDk0IDI0LjM3MUM4MS4xNjQgMzEuMTM0IDc5LjQ2NCAzNi4xODcgNzguOTc5IDM3LjQwOUM4Mi4xMzQgNDAuODMxIDgzLjk5NCA0NS4yMzEgODMuOTk0IDUwLjYwOUM4My45OTQgNjkuNTE0IDcyLjU5IDczLjY2OSA2MS42NyA3NC44OTJDNjMuNDUgNzYuNDQgNjQuOTg2IDc5LjM3MyA2NC45ODYgODQuMDE4QzY0Ljk4NiA5MC42MTggNjQuOTA2IDk1LjkxNSA2NC45MDYgOTcuNTQ0QzY0LjkwNiA5OC44NDggNjUuNzk2IDEwMC4zOTcgNjguMjIyIDk5LjkwOEM4Ny42MzQgOTMuMzg4IDEwMS42MjcgNzQuOTczIDEwMS42MjcgNTMuMjE3QzEwMS43MDcgMjYgNzkuNzg4IDQgNTIuODU0IDRaIiBmaWxsPSIjMjQyOTJGIi8+Cjwvc3ZnPgo="; const xIcon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIwIiBoZWlnaHQ9IjMyMCIgdmlld0JveD0iMCAwIDMyMCAzMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIzMjAiIGhlaWdodD0iMzIwIiByeD0iMjQiIGZpbGw9ImJsYWNrIi8+CjxwYXRoIGQ9Ik0xNzcuMzE1IDE0NS4zMzVMMjQ1LjA2OCA2N0gyMjkuMDEzTDE3MC4xODIgMTM1LjAxN0wxMjMuMTk1IDY3SDY5TDE0MC4wNTUgMTY5Ljg1NEw2OSAyNTJIODUuMDU2M0wxNDcuMTgzIDE4MC4xNzJMMTk2LjgwNSAyNTJIMjUxTDE3Ny4zMTEgMTQ1LjMzNUgxNzcuMzE1Wk0xNTUuMzIzIDE3MC43NkwxNDguMTI0IDE2MC41MThMOTAuODQxNyA3OS4wMjJIMTE1LjUwM0wxNjEuNzMxIDE0NC43OTJMMTY4LjkzIDE1NS4wMzRMMjI5LjAyIDI0MC41MjVIMjA0LjM1OUwxNTUuMzIzIDE3MC43NjRWMTcwLjc2WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg=="; -const tiktokIconUri = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjAwIDEyMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxwYXRoIGQ9Ik02MzYgMTIwMWMtMjMuMDIgMC00Ni4wNDIgMC02OS42OTctLjQwOC05Ljc5NC0xLjEzNS0xOC45OTYtMS41MTgtMjguMTAxLTIuNjY1LTE2LjQyNC0yLjA2Ny0zMi45Ni0zLjc3NS00OS4xNC03LjEyNi0yMS42OTYtNC40OTQtNDMuMjM2LTkuOTA3LTY0LjU4OS0xNS44NDItMjQuMjYtNi43NDMtNDcuNjQ1LTE2LjAyLTcwLjU3Ny0yNi40OC0yNi4yMjYtMTEuOTYzLTUxLjkxMy0yNC45NjQtNzUuNzk4LTQxLjA5OC0xOC43NDYtMTIuNjYzLTM3LjA1Ny0yNi4wNC01NC44OTktMzkuOTQ4LTEzLjU1LTEwLjU2My0yNi43MjktMjEuNzkxLTM4Ljg2Ni0zMy45My0xNi4xOS0xNi4xOS0zMS42NTgtMzMuMTYyLTQ2LjU4Mi01MC41MzItMTAuOTY1LTEyLjc2Mi0yMS4yMS0yNi4yNjUtMzAuNTk3LTQwLjIzLTExLjM3OS0xNi45MjUtMjIuMTc4LTM0LjMyMS0zMi4wMS01Mi4xODUtMTAuMTM2LTE4LjQyLTE5LjYyOC0zNy4yOTUtMjcuOTE3LTU2LjYwOC0xMC43NDEtMjUuMDI1LTE5LjkzMS01MC43MjQtMjYuMjE0LTc3LjMwNy00LjU5Mi0xOS40MjctOS4yOTgtMzguODYzLTEyLjgxMy01OC40OTktMi42NjYtMTQuODkyLTMuNS0zMC4xMS01LjI3Mi00NS4xNjdDMi44MDQgNjUxLjkyMiAxLjY2NiA2NTAuOTg5IDEgNjUwYzAtLjQ0NCAwLS44ODkuMzk5LTEuODIuMTMzLTMuNzE4LS4xMzMtNi45NS0uMzk5LTEwLjE4IDAtMjQuMzU0IDAtNDguNzA4LjM5OS03My42ODUuMTMzLTMuODUzLS4xMzMtNy4wODQtLjM5OS0xMC4zMTUgMC0uNDQ0IDAtLjg4OS4zOTEtMS43OS43OTUtMS4xMzggMS40ODUtMS43ODkgMS41Ni0yLjUwMy43NC03LjE1NiAxLjI0Ni0xNC4zMzggMi4wOC0yMS40ODIgMS4yMjgtMTAuNSAyLjAyOS0yMS4xMDcgNC4xNjMtMzEuNDMgMy44MS0xOC40MjYgOC4zMDMtMzYuNzE3IDEyLjgyNy01NC45ODYgNy44ODUtMzEuODQgMTkuNDA0LTYyLjQ2NyAzMy4yMTMtOTIuMTExIDE1LjM3NS0zMy4wMDcgMzMuMzUzLTY0LjU4NCA1NC41MjMtOTQuMzg1IDE2LjM2NC0yMy4wMzYgMzQuMTc3LTQ0LjggNTMuMjE3LTY1LjU2NSA5LjczNS0xMC42MTcgMjAuNTg5LTIwLjIxMyAzMS4wMS0zMC4xOTUgMTcuMjMtMTYuNTA2IDM1Ljg4Mi0zMS4yNSA1NS4xNDItNDUuMzMgMjEuODk3LTE2LjAwOSA0NC42NDctMzAuNjc4IDY4LjczLTQzLjAxIDIwLjgyOC0xMC42NjggNDIuMTE4LTIwLjY2NSA2My45LTI5LjE3NyAyMS43NDUtOC40OTggNDQuMTcxLTE1LjQyNiA2Ni42MzYtMjEuODYzIDIzLjkyMS02Ljg1NCA0OC4zNTQtMTEuNzI4IDczLjE5LTE0LjE4IDEzLjgyNC0xLjM2NSAyNy42NTgtMi42NSA0MS40ODItNC4wMjQgMS0uMSAxLjk1OC0uNjM2IDIuOTM2LS45NjkgMjMuMDIgMCA0Ni4wNDIgMCA2OS42OTcuNDA4IDkuNzk3IDEuMTM1IDE5LjAwMyAxLjUxMyAyOC4xMTEgMi42NjYgMTYuMjY0IDIuMDYgMzIuNjM0IDMuNzg5IDQ4LjY1NyA3LjExMyAyMS44NiA0LjUzNiA0My41NzYgOS45MzYgNjUuMSAxNS44ODMgMjQuMjggNi43MSA0Ny42NDEgMTYuMDY5IDcwLjYwNyAyNi40NzQgMjUuOTc2IDExLjc3IDUxLjA4MiAyNS4xMTggNzUuMTUgNDAuMzYgMzIuMDkgMjAuMzI0IDYxLjk2NyA0My42MjYgODkuNTUgNjkuNjk4IDEzLjY5NSAxMi45NDUgMjYuNjIgMjYuNzk2IDM5LjAwNCA0MS4wMTIgMTIuNDkgMTQuMzM5IDI0LjAyNyAyOS41MjMgMzUuNzE1IDQ0LjU0NCAxNi4xNzQgMjAuNzg3IDI5LjczNCA0My4zMjQgNDIuMTc3IDY2LjQ0MiAxOS41ODkgMzYuMzk3IDM1LjU1NCA3NC40MTggNDcuMDc2IDExNC4xNTMgNC43NDEgMTYuMzUyIDguNTM4IDMzLjAxIDEyLjAzOSA0OS42OCAyLjkyNSAxMy45MyA1LjE2NCAyOC4wMzMgNy4wMjQgNDIuMTUgMS44MTUgMTMuNzY4IDIuNzUgMjcuNjUyIDQuMTI0IDQxLjQ4LjEgMS4wMDEuNjM2IDEuOTYuOTY5IDIuOTM3IDAgMjMuMDIgMCA0Ni4wNDItLjQwOCA2OS42OTctMS4xMzUgOS43OTctMS41MTMgMTkuMDAzLTIuNjY2IDI4LjExMS0yLjA2IDE2LjI2NC0zLjgxNyAzMi42MjYtNy4xMDcgNDguNjU5LTQuNDU4IDIxLjcyMS05LjYxMyA0My4zNDctMTUuNjE2IDY0LjY5MS03LjE2MyAyNS40NzItMTYuODc1IDUwLjExLTI4IDc0LjEyNy0xMS45NzggMjUuODU4LTI1LjQ3MyA1MC45MjYtNDAuOTE4IDc0Ljg5M2E1OTguNSA1OTguNSAwIDAgMS02Ny44OSA4Ni42OWMtMTIuOTMzIDEzLjcwNS0yNi43OTIgMjYuNjI0LTQxLjAwOCAzOS4wMDctMTQuMzQgMTIuNDkxLTI5LjUyMyAyNC4wMjgtNDQuNTQ1IDM1LjcxNi0yMC43ODYgMTYuMTc0LTQzLjMyMyAyOS43MzQtNjYuNDQyIDQyLjE3Ny0zNi4zOTcgMTkuNTg5LTc0LjQxOCAzNS41NTQtMTE0LjE1MyA0Ny4wNzYtMTYuMzUyIDQuNzQxLTMzLjAxIDguNTM4LTQ5LjY4IDEyLjAzOS0xMy45MyAyLjkyNS0yOC4wMzQgNS4xNjQtNDIuMTUgNy4wMjUtMTMuNzY4IDEuODE0LTI3LjY1MiAyLjc0OC00MS40OCA0LjEyMy0xLjAwMS4xLTEuOTYuNjM2LTIuOTM3Ljk2OW0xNzkuOTM0LTg1NS45NDhzLS4wMjQuMDg3LjA0LS42NDNjLTEuMjg4LTEuNjctMi42My0zLjMtMy44NTgtNS4wMTQtOS42ODgtMTMuNTE5LTE3Ljk2NS0yNy44Mi0yNC4xMTQtNDMuMzE3LTYuMzMzLTE1Ljk2LTEwLjY0OC0zMi4zOTctMTIuMDA0LTQ5LjU1Ni0uNTA0LTYuMzY3LTEtMTIuNzMzLTEuNTItMTkuMzczLTExLjc1IDAtMjMuNTIgMC0zNS40MzYtLjE4NSAwIDAtLjE4Ni0uMTQ2LS40NjMtLjg4NmwtMy40OTItMzIuODU2SDYwMHY2LjA4NWMwIDE3Ny4wNzktLjAxNyAzNTQuMTU3LjAyOSA1MzEuMjM2LjAwNCAxNS42MDItMy44OTMgMzAuMzE0LTEwLjQ3NiA0NC4xODQtMTEuNjY4IDI0LjU4LTI5LjkxIDQyLjgwNS01NC43NTMgNTQuMzMzLTIzLjU4NiAxMC45NDUtNDcuOTU4IDEzLjY1NC03My4xMzIgNy4xMTMtOC4wNC0yLjA4OS0xNS43ODgtNS4zMDYtMjQuMjY1LTguMjUzLS44LS42OTUtMS42LTEuMzktMi40MjItMi43OS0yNi4xNy00MC43MDctMjguNTk2LTgzLjExMS0yLjI0OC0xMjMuNDc3IDMwLjA1OS00Ni4wNTEgNzQuMjQ5LTYxLjIwOCAxMjcuODktNDcuODc4VjUxNS4zMDdjLTEzLjA1LS4zOTMtMjUuNzQyLS43NzYtMzguNTgtMS4zNDMgMCAwLS4xODctLjE0NS0uMDU4LTEuMDVWNDgxLjQ5Yy0xMS4zODYtLjUwNC0yMi41My0xLjYyMy0zMy42MzYtMS4zMzMtMTQuNDUuMzc2LTI5LjAzOC44NDUtNDMuMjY0IDMuMTQ3LTE3LjE4NiAyLjc4MS0zMy44ODcgNy44NzgtNTAuMTQyIDE0LjU0LTI3LjY5NCAxMS4zNS01Mi42MzkgMjYuODM3LTc0LjQzNCA0Ny4yNzctNy40ODUgNy4wMi0xNC40NzUgMTQuNjA0LTIxLjMwOSAyMi4yNzItMTEuMDY0IDEyLjQxNS0yMC4yMzcgMjYuMTg3LTI4LjI0MiA0MC43NjQtMTEuMzYgMjAuNjg1LTE5Ljk3IDQyLjQ4MS0yNC42NzggNjUuNTU1LTIuNzY1IDEzLjU0OC0zLjgyNyAyNy40NjktNS4xNzMgNDEuMjY5LS42MjcgNi40MzItLjQ0IDEyLjk5Ny0uMDM1IDE5LjQ2NS43NiAxMi4xMTUgMS4wMjkgMjQuMzY3IDMuMTQyIDM2LjI3IDQuODYyIDI3LjM3OCAxMy45ODMgNTMuMzc0IDI3LjggNzcuNjU4IDE0LjY0IDI1LjczIDMzLjAyNCA0OC4zNDMgNTUuNTI1IDY3LjU2MyAzLjkwMSAzLjMzMiA4LjMzMSA2LjA0NiAxMi40ODkgOS4wMzQgMCAwLS4wMTMtLjAyNy4wNC41Ni41OS40OSAxLjE3OC45ODEgMi4wNTggMi4xMTEgMi4yOSAxLjg0MiA0LjU4MSAzLjY4MyA3LjA5MiA2LjA4LjY0Mi40IDEuMjg0Ljc5OCAxLjg0MyAxLjIzIDAgMCAuMDEyLS4wODguMDcuNjAxIDEyLjc3NCAxMC41MjEgMjUuMDEgMjEuNzk0IDM4LjQ0MyAzMS4zOSAyMS4wOCAxNS4wNTYgNDQuNjMgMjUuNDM1IDY5LjUzIDMyLjYxIDE4LjQzIDUuMzEyIDM3LjMxMiA4LjM2MyA1Ni40MTUgOS4zMiAxOC40NzEuOTI3IDM2Ljg2Ny0uNTY4IDU1LjE0NS0zLjc5NSAyNy4wMS00Ljc3IDUyLjQ0OS0xMy43MDcgNzYuNDA0LTI3LjA1OSAyNi4xMDMtMTQuNTQ4IDQ4LjgyNS0zMy4xMjYgNjguMjk3LTU1LjgwNyAxMi44MjktMTQuOTQ0IDI0LjIxLTMwLjkxMyAzMi41MjUtNDguNzAyIDYuNTgtMTQuMDggMTIuMDEyLTI4Ljc2MiAxNy4wMy00My40OTEgNi45MDctMjAuMjY1IDkuODA1LTQxLjQ4IDkuOTAzLTYyLjczMS40MTctOTAuMTQzLjE3Ny0xODAuMjg4LjE3Ny0yNzAuNDMzdi02Ljc0NGM1Ni42NSAzOC45MiAxMTguMDM4IDU4Ljk2NiAxODUuNzQzIDU4Ljk4NXYtMTM0LjUyYy04LjU4Ni0uODc2LTE2LjkwNC0xLjY3Ni0yNS4yMDktMi41OTUtNC41MjQtLjUtOS4wMjUtMS4yMDMtMTQuMDgtMi4wMjUtLjIwNi0uMzE3LS40MTMtLjYzNS0uMjI2LTEuNzE5di0yNy43NDVjLTcuODE4LS44Mi0xNS43ODgtMS42Ni0yMy43NTktMi40ODktMTMuOTE4LTEuNDQ2LTI3LjQ0LTQuNzU4LTQwLjMwNi0xMC4xMDUtMTEuMDE3LTQuNTc4LTIxLjYwNS0xMC4xOS0zMi40NjYtMTUuNDc0IDAgMC0uMjM0LS4yLS42NjQtLjgyM2EyMSAyMSAwIDAgMS0yLjY4LS43MzJjLTEuMTczLTEuMjg4LTIuMzQ2LTIuNTc3LTMuNTMtNC40ODctLjY1OS0uNDM1LTEuMzE4LS44Ny0xLjg4OS0xLjMzIiBzdHlsZT0iLS1kYXJrcmVhZGVyLWlubGluZS1maWxsOnZhcigtLWRhcmtyZWFkZXItYmFja2dyb3VuZC0wMDAwMDAsICMwMDAwMDApOy0tZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO29wYWNpdHk6MSIgZGF0YS1pbmRleD0iMCIvPgogIDxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik0xIDU1NC40NTRjLjI2NiAyLjc3Ny41MzIgNi4wMDguMzk5IDkuMzkyQzEgNTYwLjk3IDEgNTU3Ljk0IDEgNTU0LjQ1NCIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLXRleHQtZmZmZmZmLCAjZThlNmUzKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lO3Zpc2liaWxpdHk6dmlzaWJsZSIgZGF0YS1pbmRleD0iNSIvPgogIDxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik0xIDYzOC40NTRjLjI2NiAyLjc3Ny41MzIgNi4wMDguMzk5IDkuMzkyQzEgNjQ0Ljk3IDEgNjQxLjk0IDEgNjM4LjQ1NCIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLXRleHQtZmZmZmZmLCAjZThlNmUzKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lO3Zpc2liaWxpdHk6dmlzaWJsZSIgZGF0YS1pbmRleD0iNiIvPgogIDxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik04MjQuNDYzIDM1Mi4yMjJzLjIzNC4yLjA1NC42MjljNC44OTggNS4zNjIgOS44NTcgMTAuNDI1IDE1LjA3NSAxNS4yMDYgMTUuMjUgMTMuOTcxIDMyLjY5NCAyNC41NTYgNTEuOTIzIDMxLjk2NCA5LjUxNCAzLjY2NiAxOS41MyA2LjAyOSAyOS4zMTkgOC45OC4yMDcuMzE5LjQxNC42MzcuODkzIDEuNTE3LjI3MiAzNS4xMzUuMjcyIDY5LjcwOC4yNzIgMTA0LjU3OS0zMy44NjYtLjA1Ni02Ni4yMDMtNS4wODctOTcuNzc1LTE1LjEyNC0zMS41MTYtMTAuMDItNjAuNzIyLTI0Ljc0NS04OC4yMjQtNDMuOTcyIDAgMS41NDYuMDAzIDIuMzY1IDAgMy4xODQtLjMzMyA5NC4yMzUtLjU3NiAxODguNDctMS4xMzEgMjgyLjcwMy0uMDYgMTAuMTIxLTEuMjA0IDIwLjM3My0zLjEyOCAzMC4zMTctMy41NTMgMTguMzU1LTkuMDQ5IDM2LjE1LTE2LjQ2IDUzLjQ0Mi0xMS4xMzkgMjUuOTkxLTI2LjI5IDQ5LjI4OS00NS41MTMgNjkuODAxLTkuMzMgOS45NTYtMTkuNTczIDE5LjMxLTMwLjUxOCAyNy40MzQtMTIuODk5IDkuNTc1LTI2LjU4OCAxOC4yOS00MC42OTQgMjUuOTg0YTE5MC4zIDE5MC4zIDAgMCAxLTU1LjMxIDE5LjgzOGMtMTUuODY4IDMuMDQxLTMyLjA0OCA0LjY4LTQ4LjE2NCA2LjExOC03LjgzNy42OTgtMTUuODk5LS4wMDgtMjMuNzYtLjkyNi0xMy45MzQtMS42MjctMjcuOTY3LTMuMTAyLTQxLjY0NC02LjA4NS0yMi42OS00Ljk0OC00NC4wNzEtMTMuNjQyLTY0LjExMi0yNS41My00LjY1OS0yLjc2My05LjY1My00Ljk2LTE0LjQ5My03LjQxOCAwIDAtLjAxMi4wODcuMjE0LS4yNjctLjYxMy0uNzEyLTEuNDUtMS4wNzEtMi4yOS0xLjQzLTIuMjktMS44NDItNC41OC0zLjY4My02Ljk2NS02LjIxLS43MzQtMS4xMjktMS4zNzYtMS41Ny0yLjAxNy0yLjAxMyAwIDAgLjAxMy4wMjcuMjAzLS4zMDYtLjk1NS0xLjkzOS0xLjk4Mi0zLjY0NC0zLjI2Mi01LjEzMi0xNC44NTEtMTcuMjc1LTI2LjU3Ni0zNi40NjgtMzUuOTQ0LTU3LjIyLTExLjc2NS0yNi4wNjYtMTkuMDc1LTUzLjM5NS0yMC42OC04MS43ODgtMS4wNS0xOC41OTQtLjAwOS0zNy40NzggMi4wMS01Ni4wMjQgMS45MzgtMTcuODE4IDcuMTE4LTM1LjEzMSAxMy42LTUxLjk4NSAxMS4yMDUtMjkuMTI2IDI3LjI3Ni01NS4yODUgNDguNDQtNzguMDY0IDEwLjA4NC0xMC44NTQgMjEuMzI1LTIwLjgxMiAzMi45NjgtMzAuMDAzIDE5LjUwNy0xNS4zOTkgNDEuMjMzLTI3LjA3MiA2NC42OTUtMzUuNTU0IDI1LjgyOC05LjMzNyA1Mi40MDMtMTQuMzAyIDc5LjgxMS0xNS4wNDggMCAwIC4xODYuMTQ1LjE3OS42OS0uMDA3IDM1LjQ1OS0uMDA3IDcwLjM3NC0uMDA3IDEwNS4yMjctOS4xNzQtMS42NDMtMTcuNzQ2LTMuODQ2LTI2LjQ0Mi00LjU4LTEzLjY3Ni0xLjE1NC0yNy4xNy43MjMtNDAuMzIgNC44NTUtMjcuODg4IDguNzY0LTQ5LjQwMSAyNS44My02NC4xMDYgNTAuODgtMTIuNzg5IDIxLjc4Ni0xNy42MzcgNDUuNDY5LTE0LjI3NyA3MC44MTUgMi4wNyAxNS42MTggNy4wMDggMzAuMDc2IDE1LjE4OSA0My4zOTQgMTAuNjYgMTcuMzUzIDI0Ljc1IDMxLjIzNiA0Mi45MyA0MC43MzQuOC42OTYgMS42IDEuMzkxIDIuNzEyIDIuNTU0IDcuNDU1IDEwLjUyMiAxNi4zMzYgMTguODE3IDI2LjUzNSAyNS42MzcgMjUuNDA2IDE2Ljk4OSA1My4zMjIgMjMuNTA5IDgzLjM5NiAxNy43OTMgMzAuNDQtNS43ODUgNTQuNDk3LTIyLjE4NyA3Mi4xOTMtNDcuNTE2IDExLjI3NS0xNi4xMzkgMTguODUxLTM0LjMyOCAxOC45MzgtNTMuOTgyLjcyOC0xNjQuMTg0Ljg5My0zMjguMzcgMS4yMi00OTIuNTU2LjAzLTE1LjE1OC4wMDQtMzAuMzE3LjAwNC00NS40NzZWMjI3YzIuNSAwIDQuNDU5LjAwMyA2LjQxNyAwbDkyLjQ0LS4xODJzLjE4NS4xNDYuMTc4LjYzOGMtLjAxMS45OS0uMTE4IDEuNTE1LS4wMDYgMS45ODYgNC41MjMgMTkuMDI3IDEwLjY0MiAzNy4zOCAyMC4zMzQgNTQuNTY3IDExLjg2OCAyMS4wNDggMjcuMzMgMzguOTA3IDQ1LjY3NiA1NC4zODUgMy4yMjUgMi43MiA3LjIyNyA0LjUyIDEwLjg3IDYuNzQ1IDAgMCAuMDI1LS4wODctLjIyOC4yMzYuNTQ5Ljc1OCAxLjM1MSAxLjE5MyAyLjE1MyAxLjYyOGEyMTMgMjEzIDAgMCAwIDQuMDUgNC4zNTdjMS4yMTUuNjE1IDEuODk3LjczOSAyLjU3OS44NjIiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci1iYWNrZ3JvdW5kLWZmZmZmZiwgIzE4MWExYik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6bm9uZTtvcGFjaXR5OjEiIGRhdGEtaW5kZXg9IjciLz4KICA8cGF0aCBmaWxsPSIjRkUyQzU1IiBkPSJNMzUxLjEwMiA5MzUuMjA3YzQuODEgMi4xMTMgOS44MDUgNC4zMSAxNC40NjQgNy4wNzQgMjAuMDQgMTEuODg4IDQxLjQyMyAyMC41ODIgNjQuMTEyIDI1LjUzIDEzLjY3NyAyLjk4MyAyNy43MSA0LjQ1OCA0MS42NDUgNi4wODUgNy44Ni45MTggMTUuOTIyIDEuNjI0IDIzLjc1OS45MjYgMTYuMTE2LTEuNDM3IDMyLjI5Ni0zLjA3NyA0OC4xNjQtNi4xMThhMTkwLjMgMTkwLjMgMCAwIDAgNTUuMzEtMTkuODM4YzE0LjEwNi03LjY5NSAyNy43OTUtMTYuNDA5IDQwLjY5NC0yNS45ODQgMTAuOTQ1LTguMTI1IDIxLjE4Ny0xNy40NzggMzAuNTE4LTI3LjQzNCAxOS4yMjMtMjAuNTEyIDM0LjM3NC00My44MSA0NS41MTMtNjkuOCA3LjQxMS0xNy4yOTQgMTIuOTA3LTM1LjA4OCAxNi40Ni01My40NDMgMS45MjQtOS45NDQgMy4wNjgtMjAuMTk2IDMuMTI4LTMwLjMxNy41NTUtOTQuMjMzLjc5OC0xODguNDY4IDEuMTMtMjgyLjcwMy4wMDQtLjgyLjAwMS0xLjYzOC4wMDEtMy4xODQgMjcuNTAyIDE5LjIyNyA1Ni43MDggMzMuOTUzIDg4LjIyNCA0My45NzIgMzEuNTcyIDEwLjAzNyA2My45MSAxNS4wNjggOTcuNzc1IDE1LjEyNGwtLjAwMS0xMDQuNDc0YzQuNTEuMTU0IDkuMDEyLjg1NyAxMy41MzYgMS4zNTcgOC4zMDUuOTE5IDE2LjYyMyAxLjcxOSAyNS4yMDkgMi41OTZ2MTM0LjUxOWMtNjcuNzA1LS4wMi0xMjkuMDkzLTIwLjA2Ni0xODUuNzQzLTU4Ljk4NXY2Ljc0NGMwIDkwLjE0NS4yNCAxODAuMjktLjE3NyAyNzAuNDMzLS4wOTggMjEuMjUxLTIuOTk2IDQyLjQ2Ni05LjkwMiA2Mi43My01LjAyIDE0LjczLTEwLjQ1IDI5LjQxMi0xNy4wMzEgNDMuNDkyLTguMzE0IDE3Ljc4OS0xOS42OTYgMzMuNzU4LTMyLjUyNSA0OC43MDItMTkuNDcyIDIyLjY4LTQyLjE5NCA0MS4yNTktNjguMjk3IDU1LjgwNy0yMy45NTUgMTMuMzUyLTQ5LjM5NSAyMi4yOS03Ni40MDQgMjcuMDU5LTE4LjI3OCAzLjIyNy0zNi42NzQgNC43MjItNTUuMTQ1IDMuNzk2LTE5LjEwMy0uOTU4LTM3Ljk4NC00LjAwOS01Ni40MTUtOS4zMi0yNC45LTcuMTc2LTQ4LjQ1LTE3LjU1NS02OS41My0zMi42MTEtMTMuNDM0LTkuNTk2LTI1LjY3LTIwLjg2OS0zOC40NzItMzEuNzM1IiBzdHlsZT0iLS1kYXJrcmVhZGVyLWlubGluZS1maWxsOnZhcigtLWRhcmtyZWFkZXItYmFja2dyb3VuZC1mZTJjNTUsICNiMTAxMjMpOy0tZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO29wYWNpdHk6MSIgZGF0YS1pbmRleD0iOCIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik03MzguNzE4IDIyNi40NDhjLTMwLjY3NS40MzEtNjEuNDg4LjQ5Mi05Mi4zMDEuNTUxLTEuOTU4LjAwNC0zLjkxNi4wMDEtNi40MTcuMDAxdjUuMjg4YzAgMTUuMTU5LjAyNiAzMC4zMTgtLjAwNCA0NS40NzYtLjMyNyAxNjQuMTg2LS40OTIgMzI4LjM3Mi0xLjIyIDQ5Mi41NTYtLjA4NyAxOS42NTQtNy42NjMgMzcuODQzLTE4LjkzOCA1My45ODItMTcuNjk2IDI1LjMyOS00MS43NTMgNDEuNzMtNzIuMTkzIDQ3LjUxNi0zMC4wNzQgNS43MTYtNTcuOTktLjgwNC04My4zOTYtMTcuNzkzLTEwLjE5OS02LjgyLTE5LjA4LTE1LjExNS0yNi4yMzctMjUuNTE0IDcuODY4IDIuMzU2IDE1LjYxNiA1LjU3MyAyMy42NTYgNy42NjIgMjUuMTc0IDYuNTQxIDQ5LjU0NiAzLjgzMiA3My4xMzItNy4xMTMgMjQuODQzLTExLjUyOCA0My4wODUtMjkuNzUzIDU0Ljc1My01NC4zMzMgNi41ODMtMTMuODcgMTAuNDgtMjguNTgyIDEwLjQ3Ni00NC4xODQtLjA0Ni0xNzcuMDc5LS4wMjktMzU0LjE1Ny0uMDI5LTUzMS4yMzZ2LTYuMDg1aDEzNS4wODdjMS4xODIgMTEuMTE1IDIuMzM3IDIxLjk4NSAzLjYzIDMzLjIyNiIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLWJhY2tncm91bmQtMjVmNGVlLCAjMDliM2IwKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lO29wYWNpdHk6MSIgZGF0YS1pbmRleD0iOSIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik01MjEuOTIgNTEzLjM2N2MtMjcuNDcyIDEuMTk4LTU0LjA0NyA2LjE2My03OS44NzUgMTUuNS0yMy40NjIgOC40ODItNDUuMTg4IDIwLjE1NS02NC42OTUgMzUuNTU0LTExLjY0MyA5LjE5MS0yMi44ODQgMTkuMTQ5LTMyLjk2OSAzMC4wMDMtMjEuMTYzIDIyLjc3OS0zNy4yMzQgNDguOTM4LTQ4LjQzOCA3OC4wNjQtNi40ODMgMTYuODU0LTExLjY2MyAzNC4xNjctMTMuNjAyIDUxLjk4NS0yLjAxOCAxOC41NDYtMy4wNTkgMzcuNDMtMi4wMDggNTYuMDI0IDEuNjA0IDI4LjM5MyA4LjkxNCA1NS43MjIgMjAuNjggODEuNzg4IDkuMzY3IDIwLjc1MiAyMS4wOTIgMzkuOTQ1IDM1Ljk0MyA1Ny4yMiAxLjI4IDEuNDg4IDIuMzA3IDMuMTkzIDMuMjc2IDUuMTM3LTQuMzYyLTIuNjYtOC43OTItNS4zNzQtMTIuNjkzLTguNzA2LTIyLjUwMS0xOS4yMi00MC44ODUtNDEuODMyLTU1LjUyNS02Ny41NjMtMTMuODE3LTI0LjI4NC0yMi45MzgtNTAuMjgtMjcuOC03Ny42NTgtMi4xMTMtMTEuOTAzLTIuMzgyLTI0LjE1NS0zLjE0Mi0zNi4yNy0uNDA2LTYuNDY4LS41OTItMTMuMDMzLjAzNS0xOS40NjUgMS4zNDYtMTMuOCAyLjQwOC0yNy43MjEgNS4xNzMtNDEuMjY5IDQuNzA5LTIzLjA3NCAxMy4zMTktNDQuODcgMjQuNjc4LTY1LjU1NSA4LjAwNS0xNC41NzcgMTcuMTc4LTI4LjM1IDI4LjI0Mi00MC43NjQgNi44MzQtNy42NjggMTMuODI0LTE1LjI1MyAyMS4zMDktMjIuMjcyIDIxLjc5NS0yMC40NCA0Ni43NC0zNS45MjcgNzQuNDM0LTQ3LjI3NyAxNi4yNTUtNi42NjIgMzIuOTU2LTExLjc1OSA1MC4xNDItMTQuNTQgMTQuMjI2LTIuMzAyIDI4LjgxMy0yLjc3IDQzLjI2NC0zLjE0NyAxMS4xMDctLjI5IDIyLjI1LjgyOSAzMy42MzYgMS4zMzMgMCAxMS4xNDUgMCAyMS4yODUtLjA2NCAzMS44NzgiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci1iYWNrZ3JvdW5kLTI1ZjRlZSwgIzA5YjNiMCk7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6dmFyKC0tZGFya3JlYWRlci10ZXh0LTBkOTlmZiwgIzIzYTNmZik7b3BhY2l0eToxIiBkYXRhLWluZGV4PSIxMCIvPgogIDxwYXRoIGZpbGw9IiNGRTJDNTUiIGQ9Ik00MzQuOTkyIDgyNS40ODJjLTE4LjE3LTkuMTQ2LTMyLjI2LTIzLjAyOS00Mi45Mi00MC4zODItOC4xOC0xMy4zMTgtMTMuMTItMjcuNzc2LTE1LjE5LTQzLjM5NC0zLjM1OS0yNS4zNDYgMS40OS00OS4wMjkgMTQuMjc4LTcwLjgxNSAxNC43MDUtMjUuMDUgMzYuMjE4LTQyLjExNiA2NC4xMDYtNTAuODggMTMuMTUtNC4xMzIgMjYuNjQ0LTYuMDA5IDQwLjMyLTQuODU1IDguNjk2LjczNCAxNy4yNjggMi45MzcgMjYuNDQyIDQuNTggMC0zNC44NTMgMC02OS43NjguMDgtMTA1LjEzNSAxMi43NzMtLjA3IDI1LjQ2Ni4zMTMgMzguNTE0LjcwNnYxMzguNDY4Yy01My42NC0xMy4zMy05Ny44MyAxLjgyNy0xMjcuODkgNDcuODc4LTI2LjM0NyA0MC4zNjYtMjMuOTIxIDgyLjc3IDIuMjYgMTIzLjgyOSIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLWJhY2tncm91bmQtZmUyYzU1LCAjYjEwMTIzKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lO29wYWNpdHk6MSIgZGF0YS1pbmRleD0iMTEiLz4KICA8cGF0aCBmaWxsPSIjRkUyQzU1IiBkPSJNODE1Ljk0MiAzNDQuNzc0Yy0zLjY3Ni0xLjg2LTcuNjc4LTMuNjYtMTAuOTAzLTYuMzgtMTguMzQ1LTE1LjQ3OC0zMy44MDgtMzMuMzM3LTQ1LjY3Ni01NC4zODUtOS42OTItMTcuMTg3LTE1LjgxMS0zNS41NC0yMC4zMzQtNTQuNTY3LS4xMTItLjQ3MS0uMDA1LS45OTUuMDgtMS44OTQgMTEuODQ5LS4zOTkgMjMuNjE4LS4zOTkgMzUuMzY5LS4zOTkuNTIgNi42NCAxLjAxNiAxMy4wMDYgMS41MiAxOS4zNzMgMS4zNTYgMTcuMTYgNS42NyAzMy41OTUgMTIuMDA0IDQ5LjU1NiA2LjE1IDE1LjQ5OCAxNC40MjYgMjkuNzk4IDI0LjExNCA0My4zMTcgMS4yMjcgMS43MTMgMi41NyAzLjM0NCAzLjgyNiA1LjM3OSIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLWJhY2tncm91bmQtZmUyYzU1LCAjYjEwMTIzKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lIiBkYXRhLWluZGV4PSIxMiIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik05MjEuMDMgNDA4LjYxOWMtOS45ODUtMi41Ny0yMC00LjkzMi0yOS41MTUtOC41OTgtMTkuMjMtNy40MDgtMzYuNjczLTE3Ljk5My01MS45MjMtMzEuOTY0LTUuMjE4LTQuNzgtMTAuMTc3LTkuODQ0LTE1LjAzMy0xNS4xNDEgMTEgNC43OTIgMjEuNTg3IDEwLjQwMyAzMi42MDQgMTQuOTgxIDEyLjg2NyA1LjM0NyAyNi4zODggOC42NiA0MC4zMDYgMTAuMTA1IDcuOTcxLjgyOCAxNS45NCAxLjY3IDIzLjc1OSAyLjQ5IDAgOS4wODYgMCAxOC40MTUtLjE5NyAyOC4xMjciIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci10ZXh0LTI1ZjRlZSwgIzM2ZjVlZik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6dmFyKC0tZGFya3JlYWRlci10ZXh0LTBkOTlmZiwgIzIzYTNmZik7b3BhY2l0eToxIiBkYXRhLWluZGV4PSIxMyIvPgogIDxwYXRoIGZpbGw9IiNGRTJDNTUiIGQ9Ik04MTcuODI5IDM0Ni42OTJhMy41IDMuNSAwIDAgMS0yLjE5Mi0xLjMwNWMuODY4LjEyNCAxLjUyNy41NTkgMi4xOTIgMS4zMDUiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci10ZXh0LWZlMmM1NSwgI2ZlMzk1Zik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6bm9uZSIgZGF0YS1pbmRleD0iMTQiLz4KICA8cGF0aCBmaWxsPSIjMjVGNEVFIiBkPSJNMzQwLjA0MiA5MjUuMjM3Yy42MTQuMTQ4IDEuMjU2LjU5IDEuODQ1IDEuMzk5LS42NC0uMTI1LTEuMjMtLjYxNS0xLjg0NS0xLjQiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci10ZXh0LTI1ZjRlZSwgIzM2ZjVlZik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6bm9uZTt2aXNpYmlsaXR5OnZpc2libGUiIGRhdGEtaW5kZXg9IjE1Ii8+CiAgPHBhdGggZmlsbD0iI0ZFMkM1NSIgZD0iTTM0OS4xMDggOTMzLjQ0M2EzLjY2IDMuNjYgMCAwIDEgMi4yMiAxLjEzOGMtLjgyNi0uMDYyLTEuNDY4LS40Ni0yLjIyLTEuMTM4IiBzdHlsZT0iLS1kYXJrcmVhZGVyLWlubGluZS1maWxsOnZhcigtLWRhcmtyZWFkZXItdGV4dC1mZTJjNTUsICNmZTM5NWYpOy0tZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlIiBkYXRhLWluZGV4PSIxNiIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik04MjQuMjQ4IDM1MS45MTFjLS40NjcuMTg4LTEuMTUuMDY0LTIuMDQtLjQwMi40Ny0uMTk4IDEuMTQ3LS4wNTMgMi4wNC40MDIiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci10ZXh0LTI1ZjRlZSwgIzM2ZjVlZik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6bm9uZSIgZGF0YS1pbmRleD0iMTciLz4KPC9zdmc+Cg=="; +const tiktokIconUri = + "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjAwIDEyMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxwYXRoIGQ9Ik02MzYgMTIwMWMtMjMuMDIgMC00Ni4wNDIgMC02OS42OTctLjQwOC05Ljc5NC0xLjEzNS0xOC45OTYtMS41MTgtMjguMTAxLTIuNjY1LTE2LjQyNC0yLjA2Ny0zMi45Ni0zLjc3NS00OS4xNC03LjEyNi0yMS42OTYtNC40OTQtNDMuMjM2LTkuOTA3LTY0LjU4OS0xNS44NDItMjQuMjYtNi43NDMtNDcuNjQ1LTE2LjAyLTcwLjU3Ny0yNi40OC0yNi4yMjYtMTEuOTYzLTUxLjkxMy0yNC45NjQtNzUuNzk4LTQxLjA5OC0xOC43NDYtMTIuNjYzLTM3LjA1Ny0yNi4wNC01NC44OTktMzkuOTQ4LTEzLjU1LTEwLjU2My0yNi43MjktMjEuNzkxLTM4Ljg2Ni0zMy45My0xNi4xOS0xNi4xOS0zMS42NTgtMzMuMTYyLTQ2LjU4Mi01MC41MzItMTAuOTY1LTEyLjc2Mi0yMS4yMS0yNi4yNjUtMzAuNTk3LTQwLjIzLTExLjM3OS0xNi45MjUtMjIuMTc4LTM0LjMyMS0zMi4wMS01Mi4xODUtMTAuMTM2LTE4LjQyLTE5LjYyOC0zNy4yOTUtMjcuOTE3LTU2LjYwOC0xMC43NDEtMjUuMDI1LTE5LjkzMS01MC43MjQtMjYuMjE0LTc3LjMwNy00LjU5Mi0xOS40MjctOS4yOTgtMzguODYzLTEyLjgxMy01OC40OTktMi42NjYtMTQuODkyLTMuNS0zMC4xMS01LjI3Mi00NS4xNjdDMi44MDQgNjUxLjkyMiAxLjY2NiA2NTAuOTg5IDEgNjUwYzAtLjQ0NCAwLS44ODkuMzk5LTEuODIuMTMzLTMuNzE4LS4xMzMtNi45NS0uMzk5LTEwLjE4IDAtMjQuMzU0IDAtNDguNzA4LjM5OS03My42ODUuMTMzLTMuODUzLS4xMzMtNy4wODQtLjM5OS0xMC4zMTUgMC0uNDQ0IDAtLjg4OS4zOTEtMS43OS43OTUtMS4xMzggMS40ODUtMS43ODkgMS41Ni0yLjUwMy43NC03LjE1NiAxLjI0Ni0xNC4zMzggMi4wOC0yMS40ODIgMS4yMjgtMTAuNSAyLjAyOS0yMS4xMDcgNC4xNjMtMzEuNDMgMy44MS0xOC40MjYgOC4zMDMtMzYuNzE3IDEyLjgyNy01NC45ODYgNy44ODUtMzEuODQgMTkuNDA0LTYyLjQ2NyAzMy4yMTMtOTIuMTExIDE1LjM3NS0zMy4wMDcgMzMuMzUzLTY0LjU4NCA1NC41MjMtOTQuMzg1IDE2LjM2NC0yMy4wMzYgMzQuMTc3LTQ0LjggNTMuMjE3LTY1LjU2NSA5LjczNS0xMC42MTcgMjAuNTg5LTIwLjIxMyAzMS4wMS0zMC4xOTUgMTcuMjMtMTYuNTA2IDM1Ljg4Mi0zMS4yNSA1NS4xNDItNDUuMzMgMjEuODk3LTE2LjAwOSA0NC42NDctMzAuNjc4IDY4LjczLTQzLjAxIDIwLjgyOC0xMC42NjggNDIuMTE4LTIwLjY2NSA2My45LTI5LjE3NyAyMS43NDUtOC40OTggNDQuMTcxLTE1LjQyNiA2Ni42MzYtMjEuODYzIDIzLjkyMS02Ljg1NCA0OC4zNTQtMTEuNzI4IDczLjE5LTE0LjE4IDEzLjgyNC0xLjM2NSAyNy42NTgtMi42NSA0MS40ODItNC4wMjQgMS0uMSAxLjk1OC0uNjM2IDIuOTM2LS45NjkgMjMuMDIgMCA0Ni4wNDIgMCA2OS42OTcuNDA4IDkuNzk3IDEuMTM1IDE5LjAwMyAxLjUxMyAyOC4xMTEgMi42NjYgMTYuMjY0IDIuMDYgMzIuNjM0IDMuNzg5IDQ4LjY1NyA3LjExMyAyMS44NiA0LjUzNiA0My41NzYgOS45MzYgNjUuMSAxNS44ODMgMjQuMjggNi43MSA0Ny42NDEgMTYuMDY5IDcwLjYwNyAyNi40NzQgMjUuOTc2IDExLjc3IDUxLjA4MiAyNS4xMTggNzUuMTUgNDAuMzYgMzIuMDkgMjAuMzI0IDYxLjk2NyA0My42MjYgODkuNTUgNjkuNjk4IDEzLjY5NSAxMi45NDUgMjYuNjIgMjYuNzk2IDM5LjAwNCA0MS4wMTIgMTIuNDkgMTQuMzM5IDI0LjAyNyAyOS41MjMgMzUuNzE1IDQ0LjU0NCAxNi4xNzQgMjAuNzg3IDI5LjczNCA0My4zMjQgNDIuMTc3IDY2LjQ0MiAxOS41ODkgMzYuMzk3IDM1LjU1NCA3NC40MTggNDcuMDc2IDExNC4xNTMgNC43NDEgMTYuMzUyIDguNTM4IDMzLjAxIDEyLjAzOSA0OS42OCAyLjkyNSAxMy45MyA1LjE2NCAyOC4wMzMgNy4wMjQgNDIuMTUgMS44MTUgMTMuNzY4IDIuNzUgMjcuNjUyIDQuMTI0IDQxLjQ4LjEgMS4wMDEuNjM2IDEuOTYuOTY5IDIuOTM3IDAgMjMuMDIgMCA0Ni4wNDItLjQwOCA2OS42OTctMS4xMzUgOS43OTctMS41MTMgMTkuMDAzLTIuNjY2IDI4LjExMS0yLjA2IDE2LjI2NC0zLjgxNyAzMi42MjYtNy4xMDcgNDguNjU5LTQuNDU4IDIxLjcyMS05LjYxMyA0My4zNDctMTUuNjE2IDY0LjY5MS03LjE2MyAyNS40NzItMTYuODc1IDUwLjExLTI4IDc0LjEyNy0xMS45NzggMjUuODU4LTI1LjQ3MyA1MC45MjYtNDAuOTE4IDc0Ljg5M2E1OTguNSA1OTguNSAwIDAgMS02Ny44OSA4Ni42OWMtMTIuOTMzIDEzLjcwNS0yNi43OTIgMjYuNjI0LTQxLjAwOCAzOS4wMDctMTQuMzQgMTIuNDkxLTI5LjUyMyAyNC4wMjgtNDQuNTQ1IDM1LjcxNi0yMC43ODYgMTYuMTc0LTQzLjMyMyAyOS43MzQtNjYuNDQyIDQyLjE3Ny0zNi4zOTcgMTkuNTg5LTc0LjQxOCAzNS41NTQtMTE0LjE1MyA0Ny4wNzYtMTYuMzUyIDQuNzQxLTMzLjAxIDguNTM4LTQ5LjY4IDEyLjAzOS0xMy45MyAyLjkyNS0yOC4wMzQgNS4xNjQtNDIuMTUgNy4wMjUtMTMuNzY4IDEuODE0LTI3LjY1MiAyLjc0OC00MS40OCA0LjEyMy0xLjAwMS4xLTEuOTYuNjM2LTIuOTM3Ljk2OW0xNzkuOTM0LTg1NS45NDhzLS4wMjQuMDg3LjA0LS42NDNjLTEuMjg4LTEuNjctMi42My0zLjMtMy44NTgtNS4wMTQtOS42ODgtMTMuNTE5LTE3Ljk2NS0yNy44Mi0yNC4xMTQtNDMuMzE3LTYuMzMzLTE1Ljk2LTEwLjY0OC0zMi4zOTctMTIuMDA0LTQ5LjU1Ni0uNTA0LTYuMzY3LTEtMTIuNzMzLTEuNTItMTkuMzczLTExLjc1IDAtMjMuNTIgMC0zNS40MzYtLjE4NSAwIDAtLjE4Ni0uMTQ2LS40NjMtLjg4NmwtMy40OTItMzIuODU2SDYwMHY2LjA4NWMwIDE3Ny4wNzktLjAxNyAzNTQuMTU3LjAyOSA1MzEuMjM2LjAwNCAxNS42MDItMy44OTMgMzAuMzE0LTEwLjQ3NiA0NC4xODQtMTEuNjY4IDI0LjU4LTI5LjkxIDQyLjgwNS01NC43NTMgNTQuMzMzLTIzLjU4NiAxMC45NDUtNDcuOTU4IDEzLjY1NC03My4xMzIgNy4xMTMtOC4wNC0yLjA4OS0xNS43ODgtNS4zMDYtMjQuMjY1LTguMjUzLS44LS42OTUtMS42LTEuMzktMi40MjItMi43OS0yNi4xNy00MC43MDctMjguNTk2LTgzLjExMS0yLjI0OC0xMjMuNDc3IDMwLjA1OS00Ni4wNTEgNzQuMjQ5LTYxLjIwOCAxMjcuODktNDcuODc4VjUxNS4zMDdjLTEzLjA1LS4zOTMtMjUuNzQyLS43NzYtMzguNTgtMS4zNDMgMCAwLS4xODctLjE0NS0uMDU4LTEuMDVWNDgxLjQ5Yy0xMS4zODYtLjUwNC0yMi41My0xLjYyMy0zMy42MzYtMS4zMzMtMTQuNDUuMzc2LTI5LjAzOC44NDUtNDMuMjY0IDMuMTQ3LTE3LjE4NiAyLjc4MS0zMy44ODcgNy44NzgtNTAuMTQyIDE0LjU0LTI3LjY5NCAxMS4zNS01Mi42MzkgMjYuODM3LTc0LjQzNCA0Ny4yNzctNy40ODUgNy4wMi0xNC40NzUgMTQuNjA0LTIxLjMwOSAyMi4yNzItMTEuMDY0IDEyLjQxNS0yMC4yMzcgMjYuMTg3LTI4LjI0MiA0MC43NjQtMTEuMzYgMjAuNjg1LTE5Ljk3IDQyLjQ4MS0yNC42NzggNjUuNTU1LTIuNzY1IDEzLjU0OC0zLjgyNyAyNy40NjktNS4xNzMgNDEuMjY5LS42MjcgNi40MzItLjQ0IDEyLjk5Ny0uMDM1IDE5LjQ2NS43NiAxMi4xMTUgMS4wMjkgMjQuMzY3IDMuMTQyIDM2LjI3IDQuODYyIDI3LjM3OCAxMy45ODMgNTMuMzc0IDI3LjggNzcuNjU4IDE0LjY0IDI1LjczIDMzLjAyNCA0OC4zNDMgNTUuNTI1IDY3LjU2MyAzLjkwMSAzLjMzMiA4LjMzMSA2LjA0NiAxMi40ODkgOS4wMzQgMCAwLS4wMTMtLjAyNy4wNC41Ni41OS40OSAxLjE3OC45ODEgMi4wNTggMi4xMTEgMi4yOSAxLjg0MiA0LjU4MSAzLjY4MyA3LjA5MiA2LjA4LjY0Mi40IDEuMjg0Ljc5OCAxLjg0MyAxLjIzIDAgMCAuMDEyLS4wODguMDcuNjAxIDEyLjc3NCAxMC41MjEgMjUuMDEgMjEuNzk0IDM4LjQ0MyAzMS4zOSAyMS4wOCAxNS4wNTYgNDQuNjMgMjUuNDM1IDY5LjUzIDMyLjYxIDE4LjQzIDUuMzEyIDM3LjMxMiA4LjM2MyA1Ni40MTUgOS4zMiAxOC40NzEuOTI3IDM2Ljg2Ny0uNTY4IDU1LjE0NS0zLjc5NSAyNy4wMS00Ljc3IDUyLjQ0OS0xMy43MDcgNzYuNDA0LTI3LjA1OSAyNi4xMDMtMTQuNTQ4IDQ4LjgyNS0zMy4xMjYgNjguMjk3LTU1LjgwNyAxMi44MjktMTQuOTQ0IDI0LjIxLTMwLjkxMyAzMi41MjUtNDguNzAyIDYuNTgtMTQuMDggMTIuMDEyLTI4Ljc2MiAxNy4wMy00My40OTEgNi45MDctMjAuMjY1IDkuODA1LTQxLjQ4IDkuOTAzLTYyLjczMS40MTctOTAuMTQzLjE3Ny0xODAuMjg4LjE3Ny0yNzAuNDMzdi02Ljc0NGM1Ni42NSAzOC45MiAxMTguMDM4IDU4Ljk2NiAxODUuNzQzIDU4Ljk4NXYtMTM0LjUyYy04LjU4Ni0uODc2LTE2LjkwNC0xLjY3Ni0yNS4yMDktMi41OTUtNC41MjQtLjUtOS4wMjUtMS4yMDMtMTQuMDgtMi4wMjUtLjIwNi0uMzE3LS40MTMtLjYzNS0uMjI2LTEuNzE5di0yNy43NDVjLTcuODE4LS44Mi0xNS43ODgtMS42Ni0yMy43NTktMi40ODktMTMuOTE4LTEuNDQ2LTI3LjQ0LTQuNzU4LTQwLjMwNi0xMC4xMDUtMTEuMDE3LTQuNTc4LTIxLjYwNS0xMC4xOS0zMi40NjYtMTUuNDc0IDAgMC0uMjM0LS4yLS42NjQtLjgyM2EyMSAyMSAwIDAgMS0yLjY4LS43MzJjLTEuMTczLTEuMjg4LTIuMzQ2LTIuNTc3LTMuNTMtNC40ODctLjY1OS0uNDM1LTEuMzE4LS44Ny0xLjg4OS0xLjMzIiBzdHlsZT0iLS1kYXJrcmVhZGVyLWlubGluZS1maWxsOnZhcigtLWRhcmtyZWFkZXItYmFja2dyb3VuZC0wMDAwMDAsICMwMDAwMDApOy0tZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO29wYWNpdHk6MSIgZGF0YS1pbmRleD0iMCIvPgogIDxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik0xIDU1NC40NTRjLjI2NiAyLjc3Ny41MzIgNi4wMDguMzk5IDkuMzkyQzEgNTYwLjk3IDEgNTU3Ljk0IDEgNTU0LjQ1NCIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLXRleHQtZmZmZmZmLCAjZThlNmUzKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lO3Zpc2liaWxpdHk6dmlzaWJsZSIgZGF0YS1pbmRleD0iNSIvPgogIDxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik0xIDYzOC40NTRjLjI2NiAyLjc3Ny41MzIgNi4wMDguMzk5IDkuMzkyQzEgNjQ0Ljk3IDEgNjQxLjk0IDEgNjM4LjQ1NCIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLXRleHQtZmZmZmZmLCAjZThlNmUzKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lO3Zpc2liaWxpdHk6dmlzaWJsZSIgZGF0YS1pbmRleD0iNiIvPgogIDxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik04MjQuNDYzIDM1Mi4yMjJzLjIzNC4yLjA1NC42MjljNC44OTggNS4zNjIgOS44NTcgMTAuNDI1IDE1LjA3NSAxNS4yMDYgMTUuMjUgMTMuOTcxIDMyLjY5NCAyNC41NTYgNTEuOTIzIDMxLjk2NCA5LjUxNCAzLjY2NiAxOS41MyA2LjAyOSAyOS4zMTkgOC45OC4yMDcuMzE5LjQxNC42MzcuODkzIDEuNTE3LjI3MiAzNS4xMzUuMjcyIDY5LjcwOC4yNzIgMTA0LjU3OS0zMy44NjYtLjA1Ni02Ni4yMDMtNS4wODctOTcuNzc1LTE1LjEyNC0zMS41MTYtMTAuMDItNjAuNzIyLTI0Ljc0NS04OC4yMjQtNDMuOTcyIDAgMS41NDYuMDAzIDIuMzY1IDAgMy4xODQtLjMzMyA5NC4yMzUtLjU3NiAxODguNDctMS4xMzEgMjgyLjcwMy0uMDYgMTAuMTIxLTEuMjA0IDIwLjM3My0zLjEyOCAzMC4zMTctMy41NTMgMTguMzU1LTkuMDQ5IDM2LjE1LTE2LjQ2IDUzLjQ0Mi0xMS4xMzkgMjUuOTkxLTI2LjI5IDQ5LjI4OS00NS41MTMgNjkuODAxLTkuMzMgOS45NTYtMTkuNTczIDE5LjMxLTMwLjUxOCAyNy40MzQtMTIuODk5IDkuNTc1LTI2LjU4OCAxOC4yOS00MC42OTQgMjUuOTg0YTE5MC4zIDE5MC4zIDAgMCAxLTU1LjMxIDE5LjgzOGMtMTUuODY4IDMuMDQxLTMyLjA0OCA0LjY4LTQ4LjE2NCA2LjExOC03LjgzNy42OTgtMTUuODk5LS4wMDgtMjMuNzYtLjkyNi0xMy45MzQtMS42MjctMjcuOTY3LTMuMTAyLTQxLjY0NC02LjA4NS0yMi42OS00Ljk0OC00NC4wNzEtMTMuNjQyLTY0LjExMi0yNS41My00LjY1OS0yLjc2My05LjY1My00Ljk2LTE0LjQ5My03LjQxOCAwIDAtLjAxMi4wODcuMjE0LS4yNjctLjYxMy0uNzEyLTEuNDUtMS4wNzEtMi4yOS0xLjQzLTIuMjktMS44NDItNC41OC0zLjY4My02Ljk2NS02LjIxLS43MzQtMS4xMjktMS4zNzYtMS41Ny0yLjAxNy0yLjAxMyAwIDAgLjAxMy4wMjcuMjAzLS4zMDYtLjk1NS0xLjkzOS0xLjk4Mi0zLjY0NC0zLjI2Mi01LjEzMi0xNC44NTEtMTcuMjc1LTI2LjU3Ni0zNi40NjgtMzUuOTQ0LTU3LjIyLTExLjc2NS0yNi4wNjYtMTkuMDc1LTUzLjM5NS0yMC42OC04MS43ODgtMS4wNS0xOC41OTQtLjAwOS0zNy40NzggMi4wMS01Ni4wMjQgMS45MzgtMTcuODE4IDcuMTE4LTM1LjEzMSAxMy42LTUxLjk4NSAxMS4yMDUtMjkuMTI2IDI3LjI3Ni01NS4yODUgNDguNDQtNzguMDY0IDEwLjA4NC0xMC44NTQgMjEuMzI1LTIwLjgxMiAzMi45NjgtMzAuMDAzIDE5LjUwNy0xNS4zOTkgNDEuMjMzLTI3LjA3MiA2NC42OTUtMzUuNTU0IDI1LjgyOC05LjMzNyA1Mi40MDMtMTQuMzAyIDc5LjgxMS0xNS4wNDggMCAwIC4xODYuMTQ1LjE3OS42OS0uMDA3IDM1LjQ1OS0uMDA3IDcwLjM3NC0uMDA3IDEwNS4yMjctOS4xNzQtMS42NDMtMTcuNzQ2LTMuODQ2LTI2LjQ0Mi00LjU4LTEzLjY3Ni0xLjE1NC0yNy4xNy43MjMtNDAuMzIgNC44NTUtMjcuODg4IDguNzY0LTQ5LjQwMSAyNS44My02NC4xMDYgNTAuODgtMTIuNzg5IDIxLjc4Ni0xNy42MzcgNDUuNDY5LTE0LjI3NyA3MC44MTUgMi4wNyAxNS42MTggNy4wMDggMzAuMDc2IDE1LjE4OSA0My4zOTQgMTAuNjYgMTcuMzUzIDI0Ljc1IDMxLjIzNiA0Mi45MyA0MC43MzQuOC42OTYgMS42IDEuMzkxIDIuNzEyIDIuNTU0IDcuNDU1IDEwLjUyMiAxNi4zMzYgMTguODE3IDI2LjUzNSAyNS42MzcgMjUuNDA2IDE2Ljk4OSA1My4zMjIgMjMuNTA5IDgzLjM5NiAxNy43OTMgMzAuNDQtNS43ODUgNTQuNDk3LTIyLjE4NyA3Mi4xOTMtNDcuNTE2IDExLjI3NS0xNi4xMzkgMTguODUxLTM0LjMyOCAxOC45MzgtNTMuOTgyLjcyOC0xNjQuMTg0Ljg5My0zMjguMzcgMS4yMi00OTIuNTU2LjAzLTE1LjE1OC4wMDQtMzAuMzE3LjAwNC00NS40NzZWMjI3YzIuNSAwIDQuNDU5LjAwMyA2LjQxNyAwbDkyLjQ0LS4xODJzLjE4NS4xNDYuMTc4LjYzOGMtLjAxMS45OS0uMTE4IDEuNTE1LS4wMDYgMS45ODYgNC41MjMgMTkuMDI3IDEwLjY0MiAzNy4zOCAyMC4zMzQgNTQuNTY3IDExLjg2OCAyMS4wNDggMjcuMzMgMzguOTA3IDQ1LjY3NiA1NC4zODUgMy4yMjUgMi43MiA3LjIyNyA0LjUyIDEwLjg3IDYuNzQ1IDAgMCAuMDI1LS4wODctLjIyOC4yMzYuNTQ5Ljc1OCAxLjM1MSAxLjE5MyAyLjE1MyAxLjYyOGEyMTMgMjEzIDAgMCAwIDQuMDUgNC4zNTdjMS4yMTUuNjE1IDEuODk3LjczOSAyLjU3OS44NjIiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci1iYWNrZ3JvdW5kLWZmZmZmZiwgIzE4MWExYik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6bm9uZTtvcGFjaXR5OjEiIGRhdGEtaW5kZXg9IjciLz4KICA8cGF0aCBmaWxsPSIjRkUyQzU1IiBkPSJNMzUxLjEwMiA5MzUuMjA3YzQuODEgMi4xMTMgOS44MDUgNC4zMSAxNC40NjQgNy4wNzQgMjAuMDQgMTEuODg4IDQxLjQyMyAyMC41ODIgNjQuMTEyIDI1LjUzIDEzLjY3NyAyLjk4MyAyNy43MSA0LjQ1OCA0MS42NDUgNi4wODUgNy44Ni45MTggMTUuOTIyIDEuNjI0IDIzLjc1OS45MjYgMTYuMTE2LTEuNDM3IDMyLjI5Ni0zLjA3NyA0OC4xNjQtNi4xMThhMTkwLjMgMTkwLjMgMCAwIDAgNTUuMzEtMTkuODM4YzE0LjEwNi03LjY5NSAyNy43OTUtMTYuNDA5IDQwLjY5NC0yNS45ODQgMTAuOTQ1LTguMTI1IDIxLjE4Ny0xNy40NzggMzAuNTE4LTI3LjQzNCAxOS4yMjMtMjAuNTEyIDM0LjM3NC00My44MSA0NS41MTMtNjkuOCA3LjQxMS0xNy4yOTQgMTIuOTA3LTM1LjA4OCAxNi40Ni01My40NDMgMS45MjQtOS45NDQgMy4wNjgtMjAuMTk2IDMuMTI4LTMwLjMxNy41NTUtOTQuMjMzLjc5OC0xODguNDY4IDEuMTMtMjgyLjcwMy4wMDQtLjgyLjAwMS0xLjYzOC4wMDEtMy4xODQgMjcuNTAyIDE5LjIyNyA1Ni43MDggMzMuOTUzIDg4LjIyNCA0My45NzIgMzEuNTcyIDEwLjAzNyA2My45MSAxNS4wNjggOTcuNzc1IDE1LjEyNGwtLjAwMS0xMDQuNDc0YzQuNTEuMTU0IDkuMDEyLjg1NyAxMy41MzYgMS4zNTcgOC4zMDUuOTE5IDE2LjYyMyAxLjcxOSAyNS4yMDkgMi41OTZ2MTM0LjUxOWMtNjcuNzA1LS4wMi0xMjkuMDkzLTIwLjA2Ni0xODUuNzQzLTU4Ljk4NXY2Ljc0NGMwIDkwLjE0NS4yNCAxODAuMjktLjE3NyAyNzAuNDMzLS4wOTggMjEuMjUxLTIuOTk2IDQyLjQ2Ni05LjkwMiA2Mi43My01LjAyIDE0LjczLTEwLjQ1IDI5LjQxMi0xNy4wMzEgNDMuNDkyLTguMzE0IDE3Ljc4OS0xOS42OTYgMzMuNzU4LTMyLjUyNSA0OC43MDItMTkuNDcyIDIyLjY4LTQyLjE5NCA0MS4yNTktNjguMjk3IDU1LjgwNy0yMy45NTUgMTMuMzUyLTQ5LjM5NSAyMi4yOS03Ni40MDQgMjcuMDU5LTE4LjI3OCAzLjIyNy0zNi42NzQgNC43MjItNTUuMTQ1IDMuNzk2LTE5LjEwMy0uOTU4LTM3Ljk4NC00LjAwOS01Ni40MTUtOS4zMi0yNC45LTcuMTc2LTQ4LjQ1LTE3LjU1NS02OS41My0zMi42MTEtMTMuNDM0LTkuNTk2LTI1LjY3LTIwLjg2OS0zOC40NzItMzEuNzM1IiBzdHlsZT0iLS1kYXJrcmVhZGVyLWlubGluZS1maWxsOnZhcigtLWRhcmtyZWFkZXItYmFja2dyb3VuZC1mZTJjNTUsICNiMTAxMjMpOy0tZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO29wYWNpdHk6MSIgZGF0YS1pbmRleD0iOCIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik03MzguNzE4IDIyNi40NDhjLTMwLjY3NS40MzEtNjEuNDg4LjQ5Mi05Mi4zMDEuNTUxLTEuOTU4LjAwNC0zLjkxNi4wMDEtNi40MTcuMDAxdjUuMjg4YzAgMTUuMTU5LjAyNiAzMC4zMTgtLjAwNCA0NS40NzYtLjMyNyAxNjQuMTg2LS40OTIgMzI4LjM3Mi0xLjIyIDQ5Mi41NTYtLjA4NyAxOS42NTQtNy42NjMgMzcuODQzLTE4LjkzOCA1My45ODItMTcuNjk2IDI1LjMyOS00MS43NTMgNDEuNzMtNzIuMTkzIDQ3LjUxNi0zMC4wNzQgNS43MTYtNTcuOTktLjgwNC04My4zOTYtMTcuNzkzLTEwLjE5OS02LjgyLTE5LjA4LTE1LjExNS0yNi4yMzctMjUuNTE0IDcuODY4IDIuMzU2IDE1LjYxNiA1LjU3MyAyMy42NTYgNy42NjIgMjUuMTc0IDYuNTQxIDQ5LjU0NiAzLjgzMiA3My4xMzItNy4xMTMgMjQuODQzLTExLjUyOCA0My4wODUtMjkuNzUzIDU0Ljc1My01NC4zMzMgNi41ODMtMTMuODcgMTAuNDgtMjguNTgyIDEwLjQ3Ni00NC4xODQtLjA0Ni0xNzcuMDc5LS4wMjktMzU0LjE1Ny0uMDI5LTUzMS4yMzZ2LTYuMDg1aDEzNS4wODdjMS4xODIgMTEuMTE1IDIuMzM3IDIxLjk4NSAzLjYzIDMzLjIyNiIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLWJhY2tncm91bmQtMjVmNGVlLCAjMDliM2IwKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lO29wYWNpdHk6MSIgZGF0YS1pbmRleD0iOSIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik01MjEuOTIgNTEzLjM2N2MtMjcuNDcyIDEuMTk4LTU0LjA0NyA2LjE2My03OS44NzUgMTUuNS0yMy40NjIgOC40ODItNDUuMTg4IDIwLjE1NS02NC42OTUgMzUuNTU0LTExLjY0MyA5LjE5MS0yMi44ODQgMTkuMTQ5LTMyLjk2OSAzMC4wMDMtMjEuMTYzIDIyLjc3OS0zNy4yMzQgNDguOTM4LTQ4LjQzOCA3OC4wNjQtNi40ODMgMTYuODU0LTExLjY2MyAzNC4xNjctMTMuNjAyIDUxLjk4NS0yLjAxOCAxOC41NDYtMy4wNTkgMzcuNDMtMi4wMDggNTYuMDI0IDEuNjA0IDI4LjM5MyA4LjkxNCA1NS43MjIgMjAuNjggODEuNzg4IDkuMzY3IDIwLjc1MiAyMS4wOTIgMzkuOTQ1IDM1Ljk0MyA1Ny4yMiAxLjI4IDEuNDg4IDIuMzA3IDMuMTkzIDMuMjc2IDUuMTM3LTQuMzYyLTIuNjYtOC43OTItNS4zNzQtMTIuNjkzLTguNzA2LTIyLjUwMS0xOS4yMi00MC44ODUtNDEuODMyLTU1LjUyNS02Ny41NjMtMTMuODE3LTI0LjI4NC0yMi45MzgtNTAuMjgtMjcuOC03Ny42NTgtMi4xMTMtMTEuOTAzLTIuMzgyLTI0LjE1NS0zLjE0Mi0zNi4yNy0uNDA2LTYuNDY4LS41OTItMTMuMDMzLjAzNS0xOS40NjUgMS4zNDYtMTMuOCAyLjQwOC0yNy43MjEgNS4xNzMtNDEuMjY5IDQuNzA5LTIzLjA3NCAxMy4zMTktNDQuODcgMjQuNjc4LTY1LjU1NSA4LjAwNS0xNC41NzcgMTcuMTc4LTI4LjM1IDI4LjI0Mi00MC43NjQgNi44MzQtNy42NjggMTMuODI0LTE1LjI1MyAyMS4zMDktMjIuMjcyIDIxLjc5NS0yMC40NCA0Ni43NC0zNS45MjcgNzQuNDM0LTQ3LjI3NyAxNi4yNTUtNi42NjIgMzIuOTU2LTExLjc1OSA1MC4xNDItMTQuNTQgMTQuMjI2LTIuMzAyIDI4LjgxMy0yLjc3IDQzLjI2NC0zLjE0NyAxMS4xMDctLjI5IDIyLjI1LjgyOSAzMy42MzYgMS4zMzMgMCAxMS4xNDUgMCAyMS4yODUtLjA2NCAzMS44NzgiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci1iYWNrZ3JvdW5kLTI1ZjRlZSwgIzA5YjNiMCk7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6dmFyKC0tZGFya3JlYWRlci10ZXh0LTBkOTlmZiwgIzIzYTNmZik7b3BhY2l0eToxIiBkYXRhLWluZGV4PSIxMCIvPgogIDxwYXRoIGZpbGw9IiNGRTJDNTUiIGQ9Ik00MzQuOTkyIDgyNS40ODJjLTE4LjE3LTkuMTQ2LTMyLjI2LTIzLjAyOS00Mi45Mi00MC4zODItOC4xOC0xMy4zMTgtMTMuMTItMjcuNzc2LTE1LjE5LTQzLjM5NC0zLjM1OS0yNS4zNDYgMS40OS00OS4wMjkgMTQuMjc4LTcwLjgxNSAxNC43MDUtMjUuMDUgMzYuMjE4LTQyLjExNiA2NC4xMDYtNTAuODggMTMuMTUtNC4xMzIgMjYuNjQ0LTYuMDA5IDQwLjMyLTQuODU1IDguNjk2LjczNCAxNy4yNjggMi45MzcgMjYuNDQyIDQuNTggMC0zNC44NTMgMC02OS43NjguMDgtMTA1LjEzNSAxMi43NzMtLjA3IDI1LjQ2Ni4zMTMgMzguNTE0LjcwNnYxMzguNDY4Yy01My42NC0xMy4zMy05Ny44MyAxLjgyNy0xMjcuODkgNDcuODc4LTI2LjM0NyA0MC4zNjYtMjMuOTIxIDgyLjc3IDIuMjYgMTIzLjgyOSIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLWJhY2tncm91bmQtZmUyYzU1LCAjYjEwMTIzKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lO29wYWNpdHk6MSIgZGF0YS1pbmRleD0iMTEiLz4KICA8cGF0aCBmaWxsPSIjRkUyQzU1IiBkPSJNODE1Ljk0MiAzNDQuNzc0Yy0zLjY3Ni0xLjg2LTcuNjc4LTMuNjYtMTAuOTAzLTYuMzgtMTguMzQ1LTE1LjQ3OC0zMy44MDgtMzMuMzM3LTQ1LjY3Ni01NC4zODUtOS42OTItMTcuMTg3LTE1LjgxMS0zNS41NC0yMC4zMzQtNTQuNTY3LS4xMTItLjQ3MS0uMDA1LS45OTUuMDgtMS44OTQgMTEuODQ5LS4zOTkgMjMuNjE4LS4zOTkgMzUuMzY5LS4zOTkuNTIgNi42NCAxLjAxNiAxMy4wMDYgMS41MiAxOS4zNzMgMS4zNTYgMTcuMTYgNS42NyAzMy41OTUgMTIuMDA0IDQ5LjU1NiA2LjE1IDE1LjQ5OCAxNC40MjYgMjkuNzk4IDI0LjExNCA0My4zMTcgMS4yMjcgMS43MTMgMi41NyAzLjM0NCAzLjgyNiA1LjM3OSIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtZmlsbDp2YXIoLS1kYXJrcmVhZGVyLWJhY2tncm91bmQtZmUyYzU1LCAjYjEwMTIzKTstLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTpub25lIiBkYXRhLWluZGV4PSIxMiIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik05MjEuMDMgNDA4LjYxOWMtOS45ODUtMi41Ny0yMC00LjkzMi0yOS41MTUtOC41OTgtMTkuMjMtNy40MDgtMzYuNjczLTE3Ljk5My01MS45MjMtMzEuOTY0LTUuMjE4LTQuNzgtMTAuMTc3LTkuODQ0LTE1LjAzMy0xNS4xNDEgMTEgNC43OTIgMjEuNTg3IDEwLjQwMyAzMi42MDQgMTQuOTgxIDEyLjg2NyA1LjM0NyAyNi4zODggOC42NiA0MC4zMDYgMTAuMTA1IDcuOTcxLjgyOCAxNS45NCAxLjY3IDIzLjc1OSAyLjQ5IDAgOS4wODYgMCAxOC40MTUtLjE5NyAyOC4xMjciIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci10ZXh0LTI1ZjRlZSwgIzM2ZjVlZik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6dmFyKC0tZGFya3JlYWRlci10ZXh0LTBkOTlmZiwgIzIzYTNmZik7b3BhY2l0eToxIiBkYXRhLWluZGV4PSIxMyIvPgogIDxwYXRoIGZpbGw9IiNGRTJDNTUiIGQ9Ik04MTcuODI5IDM0Ni42OTJhMy41IDMuNSAwIDAgMS0yLjE5Mi0xLjMwNWMuODY4LjEyNCAxLjUyNy41NTkgMi4xOTIgMS4zMDUiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci10ZXh0LWZlMmM1NSwgI2ZlMzk1Zik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6bm9uZSIgZGF0YS1pbmRleD0iMTQiLz4KICA8cGF0aCBmaWxsPSIjMjVGNEVFIiBkPSJNMzQwLjA0MiA5MjUuMjM3Yy42MTQuMTQ4IDEuMjU2LjU5IDEuODQ1IDEuMzk5LS42NC0uMTI1LTEuMjMtLjYxNS0xLjg0NS0xLjQiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci10ZXh0LTI1ZjRlZSwgIzM2ZjVlZik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6bm9uZTt2aXNpYmlsaXR5OnZpc2libGUiIGRhdGEtaW5kZXg9IjE1Ii8+CiAgPHBhdGggZmlsbD0iI0ZFMkM1NSIgZD0iTTM0OS4xMDggOTMzLjQ0M2EzLjY2IDMuNjYgMCAwIDEgMi4yMiAxLjEzOGMtLjgyNi0uMDYyLTEuNDY4LS40Ni0yLjIyLTEuMTM4IiBzdHlsZT0iLS1kYXJrcmVhZGVyLWlubGluZS1maWxsOnZhcigtLWRhcmtyZWFkZXItdGV4dC1mZTJjNTUsICNmZTM5NWYpOy0tZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlIiBkYXRhLWluZGV4PSIxNiIvPgogIDxwYXRoIGZpbGw9IiMyNUY0RUUiIGQ9Ik04MjQuMjQ4IDM1MS45MTFjLS40NjcuMTg4LTEuMTUuMDY0LTIuMDQtLjQwMi40Ny0uMTk4IDEuMTQ3LS4wNTMgMi4wNC40MDIiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6dmFyKC0tZGFya3JlYWRlci10ZXh0LTI1ZjRlZSwgIzM2ZjVlZik7LS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6bm9uZSIgZGF0YS1pbmRleD0iMTciLz4KPC9zdmc+Cg=="; const emailIcon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEzLjMzMzUgMi42NjY1SDIuNjY2ODNDMS45MzA0NSAyLjY2NjUgMS4zMzM1IDMuMjYzNDYgMS4zMzM1IDMuOTk5ODRWMTEuOTk5OEMxLjMzMzUgMTIuNzM2MiAxLjkzMDQ1IDEzLjMzMzIgMi42NjY4MyAxMy4zMzMySDEzLjMzMzVDMTQuMDY5OSAxMy4zMzMyIDE0LjY2NjggMTIuNzM2MiAxNC42NjY4IDExLjk5OThWMy45OTk4NEMxNC42NjY4IDMuMjYzNDYgMTQuMDY5OSAyLjY2NjUgMTMuMzMzNSAyLjY2NjVaIiBzdHJva2U9IiMzMzg1RkYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTQuNjY2OCA0LjY2NjVMOC42ODY4MyA4LjQ2NjVDOC40ODEwMSA4LjU5NTQ1IDguMjQzMDQgOC42NjM4NCA4LjAwMDE2IDguNjYzODRDNy43NTcyOCA4LjY2Mzg0IDcuNTE5MzEgOC41OTU0NSA3LjMxMzUgOC40NjY1TDEuMzMzNSA0LjY2NjUiIHN0cm9rZT0iIzMzODVGRiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPgo="; const phoneIcon =