From cf175de3cfd99bef341ae84bbdb648aa01193936 Mon Sep 17 00:00:00 2001 From: JCNoguera <88061365+VmMad@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:03:42 +0200 Subject: [PATCH 01/11] fix(apps-ui-kit): text overflowing tooltip (#2737) * fix: tooltip overflowing text * fix: words overflow in DisplayStats * fix: make tooltip take the max width if possible --- apps/ui-kit/src/lib/components/atoms/tooltip/Tooltip.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/ui-kit/src/lib/components/atoms/tooltip/Tooltip.tsx b/apps/ui-kit/src/lib/components/atoms/tooltip/Tooltip.tsx index db0e6d878ed..467aa94d316 100644 --- a/apps/ui-kit/src/lib/components/atoms/tooltip/Tooltip.tsx +++ b/apps/ui-kit/src/lib/components/atoms/tooltip/Tooltip.tsx @@ -22,12 +22,12 @@ export function Tooltip({ {children} ); From 1c21f106b3e416c049f702bfd334b54eeb4421d0 Mon Sep 17 00:00:00 2001 From: JCNoguera <88061365+VmMad@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:09:18 +0200 Subject: [PATCH 02/11] feat(explorer): rebrand validators page table (#2428) * feat(explorer): rebrand validators page table * feat(explorer): Epoch page. Validators. Cherry picked. Signed-off-by: Eugene Panteleymonchuk * feat(explorer): Epoch page. Validators. Merge with another PR. Signed-off-by: Eugene Panteleymonchuk * feat(explorer): Epoch page. Validators. Merge with another PR. Signed-off-by: Eugene Panteleymonchuk * feat(explorer): Epoch page. Validators. Merge with another PR. Improve TopValidatorsCard. Signed-off-by: Eugene Panteleymonchuk * feat(explorer): Epoch page. Validators. Remove isRenderCell meta. Signed-off-by: Eugene Panteleymonchuk * feat(explorer): Epoch page. Validators. Remove unused file. Signed-off-by: Eugene Panteleymonchuk * fix: update imports * feat(explorer): Epoch page. Validators. PR updates. Signed-off-by: Eugene Panteleymonchuk * feat(explorer): Epoch page. Validators. Change default tab. Signed-off-by: Eugene Panteleymonchuk * fix: update table to not have duplicated `` * fix: validators accent color * fix: remove unnecessary number in epoch validator table * fix: replace import path * feat: improve validators table * fix: add missing number column --------- Signed-off-by: Eugene Panteleymonchuk Co-authored-by: Eugene Panteleymonchuk --- .../top-validators-card/StakeColumn.tsx | 19 ++++---- .../utils/generateValidatorsTableColumns.tsx | 47 +++++++++++++++++-- .../src/pages/validators/Validators.tsx | 12 +++++ .../molecules/table-cell/TableCell.tsx | 4 -- 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/apps/explorer/src/components/top-validators-card/StakeColumn.tsx b/apps/explorer/src/components/top-validators-card/StakeColumn.tsx index 486f11af235..1da7d3bc7a4 100644 --- a/apps/explorer/src/components/top-validators-card/StakeColumn.tsx +++ b/apps/explorer/src/components/top-validators-card/StakeColumn.tsx @@ -2,9 +2,9 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +import { TableCellText } from '@iota/apps-ui-kit'; import { useFormatCoin, CoinFormat, formatBalance } from '@iota/core'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; -import { Text } from '@iota/ui'; type StakeColumnProps = { stake: bigint | number | string; @@ -19,16 +19,13 @@ export function StakeColumn({ }: StakeColumnProps): JSX.Element { const coinFormat = hideCoinSymbol ? CoinFormat.FULL : CoinFormat.ROUNDED; const [amount, symbol] = useFormatCoin(stake, IOTA_TYPE_ARG, coinFormat); + + const label = inNano ? formatBalance(stake, 0, coinFormat) : amount; + const supportingLabel = inNano ? 'nano' : symbol; + return ( -
- - {inNano ? formatBalance(stake, 0, coinFormat) : amount} - - {!hideCoinSymbol && ( - - {inNano ? 'nano' : symbol} - - )} -
+ + {label} + ); } diff --git a/apps/explorer/src/lib/ui/utils/generateValidatorsTableColumns.tsx b/apps/explorer/src/lib/ui/utils/generateValidatorsTableColumns.tsx index 6875e772638..e175ba717d1 100644 --- a/apps/explorer/src/lib/ui/utils/generateValidatorsTableColumns.tsx +++ b/apps/explorer/src/lib/ui/utils/generateValidatorsTableColumns.tsx @@ -8,6 +8,7 @@ import { type ApyByValidator, formatPercentageDisplay } from '@iota/core'; import { ampli, getValidatorMoveEvent, VALIDATOR_LOW_STAKE_GRACE_PERIOD } from '~/lib'; import { StakeColumn, ValidatorLink, ImageIcon } from '~/components'; import type { IotaEvent, IotaValidatorSummary } from '@iota/iota-sdk/dist/cjs/client'; +import clsx from 'clsx'; interface generateValidatorsTableColumnsArgs { atRiskValidators: [string, string][]; @@ -16,9 +17,16 @@ interface generateValidatorsTableColumnsArgs { limit?: number; showValidatorIcon?: boolean; includeColumns?: string[]; + highlightValidatorName?: boolean; } -function ValidatorWithImage({ validator }: { validator: IotaValidatorSummary }) { +function ValidatorWithImage({ + validator, + highlightValidatorName, +}: { + validator: IotaValidatorSummary; + highlightValidatorName?: boolean; +}) { return ( - {validator.name} + + {validator.name} + } /> @@ -50,8 +64,20 @@ export function generateValidatorsTableColumns({ rollingAverageApys = null, showValidatorIcon = true, includeColumns, + highlightValidatorName, }: generateValidatorsTableColumnsArgs): ColumnDef[] { let columns: ColumnDef[] = [ + { + header: '#', + id: 'number', + cell({ row }) { + return ( + + {row.index + 1} + + ); + }, + }, { header: 'Name', id: 'name', @@ -59,9 +85,22 @@ export function generateValidatorsTableColumns({ return ( {showValidatorIcon ? ( - + ) : ( - {validator.name} + + + {validator.name} + + )} ); diff --git a/apps/explorer/src/pages/validators/Validators.tsx b/apps/explorer/src/pages/validators/Validators.tsx index 52255bff5c2..3d2baa90aae 100644 --- a/apps/explorer/src/pages/validators/Validators.tsx +++ b/apps/explorer/src/pages/validators/Validators.tsx @@ -80,6 +80,18 @@ function ValidatorPageResult(): JSX.Element { atRiskValidators: data.atRiskValidators, validatorEvents, rollingAverageApys: validatorsApy || null, + highlightValidatorName: true, + includeColumns: [ + '#', + 'Name', + 'Stake', + 'Proposed next Epoch gas price', + 'APY', + 'Comission', + 'Last Epoch Rewards', + 'Voting Power', + 'Status', + ], }); }, [data, validatorEvents, validatorsApy]); diff --git a/apps/ui-kit/src/lib/components/molecules/table-cell/TableCell.tsx b/apps/ui-kit/src/lib/components/molecules/table-cell/TableCell.tsx index 6b9d7e7a345..b12b9f83140 100644 --- a/apps/ui-kit/src/lib/components/molecules/table-cell/TableCell.tsx +++ b/apps/ui-kit/src/lib/components/molecules/table-cell/TableCell.tsx @@ -16,10 +16,6 @@ interface TableCellBaseProps { * Whether the cell content should be centered. */ isContentCentered?: boolean; - /** - * Whether to not wrap the text in the cell. - */ - noWrap?: boolean; } export function TableCellBase({ From 129bd743eac9a612b5165ec803b3b1a55e10e797 Mon Sep 17 00:00:00 2001 From: cpl121 <100352899+cpl121@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:40:24 +0200 Subject: [PATCH 03/11] refactor(wallet): modify version format from `date.patch` to `x.y.z.n` (#2775) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(wallet): modify the wallet version format * refactor: update versioning logic to allow for RCs * refactor: rename beta to rc * refactor: improve naming * chore: rename WALLET_BETA to WALLET_RC * chore: update version number formatting * chore: rename beta to rc --------- Co-authored-by: Begoña Alvarez --- .../configs/webpack/webpack.config.common.ts | 40 +++++++++---------- apps/wallet/package.json | 3 +- .../src/shared/experimentation/features.ts | 2 +- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/apps/wallet/configs/webpack/webpack.config.common.ts b/apps/wallet/configs/webpack/webpack.config.common.ts index d039a578a49..8ef3c5cfa15 100644 --- a/apps/wallet/configs/webpack/webpack.config.common.ts +++ b/apps/wallet/configs/webpack/webpack.config.common.ts @@ -16,24 +16,8 @@ import type { Configuration } from 'webpack'; import packageJson from '../../package.json'; -function generateDateVersion(patch: number) { - const sha = gitRevSync.short(); - const date = new Date(); - const version = [ - String(date.getUTCFullYear()).slice(2), - String(date.getUTCMonth() + 1), - String(date.getUTCDate()), - patch, - ].join('.'); - - return { - version, - version_name: `${version} (${sha})`, - }; -} - -const WALLET_BETA = process.env.WALLET_BETA === 'true'; -const PATCH_VERSION = Number(process.env.PATCH_VERSION) || 0; +const WALLET_RC = process.env.WALLET_RC === 'true'; +const RC_VERSION = WALLET_RC ? Number(process.env.RC_VERSION) || 0 : undefined; const SDK_ROOT = resolve(__dirname, '..', '..', '..', '..', 'sdk'); const PROJECT_ROOT = resolve(__dirname, '..', '..'); @@ -44,12 +28,26 @@ const TS_CONFIGS_ROOT = resolve(CONFIGS_ROOT, 'ts'); const IS_DEV = process.env.NODE_ENV === 'development'; const IS_PROD = process.env.NODE_ENV === 'production'; const TS_CONFIG_FILE = resolve(TS_CONFIGS_ROOT, `tsconfig.${IS_DEV ? 'dev' : 'prod'}.json`); -const APP_NAME = WALLET_BETA ? 'IOTA Wallet (BETA)' : IS_DEV ? 'IOTA Wallet (DEV)' : 'IOTA Wallet'; +const APP_NAME = WALLET_RC ? 'IOTA Wallet (RC)' : IS_DEV ? 'IOTA Wallet (DEV)' : 'IOTA Wallet'; dotenv.config({ path: [resolve(SDK_ROOT, '.env'), resolve(SDK_ROOT, '.env.defaults')], }); +// From package.json: x.y.z +// App version (production): x.y.z +// App version (rc): x.y.z.n +function generateVersion(n: number | undefined) { + const sha = gitRevSync.short(); + const packageVersion = packageJson.version; + const version = n !== undefined ? `${packageVersion}.${n}` : packageVersion; + const improved_version = n !== undefined ? `${packageVersion}-rc.${n}` : packageVersion; + return { + version, + version_name: `${improved_version} (${sha})`, + }; +} + function loadTsConfig(tsConfigFilePath: string) { return new Promise((res, rej) => { exec( @@ -99,7 +97,7 @@ async function generateAliasFromTs() { const commonConfig: () => Promise = async () => { const alias = await generateAliasFromTs(); - const walletVersionDetails = generateDateVersion(PATCH_VERSION); + const walletVersionDetails = generateVersion(RC_VERSION); const sentryAuthToken = process.env.SENTRY_AUTH_TOKEN; return { context: SRC_ROOT, @@ -218,7 +216,7 @@ const commonConfig: () => Promise = async () => { 'process.env.WALLET_KEYRING_PASSWORD': JSON.stringify( IS_DEV ? 'DEV_PASS' : Buffer.from(randomBytes(64)).toString('hex'), ), - 'process.env.WALLET_BETA': WALLET_BETA, + 'process.env.WALLET_RC': WALLET_RC, 'process.env.APP_NAME': JSON.stringify(APP_NAME), 'process.env.DEFAULT_NETWORK': JSON.stringify(process.env.DEFAULT_NETWORK), 'process.env.IOTA_NETWORKS': JSON.stringify(process.env.IOTA_NETWORKS), diff --git a/apps/wallet/package.json b/apps/wallet/package.json index 02b03ae7cb8..2a50ed39fb3 100644 --- a/apps/wallet/package.json +++ b/apps/wallet/package.json @@ -1,5 +1,6 @@ { "name": "iota-wallet", + "version": "0.1.0", "private": true, "description": "A wallet for IOTA", "main": "./dist/ui.js", @@ -7,7 +8,7 @@ "build": "pnpm build:prod", "build:prod": "cross-env NODE_ENV=\"production\" TS_NODE_PROJECT=\"./configs/ts/tsconfig.webpack.json\" webpack --progress", "build:dev": "cross-env NODE_ENV=\"development\" TS_NODE_PROJECT=\"./configs/ts/tsconfig.webpack.json\" webpack --progress", - "build:beta": "cross-env WALLET_BETA=true NODE_ENV=\"production\" TS_NODE_PROJECT=\"./configs/ts/tsconfig.webpack.json\" webpack --progress", + "build:rc": "cross-env WALLET_RC=true NODE_ENV=\"production\" TS_NODE_PROJECT=\"./configs/ts/tsconfig.webpack.json\" webpack --progress", "prettier:check": "prettier -c --ignore-unknown --ignore-path=../../.prettierignore --ignore-path=.prettierignore .", "prettier:fix": "prettier -w --ignore-unknown --ignore-path=../../.prettierignore --ignore-path=.prettierignore .", "prettier:fix:watch": "onchange '**' -i -f add -f change -j 5 -- prettier -w --ignore-unknown --ignore-path=../../.prettierignore --ignore-path=.prettierignore {{file}}", diff --git a/apps/wallet/src/shared/experimentation/features.ts b/apps/wallet/src/shared/experimentation/features.ts index 1078472a2a1..22e2eaffaea 100644 --- a/apps/wallet/src/shared/experimentation/features.ts +++ b/apps/wallet/src/shared/experimentation/features.ts @@ -60,7 +60,7 @@ export function setAttributes(network?: { network: Network; customRpc?: string | growthbook.setAttributes({ network: activeNetwork, version: Browser.runtime.getManifest().version, - beta: process.env.WALLET_BETA || false, + rc: process.env.WALLET_RC || false, }); } From 27e2f51f929128f4a5bd4b24287c20e6e36b013a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bego=C3=B1a=20=C3=81lvarez=20de=20la=20Cruz?= Date: Mon, 23 Sep 2024 16:06:48 +0200 Subject: [PATCH 04/11] refactor: update wallet development key (#2809) --- apps/wallet/configs/webpack/webpack.config.common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wallet/configs/webpack/webpack.config.common.ts b/apps/wallet/configs/webpack/webpack.config.common.ts index 8ef3c5cfa15..3645f4e838a 100644 --- a/apps/wallet/configs/webpack/webpack.config.common.ts +++ b/apps/wallet/configs/webpack/webpack.config.common.ts @@ -198,7 +198,7 @@ const commonConfig: () => Promise = async () => { description: packageJson.description, ...(IS_DEV ? { - key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2HTQu/66edl64fM/SKDnHJmCz9SIYqM/QK7NM3vD1LTE2UNXzHX5Clj8geuoWAYS6HE/aFcd//qPnAh8TnPgqTS3IX+IbZsY/+kcokxIEWHly3eKEHWB32tQsGdJx6tgDzx8TRkFZEcCCdE4pFqQO68W3I/+8AQPosdd5fsIoF6OGKZ/i29mpGkYJSmMroCN5zYCQqvpjTBIkiTkI9TTjxmBid77pHyG4TsHz0wda4KxHV9ZtzZQXB4vexTku/Isczdtif7pDqFEDCAqEkpiGPyKoIuqrxc75IfpzIGFsIylycBr0fZellSsl2M6FM34R99/vUrGj5iWcjNmhYvZ8QIDAQAB', + key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1uOw+go7YTI8xNuHzIQC7J7x6Jw0in2UPptgcR1c+7aA3XSb03TVZkMXSMslCB7KTpaJOOQK1urLN3/FFos55f3vXixsjHT3pVbX/FhUmBK0kLOx8kl5Ns9ywVgBJyCSEnMrR1IlbiiU8GoXH1Bzb4SxkDELSQIZRetd+zTnwsUx/74grPT4EmgVglHBBYO75iJMsiJ/F0zMEsEQ+0SDfmU0v5Qh8slzryZr+8p8Q/mpNADzwS51o74feeGC5nAM5IgX0LyjRzCLXsiEmdC57KOaCpI7yhzDjXpye374oTdZJulv/tVeA1ymLIKQM5rfyeoqnxSOMsgGsvoM60WoYQIDAQAB', } : undefined), }; From 794729c2bd53f5b18d139d53581de3cddc411a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bego=C3=B1a=20=C3=81lvarez=20de=20la=20Cruz?= Date: Mon, 23 Sep 2024 16:17:21 +0200 Subject: [PATCH 05/11] chore: remove identity permission from manifest (#2801) --- apps/wallet/src/manifest/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wallet/src/manifest/manifest.json b/apps/wallet/src/manifest/manifest.json index a3bd5bf8ec2..f43d531e439 100644 --- a/apps/wallet/src/manifest/manifest.json +++ b/apps/wallet/src/manifest/manifest.json @@ -6,7 +6,7 @@ "background": { "service_worker": "background.js" }, - "permissions": ["storage", "tabs", "alarms", "unlimitedStorage", "identity"], + "permissions": ["storage", "tabs", "alarms", "unlimitedStorage"], "action": { "default_popup": "ui.html?type=popup" }, From edf4cf15c368c98bd77cb5d01c00def402e5c939 Mon Sep 17 00:00:00 2001 From: Dr-Electron Date: Mon, 23 Sep 2024 21:06:07 +0200 Subject: [PATCH 06/11] feat(docs): Add reference doc for importing code with examples (#2381) --- .../developer/cryptography/on-chain/ecvrf.mdx | 15 +---- .../cryptography/on-chain/groth16.mdx | 18 +---- .../cryptography/on-chain/hashing.mdx | 46 +------------ .../developer/evm-to-move/creating-nft.mdx | 67 +------------------ .../developer/evm-to-move/creating-token.mdx | 31 +-------- .../developer/getting-started/build-test.mdx | 18 ++--- .../getting-started/create-a-module.mdx | 59 +++------------- .../getting-started/create-a-package.mdx | 53 ++------------- .../contribute/import-code-docs.mdx | 48 +++++++++++++ docs/content/sidebars/references.js | 1 + docs/examples/move/cryptography/Move.toml | 10 +++ .../move/cryptography/sources/ecvrf.move | 13 ++++ .../move/cryptography/sources/goth16.move | 16 +++++ .../cryptography/sources/hashing_iota.move | 18 +++++ .../cryptography/sources/hashing_std.move | 18 +++++ docs/examples/move/evm-to-move/Move.toml | 10 +++ .../evm-to-move/sources/coin_manager.move | 35 ++++++++++ .../move/evm-to-move/sources/nft.move | 65 ++++++++++++++++++ .../move/evm-to-move/sources/token.move | 24 +++++++ examples/move/first_package/Move.toml | 30 ++++++++- .../{example.move => first_package.move} | 22 +++++- 21 files changed, 335 insertions(+), 282 deletions(-) create mode 100644 docs/content/references/contribute/import-code-docs.mdx create mode 100644 docs/examples/move/cryptography/Move.toml create mode 100644 docs/examples/move/cryptography/sources/ecvrf.move create mode 100644 docs/examples/move/cryptography/sources/goth16.move create mode 100644 docs/examples/move/cryptography/sources/hashing_iota.move create mode 100644 docs/examples/move/cryptography/sources/hashing_std.move create mode 100644 docs/examples/move/evm-to-move/Move.toml create mode 100644 docs/examples/move/evm-to-move/sources/coin_manager.move create mode 100644 docs/examples/move/evm-to-move/sources/nft.move create mode 100644 docs/examples/move/evm-to-move/sources/token.move rename examples/move/first_package/sources/{example.move => first_package.move} (86%) diff --git a/docs/content/developer/cryptography/on-chain/ecvrf.mdx b/docs/content/developer/cryptography/on-chain/ecvrf.mdx index 308dedaf339..9c11bbaf24d 100644 --- a/docs/content/developer/cryptography/on-chain/ecvrf.mdx +++ b/docs/content/developer/cryptography/on-chain/ecvrf.mdx @@ -46,20 +46,7 @@ Output: 2b7e45821d80567761e8bb3fc519efe5ad80cdb4423227289f960319bbcf6eea1aef30c0 You can verify the proof and output in a smart contract using `iota::ecvrf::ecvrf_verify` from the IOTA Move framework: -```move -module math::ecvrf_test { - use iota::ecvrf; - use iota::event; - - /// Event on whether the output is verified - public struct VerifiedEvent has copy, drop { - is_verified: bool, - } - - public fun verify_ecvrf_output(output: vector, alpha_string: vector, public_key: vector, proof: vector) { - event::emit(VerifiedEvent {is_verified: ecvrf::ecvrf_verify(&output, &alpha_string, &public_key, &proof)}); - } -} +```move file=/docs/examples/move/cryptography/sources/ecvrf.move ``` You can also use the CLI tool for verification: diff --git a/docs/content/developer/cryptography/on-chain/groth16.mdx b/docs/content/developer/cryptography/on-chain/groth16.mdx index caa0a19bc7b..11e70a203ea 100644 --- a/docs/content/developer/cryptography/on-chain/groth16.mdx +++ b/docs/content/developer/cryptography/on-chain/groth16.mdx @@ -104,21 +104,5 @@ proof.c.serialize_compressed(&mut proof_points_bytes).unwrap(); The following example smart contract prepares a verification key and verifies the corresponding proof. This example uses the BN254 elliptic curve construction, which is given as the first parameter to the `prepare_verifying_key` and `verify_groth16_proof` functions. You can use the `bls12381` function instead for BLS12-381 construction. -```rust -module test::groth16_test { - use iota::groth16; - use iota::event; - - /// Event on whether the proof is verified - struct VerifiedEvent has copy, drop { - is_verified: bool, - } - - public fun verify_proof(vk: vector, public_inputs_bytes: vector, proof_points_bytes: vector) { - let pvk = groth16::prepare_verifying_key(&groth16::bn254(), &vk); - let public_inputs = groth16::public_proof_inputs_from_bytes(public_inputs_bytes); - let proof_points = groth16::proof_points_from_bytes(proof_points_bytes); - event::emit(VerifiedEvent {is_verified: groth16::verify_groth16_proof(&groth16::bn254(), &pvk, &public_inputs, &proof_points)}); - } -} +```move file=/docs/examples/move/cryptography/sources/goth16.move ``` diff --git a/docs/content/developer/cryptography/on-chain/hashing.mdx b/docs/content/developer/cryptography/on-chain/hashing.mdx index ed63b7d95eb..51b22f1cef7 100644 --- a/docs/content/developer/cryptography/on-chain/hashing.mdx +++ b/docs/content/developer/cryptography/on-chain/hashing.mdx @@ -16,52 +16,10 @@ The IOTA Move API supports the following cryptographic hash functions: The SHA2-256 and SHA3-256 hash functions are available in the Move Standard Library in the `std::hash` module. The following example shows how to use the SHA2-256 hash function in a smart contract: -```move -module test::hashing_std { - use std::hash; - use iota::object::{Self, UID}; - use iota::tx_context::TxContext; - use iota::transfer; - - /// Object that holds the output hash value. - public struct Output has key, store { - id: UID, - value: vector - } - - public fun hash_data(data: vector, recipient: address, ctx: &mut TxContext) { - let hashed = Output { - id: object::new(ctx), - value: hash::sha2_256(data), - }; - // Transfer an output data object holding the hashed data to the recipient. - transfer::public_transfer(hashed, recipient) - } -} +```move file=/docs/examples/move/cryptography/sources/hashing_std.move ``` The Keccak256 and Blake2b-256 hash functions are available through the `iota::hash` module in the IOTA Move Library. An example of how to use the Keccak256 hash function in a smart contract is shown below. Notice that here, the input to the hash function is given as a reference. This is the case for both Keccak256 and Blake2b-256. -```move -module test::hashing_iota { - use iota::hash; - use iota::object::{Self, UID}; - use iota::tx_context::TxContext; - use iota::transfer; - - /// Object that holds the output hash value. - public struct Output has key, store { - id: UID, - value: vector - } - - public fun hash_data(data: vector, recipient: address, ctx: &mut TxContext) { - let hashed = Output { - id: object::new(ctx), - value: hash::keccak256(&data), - }; - // Transfer an output data object holding the hashed data to the recipient. - transfer::public_transfer(hashed, recipient) - } -} +```move file=/docs/examples/move/cryptography/sources/hashing_iota.move ``` diff --git a/docs/content/developer/evm-to-move/creating-nft.mdx b/docs/content/developer/evm-to-move/creating-nft.mdx index c0dd39c9fca..0ce4713f47d 100644 --- a/docs/content/developer/evm-to-move/creating-nft.mdx +++ b/docs/content/developer/evm-to-move/creating-nft.mdx @@ -41,72 +41,7 @@ A lot of logic here is abstracted away, but every bit of logic can be overwritte IOTA Move is based on objects instead of a global storage like EVM. Objects in IOTA Move can be owned, non-fungible, and unique - the exact properties of an NFT! This makes creating NFTs in IOTA Move very trivial, given that everything is already an NFT by default. An example of a very simplified NFT in IOTA Move: -```move title="examples/Examplenft.move" -module example::examplenft { - use std::string; - use iota::event; - - /// An example NFT that can be minted by anybody - public struct ExampleNFT has key, store { - id: UID, - /// Name for the token - name: string::String, - } - - // ===== Events ===== - - public struct NFTMinted has copy, drop { - // The Object ID of the NFT - object_id: ID, - // The creator of the NFT - creator: address, - // The name of the NFT - name: string::String, - } - - // ===== Public view functions ===== - - /// Get the NFT's `name` - public fun name(nft: &ExampleNFT): &string::String { - &nft.name - } - - // ===== Entrypoints ===== - - #[allow(lint(self_transfer))] - /// Anyone can mint a new NFT on this one - public fun mint_to_sender( - name: vector, - ctx: &mut TxContext - ) { - let sender = tx_context::sender(ctx); - let nft = ExampleNFT { - id: object::new(ctx), - name: string::utf8(name) - }; - - event::emit(NFTMinted { - object_id: object::id(&nft), - creator: sender, - name: nft.name, - }); - - transfer::public_transfer(nft, sender); - } - - /// Transfer `nft` to `recipient` - public fun transfer( - nft: ExampleNFT, recipient: address, _: &mut TxContext - ) { - transfer::public_transfer(nft, recipient) - } - - /// Permanently delete `nft` - public fun burn(nft: ExampleNFT, _: &mut TxContext) { - let ExampleNFT { id, name: _} = nft; - object::delete(id) - } -} +```move file=/docs/examples/move/evm-to-move/sources/nft.move ``` This is just a simplified NFT that anyone can mint, with just a name in it as metadata. It can freely be transferred and burned, and the metadata can be directly accessed through the object from another contract. Given that this is just a normal object being created, there's little to add to this code example. It's straightforward if you know the basics of working with objects in IOTA. diff --git a/docs/content/developer/evm-to-move/creating-token.mdx b/docs/content/developer/evm-to-move/creating-token.mdx index f8597fd1903..10cbc9d4a19 100644 --- a/docs/content/developer/evm-to-move/creating-token.mdx +++ b/docs/content/developer/evm-to-move/creating-token.mdx @@ -34,33 +34,7 @@ IOTA Move takes a radically different approach regarding tokens in terms of owne The initial creation of `Coin` takes place by minting these coins. This is usually done in a smart contract (module) in IOTA Move and looks like this: -```move title="examples/Exampletoken.move" -module examples::exampletoken { - use iota::coin; - use iota::transfer; - use iota::tx_context::{Self, TxContext}; - - /// One-Time-Witness of kind: `Coin` - public struct EXAMPLETOKEN has drop {} - - fun init(witness: EXAMPLETOKEN, ctx: &mut TxContext) { - let (treasurycap, metadata) = coin::create_currency( - witness, - 6, // decimals - b"EXAMPLE", // symbol - b"Example Coin", // name - b"Just an example", // description - option::none(), // icon URL - ctx - ); - - // transfer the `TreasuryCap` to the sender, admin capabilities - transfer::public_transfer(treasurycap, tx_context::sender(ctx)); - - // metadata is typically frozen after creation - transfer::public_freeze_object(metadata); - } -} +```move file=/docs/examples/move/evm-to-move/sources/token.move ``` There's a lot to unpack here; let's look at it piece by piece. In IOTA Move, there is no 'compiler version' defined within the module itself, like in Solidity. @@ -69,8 +43,7 @@ A `module` is defined (`exampletoken`) as part of the `examples` package (module After the aliases, we see an empty struct defined: -```move -public struct EXAMPLETOKEN has drop {} +```move file=/docs/examples/move/evm-to-move/sources/token.move#L5 ``` This is the definition of the so-called [One-Time-Witness](../iota-101/move-overview/one-time-witness.mdx). Together with the `init` function definition, this ensures that this `Coin` will only be created once and never more on this chain. diff --git a/docs/content/developer/getting-started/build-test.mdx b/docs/content/developer/getting-started/build-test.mdx index 1da979c9e3f..ca5a1acf8da 100644 --- a/docs/content/developer/getting-started/build-test.mdx +++ b/docs/content/developer/getting-started/build-test.mdx @@ -11,7 +11,7 @@ Once you have [created a package](create-a-package.mdx) and [added a module](cre ## Building your package -You can use the `iota move build` command to build Move packages in the working directory, `my_first_package` in this case. +You can use the `iota move build` command to build Move packages in the working directory, `first_package` in this case. ```shell iota move build @@ -25,7 +25,7 @@ If your build is successful, the IOTA client will return the following: UPDATING GIT DEPENDENCY https://github.com/iotaledger/iota.git INCLUDING DEPENDENCY IOTA INCLUDING DEPENDENCY MoveStdlib -BUILDING my_first_package +BUILDING first_package ``` ## Test a Package @@ -34,7 +34,7 @@ You can use the Move testing framework to write unit tests for your IOTA package ### Test Syntax -You should add unit tests in their corresponding [module file](create-a-module.mdx). In Move, test functions are identified by the following: +You should add unit tests in their corresponding [test file](create-a-module.mdx). In Move, test functions are identified by the following: * They are [`public`](create-a-module.mdx#public-functions) functions. * They have no parameters. @@ -51,14 +51,14 @@ If you haven't added any tests, you should see the following output. ```shell INCLUDING DEPENDENCY Iota INCLUDING DEPENDENCY MoveStdlib -BUILDING my_first_package +BUILDING first_package Running Move unit tests Test result: OK. Total tests: 0; passed: 0; failed: 0 ``` ### Add Tests -You can add your first unit test by copying the following public test function and adding it to the end of `my_first_module`, within the module definition. +You can add your first unit test by copying the following public test function and adding it to `first_package_tests` file, within the `tests` directory. ```move #[test] @@ -120,13 +120,7 @@ The compilation error provides all the necessary information to help you [debug] Move has many features to ensure your code is safe. In this case, the `Sword` struct represents a game asset that digitally mimics a real-world item. Much like a real sword, it cannot simply disappear. Since the `Sword` struct doesn't have the [`drop`](create-a-module.mdx#drop) ability, it has to be consumed before the function returns. However, since the `sword` mimics a real-world item, you don't want to allow it to disappear. -Instead, you can fix the compilation error by adequately disposing of the `sword`. Add the following line to the beginning of the `test_sword_create` function to import the [`Transfer`](../../references/framework/iota-framework/transfer.mdx) module: - -```move -use iota::transfer; -``` - -Now, you can add the following after the function's `!assert` call to transfer the `sword` to a freshly created dummy address: +Instead, you can fix the compilation error by adequately disposing of the `sword`. Add the following after the function's `!assert` call to transfer the `sword` to a freshly created dummy address: ```move // Create a dummy address and transfer the sword. diff --git a/docs/content/developer/getting-started/create-a-module.mdx b/docs/content/developer/getting-started/create-a-module.mdx index 6b3bbd3a453..9303c9fb4f8 100644 --- a/docs/content/developer/getting-started/create-a-module.mdx +++ b/docs/content/developer/getting-started/create-a-module.mdx @@ -5,7 +5,14 @@ tags: [move, getting-started] # Create a Move Module -A [package](create-a-package.mdx)'s utility is defined by its modules. A module contains the logic for your package. You can create any number of modules per package. To add a module, create a `.move` file in the `sources` directory. For this guide, create a file called `my_module.move` and add the following content: +A [package](create-a-package.mdx)'s utility is defined by its modules. A module contains the logic for your package. You can create any number of modules per package. To add a module, create a `.move` file in the `sources` directory. For this guide, create a file called `first_package.move` and add the following content: + +```move +module first_package::my_module { +} +``` + +And now let's add some code: :::tip Comments in `.move` files @@ -14,57 +21,11 @@ In `.move` files, use double slashes (`//`) to denote a comment. ::: -```move -module my_first_package::my_module { - - // Imports - use iota::object::{Self, UID}; - use iota::transfer; - use iota::tx_context::{Self, TxContext}; - - // Struct definitions - struct Sword has key, store { - id: UID, - magic: u64, - strength: u64, - } - - struct Forge has key, store { - id: UID, - swords_created: u64, - } - - // Module initializer to be executed when this module is published - fun init(ctx: &mut TxContext) { - let admin = Forge { - id: object::new(ctx), - swords_created: 0, - }; - // Transfer the forge object to the module/package publisher - transfer::public_transfer(admin, tx_context::sender(ctx)); - } - - // Accessors required to read the struct attributes - public fun magic(self: &Sword): u64 { - self.magic - } - - public fun strength(self: &Sword): u64 { - self.strength - } - - public fun swords_created(self: &Forge): u64 { - self.swords_created - } - - // Public/entry functions - - // Private functions -} +```move file=/examples/move/first_package/sources/first_package.move#L7-L41 ``` ## Module Name -The first line of the module defines the module's name and the package it belongs to. In this case, `my_module` belongs to [`my_first_package`](create-a-package.mdx). +The first line of the module defines the module's name and the package it belongs to. In this case, `my_module` belongs to [`first_package`](create-a-package.mdx). ## Imports diff --git a/docs/content/developer/getting-started/create-a-package.mdx b/docs/content/developer/getting-started/create-a-package.mdx index 653e2ce44af..9a7001e47fc 100644 --- a/docs/content/developer/getting-started/create-a-package.mdx +++ b/docs/content/developer/getting-started/create-a-package.mdx @@ -14,10 +14,10 @@ issuing [transactions](../iota-101/transactions/transactions.mdx). Use the following command to create a standard package: ```shell -iota move new my_first_package +iota move new first_package ``` -The command will create and populate the `my_first_package` directory with a skeleton for an IOTA Move project, +The command will create and populate the `first_package` directory with a skeleton for an IOTA Move project, consisting of the following files and directories: ## `Move.toml` @@ -28,43 +28,7 @@ The `Move.toml` file is the package's manifest. It describes the package and its In `.toml` files, use the hash mark (`#`) to denote a comment. ::: -```toml title="my_first_package/Move.toml" -[package] -name = "my_first_package" -edition = "2024.beta" # edition -# license = "" # e.g., "MIT", "GPL", "Apache 2.0" -# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] - -[dependencies] -Iota = { git = "https://github.com/iotaledger/iota.git", subdir = "crates/iota-framework/packages/iota-framework", rev = "framework/testnet" } - -# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. -# Revision can be a branch, a tag, and a commit hash. -# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" } - -# For local dependencies use `local = path`. Path is relative to the package root -# Iota = { local = "../path/to/iota/crates/iota-framework/packages/iota-framework" } - -# To resolve a version conflict and force a specific version for dependency -# override use `override = true` -# Override = { local = "../conflicting/version", override = true } - -[addresses] -my_first_package = "0x0" - -# Named addresses will be accessible in Move as `@name`. They're also exported: -# for example, `std = "0x1"` is exported by the Standard Library. -# alice = "0xA11CE" - -[dev-dependencies] -# The dev-dependencies section allows overriding dependencies for `--test` and -# `--dev` modes. You can introduce test-only dependencies here. -# Local = { local = "../path/to/dev-build" } - -[dev-addresses] -# The dev-addresses section allows overwriting named addresses for the `--test` -# and `--dev` modes. -# alice = "0xB0B" +```toml file=/examples/move/first_package/Move.toml ``` :::tip Using a local version of IOTA @@ -87,12 +51,7 @@ of the metadata. The `[dependencies]` section specifies the dependencies of the project. The dependency specification can be a git repository URL or a path to the local directory. -```rust -# git repository -Iota = { git = "https://github.com/iotaledger/iota.git", subdir = "crates/iota-framework/packages/iota-framework", rev = "framework/testnet" } - -# local directory -MyPackage = { local = "../my-package" } +```toml file=/examples/move/first_package/Move.toml#L10-L15 ``` Packages also import addresses from other packages. For example, the `Iota` dependency adds the `std` and `iota` addresses @@ -104,9 +63,7 @@ If you have two dependencies that use different versions of the same package, yo the `[dependencies]` section. To do so, add the `override` field to the dependency. The version specified in the `[dependencies]` section will be used instead of the one specified in the dependency itself. -```rust -[dependencies] -Iota = { override = true, git = "https://github.com/iotaledger/iota.git", subdir = "crates/iota-framework/packages/iota-framework", rev = "framework/testnet" } +```toml file=/examples/move/first_package/Move.toml#L17-L19 ``` ### Dev-dependencies diff --git a/docs/content/references/contribute/import-code-docs.mdx b/docs/content/references/contribute/import-code-docs.mdx new file mode 100644 index 00000000000..53e02675e11 --- /dev/null +++ b/docs/content/references/contribute/import-code-docs.mdx @@ -0,0 +1,48 @@ +--- +description: Import code documentation from this or any other repository. +tags: [reference] +--- + +# Import Code for Documentation + +We import code snippets directly from the source code to ensure the documentation is always up-to-date and correct. +To achieve this, we use two tools to either [import code from the IOTA repo](https://www.npmjs.com/package/remark-code-import) or [any other repository](https://www.npmjs.com/package/@saucelabs/theme-github-codeblock). + +:::info + +These tools only allow the import of complete files or specific lines from a file. So, if the code changes, make sure to update the lines. + +::: + +## Local Code Import + +We use the [`remark-code-import`](https://www.npmjs.com/package/remark-code-import) package to reference the code from the IOTA repo. + +:::note CI/CD + +If you import local code, ensure it is part of a CI/CD workflow so the code gets tested for correctness. + +::: + +### Usage + +Use it like a standard code block, specifying a path to the file you want to import. You can use `rootDir` as a base path to the repository. + +```` +```rust file=/some-path/to-rust-file.rs +``` +```` + +## Remote Code Import + +We use the [`@saucelabs/theme-github-codeblock`](https://www.npmjs.com/package/@saucelabs/theme-github-codeblock) package to reference code from any other repository. + +### Usage + +Create a code block with the keyword `reference` and add the GitHub link (with specific line numbers) to the file you want to import. + +```` +```rust reference +https://github.com/some-repo/with-a-great-project/blob/some-branch/some-path/to-rust-file.rs#L7-L41 +``` +```` \ No newline at end of file diff --git a/docs/content/sidebars/references.js b/docs/content/sidebars/references.js index dd1d40015fd..31d0622f7e4 100644 --- a/docs/content/sidebars/references.js +++ b/docs/content/sidebars/references.js @@ -355,6 +355,7 @@ const references = [ 'references/contribute/contribution-process', 'references/contribute/code-of-conduct', 'references/contribute/style-guide', + 'references/contribute/import-code-docs' ], }, ]; diff --git a/docs/examples/move/cryptography/Move.toml b/docs/examples/move/cryptography/Move.toml new file mode 100644 index 00000000000..bb123539023 --- /dev/null +++ b/docs/examples/move/cryptography/Move.toml @@ -0,0 +1,10 @@ +[package] +name = "cryptography" +edition = "2024.beta" + +[dependencies] +Iota = { local = "../../../../crates/iota-framework/packages/iota-framework" } +Stardust = { local = "../../../../crates/iota-framework/packages/stardust" } + +[addresses] +example = "0x0" diff --git a/docs/examples/move/cryptography/sources/ecvrf.move b/docs/examples/move/cryptography/sources/ecvrf.move new file mode 100644 index 00000000000..cc46ff5676a --- /dev/null +++ b/docs/examples/move/cryptography/sources/ecvrf.move @@ -0,0 +1,13 @@ +module example::ecvrf { + use iota::ecvrf; + use iota::event; + + /// Event on whether the output is verified + public struct VerifiedEvent has copy, drop { + is_verified: bool, + } + + public fun verify_ecvrf_output(output: vector, alpha_string: vector, public_key: vector, proof: vector) { + event::emit(VerifiedEvent {is_verified: ecvrf::ecvrf_verify(&output, &alpha_string, &public_key, &proof)}); + } +} \ No newline at end of file diff --git a/docs/examples/move/cryptography/sources/goth16.move b/docs/examples/move/cryptography/sources/goth16.move new file mode 100644 index 00000000000..71fa9a30d73 --- /dev/null +++ b/docs/examples/move/cryptography/sources/goth16.move @@ -0,0 +1,16 @@ +module example::groth16 { + use iota::groth16; + use iota::event; + + /// Event on whether the proof is verified + public struct VerifiedEvent has copy, drop { + is_verified: bool, + } + + public fun verify_proof(vk: vector, public_inputs_bytes: vector, proof_points_bytes: vector) { + let pvk = groth16::prepare_verifying_key(&groth16::bn254(), &vk); + let public_inputs = groth16::public_proof_inputs_from_bytes(public_inputs_bytes); + let proof_points = groth16::proof_points_from_bytes(proof_points_bytes); + event::emit(VerifiedEvent {is_verified: groth16::verify_groth16_proof(&groth16::bn254(), &pvk, &public_inputs, &proof_points)}); + } +} \ No newline at end of file diff --git a/docs/examples/move/cryptography/sources/hashing_iota.move b/docs/examples/move/cryptography/sources/hashing_iota.move new file mode 100644 index 00000000000..aa414a78815 --- /dev/null +++ b/docs/examples/move/cryptography/sources/hashing_iota.move @@ -0,0 +1,18 @@ +module example::hashing_iota { + use iota::hash; + + /// Object that holds the output hash value. + public struct Output has key, store { + id: UID, + value: vector + } + + public fun hash_data(data: vector, recipient: address, ctx: &mut TxContext) { + let hashed = Output { + id: object::new(ctx), + value: hash::keccak256(&data), + }; + // Transfer an output data object holding the hashed data to the recipient. + transfer::public_transfer(hashed, recipient) + } +} \ No newline at end of file diff --git a/docs/examples/move/cryptography/sources/hashing_std.move b/docs/examples/move/cryptography/sources/hashing_std.move new file mode 100644 index 00000000000..1e510a5ecb7 --- /dev/null +++ b/docs/examples/move/cryptography/sources/hashing_std.move @@ -0,0 +1,18 @@ +module example::hashing_std { + use std::hash; + + /// Object that holds the output hash value. + public struct Output has key, store { + id: UID, + value: vector + } + + public fun hash_data(data: vector, recipient: address, ctx: &mut TxContext) { + let hashed = Output { + id: object::new(ctx), + value: hash::sha2_256(data), + }; + // Transfer an output data object holding the hashed data to the recipient. + transfer::public_transfer(hashed, recipient) + } +} \ No newline at end of file diff --git a/docs/examples/move/evm-to-move/Move.toml b/docs/examples/move/evm-to-move/Move.toml new file mode 100644 index 00000000000..23a76788d6a --- /dev/null +++ b/docs/examples/move/evm-to-move/Move.toml @@ -0,0 +1,10 @@ +[package] +name = "evm-to-move" +edition = "2024.beta" + +[dependencies] +Iota = { local = "../../../../crates/iota-framework/packages/iota-framework" } +Stardust = { local = "../../../../crates/iota-framework/packages/stardust" } + +[addresses] +example = "0x0" diff --git a/docs/examples/move/evm-to-move/sources/coin_manager.move b/docs/examples/move/evm-to-move/sources/coin_manager.move new file mode 100644 index 00000000000..8a11504e05d --- /dev/null +++ b/docs/examples/move/evm-to-move/sources/coin_manager.move @@ -0,0 +1,35 @@ +module example::token { + use iota::coin; + use iota::coin_manager; + + /// One-Time-Witness of kind: `Coin` + public struct TOKEN has drop {} + + #[allow(lint(share_owned))] + fun init(witness: TOKEN, ctx: &mut TxContext) { + let (treasurycap, metadata) = coin::create_currency( + witness, + 6, // decimals + b"EXAMPLE", // symbol + b"Example Coin", // name + b"Just an example", // description + option::none(), // icon URL + ctx + ); + + // Creating the Manager, transferring ownership of the `TreasuryCap` to it + let (newtreasurycap, metacap, mut manager) = coin_manager::new(treasurycap, metadata, ctx); + + // Limiting the maximum supply to `100` + newtreasurycap.enforce_maximum_supply(&mut manager, 100); + + // Returning a new `CoinManagerTreasuryCap` to the creator of the `Coin` + transfer::public_transfer(newtreasurycap, ctx.sender()); + + // Returning a new `CoinManagerMetadataCap` to the creator of the `Coin` + transfer::public_transfer(metacap, ctx.sender()); + + // Publicly sharing the `CoinManager` object for convenient usage by anyone interested + transfer::public_share_object(manager); + } +} \ No newline at end of file diff --git a/docs/examples/move/evm-to-move/sources/nft.move b/docs/examples/move/evm-to-move/sources/nft.move new file mode 100644 index 00000000000..89a7272002f --- /dev/null +++ b/docs/examples/move/evm-to-move/sources/nft.move @@ -0,0 +1,65 @@ +module example::examplenft { + use std::string; + use iota::event; + + /// An example NFT that can be minted by anybody + public struct ExampleNFT has key, store { + id: UID, + /// Name for the token + name: string::String, + } + + // ===== Events ===== + + public struct NFTMinted has copy, drop { + // The Object ID of the NFT + object_id: ID, + // The creator of the NFT + creator: address, + // The name of the NFT + name: string::String, + } + + // ===== Public view functions ===== + + /// Get the NFT's `name` + public fun name(nft: &ExampleNFT): &string::String { + &nft.name + } + + // ===== Entrypoints ===== + + #[allow(lint(self_transfer))] + /// Anyone can mint a new NFT on this one + public fun mint_to_sender( + name: vector, + ctx: &mut TxContext + ) { + let sender = tx_context::sender(ctx); + let nft = ExampleNFT { + id: object::new(ctx), + name: string::utf8(name) + }; + + event::emit(NFTMinted { + object_id: object::id(&nft), + creator: sender, + name: nft.name, + }); + + transfer::public_transfer(nft, sender); + } + + /// Transfer `nft` to `recipient` + public fun transfer( + nft: ExampleNFT, recipient: address, _: &mut TxContext + ) { + transfer::public_transfer(nft, recipient) + } + + /// Permanently delete `nft` + public fun burn(nft: ExampleNFT, _: &mut TxContext) { + let ExampleNFT { id, name: _} = nft; + object::delete(id) + } +} \ No newline at end of file diff --git a/docs/examples/move/evm-to-move/sources/token.move b/docs/examples/move/evm-to-move/sources/token.move new file mode 100644 index 00000000000..5e0b6979d57 --- /dev/null +++ b/docs/examples/move/evm-to-move/sources/token.move @@ -0,0 +1,24 @@ +module example::exampletoken { + use iota::coin; + + /// One-Time-Witness of kind: `Coin` + public struct EXAMPLETOKEN has drop {} + + fun init(witness: EXAMPLETOKEN, ctx: &mut TxContext) { + let (treasurycap, metadata) = coin::create_currency( + witness, + 6, // decimals + b"EXAMPLE", // symbol + b"Example Coin", // name + b"Just an example", // description + option::none(), // icon URL + ctx + ); + + // transfer the `TreasuryCap` to the sender, admin capabilities + transfer::public_transfer(treasurycap, tx_context::sender(ctx)); + + // metadata is typically frozen after creation + transfer::public_freeze_object(metadata); + } +} \ No newline at end of file diff --git a/examples/move/first_package/Move.toml b/examples/move/first_package/Move.toml index 24f267c9634..9003fc5d467 100644 --- a/examples/move/first_package/Move.toml +++ b/examples/move/first_package/Move.toml @@ -1,10 +1,36 @@ [package] name = "first_package" -version = "0.0.1" -edition = "2024.beta" +edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move +# license = "" # e.g., "MIT", "GPL", "Apache 2.0" +# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] [dependencies] Iota = { local = "../../../crates/iota-framework/packages/iota-framework" } +# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. +# Revision can be a branch, a tag, and a commit hash. +# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" } + +# For local dependencies use `local = path`. Path is relative to the package root +# Local = { local = "../path/to" } + +# To resolve a version conflict and force a specific version for dependency +# override use `override = true` +# Override = { local = "../conflicting/version", override = true } + [addresses] first_package = "0x0" + +# Named addresses will be accessible in Move as `@name`. They're also exported: +# for example, `std = "0x1"` is exported by the Standard Library. +# alice = "0xA11CE" + +[dev-dependencies] +# The dev-dependencies section allows overriding dependencies for `--test` and +# `--dev` modes. You can introduce test-only dependencies here. +# Local = { local = "../path/to/dev-build" } + +[dev-addresses] +# The dev-addresses section allows overwriting named addresses for the `--test` +# and `--dev` modes. +# alice = "0xB0B" diff --git a/examples/move/first_package/sources/example.move b/examples/move/first_package/sources/first_package.move similarity index 86% rename from examples/move/first_package/sources/example.move rename to examples/move/first_package/sources/first_package.move index a9f369a44a0..eb431da5c84 100644 --- a/examples/move/first_package/sources/example.move +++ b/examples/move/first_package/sources/first_package.move @@ -2,7 +2,7 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -module first_package::example { +module first_package::my_module { public struct Sword has key, store { id: UID, @@ -62,6 +62,26 @@ module first_package::example { #[test_only] const ALICE: address = @0xA; #[test_only] const BOB: address = @0xB; + #[test] + public fun test_sword() { + // Create a dummy TxContext for testing. + let mut ctx = tx_context::dummy(); + + // Create a sword. + let sword = Sword { + id: object::new(&mut ctx), + magic: 42, + strength: 7, + }; + + // Check if accessor functions return correct values. + assert!(magic(&sword) == 42 && strength(&sword) == 7, 1) + + // Create a dummy address and transfer the sword. + let dummy_address = @0xCAFE; + transfer::transfer(sword, dummy_address); + } + #[test] public fun test_module_init() { let mut ts = ts::begin(@0x0); From 5e48fbdebf728fe1a2759fe5df1dbaf39dcad65e Mon Sep 17 00:00:00 2001 From: Samuel Rufinatscha Date: Tue, 24 Sep 2024 17:02:52 +0200 Subject: [PATCH 07/11] fix(iota-graphql-rpc): Transform stored transaction for genesis (#2836) * fix: Stored transaction from genesis objects * fix: Remove &mut --- crates/iota-graphql-rpc/src/types/transaction_block.rs | 6 +++++- crates/iota-indexer/src/indexer_reader.rs | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/iota-graphql-rpc/src/types/transaction_block.rs b/crates/iota-graphql-rpc/src/types/transaction_block.rs index 512a3389bfc..271a429204f 100644 --- a/crates/iota-graphql-rpc/src/types/transaction_block.rs +++ b/crates/iota-graphql-rpc/src/types/transaction_block.rs @@ -442,7 +442,11 @@ impl TransactionBlock { // Defer to the provided checkpoint_viewed_at, but if it is not provided, use // the current available range. This sets a consistent upper bound for // the nested queries. - for stored in results { + for mut stored in results { + if stored.is_genesis() { + stored = stored.set_genesis_large_object_as_inner_data(db.inner.get_pool())?; + } + let cursor = stored.cursor(checkpoint_viewed_at).encode_cursor(); let inner = TransactionBlockInner::try_from(stored)?; let transaction = TransactionBlock { diff --git a/crates/iota-indexer/src/indexer_reader.rs b/crates/iota-indexer/src/indexer_reader.rs index a9e541cc0a4..bcef95c8d7f 100644 --- a/crates/iota-indexer/src/indexer_reader.rs +++ b/crates/iota-indexer/src/indexer_reader.rs @@ -100,6 +100,10 @@ impl IndexerReader { }) } + pub fn get_pool(&self) -> &crate::db::PgConnectionPool { + &self.pool + } + fn get_connection(&self) -> Result { self.pool.get().map_err(|e| { IndexerError::PgPoolConnectionError(format!( From a810d8b53178e0a365800a752d9c397c86e2e797 Mon Sep 17 00:00:00 2001 From: "Eugene P." Date: Wed, 25 Sep 2024 10:41:00 +0300 Subject: [PATCH 08/11] fix(wallet): update isMainAccount function. (#2814) Signed-off-by: Eugene Panteleymonchuk --- .../src/background/accounts/isMainAccount.ts | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/apps/wallet/src/background/accounts/isMainAccount.ts b/apps/wallet/src/background/accounts/isMainAccount.ts index f329358f40b..0e385c52cf0 100644 --- a/apps/wallet/src/background/accounts/isMainAccount.ts +++ b/apps/wallet/src/background/accounts/isMainAccount.ts @@ -8,21 +8,19 @@ import { parseDerivationPath } from '_src/background/account-sources/bip44Path'; import type { SerializedUIAccount } from '_src/background/accounts/Account'; export function isMainAccount(account: SerializedUIAccount | null) { - { - if (!account) { - return false; - } + if (!account) { + return false; + } - if ( - isLedgerAccountSerializedUI(account) || - isMnemonicSerializedUiAccount(account) || - isSeedSerializedUiAccount(account) - ) { - const { addressIndex, changeIndex, accountIndex } = parseDerivationPath( - account.derivationPath, - ); + if ( + isLedgerAccountSerializedUI(account) || + isMnemonicSerializedUiAccount(account) || + isSeedSerializedUiAccount(account) + ) { + const { addressIndex, changeIndex, accountIndex } = parseDerivationPath( + account.derivationPath, + ); - return addressIndex === 0 && changeIndex === 0 && accountIndex === 0; - } + return addressIndex === 0 && changeIndex === 0 && accountIndex === 0; } } From 872ba396f5d7d9da42591a6b874ac2df147623bf Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 25 Sep 2024 11:43:04 +0200 Subject: [PATCH 09/11] fix(tooling-ci): Use turbo@2 in the packages diff command (#2848) --- .github/actions/turbo-diffs/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/turbo-diffs/action.yml b/.github/actions/turbo-diffs/action.yml index 516f1e549c9..9f4056f39cb 100644 --- a/.github/actions/turbo-diffs/action.yml +++ b/.github/actions/turbo-diffs/action.yml @@ -17,7 +17,7 @@ runs: - id: changes name: Detect changes shell: bash - run: echo "packages=$(pnpm --silent dlx turbo@1 run build --filter="...[origin/develop]" --dry=json | jq -c ".packages")" >> $GITHUB_OUTPUT + run: echo "packages=$(pnpm --silent dlx turbo@2 run build --filter="...[origin/develop]" --dry=json | jq -c ".packages")" >> $GITHUB_OUTPUT - name: Print changes for easy debugging shell: bash run: echo ${{ steps.changes.outputs.packages }} From 86af63a0d52000ebf4d79d91f86653e5baeef30a Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Wed, 25 Sep 2024 11:47:14 +0200 Subject: [PATCH 10/11] feat(wallet): Add RC Release workflow (#2847) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(wallet): Add RC Release workflow * refactor: dprint fmt * refactor: Remove unnecessary permission * refactor: Remove turbo cache * Update turbo.json * Update .github/workflows/apps-wallet-rc.build.yml Co-authored-by: Begoña Álvarez de la Cruz * refactor: Use RC env vars * refactor: Rename RC env vars --------- Co-authored-by: Begoña Álvarez de la Cruz --- .github/workflows/apps-wallet-rc.build.yml | 47 +++++++++++++++++++ .../configs/webpack/webpack.config.common.ts | 4 +- turbo.json | 14 ++++++ 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/apps-wallet-rc.build.yml diff --git a/.github/workflows/apps-wallet-rc.build.yml b/.github/workflows/apps-wallet-rc.build.yml new file mode 100644 index 00000000000..4c623b6ccb7 --- /dev/null +++ b/.github/workflows/apps-wallet-rc.build.yml @@ -0,0 +1,47 @@ +name: Build Wallet App (RC) + +on: + workflow_dispatch: + inputs: + iota_branch: + description: "IOTA repo release branch to build artifacts from" + type: string + required: true + rc_version: + description: "The RC numeric version (X.Y.Z.RC)" + type: number + required: true + +env: + DEFAULT_NETWORK: ${{ secrets.WALLET_RC_DEFAULT_NETWORK }} + IOTA_NETWORKS: ${{ secrets.WALLET_RC_IOTA_NETWORKS }} + APPS_BACKEND: ${{ secrets.WALLET_RC_APPS_BACKEND }} + RC_VERSION: "${{ github.event.inputs.rc_version }}" + WALLET_RC: "true" + +jobs: + wallet-rc-build: + permissions: + contents: read + runs-on: [self-hosted] + steps: + - name: Checking out ${{ env.iota_branch }} + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # pin@v3 + with: + ref: ${{ env.iota_branch }} + - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # pin@v4.0.0 + - name: Install Nodejs + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # pin@v4.0.2 + with: + node-version: "20" + cache: "pnpm" + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Build Wallet + run: pnpm wallet build:rc + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ env.artifact_name }} + path: | + ./apps/wallet/dist diff --git a/apps/wallet/configs/webpack/webpack.config.common.ts b/apps/wallet/configs/webpack/webpack.config.common.ts index 3645f4e838a..bfe06d71863 100644 --- a/apps/wallet/configs/webpack/webpack.config.common.ts +++ b/apps/wallet/configs/webpack/webpack.config.common.ts @@ -41,10 +41,10 @@ function generateVersion(n: number | undefined) { const sha = gitRevSync.short(); const packageVersion = packageJson.version; const version = n !== undefined ? `${packageVersion}.${n}` : packageVersion; - const improved_version = n !== undefined ? `${packageVersion}-rc.${n}` : packageVersion; + const version_name = n !== undefined ? `${packageVersion}-rc.${n}` : packageVersion; return { version, - version_name: `${improved_version} (${sha})`, + version_name: `${version_name} (${sha})`, }; } diff --git a/turbo.json b/turbo.json index a7fefbe13e4..81d6773f367 100644 --- a/turbo.json +++ b/turbo.json @@ -64,6 +64,20 @@ "!.next/cache/**", "pkg/**" ] + }, + "build:rc": { + "dependsOn": [ + "^build" + ], + "outputs": [ + "build/**", + "dist/**", + "storybook-static/**", + ".next/**", + "!.next/cache/**", + "pkg/**" + ] } + } } From 065513f48226154d9d83bb89d7890e854565171f Mon Sep 17 00:00:00 2001 From: evavirseda Date: Wed, 25 Sep 2024 12:26:50 +0200 Subject: [PATCH 11/11] feat(sdk): rebrand dapp-kit components (#2783) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: refine button and modal * feat: refine styles * feat: style dropwdown * feat: update text * feat: add missing style * fix format * fix format * feat: refine styles * feat: add iota wallet logo * fix: remove console.log * chore: add changeset --------- Co-authored-by: Begoña Alvarez Co-authored-by: Branko Bosnic --- .changeset/gold-bobcats-check.md | 5 + .../wallet-dashboard/app/dashboard/layout.tsx | 37 ++- apps/wallet-dashboard/app/globals.css | 22 +- apps/wallet-dashboard/tailwind.config.ts | 1 + .../dapp-interface/WalletStandardInterface.ts | 2 +- .../src/components/AccountDropdownMenu.css.ts | 28 ++- .../src/components/AccountDropdownMenu.tsx | 11 +- sdk/dapp-kit/src/components/ConnectButton.tsx | 5 +- .../src/components/WalletProvider.tsx | 15 +- .../connect-modal/ConnectModal.css.ts | 75 ++---- .../components/connect-modal/ConnectModal.tsx | 117 ++------- .../views/ConnectionStatus.css.ts | 36 --- .../connect-modal/views/ConnectionStatus.tsx | 55 ----- .../connect-modal/views/GetTheWallet.css.ts | 38 +++ .../connect-modal/views/GetTheWallet.tsx | 25 ++ .../connect-modal/views/GettingStarted.css.ts | 26 -- .../connect-modal/views/GettingStarted.tsx | 40 --- .../connect-modal/views/WalletConnect.css.ts | 55 +++++ .../connect-modal/views/WalletConnect.tsx | 79 ++++++ .../connect-modal/views/WhatIsAWallet.css.ts | 20 -- .../connect-modal/views/WhatIsAWallet.tsx | 24 -- .../components/connect-modal/views/index.ts | 5 + .../wallet-list/WalletList.css.ts | 6 +- .../connect-modal/wallet-list/WalletList.tsx | 36 +-- .../wallet-list/WalletListItem.css.ts | 20 +- .../wallet-list/WalletListItem.tsx | 21 +- .../src/components/icons/ArrowRightIcon.tsx | 23 ++ .../components/icons/ArrowTopRightIcon.tsx | 23 ++ .../src/components/icons/BackIcon.tsx | 18 -- .../src/components/icons/IotaIcon.tsx | 228 +++++++++++++++++- .../src/components/styling/StyleMarker.css.ts | 15 ++ sdk/dapp-kit/src/components/ui/Button.css.ts | 8 +- .../src/components/ui/IconButton.css.ts | 3 - sdk/dapp-kit/src/components/ui/Text.css.ts | 2 +- sdk/dapp-kit/src/constants/walletDefaults.ts | 5 + sdk/dapp-kit/src/index.ts | 1 + sdk/dapp-kit/src/themes/darkTheme.ts | 69 ++++++ sdk/dapp-kit/src/themes/lightTheme.ts | 53 ++-- sdk/dapp-kit/src/themes/themeContract.ts | 17 +- .../test/components/ConnectButton.test.tsx | 2 +- 40 files changed, 748 insertions(+), 523 deletions(-) create mode 100644 .changeset/gold-bobcats-check.md delete mode 100644 sdk/dapp-kit/src/components/connect-modal/views/ConnectionStatus.css.ts delete mode 100644 sdk/dapp-kit/src/components/connect-modal/views/ConnectionStatus.tsx create mode 100644 sdk/dapp-kit/src/components/connect-modal/views/GetTheWallet.css.ts create mode 100644 sdk/dapp-kit/src/components/connect-modal/views/GetTheWallet.tsx delete mode 100644 sdk/dapp-kit/src/components/connect-modal/views/GettingStarted.css.ts delete mode 100644 sdk/dapp-kit/src/components/connect-modal/views/GettingStarted.tsx create mode 100644 sdk/dapp-kit/src/components/connect-modal/views/WalletConnect.css.ts create mode 100644 sdk/dapp-kit/src/components/connect-modal/views/WalletConnect.tsx delete mode 100644 sdk/dapp-kit/src/components/connect-modal/views/WhatIsAWallet.css.ts delete mode 100644 sdk/dapp-kit/src/components/connect-modal/views/WhatIsAWallet.tsx create mode 100644 sdk/dapp-kit/src/components/connect-modal/views/index.ts create mode 100644 sdk/dapp-kit/src/components/icons/ArrowRightIcon.tsx create mode 100644 sdk/dapp-kit/src/components/icons/ArrowTopRightIcon.tsx delete mode 100644 sdk/dapp-kit/src/components/icons/BackIcon.tsx create mode 100644 sdk/dapp-kit/src/themes/darkTheme.ts diff --git a/.changeset/gold-bobcats-check.md b/.changeset/gold-bobcats-check.md new file mode 100644 index 00000000000..99e18ec0a8a --- /dev/null +++ b/.changeset/gold-bobcats-check.md @@ -0,0 +1,5 @@ +--- +'@iota/dapp-kit': minor +--- + +Rebrand diff --git a/apps/wallet-dashboard/app/dashboard/layout.tsx b/apps/wallet-dashboard/app/dashboard/layout.tsx index 7cd36c639ce..ac56873f2b4 100644 --- a/apps/wallet-dashboard/app/dashboard/layout.tsx +++ b/apps/wallet-dashboard/app/dashboard/layout.tsx @@ -2,31 +2,42 @@ // SPDX-License-Identifier: Apache-2.0 'use client'; -import { Notifications, RouteLink } from '@/components/index'; -import React, { type PropsWithChildren } from 'react'; +import { Button, Notifications, RouteLink } from '@/components/index'; +import React, { useState, type PropsWithChildren } from 'react'; import { ConnectButton } from '@iota/dapp-kit'; +const routes = [ + { title: 'Home', path: '/dashboard/home' }, + { title: 'Assets', path: '/dashboard/assets' }, + { title: 'Staking', path: '/dashboard/staking' }, + { title: 'Apps', path: '/dashboard/apps' }, + { title: 'Activity', path: '/dashboard/activity' }, + { title: 'Migrations', path: '/dashboard/migrations' }, + { title: 'Vesting', path: '/dashboard/vesting' }, +]; + function DashboardLayout({ children }: PropsWithChildren): JSX.Element { - const routes = [ - { title: 'Home', path: '/dashboard/home' }, - { title: 'Assets', path: '/dashboard/assets' }, - { title: 'Staking', path: '/dashboard/staking' }, - { title: 'Apps', path: '/dashboard/apps' }, - { title: 'Activity', path: '/dashboard/activity' }, - { title: 'Migrations', path: '/dashboard/migrations' }, - { title: 'Vesting', path: '/dashboard/vesting' }, - ]; + const [isDarkMode, setIsDarkMode] = useState(false); + + const toggleDarkMode = () => { + setIsDarkMode(!isDarkMode); + if (isDarkMode) { + document.documentElement.classList.remove('dark'); + } else { + document.documentElement.classList.add('dark'); + } + }; // TODO: check if the wallet is connected and if not redirect to the welcome screen return ( <>
- - {routes.map((route) => { return ; })} + +
{children}
diff --git a/apps/wallet-dashboard/app/globals.css b/apps/wallet-dashboard/app/globals.css index c7e0f0b6ea9..f20990e2944 100644 --- a/apps/wallet-dashboard/app/globals.css +++ b/apps/wallet-dashboard/app/globals.css @@ -2,29 +2,11 @@ @tailwind components; @tailwind utilities; -:root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } -} - html, body { height: 100%; -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb))) - rgb(var(--background-start-rgb)); + @apply bg-gray-100 dark:bg-gray-900; + @apply text-gray-900 dark:text-gray-100; } @layer utilities { diff --git a/apps/wallet-dashboard/tailwind.config.ts b/apps/wallet-dashboard/tailwind.config.ts index c3b8ec17293..ca8c6b84b6a 100644 --- a/apps/wallet-dashboard/tailwind.config.ts +++ b/apps/wallet-dashboard/tailwind.config.ts @@ -9,6 +9,7 @@ const config: Config = { './pages/**/*.{js,ts,jsx,tsx,mdx}', './components/**/*.{js,ts,jsx,tsx,mdx}', ], + darkMode: 'class', theme: { extend: { backgroundImage: { diff --git a/apps/wallet/src/dapp-interface/WalletStandardInterface.ts b/apps/wallet/src/dapp-interface/WalletStandardInterface.ts index 15bdeb5bbe2..a130383ad76 100644 --- a/apps/wallet/src/dapp-interface/WalletStandardInterface.ts +++ b/apps/wallet/src/dapp-interface/WalletStandardInterface.ts @@ -71,7 +71,7 @@ export class IotaWallet implements Wallet { } get icon() { - return 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzIiIGhlaWdodD0iNzIiIHZpZXdCb3g9IjAgMCA3MiA3MiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjcyIiBoZWlnaHQ9IjcyIiByeD0iMTYiIGZpbGw9IiM2RkJDRjAiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yMC40MjEzIDUyLjc4MzhDMjMuNjQ5NiA1OC4zNzYgMjkuNDMyMSA2MS43MTQyIDM1Ljg4ODggNjEuNzE0MkM0Mi4zNDU1IDYxLjcxNDIgNDguMTI3IDU4LjM3NiA1MS4zNTY0IDUyLjc4MzhDNTQuNTg0OCA0Ny4xOTI2IDU0LjU4NDggNDAuNTE2MyA1MS4zNTY0IDM0LjkyNEwzNy43NTI0IDExLjM2MTVDMzYuOTI0MSA5LjkyNzAxIDM0Ljg1MzUgOS45MjcwMSAzNC4wMjUzIDExLjM2MTVMMjAuNDIxMyAzNC45MjRDMTcuMTkyOSA0MC41MTUyIDE3LjE5MjkgNDcuMTkxNSAyMC40MjEzIDUyLjc4MzhaTTMyLjA1NjYgMjIuNTcxM0wzNC45NTcxIDE3LjU0NzRDMzUuMzcxMiAxNi44MzAxIDM2LjQwNjUgMTYuODMwMSAzNi44MjA2IDE3LjU0NzRMNDcuOTc5MSAzNi44NzQ4QzUwLjAyOTEgNDAuNDI1NCA1MC40MTM5IDQ0LjUzNSA0OS4xMzM1IDQ4LjI5NTRDNDkuMDAwMiA0Ny42ODE5IDQ4LjgxMzggNDcuMDU0MiA0OC41NjI2IDQ2LjQyMDFDNDcuMDIxMyA0Mi41MzA0IDQzLjUzNjMgMzkuNTI4OSAzOC4yMDIzIDM3LjQ5ODJDMzQuNTM1MSAzNi4xMDcxIDMyLjE5NDMgMzQuMDYxMyAzMS4yNDMxIDMxLjQxNzFDMzAuMDE4IDI4LjAwODkgMzEuMjk3NiAyNC4yOTI0IDMyLjA1NjYgMjIuNTcxM1pNMjcuMTEwNyAzMS4xMzc5TDIzLjc5ODYgMzYuODc0OEMyMS4yNzQ4IDQxLjI0NTkgMjEuMjc0OCA0Ni40NjQxIDIzLjc5ODYgNTAuODM1M0MyNi4zMjIzIDU1LjIwNjQgMzAuODQxMyA1Ny44MTUgMzUuODg4OCA1Ny44MTVDMzkuMjQxMyA1Ny44MTUgNDIuMzYxNSA1Ni42NjMzIDQ0LjgxODQgNTQuNjA4OEM0NS4xMzg4IDUzLjgwMjEgNDYuMTMxIDUwLjg0OTIgNDQuOTA1MiA0Ny44MDU4QzQzLjc3MyA0NC45OTU0IDQxLjA0ODIgNDIuNzUxOSAzNi44MDYxIDQxLjEzNkMzMi4wMTEgMzkuMzE3MSAyOC44OTU4IDM2LjQ3NzQgMjcuNTQ4NiAzMi42OTg0QzI3LjM2MzEgMzIuMTc4MSAyNy4yMTg5IDMxLjY1NjggMjcuMTEwNyAzMS4xMzc5WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+' as const; + return 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjU2IiBoZWlnaHQ9IjI1NiIgdmlld0JveD0iMCAwIDI1NiAyNTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyNTYiIGhlaWdodD0iMjU2IiByeD0iMTI4IiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTY5LjIyNyA2MS42Mjk1QzE2OS4yMjcgNjYuMzk1NCAxNjUuMzc1IDcwLjI1OSAxNjAuNjI0IDcwLjI1OUMxNTUuODczIDcwLjI1OSAxNTIuMDIxIDY2LjM5NTQgMTUyLjAyMSA2MS42Mjk1QzE1Mi4wMjEgNTYuODYzNSAxNTUuODczIDUzIDE2MC42MjQgNTNDMTY1LjM3NSA1MyAxNjkuMjI3IDU2Ljg2MzUgMTY5LjIyNyA2MS42Mjk1WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTc4Ljg5OSAxODMuNTMxQzE3OC44OTkgMTg4LjI5NiAxNzUuMDQ3IDE5Mi4xNiAxNzAuMjk2IDE5Mi4xNkMxNjUuNTQ0IDE5Mi4xNiAxNjEuNjkzIDE4OC4yOTYgMTYxLjY5MyAxODMuNTMxQzE2MS42OTMgMTc4Ljc2NSAxNjUuNTQ0IDE3NC45MDEgMTcwLjI5NiAxNzQuOTAxQzE3NS4wNDcgMTc0LjkwMSAxNzguODk5IDE3OC43NjUgMTc4Ljg5OSAxODMuNTMxWiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTYzLjA4MyA5MS44MzFDMTY3LjA3IDkxLjgzMSAxNzAuMzAyIDg4LjU4OTEgMTcwLjMwMiA4NC41OUMxNzAuMzAyIDgwLjU5MDkgMTY3LjA3IDc3LjM0OSAxNjMuMDgzIDc3LjM0OUMxNTkuMDk3IDc3LjM0OSAxNTUuODY1IDgwLjU5MDkgMTU1Ljg2NSA4NC41OUMxNTUuODY1IDg4LjU4OTEgMTU5LjA5NyA5MS44MzEgMTYzLjA4MyA5MS44MzFaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xODkuNSA4Ny4zNjZDMTg5LjUgOTEuMzY1MSAxODYuMjY4IDk0LjYwNyAxODIuMjgxIDk0LjYwN0MxNzguMjk1IDk0LjYwNyAxNzUuMDYyIDkxLjM2NTEgMTc1LjA2MiA4Ny4zNjZDMTc1LjA2MiA4My4zNjY5IDE3OC4yOTUgODAuMTI1IDE4Mi4yODEgODAuMTI1QzE4Ni4yNjggODAuMTI1IDE4OS41IDgzLjM2NjkgMTg5LjUgODcuMzY2WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTYwLjYyNiAxMTAuMTg2QzE2NC4wMjEgMTEwLjE4NiAxNjYuNzc0IDEwNy40MjYgMTY2Ljc3NCAxMDQuMDJDMTY2Ljc3NCAxMDAuNjE1IDE2NC4wMjEgOTcuODU0MiAxNjAuNjI2IDk3Ljg1NDJDMTU3LjIzMSA5Ny44NTQyIDE1NC40NzkgMTAwLjYxNSAxNTQuNDc5IDEwNC4wMkMxNTQuNDc5IDEwNy40MjYgMTU3LjIzMSAxMTAuMTg2IDE2MC42MjYgMTEwLjE4NloiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTE4NS45NzcgMTA2LjYyNEMxODUuOTc3IDExMC4wMyAxODMuMjI1IDExMi43OSAxNzkuODMgMTEyLjc5QzE3Ni40MzQgMTEyLjc5IDE3My42ODIgMTEwLjAzIDE3My42ODIgMTA2LjYyNEMxNzMuNjgyIDEwMy4yMTkgMTc2LjQzNCAxMDAuNDU4IDE3OS44MyAxMDAuNDU4QzE4My4yMjUgMTAwLjQ1OCAxODUuOTc3IDEwMy4yMTkgMTg1Ljk3NyAxMDYuNjI0WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTk1LjE4OSAxMTguODA2QzE5OC41ODQgMTE4LjgwNiAyMDEuMzM2IDExNi4wNDUgMjAxLjMzNiAxMTIuNjRDMjAxLjMzNiAxMDkuMjM1IDE5OC41ODQgMTA2LjQ3NCAxOTUuMTg5IDEwNi40NzRDMTkxLjc5NCAxMDYuNDc0IDE4OS4wNDIgMTA5LjIzNSAxODkuMDQyIDExMi42NEMxODkuMDQyIDExNi4wNDUgMTkxLjc5NCAxMTguODA2IDE5NS4xODkgMTE4LjgwNloiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTE3OS41MTIgMTIyLjE5N0MxNzkuNTEyIDEyNS4xNzQgMTc3LjEwNiAxMjcuNTg3IDE3NC4xMzkgMTI3LjU4N0MxNzEuMTcxIDEyNy41ODcgMTY4Ljc2NiAxMjUuMTc0IDE2OC43NjYgMTIyLjE5N0MxNjguNzY2IDExOS4yMiAxNzEuMTcxIDExNi44MDcgMTc0LjEzOSAxMTYuODA3QzE3Ny4xMDYgMTE2LjgwNyAxNzkuNTEyIDExOS4yMiAxNzkuNTEyIDEyMi4xOTdaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xNTQuOTQxIDEyNC45NjJDMTU3LjkwOSAxMjQuOTYyIDE2MC4zMTQgMTIyLjU0OSAxNjAuMzE0IDExOS41NzJDMTYwLjMxNCAxMTYuNTk1IDE1Ny45MDkgMTE0LjE4MiAxNTQuOTQxIDExNC4xODJDMTUxLjk3MyAxMTQuMTgyIDE0OS41NjggMTE2LjU5NSAxNDkuNTY4IDExOS41NzJDMTQ5LjU2OCAxMjIuNTQ5IDE1MS45NzMgMTI0Ljk2MiAxNTQuOTQxIDEyNC45NjJaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xOTUuMDM4IDEyOC4xOTdDMTk1LjAzOCAxMzEuMTc0IDE5Mi42MzIgMTMzLjU4NyAxODkuNjY1IDEzMy41ODdDMTg2LjY5NyAxMzMuNTg3IDE4NC4yOTIgMTMxLjE3NCAxODQuMjkyIDEyOC4xOTdDMTg0LjI5MiAxMjUuMjIgMTg2LjY5NyAxMjIuODA3IDE4OS42NjUgMTIyLjgwN0MxOTIuNjMyIDEyMi44MDcgMTk1LjAzOCAxMjUuMjIgMTk1LjAzOCAxMjguMTk3WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTY2LjQ1OCAxMzguODQ1QzE2OS4wMDYgMTM4Ljg0NSAxNzEuMDcyIDEzNi43NzMgMTcxLjA3MiAxMzQuMjE3QzE3MS4wNzIgMTMxLjY2MSAxNjkuMDA2IDEyOS41ODkgMTY2LjQ1OCAxMjkuNTg5QzE2My45MSAxMjkuNTg5IDE2MS44NDQgMTMxLjY2MSAxNjEuODQ0IDEzNC4yMTdDMTYxLjg0NCAxMzYuNzczIDE2My45MSAxMzguODQ1IDE2Ni40NTggMTM4Ljg0NVoiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTE4Ni42MDMgMTQwLjA2NkMxODYuNjAzIDE0Mi42MjIgMTg0LjUzNyAxNDQuNjk0IDE4MS45ODkgMTQ0LjY5NEMxNzkuNDQxIDE0NC42OTQgMTc3LjM3NSAxNDIuNjIyIDE3Ny4zNzUgMTQwLjA2NkMxNzcuMzc1IDEzNy41MSAxNzkuNDQxIDEzNS40MzggMTgxLjk4OSAxMzUuNDM4QzE4NC41MzcgMTM1LjQzOCAxODYuNjAzIDEzNy41MSAxODYuNjAzIDE0MC4wNjZaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xNDcuNDExIDEzNi4wNzRDMTQ5Ljk1OSAxMzYuMDc0IDE1Mi4wMjUgMTM0LjAwMiAxNTIuMDI1IDEzMS40NDZDMTUyLjAyNSAxMjguODkgMTQ5Ljk1OSAxMjYuODE4IDE0Ny40MTEgMTI2LjgxOEMxNDQuODYzIDEyNi44MTggMTQyLjc5NyAxMjguODkgMTQyLjc5NyAxMzEuNDQ2QzE0Mi43OTcgMTM0LjAwMiAxNDQuODYzIDEzNi4wNzQgMTQ3LjQxMSAxMzYuMDc0WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTYxLjg0NyAxNDIuNTM0QzE2MS44NDcgMTQ0LjY2MiAxNjAuMTI4IDE0Ni4zODYgMTU4LjAwNyAxNDYuMzg2QzE1NS44ODYgMTQ2LjM4NiAxNTQuMTY3IDE0NC42NjIgMTU0LjE2NyAxNDIuNTM0QzE1NC4xNjcgMTQwLjQwNyAxNTUuODg2IDEzOC42ODIgMTU4LjAwNyAxMzguNjgyQzE2MC4xMjggMTM4LjY4MiAxNjEuODQ3IDE0MC40MDcgMTYxLjg0NyAxNDIuNTM0WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTY0LjYxMyAxNTcuMDI4QzE2Ni40NzkgMTU3LjAyOCAxNjcuOTkyIDE1NS41MTEgMTY3Ljk5MiAxNTMuNjM5QzE2Ny45OTIgMTUxLjc2NyAxNjYuNDc5IDE1MC4yNSAxNjQuNjEzIDE1MC4yNUMxNjIuNzQ3IDE1MC4yNSAxNjEuMjM0IDE1MS43NjcgMTYxLjIzNCAxNTMuNjM5QzE2MS4yMzQgMTU1LjUxMSAxNjIuNzQ3IDE1Ny4wMjggMTY0LjYxMyAxNTcuMDI4WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTU5LjI0IDE1Ni40MTJDMTU5LjI0IDE1Ny45NDYgMTU4LjAwMSAxNTkuMTg5IDE1Ni40NzIgMTU5LjE4OUMxNTQuOTQzIDE1OS4xODkgMTUzLjcwMyAxNTcuOTQ2IDE1My43MDMgMTU2LjQxMkMxNTMuNzAzIDE1NC44NzkgMTU0Ljk0MyAxNTMuNjM1IDE1Ni40NzIgMTUzLjYzNUMxNTguMDAxIDE1My42MzUgMTU5LjI0IDE1NC44NzkgMTU5LjI0IDE1Ni40MTJaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xNDkuMjU0IDE1MS4xNjlDMTUxLjEyIDE1MS4xNjkgMTUyLjYzMiAxNDkuNjUxIDE1Mi42MzIgMTQ3Ljc4QzE1Mi42MzIgMTQ1LjkwOCAxNTEuMTIgMTQ0LjM5MSAxNDkuMjU0IDE0NC4zOTFDMTQ3LjM4OCAxNDQuMzkxIDE0NS44NzUgMTQ1LjkwOCAxNDUuODc1IDE0Ny43OEMxNDUuODc1IDE0OS42NTEgMTQ3LjM4OCAxNTEuMTY5IDE0OS4yNTQgMTUxLjE2OVoiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTE0Mi42NDQgMTM5LjkyNUMxNDIuNjQ0IDE0Mi4wNTIgMTQwLjkyNSAxNDMuNzc3IDEzOC44MDQgMTQzLjc3N0MxMzYuNjgzIDE0My43NzcgMTM0Ljk2NCAxNDIuMDUyIDEzNC45NjQgMTM5LjkyNUMxMzQuOTY0IDEzNy43OTcgMTM2LjY4MyAxMzYuMDczIDEzOC44MDQgMTM2LjA3M0MxNDAuOTI1IDEzNi4wNzMgMTQyLjY0NCAxMzcuNzk3IDE0Mi42NDQgMTM5LjkyNVoiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTEzOC42NTggMTE0Ljc5OEMxNDAuNzc5IDExNC43OTggMTQyLjQ5OCAxMTMuMDczIDE0Mi40OTggMTEwLjk0NkMxNDIuNDk4IDEwOC44MTggMTQwLjc3OSAxMDcuMDk0IDEzOC42NTggMTA3LjA5NEMxMzYuNTM3IDEwNy4wOTQgMTM0LjgxOCAxMDguODE4IDEzNC44MTggMTEwLjk0NkMxMzQuODE4IDExMy4wNzMgMTM2LjUzNyAxMTQuNzk4IDEzOC42NTggMTE0Ljc5OFoiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTE0My43MTYgOTcuODQ3NEMxNDMuNzE2IDk5LjcxOTIgMTQyLjIwMyAxMDEuMjM3IDE0MC4zMzcgMTAxLjIzN0MxMzguNDcxIDEwMS4yMzcgMTM2Ljk1OCA5OS43MTkyIDEzNi45NTggOTcuODQ3NEMxMzYuOTU4IDk1Ljk3NTcgMTM4LjQ3MSA5NC40NTgzIDE0MC4zMzcgOTQuNDU4M0MxNDIuMjAzIDk0LjQ1ODMgMTQzLjcxNiA5NS45NzU3IDE0My43MTYgOTcuODQ3NFoiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTE0NC4wMjkgOTAuMTQ3N0MxNDUuNTU4IDkwLjE0NzcgMTQ2Ljc5NyA4OC45MDQ0IDE0Ni43OTcgODcuMzcwN0MxNDYuNzk3IDg1LjgzNyAxNDUuNTU4IDg0LjU5MzggMTQ0LjAyOSA4NC41OTM4QzE0Mi41IDg0LjU5MzggMTQxLjI2IDg1LjgzNyAxNDEuMjYgODcuMzcwN0MxNDEuMjYgODguOTA0NCAxNDIuNSA5MC4xNDc3IDE0NC4wMjkgOTAuMTQ3N1oiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTE0MC45NjEgODEuNTE0MUMxNDAuOTYxIDgzLjM4NTggMTM5LjQ0OCA4NC45MDMyIDEzNy41ODIgODQuOTAzMkMxMzUuNzE2IDg0LjkwMzIgMTM0LjIwMyA4My4zODU4IDEzNC4yMDMgODEuNTE0MUMxMzQuMjAzIDc5LjY0MjMgMTM1LjcxNiA3OC4xMjUgMTM3LjU4MiA3OC4xMjVDMTM5LjQ0OCA3OC4xMjUgMTQwLjk2MSA3OS42NDIzIDE0MC45NjEgODEuNTE0MVoiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTE3My4zODIgMTUyLjI1NkMxNzUuNTAzIDE1Mi4yNTYgMTc3LjIyMiAxNTAuNTMxIDE3Ny4yMjIgMTQ4LjQwNEMxNzcuMjIyIDE0Ni4yNzcgMTc1LjUwMyAxNDQuNTUyIDE3My4zODIgMTQ0LjU1MkMxNzEuMjYxIDE0NC41NTIgMTY5LjU0MiAxNDYuMjc3IDE2OS41NDIgMTQ4LjQwNEMxNjkuNTQyIDE1MC41MzEgMTcxLjI2MSAxNTIuMjU2IDE3My4zODIgMTUyLjI1NloiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTEzMi42NTkgNzYuNTcwN0MxMzIuNjU5IDc4LjY5OCAxMzAuOTQgODAuNDIyNiAxMjguODE5IDgwLjQyMjZDMTI2LjY5OCA4MC40MjI2IDEyNC45NzkgNzguNjk4IDEyNC45NzkgNzYuNTcwN0MxMjQuOTc5IDc0LjQ0MzMgMTI2LjY5OCA3Mi43MTg4IDEyOC44MTkgNzIuNzE4OEMxMzAuOTQgNzIuNzE4OCAxMzIuNjU5IDc0LjQ0MzMgMTMyLjY1OSA3Ni41NzA3WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTMxLjQzOSA5Ni43NjExQzEzMy41NiA5Ni43NjExIDEzNS4yNzkgOTUuMDM2NiAxMzUuMjc5IDkyLjkwOTJDMTM1LjI3OSA5MC43ODE5IDEzMy41NiA4OS4wNTczIDEzMS40MzkgODkuMDU3M0MxMjkuMzE4IDg5LjA1NzMgMTI3LjU5OSA5MC43ODE5IDEyNy41OTkgOTIuOTA5MkMxMjcuNTk5IDk1LjAzNjYgMTI5LjMxOCA5Ni43NjExIDEzMS40MzkgOTYuNzYxMVoiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTEzMS43NDkgMTA3LjcwNkMxMzEuNzQ5IDExMC4yNjMgMTI5LjY4MyAxMTIuMzM1IDEyNy4xMzUgMTEyLjMzNUMxMjQuNTg3IDExMi4zMzUgMTIyLjUyMSAxMTAuMjYzIDEyMi41MjEgMTA3LjcwNkMxMjIuNTIxIDEwNS4xNSAxMjQuNTg3IDEwMy4wNzggMTI3LjEzNSAxMDMuMDc4QzEyOS42ODMgMTAzLjA3OCAxMzEuNzQ5IDEwNS4xNSAxMzEuNzQ5IDEwNy43MDZaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xMTMuMTQ0IDExMi4zMzdDMTE2LjExMiAxMTIuMzM3IDExOC41MTcgMTA5LjkyNCAxMTguNTE3IDEwNi45NDdDMTE4LjUxNyAxMDMuOTcgMTE2LjExMiAxMDEuNTU3IDExMy4xNDQgMTAxLjU1N0MxMTAuMTc3IDEwMS41NTcgMTA3Ljc3MSAxMDMuOTcgMTA3Ljc3MSAxMDYuOTQ3QzEwNy43NzEgMTA5LjkyNCAxMTAuMTc3IDExMi4zMzcgMTEzLjE0NCAxMTIuMzM3WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTAzLjAwOCAxMDkuODY5QzEwMy4wMDggMTEzLjI3NSAxMDAuMjU2IDExNi4wMzUgOTYuODYwOCAxMTYuMDM1QzkzLjQ2NTcgMTE2LjAzNSA5MC43MTM1IDExMy4yNzUgOTAuNzEzNSAxMDkuODY5QzkwLjcxMzUgMTA2LjQ2NCA5My40NjU3IDEwMy43MDMgOTYuODYwOCAxMDMuNzAzQzEwMC4yNTYgMTAzLjcwMyAxMDMuMDA4IDEwNi40NjQgMTAzLjAwOCAxMDkuODY5WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNNzkuMDQ3IDEyNC44MTVDODMuMDMzOSAxMjQuODE1IDg2LjI2NTkgMTIxLjU3MyA4Ni4yNjU5IDExNy41NzRDODYuMjY1OSAxMTMuNTc1IDgzLjAzMzkgMTEwLjMzMyA3OS4wNDcgMTEwLjMzM0M3NS4wNjAxIDExMC4zMzMgNzEuODI4MSAxMTMuNTc1IDcxLjgyODEgMTE3LjU3NEM3MS44MjgxIDEyMS41NzMgNzUuMDYwMSAxMjQuODE1IDc5LjA0NyAxMjQuODE1WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNNjkuMjA2MyAxMzAuOTg0QzY5LjIwNjMgMTM1Ljc1IDY1LjM1NDUgMTM5LjYxMyA2MC42MDMxIDEzOS42MTNDNTUuODUxOCAxMzkuNjEzIDUyIDEzNS43NSA1MiAxMzAuOTg0QzUyIDEyNi4yMTggNTUuODUxOCAxMjIuMzU0IDYwLjYwMzEgMTIyLjM1NEM2NS4zNTQ1IDEyMi4zNTQgNjkuMjA2MyAxMjYuMjE4IDY5LjIwNjMgMTMwLjk4NFoiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTcxLjgyODMgMTA2Ljc3OUM3NS44MTUyIDEwNi43NzkgNzkuMDQ3MiAxMDMuNTM3IDc5LjA0NzIgOTkuNTM3OUM3OS4wNDcyIDk1LjUzODggNzUuODE1MiA5Mi4yOTY5IDcxLjgyODMgOTIuMjk2OUM2Ny44NDE0IDkyLjI5NjkgNjQuNjA5NCA5NS41Mzg4IDY0LjYwOTQgOTkuNTM3OUM2NC42MDk0IDEwMy41MzcgNjcuODQxNCAxMDYuNzc5IDcxLjgyODMgMTA2Ljc3OVoiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTk1Ljc4OTIgOTEuODMyN0M5NS43ODkyIDk1LjIzODEgOTMuMDM3IDk3Ljk5ODcgODkuNjQyIDk3Ljk5ODdDODYuMjQ3IDk3Ljk5ODcgODMuNDk0OCA5NS4yMzgxIDgzLjQ5NDggOTEuODMyN0M4My40OTQ4IDg4LjQyNzMgODYuMjQ3IDg1LjY2NjcgODkuNjQyIDg1LjY2NjdDOTMuMDM3IDg1LjY2NjcgOTUuNzg5MiA4OC40MjczIDk1Ljc4OTIgOTEuODMyN1oiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTg3LjAzNzkgODEuNjY1NEM5MC40MzI5IDgxLjY2NTQgOTMuMTg1MSA3OC45MDQ4IDkzLjE4NTEgNzUuNDk5NEM5My4xODUxIDcyLjA5NCA5MC40MzI5IDY5LjMzMzMgODcuMDM3OSA2OS4zMzMzQzgzLjY0MjggNjkuMzMzMyA4MC44OTA2IDcyLjA5NCA4MC44OTA2IDc1LjQ5OTRDODAuODkwNiA3OC45MDQ4IDgzLjY0MjggODEuNjY1NCA4Ny4wMzc5IDgxLjY2NTRaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xMDguNjk0IDcyLjU3NzJDMTA4LjY5NCA3NS41NTM4IDEwNi4yODkgNzcuOTY2OSAxMDMuMzIxIDc3Ljk2NjlDMTAwLjM1NCA3Ny45NjY5IDk3Ljk0NzkgNzUuNTUzOCA5Ny45NDc5IDcyLjU3NzJDOTcuOTQ3OSA2OS42MDA1IDEwMC4zNTQgNjcuMTg3NSAxMDMuMzIxIDY3LjE4NzVDMTA2LjI4OSA2Ny4xODc1IDEwOC42OTQgNjkuNjAwNSAxMDguNjk0IDcyLjU3NzJaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xMTcuMjk2IDc3Ljk3NTNDMTE5Ljg0NSA3Ny45NzUzIDEyMS45MTEgNzUuOTAzMSAxMjEuOTExIDczLjM0N0MxMjEuOTExIDcwLjc5MDkgMTE5Ljg0NSA2OC43MTg4IDExNy4yOTYgNjguNzE4OEMxMTQuNzQ4IDY4LjcxODggMTEyLjY4MiA3MC43OTA5IDExMi42ODIgNzMuMzQ3QzExMi42ODIgNzUuOTAzMSAxMTQuNzQ4IDc3Ljk3NTMgMTE3LjI5NiA3Ny45NzUzWiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTI0LjUxNSA4OS42ODU2QzEyNC41MTUgOTIuMjQxNyAxMjIuNDQ5IDk0LjMxMzggMTE5LjkwMSA5NC4zMTM4QzExNy4zNTIgOTQuMzEzOCAxMTUuMjg2IDkyLjI0MTcgMTE1LjI4NiA4OS42ODU2QzExNS4yODYgODcuMTI5NCAxMTcuMzUyIDg1LjA1NzMgMTE5LjkwMSA4NS4wNTczQzEyMi40NDkgODUuMDU3MyAxMjQuNTE1IDg3LjEyOTQgMTI0LjUxNSA4OS42ODU2WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTA1LjkyNSA5NC40NTEzQzEwOC44OTMgOTQuNDUxMyAxMTEuMjk5IDkyLjAzODIgMTExLjI5OSA4OS4wNjE2QzExMS4yOTkgODYuMDg0OSAxMDguODkzIDgzLjY3MTkgMTA1LjkyNSA4My42NzE5QzEwMi45NTggODMuNjcxOSAxMDAuNTUyIDg2LjA4NDkgMTAwLjU1MiA4OS4wNjE2QzEwMC41NTIgOTIuMDM4MiAxMDIuOTU4IDk0LjQ1MTMgMTA1LjkyNSA5NC40NTEzWiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNOTMuNDg0OSAxMzIuNjgzQzkzLjQ4NDkgMTM0LjIxNyA5Mi4yNDU0IDEzNS40NiA5MC43MTY0IDEzNS40NkM4OS4xODc0IDEzNS40NiA4Ny45NDc5IDEzNC4yMTcgODcuOTQ3OSAxMzIuNjgzQzg3Ljk0NzkgMTMxLjE1IDg5LjE4NzQgMTI5LjkwNiA5MC43MTY0IDEyOS45MDZDOTIuMjQ1NCAxMjkuOTA2IDkzLjQ4NDkgMTMxLjE1IDkzLjQ4NDkgMTMyLjY4M1oiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTEwMS43NzUgMTM0LjA1OUMxMDMuNjQxIDEzNC4wNTkgMTA1LjE1MyAxMzIuNTQyIDEwNS4xNTMgMTMwLjY3QzEwNS4xNTMgMTI4Ljc5OSAxMDMuNjQxIDEyNy4yODEgMTAxLjc3NSAxMjcuMjgxQzk5LjkwODUgMTI3LjI4MSA5OC4zOTU4IDEyOC43OTkgOTguMzk1OCAxMzAuNjdDOTguMzk1OCAxMzIuNTQyIDk5LjkwODUgMTM0LjA1OSAxMDEuNzc1IDEzNC4wNTlaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xMTcuNzU4IDEyNS41ODZDMTE3Ljc1OCAxMjcuNzE0IDExNi4wMzkgMTI5LjQzOCAxMTMuOTE4IDEyOS40MzhDMTExLjc5NyAxMjkuNDM4IDExMC4wNzggMTI3LjcxNCAxMTAuMDc4IDEyNS41ODZDMTEwLjA3OCAxMjMuNDU5IDExMS43OTcgMTIxLjczNCAxMTMuOTE4IDEyMS43MzRDMTE2LjAzOSAxMjEuNzM0IDExNy43NTggMTIzLjQ1OSAxMTcuNzU4IDEyNS41ODZaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xMTYuODM4IDE0MS43NzJDMTE5LjM4NiAxNDEuNzcyIDEyMS40NTIgMTM5LjcgMTIxLjQ1MiAxMzcuMTQ0QzEyMS40NTIgMTM0LjU4OCAxMTkuMzg2IDEzMi41MTYgMTE2LjgzOCAxMzIuNTE2QzExNC4yOSAxMzIuNTE2IDExMi4yMjQgMTM0LjU4OCAxMTIuMjI0IDEzNy4xNDRDMTEyLjIyNCAxMzkuNyAxMTQuMjkgMTQxLjc3MiAxMTYuODM4IDE0MS43NzJaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xMDUuOTI1IDE0MC44NDdDMTA1LjkyNSAxNDIuOTc0IDEwNC4yMDYgMTQ0LjY5OSAxMDIuMDg1IDE0NC42OTlDOTkuOTY0MSAxNDQuNjk5IDk4LjI0NDggMTQyLjk3NCA5OC4yNDQ4IDE0MC44NDdDOTguMjQ0OCAxMzguNzE5IDk5Ljk2NDEgMTM2Ljk5NSAxMDIuMDg1IDEzNi45OTVDMTA0LjIwNiAxMzYuOTk1IDEwNS45MjUgMTM4LjcxOSAxMDUuOTI1IDE0MC44NDdaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik04OS4wMzUgMTQ0LjUzM0M5MC45MDEgMTQ0LjUzMyA5Mi40MTM3IDE0My4wMTYgOTIuNDEzNyAxNDEuMTQ0QzkyLjQxMzcgMTM5LjI3MyA5MC45MDEgMTM3Ljc1NSA4OS4wMzUgMTM3Ljc1NUM4Ny4xNjkgMTM3Ljc1NSA4NS42NTYyIDEzOS4yNzMgODUuNjU2MiAxNDEuMTQ0Qzg1LjY1NjIgMTQzLjAxNiA4Ny4xNjkgMTQ0LjUzMyA4OS4wMzUgMTQ0LjUzM1oiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTkzLjAyNDEgMTUxLjMyNkM5My4wMjQxIDE1My40NTMgOTEuMzA0OCAxNTUuMTc4IDg5LjE4MzkgMTU1LjE3OEM4Ny4wNjMgMTU1LjE3OCA4NS4zNDM4IDE1My40NTMgODUuMzQzOCAxNTEuMzI2Qzg1LjM0MzggMTQ5LjE5OSA4Ny4wNjMgMTQ3LjQ3NCA4OS4xODM5IDE0Ny40NzRDOTEuMzA0OCAxNDcuNDc0IDkzLjAyNDEgMTQ5LjE5OSA5My4wMjQxIDE1MS4zMjZaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xMDQuODU0IDE1Ny4xODRDMTA3LjQwMiAxNTcuMTg0IDEwOS40NjggMTU1LjExMSAxMDkuNDY4IDE1Mi41NTVDMTA5LjQ2OCAxNDkuOTk5IDEwNy40MDIgMTQ3LjkyNyAxMDQuODU0IDE0Ny45MjdDMTAyLjMwNSAxNDcuOTI3IDEwMC4yNCAxNDkuOTk5IDEwMC4yNCAxNTIuNTU1QzEwMC4yNCAxNTUuMTExIDEwMi4zMDUgMTU3LjE4NCAxMDQuODU0IDE1Ny4xODRaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xMjguNTA3IDE0OS43OEMxMjguNTA3IDE1Mi43NTcgMTI2LjEwMSAxNTUuMTcgMTIzLjEzNCAxNTUuMTdDMTIwLjE2NiAxNTUuMTcgMTE3Ljc2IDE1Mi43NTcgMTE3Ljc2IDE0OS43OEMxMTcuNzYgMTQ2LjgwNCAxMjAuMTY2IDE0NC4zOTEgMTIzLjEzNCAxNDQuMzkxQzEyNi4xMDEgMTQ0LjM5MSAxMjguNTA3IDE0Ni44MDQgMTI4LjUwNyAxNDkuNzhaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xMzMuODkyIDE2OC41ODdDMTM3LjI4NyAxNjguNTg3IDE0MC4wMzkgMTY1LjgyNyAxNDAuMDM5IDE2Mi40MjFDMTQwLjAzOSAxNTkuMDE2IDEzNy4yODcgMTU2LjI1NSAxMzMuODkyIDE1Ni4yNTVDMTMwLjQ5NyAxNTYuMjU1IDEyNy43NDUgMTU5LjAxNiAxMjcuNzQ1IDE2Mi40MjFDMTI3Ljc0NSAxNjUuODI3IDEzMC40OTcgMTY4LjU4NyAxMzMuODkyIDE2OC41ODdaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xMTYuNjc0IDE2NS4wM0MxMTYuNjc0IDE2OC4wMDcgMTE0LjI2OCAxNzAuNDIgMTExLjMgMTcwLjQyQzEwOC4zMzMgMTcwLjQyIDEwNS45MjcgMTY4LjAwNyAxMDUuOTI3IDE2NS4wM0MxMDUuOTI3IDE2Mi4wNTQgMTA4LjMzMyAxNTkuNjQxIDExMS4zIDE1OS42NDFDMTE0LjI2OCAxNTkuNjQxIDExNi42NzQgMTYyLjA1NCAxMTYuNjc0IDE2NS4wM1oiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTk4LjU1NTUgMTgwLjkxQzEwMS41MjMgMTgwLjkxIDEwMy45MjkgMTc4LjQ5NyAxMDMuOTI5IDE3NS41MkMxMDMuOTI5IDE3Mi41NDMgMTAxLjUyMyAxNzAuMTMgOTguNTU1NSAxNzAuMTNDOTUuNTg4IDE3MC4xMyA5My4xODIzIDE3Mi41NDMgOTMuMTgyMyAxNzUuNTJDOTMuMTgyMyAxNzguNDk3IDk1LjU4OCAxODAuOTEgOTguNTU1NSAxODAuOTFaIiBmaWxsPSIjMTcxRDI2Ii8+CjxwYXRoIGQ9Ik0xMTUuMyAxODguMzA3QzExNS4zIDE5MS43MTIgMTEyLjU0NyAxOTQuNDczIDEwOS4xNTIgMTk0LjQ3M0MxMDUuNzU3IDE5NC40NzMgMTAzLjAwNSAxOTEuNzEyIDEwMy4wMDUgMTg4LjMwN0MxMDMuMDA1IDE4NC45MDEgMTA1Ljc1NyAxODIuMTQxIDEwOS4xNTIgMTgyLjE0MUMxMTIuNTQ3IDE4Mi4xNDEgMTE1LjMgMTg0LjkwMSAxMTUuMyAxODguMzA3WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTM3LjQyMiAxOTYuOTRDMTQxLjQwOSAxOTYuOTQgMTQ0LjY0MSAxOTMuNjk4IDE0NC42NDEgMTg5LjY5OUMxNDQuNjQxIDE4NS43IDE0MS40MDkgMTgyLjQ1OCAxMzcuNDIyIDE4Mi40NThDMTMzLjQzNSAxODIuNDU4IDEzMC4yMDMgMTg1LjcgMTMwLjIwMyAxODkuNjk5QzEzMC4yMDMgMTkzLjY5OCAxMzMuNDM1IDE5Ni45NCAxMzcuNDIyIDE5Ni45NFoiIGZpbGw9IiMxNzFEMjYiLz4KPHBhdGggZD0iTTEyOC4wNiAxNzcuODMzQzEyOC4wNiAxODEuMjM4IDEyNS4zMDggMTgzLjk5OSAxMjEuOTEzIDE4My45OTlDMTE4LjUxOCAxODMuOTk5IDExNS43NjYgMTgxLjIzOCAxMTUuNzY2IDE3Ny44MzNDMTE1Ljc2NiAxNzQuNDI3IDExOC41MTggMTcxLjY2NyAxMjEuOTEzIDE3MS42NjdDMTI1LjMwOCAxNzEuNjY3IDEyOC4wNiAxNzQuNDI3IDEyOC4wNiAxNzcuODMzWiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNMTQ5LjI1NSAxODEuNTI5QzE1My4yNDIgMTgxLjUyOSAxNTYuNDc0IDE3OC4yODcgMTU2LjQ3NCAxNzQuMjg4QzE1Ni40NzQgMTcwLjI4OSAxNTMuMjQyIDE2Ny4wNDcgMTQ5LjI1NSAxNjcuMDQ3QzE0NS4yNjggMTY3LjA0NyAxNDIuMDM2IDE3MC4yODkgMTQyLjAzNiAxNzQuMjg4QzE0Mi4wMzYgMTc4LjI4NyAxNDUuMjY4IDE4MS41MjkgMTQ5LjI1NSAxODEuNTI5WiIgZmlsbD0iIzE3MUQyNiIvPgo8cGF0aCBkPSJNOTYuNzEyNyAxNjMuMDM1Qzk2LjcxMjcgMTY1LjU5MSA5NC42NDY4IDE2Ny42NjMgOTIuMDk4NSAxNjcuNjYzQzg5LjU1MDIgMTY3LjY2MyA4Ny40ODQ0IDE2NS41OTEgODcuNDg0NCAxNjMuMDM1Qzg3LjQ4NDQgMTYwLjQ3OCA4OS41NTAyIDE1OC40MDYgOTIuMDk4NSAxNTguNDA2Qzk0LjY0NjggMTU4LjQwNiA5Ni43MTI3IDE2MC40NzggOTYuNzEyNyAxNjMuMDM1WiIgZmlsbD0iIzE3MUQyNiIvPgo8L3N2Zz4K' as const; } get chains() { diff --git a/sdk/dapp-kit/src/components/AccountDropdownMenu.css.ts b/sdk/dapp-kit/src/components/AccountDropdownMenu.css.ts index ef0b38e5e9f..936061114ae 100644 --- a/sdk/dapp-kit/src/components/AccountDropdownMenu.css.ts +++ b/sdk/dapp-kit/src/components/AccountDropdownMenu.css.ts @@ -3,40 +3,46 @@ // SPDX-License-Identifier: Apache-2.0 import { style } from '@vanilla-extract/css'; - import { themeVars } from '../themes/themeContract.js'; export const connectedAccount = style({ - gap: 8, + gap: themeVars.spacing.xsmall, }); export const menuContainer = style({ zIndex: 999999999, }); +export const icon = style({ + color: themeVars.colors.body, +}); + export const menuContent = style({ display: 'flex', flexDirection: 'column', width: 180, maxHeight: 200, - marginTop: 4, - padding: 8, - gap: 8, + marginTop: themeVars.spacing.xsmall, + gap: themeVars.spacing.xxsmall, borderRadius: themeVars.radii.large, backgroundColor: themeVars.backgroundColors.dropdownMenu, + padding: themeVars.spacing.xxsmall, }); export const menuItem = style({ - padding: 8, userSelect: 'none', outline: 'none', display: 'flex', alignItems: 'center', - borderRadius: themeVars.radii.large, - selectors: { - '&[data-highlighted]': { - backgroundColor: themeVars.backgroundColors.primaryButton, - }, + borderRadius: themeVars.radii.medium, + fontSize: themeVars.fontSizes.medium, + fontWeight: themeVars.fontWeights.normal, + letterSpacing: themeVars.typography.letterSpacing, + lineHeight: themeVars.typography.lineHeight, + padding: themeVars.spacing.small, + ':hover': { + backgroundColor: themeVars.backgroundColors.primaryButtonHover, + cursor: 'pointer', }, }); diff --git a/sdk/dapp-kit/src/components/AccountDropdownMenu.tsx b/sdk/dapp-kit/src/components/AccountDropdownMenu.tsx index af5b23924e8..ecf04df5fbc 100644 --- a/sdk/dapp-kit/src/components/AccountDropdownMenu.tsx +++ b/sdk/dapp-kit/src/components/AccountDropdownMenu.tsx @@ -6,7 +6,6 @@ import { formatAddress } from '@iota/iota-sdk/utils'; import type { WalletAccount } from '@iota/wallet-standard'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import clsx from 'clsx'; - import { useAccounts } from '../hooks/wallet/useAccounts.js'; import { useDisconnectWallet } from '../hooks/wallet/useDisconnectWallet.js'; import { useSwitchAccount } from '../hooks/wallet/useSwitchAccount.js'; @@ -29,11 +28,9 @@ export function AccountDropdownMenu({ currentAccount }: AccountDropdownMenuProps - @@ -74,7 +71,7 @@ export function AccountDropdownMenuItem({ className={clsx(styles.menuItem, styles.switchAccountMenuItem)} onSelect={() => switchAccount({ account })} > - {account.label ?? formatAddress(account.address)} + {account.label ?? formatAddress(account.address)} {active ? : null} ); diff --git a/sdk/dapp-kit/src/components/ConnectButton.tsx b/sdk/dapp-kit/src/components/ConnectButton.tsx index 58016b4b43b..1d808f818c7 100644 --- a/sdk/dapp-kit/src/components/ConnectButton.tsx +++ b/sdk/dapp-kit/src/components/ConnectButton.tsx @@ -14,10 +14,7 @@ type ConnectButtonProps = { connectText?: ReactNode; } & ButtonHTMLAttributes; -export function ConnectButton({ - connectText = 'Connect Wallet', - ...buttonProps -}: ConnectButtonProps) { +export function ConnectButton({ connectText = 'Connect', ...buttonProps }: ConnectButtonProps) { const currentAccount = useCurrentAccount(); return currentAccount ? ( diff --git a/sdk/dapp-kit/src/components/WalletProvider.tsx b/sdk/dapp-kit/src/components/WalletProvider.tsx index 0dae6322522..e9cbfaca974 100644 --- a/sdk/dapp-kit/src/components/WalletProvider.tsx +++ b/sdk/dapp-kit/src/components/WalletProvider.tsx @@ -19,11 +19,22 @@ import { useUnsafeBurnerWallet } from '../hooks/wallet/useUnsafeBurnerWallet.js' import { useWalletPropertiesChanged } from '../hooks/wallet/useWalletPropertiesChanged.js'; import { useWalletsChanged } from '../hooks/wallet/useWalletsChanged.js'; import { lightTheme } from '../themes/lightTheme.js'; -import type { Theme } from '../themes/themeContract.js'; +import type { DynamicTheme, Theme } from '../themes/themeContract.js'; import { createInMemoryStore } from '../utils/stateStorage.js'; import { getRegisteredWallets } from '../utils/walletUtils.js'; import { createWalletStore } from '../walletStore.js'; import { InjectedThemeStyles } from './styling/InjectedThemeStyles.js'; +import { darkTheme } from '../themes/darkTheme.js'; + +const themeWithSelectorAndMediaQuery: DynamicTheme[] = [ + { + selector: '.dark', + variables: darkTheme, + }, + { + variables: lightTheme, + }, +]; export type WalletProviderProps = { /** A list of wallets that are sorted to the top of the wallet list, if they are available to connect to. By default, wallets are sorted by the order they are loaded in. */ @@ -59,7 +70,7 @@ export function WalletProvider({ storageKey = DEFAULT_STORAGE_KEY, enableUnsafeBurner = false, autoConnect = false, - theme = lightTheme, + theme = themeWithSelectorAndMediaQuery, children, }: WalletProviderProps) { const storeRef = useRef( diff --git a/sdk/dapp-kit/src/components/connect-modal/ConnectModal.css.ts b/sdk/dapp-kit/src/components/connect-modal/ConnectModal.css.ts index 99473feeb19..aa508ff559d 100644 --- a/sdk/dapp-kit/src/components/connect-modal/ConnectModal.css.ts +++ b/sdk/dapp-kit/src/components/connect-modal/ConnectModal.css.ts @@ -15,7 +15,16 @@ export const overlay = style({ }); export const title = style({ - paddingLeft: 8, + color: themeVars.colors.body, + fontSize: themeVars.fontSizes.xlarge, + fontWeight: themeVars.fontWeights.medium, + margin: 0, +}); + +export const separator = style({ + height: 1, + backgroundColor: themeVars.backgroundColors.dropdownMenuSeparator, + width: '100%', }); export const content = style({ @@ -23,20 +32,18 @@ export const content = style({ borderRadius: themeVars.radii.xlarge, color: themeVars.colors.body, position: 'fixed', - bottom: 16, - left: 16, - right: 16, + top: 0, + left: '50%', display: 'flex', flexDirection: 'column', - justifyContent: 'space-between', overflow: 'hidden', minHeight: '50vh', maxHeight: '85vh', - maxWidth: 700, + width: '330px', + transform: 'translate(-50%, 100%)', '@media': { 'screen and (min-width: 768px)': { flexDirection: 'row', - width: '100%', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', @@ -44,31 +51,6 @@ export const content = style({ }, }); -export const whatIsAWalletButton = style({ - backgroundColor: themeVars.backgroundColors.modalSecondary, - padding: 16, - '@media': { - 'screen and (min-width: 768px)': { - display: 'none', - }, - }, -}); - -export const viewContainer = style({ - display: 'none', - padding: 20, - flexGrow: 1, - '@media': { - 'screen and (min-width: 768px)': { - display: 'flex', - }, - }, -}); - -export const selectedViewContainer = style({ - display: 'flex', -}); - export const backButtonContainer = style({ position: 'absolute', top: 20, @@ -89,9 +71,10 @@ export const closeButtonContainer = style({ export const walletListContent = style({ display: 'flex', flexDirection: 'column', + width: '100%', flexGrow: 1, - gap: 24, - padding: 20, + gap: themeVars.spacing.medium, + padding: themeVars.spacing.medium, backgroundColor: themeVars.backgroundColors.modalPrimary, '@media': { 'screen and (min-width: 768px)': { @@ -99,27 +82,3 @@ export const walletListContent = style({ }, }, }); - -export const walletListContainer = style({ - display: 'flex', - justifyContent: 'space-between', - flexDirection: 'column', - flexGrow: 1, - '@media': { - 'screen and (min-width: 768px)': { - flexDirection: 'row', - flexBasis: 240, - flexGrow: 0, - flexShrink: 0, - }, - }, -}); - -export const walletListContainerWithViewSelected = style({ - display: 'none', - '@media': { - 'screen and (min-width: 768px)': { - display: 'flex', - }, - }, -}); diff --git a/sdk/dapp-kit/src/components/connect-modal/ConnectModal.tsx b/sdk/dapp-kit/src/components/connect-modal/ConnectModal.tsx index a26c6701ecb..542e0bf5edb 100644 --- a/sdk/dapp-kit/src/components/connect-modal/ConnectModal.tsx +++ b/sdk/dapp-kit/src/components/connect-modal/ConnectModal.tsx @@ -2,26 +2,17 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import type { WalletWithRequiredFeatures } from '@iota/wallet-standard'; import * as Dialog from '@radix-ui/react-dialog'; -import clsx from 'clsx'; import { useState } from 'react'; import type { ReactNode } from 'react'; - -import { useConnectWallet } from '../../hooks/wallet/useConnectWallet.js'; -import { getWalletUniqueIdentifier } from '../../utils/walletUtils.js'; -import { BackIcon } from '../icons/BackIcon.js'; import { CloseIcon } from '../icons/CloseIcon.js'; import { StyleMarker } from '../styling/StyleMarker.js'; import { Heading } from '../ui/Heading.js'; import { IconButton } from '../ui/IconButton.js'; import * as styles from './ConnectModal.css.js'; -import { ConnectionStatus } from './views/ConnectionStatus.js'; -import { GettingStarted } from './views/GettingStarted.js'; -import { WhatIsAWallet } from './views/WhatIsAWallet.js'; -import { WalletList } from './wallet-list/WalletList.js'; - -type ConnectModalView = 'getting-started' | 'what-is-a-wallet' | 'connection-status'; +import { useWallets } from '../../hooks/wallet/useWallets.js'; +import { WalletConnectListView } from './views/WalletConnect.js'; +import { GetTheWalletView } from './views/GetTheWallet.js'; type ControlledModalProps = { /** The controlled open state of the dialog. */ @@ -48,55 +39,14 @@ type ConnectModalProps = { } & (ControlledModalProps | UncontrolledModalProps); export function ConnectModal({ trigger, open, defaultOpen, onOpenChange }: ConnectModalProps) { + const wallets = useWallets(); const [isModalOpen, setModalOpen] = useState(open ?? defaultOpen); - const [currentView, setCurrentView] = useState(); - const [selectedWallet, setSelectedWallet] = useState(); - const { mutate, isError } = useConnectWallet(); - - const resetSelection = () => { - setSelectedWallet(undefined); - setCurrentView(undefined); - }; const handleOpenChange = (open: boolean) => { - if (!open) { - resetSelection(); - } setModalOpen(open); onOpenChange?.(open); }; - const connectWallet = (wallet: WalletWithRequiredFeatures) => { - setCurrentView('connection-status'); - mutate( - { wallet }, - { - onSuccess: () => handleOpenChange(false), - }, - ); - }; - - let modalContent: ReactNode | undefined; - switch (currentView) { - case 'what-is-a-wallet': - modalContent = ; - break; - case 'getting-started': - modalContent = ; - break; - case 'connection-status': - modalContent = ( - - ); - break; - default: - modalContent = ; - } - return ( {trigger} @@ -104,54 +54,19 @@ export function ConnectModal({ trigger, open, defaultOpen, onOpenChange }: Conne -
-
- - Connect a Wallet - - setCurrentView('getting-started')} - onSelect={(wallet) => { - if ( - getWalletUniqueIdentifier(selectedWallet) !== - getWalletUniqueIdentifier(wallet) - ) { - setSelectedWallet(wallet); - connectWallet(wallet); - } - }} +
+ + Connect a Wallet + +
+ {wallets?.length ? ( + -
- -
-
-
- resetSelection()} - > - - -
- {modalContent} + ) : ( + + )}
diff --git a/sdk/dapp-kit/src/components/connect-modal/views/ConnectionStatus.css.ts b/sdk/dapp-kit/src/components/connect-modal/views/ConnectionStatus.css.ts deleted file mode 100644 index 9bb2cb87622..00000000000 --- a/sdk/dapp-kit/src/components/connect-modal/views/ConnectionStatus.css.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { style } from '@vanilla-extract/css'; - -import { themeVars } from '../../../themes/themeContract.js'; - -export const container = style({ - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - width: '100%', -}); - -export const walletIcon = style({ - objectFit: 'cover', - width: 72, - height: 72, - borderRadius: themeVars.radii.large, -}); - -export const title = style({ - marginTop: 12, -}); - -export const connectionStatus = style({ - marginTop: 4, -}); - -export const retryButtonContainer = style({ - position: 'absolute', - bottom: 20, - right: 20, -}); diff --git a/sdk/dapp-kit/src/components/connect-modal/views/ConnectionStatus.tsx b/sdk/dapp-kit/src/components/connect-modal/views/ConnectionStatus.tsx deleted file mode 100644 index 282ed37348e..00000000000 --- a/sdk/dapp-kit/src/components/connect-modal/views/ConnectionStatus.tsx +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import type { WalletWithRequiredFeatures } from '@iota/wallet-standard'; - -import { Button } from '../../ui/Button.js'; -import { Heading } from '../../ui/Heading.js'; -import { Text } from '../../ui/Text.js'; -import * as styles from './ConnectionStatus.css.js'; - -type ConnectionStatusProps = { - selectedWallet: WalletWithRequiredFeatures; - hadConnectionError: boolean; - onRetryConnection: (selectedWallet: WalletWithRequiredFeatures) => void; -}; - -export function ConnectionStatus({ - selectedWallet, - hadConnectionError, - onRetryConnection, -}: ConnectionStatusProps) { - return ( -
- {`${selectedWallet.name} -
- - Opening {selectedWallet.name} - -
-
- {hadConnectionError ? ( - Connection failed - ) : ( - Confirm connection in the wallet... - )} -
- {hadConnectionError ? ( -
- -
- ) : null} -
- ); -} diff --git a/sdk/dapp-kit/src/components/connect-modal/views/GetTheWallet.css.ts b/sdk/dapp-kit/src/components/connect-modal/views/GetTheWallet.css.ts new file mode 100644 index 00000000000..fc06498d12f --- /dev/null +++ b/sdk/dapp-kit/src/components/connect-modal/views/GetTheWallet.css.ts @@ -0,0 +1,38 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { style } from '@vanilla-extract/css'; +import { themeVars } from '../../../themes/themeContract.js'; + +export const container = style({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + padding: themeVars.spacing.medium, + height: '100%', + gap: themeVars.spacing.xlarge, +}); + +export const content = style({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: themeVars.spacing.medium, +}); + +export const text = style({ + fontSize: themeVars.fontSizes.medium, + textAlign: 'center', + color: themeVars.colors.body, +}); + +export const button = style({ + display: 'flex', + alignItems: 'center', + gap: themeVars.spacing.xsmall, +}); + +export const icon = style({ + color: themeVars.colors.body, +}); diff --git a/sdk/dapp-kit/src/components/connect-modal/views/GetTheWallet.tsx b/sdk/dapp-kit/src/components/connect-modal/views/GetTheWallet.tsx new file mode 100644 index 00000000000..0c765a2b740 --- /dev/null +++ b/sdk/dapp-kit/src/components/connect-modal/views/GetTheWallet.tsx @@ -0,0 +1,25 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { WALLET_DOWNLOAD_URL } from '../../../constants/walletDefaults.js'; +import { ArrowTopRightIcon } from '../../icons/ArrowTopRightIcon.js'; +import { IotaIcon } from '../../icons/IotaIcon.js'; +import { Button } from '../../ui/Button.js'; +import * as styles from './GetTheWallet.css.js'; + +export function GetTheWalletView() { + return ( +
+
+ +

Don't have a wallet yet?

+
+ + + +
+ ); +} diff --git a/sdk/dapp-kit/src/components/connect-modal/views/GettingStarted.css.ts b/sdk/dapp-kit/src/components/connect-modal/views/GettingStarted.css.ts deleted file mode 100644 index a76855c904d..00000000000 --- a/sdk/dapp-kit/src/components/connect-modal/views/GettingStarted.css.ts +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { style } from '@vanilla-extract/css'; - -export const container = style({ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', -}); - -export const content = style({ - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - flexGrow: 1, - gap: 20, - padding: 40, -}); - -export const installButtonContainer = style({ - position: 'absolute', - bottom: 20, - right: 20, -}); diff --git a/sdk/dapp-kit/src/components/connect-modal/views/GettingStarted.tsx b/sdk/dapp-kit/src/components/connect-modal/views/GettingStarted.tsx deleted file mode 100644 index 12ab3cb4930..00000000000 --- a/sdk/dapp-kit/src/components/connect-modal/views/GettingStarted.tsx +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { Button } from '../../ui/Button.js'; -import { Heading } from '../../ui/Heading.js'; -import { InfoSection } from '../InfoSection.js'; -import * as styles from './GettingStarted.css.js'; - -export function GettingStarted() { - return ( -
- Get Started with Iota -
- - We recommend pinning Iota Wallet to your taskbar for quicker access. - - - Be sure to back up your wallet using a secure method. Never share your secret - phrase with anyone. - - - Once you set up your wallet, refresh this window browser to load up the - extension. - - -
-
- ); -} diff --git a/sdk/dapp-kit/src/components/connect-modal/views/WalletConnect.css.ts b/sdk/dapp-kit/src/components/connect-modal/views/WalletConnect.css.ts new file mode 100644 index 00000000000..6e7fd4a2a85 --- /dev/null +++ b/sdk/dapp-kit/src/components/connect-modal/views/WalletConnect.css.ts @@ -0,0 +1,55 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { style } from '@vanilla-extract/css'; +import { themeVars } from '../../../themes/themeContract.js'; + +export const walletConnectContainer = style({ + height: '100%', + width: '100%', + overflow: 'auto', +}); + +export const walletConnectFooter = style({ + display: 'flex', + flexDirection: 'column', + gap: themeVars.spacing.small, +}); + +export const errorContainer = style({ + display: 'flex', + width: '100%', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + gap: themeVars.spacing.small, +}); + +export const errorMessage = style({ + fontSize: themeVars.fontSizes.small, + color: themeVars.colors.bodyDanger, +}); + +export const successContainer = style({ + display: 'flex', + flexDirection: 'column', + alignItems: 'flex-start', + gap: themeVars.spacing.small, +}); + +export const openingText = style({ + fontSize: themeVars.fontSizes.small, + color: themeVars.colors.bodyMuted, +}); + +export const confirmText = style({ + fontSize: themeVars.fontSizes.medium, + color: themeVars.colors.body, +}); + +export const separator = style({ + height: 1, + backgroundColor: themeVars.backgroundColors.dropdownMenuSeparator, + width: '100%', +}); diff --git a/sdk/dapp-kit/src/components/connect-modal/views/WalletConnect.tsx b/sdk/dapp-kit/src/components/connect-modal/views/WalletConnect.tsx new file mode 100644 index 00000000000..2074a6905e7 --- /dev/null +++ b/sdk/dapp-kit/src/components/connect-modal/views/WalletConnect.tsx @@ -0,0 +1,79 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import type { WalletWithRequiredFeatures } from '@iota/wallet-standard'; +import { useState } from 'react'; +import { getWalletUniqueIdentifier } from '../../../utils/walletUtils.js'; +import { useConnectWallet } from '../../../hooks/wallet/useConnectWallet.js'; +import { WalletList } from '../wallet-list/WalletList.js'; +import * as styles from './WalletConnect.css.js'; +import { Button } from '../../ui/Button.js'; + +interface WalletConnectListViewProps { + wallets: WalletWithRequiredFeatures[]; + onOpenChange: (isOpen: boolean) => void; +} +export function WalletConnectListView({ wallets, onOpenChange }: WalletConnectListViewProps) { + const [selectedWallet, setSelectedWallet] = useState(); + const { mutate, isError } = useConnectWallet(); + function handleSelectWallet(wallet: WalletWithRequiredFeatures) { + if (getWalletUniqueIdentifier(selectedWallet) !== getWalletUniqueIdentifier(wallet)) { + setSelectedWallet(wallet); + connectWallet(wallet); + } + } + function resetSelection() { + setSelectedWallet(undefined); + } + + function handleOpenChange(open: boolean) { + if (!open) { + resetSelection(); + } + onOpenChange(open); + } + + function connectWallet(wallet: WalletWithRequiredFeatures) { + mutate( + { wallet }, + { + onSuccess: () => handleOpenChange(false), + }, + ); + } + return ( + <> +
+ +
+ {selectedWallet && ( +
+ {isError ? ( +
+

Connection failed

+ +
+ ) : ( +
+

Opening {selectedWallet.name}...

+
+

+ Please confirm the connection in your wallet +

+
+ )} +
+ )} + + ); +} diff --git a/sdk/dapp-kit/src/components/connect-modal/views/WhatIsAWallet.css.ts b/sdk/dapp-kit/src/components/connect-modal/views/WhatIsAWallet.css.ts deleted file mode 100644 index 36c9a667384..00000000000 --- a/sdk/dapp-kit/src/components/connect-modal/views/WhatIsAWallet.css.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { style } from '@vanilla-extract/css'; - -export const container = style({ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', -}); - -export const content = style({ - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - flexGrow: 1, - gap: 20, - padding: 40, -}); diff --git a/sdk/dapp-kit/src/components/connect-modal/views/WhatIsAWallet.tsx b/sdk/dapp-kit/src/components/connect-modal/views/WhatIsAWallet.tsx deleted file mode 100644 index 4e9c710dec5..00000000000 --- a/sdk/dapp-kit/src/components/connect-modal/views/WhatIsAWallet.tsx +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { Heading } from '../../ui/Heading.js'; -import { InfoSection } from '../InfoSection.js'; -import * as styles from './WhatIsAWallet.css.js'; - -export function WhatIsAWallet() { - return ( -
- What is a Wallet -
- - No need to create new accounts and passwords for every website. Just connect - your wallet and get going. - - - Send, receive, store, and display your digital assets like NFTs & coins. - -
-
- ); -} diff --git a/sdk/dapp-kit/src/components/connect-modal/views/index.ts b/sdk/dapp-kit/src/components/connect-modal/views/index.ts new file mode 100644 index 00000000000..f1e517673dc --- /dev/null +++ b/sdk/dapp-kit/src/components/connect-modal/views/index.ts @@ -0,0 +1,5 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './WalletConnect.js'; +export * from './GetTheWallet.js'; diff --git a/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletList.css.ts b/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletList.css.ts index c22d8ba24ef..8152bb51bd5 100644 --- a/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletList.css.ts +++ b/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletList.css.ts @@ -3,9 +3,13 @@ // SPDX-License-Identifier: Apache-2.0 import { style } from '@vanilla-extract/css'; +import { themeVars } from '../../../themes/themeContract.js'; export const container = style({ display: 'flex', flexDirection: 'column', - gap: 4, + gap: themeVars.spacing.xsmall, + height: '100%', + width: '100%', + maxHeight: '330px', }); diff --git a/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletList.tsx b/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletList.tsx index 88381c782e8..bc228bf1b31 100644 --- a/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletList.tsx +++ b/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletList.tsx @@ -3,41 +3,27 @@ // SPDX-License-Identifier: Apache-2.0 import type { WalletWithRequiredFeatures } from '@iota/wallet-standard'; - -import { useWallets } from '../../../hooks/wallet/useWallets.js'; import { getWalletUniqueIdentifier } from '../../../utils/walletUtils.js'; -import { IotaIcon } from '../../icons/IotaIcon.js'; import * as styles from './WalletList.css.js'; import { WalletListItem } from './WalletListItem.js'; -type WalletListProps = { +interface WalletListProps { selectedWalletName?: string; - onPlaceholderClick: () => void; onSelect: (wallet: WalletWithRequiredFeatures) => void; -}; - -export function WalletList({ selectedWalletName, onPlaceholderClick, onSelect }: WalletListProps) { - const wallets = useWallets(); + wallets: WalletWithRequiredFeatures[]; +} +export function WalletList({ selectedWalletName, onSelect, wallets }: WalletListProps) { return (
    - {wallets.length > 0 ? ( - wallets.map((wallet) => ( - onSelect(wallet)} - /> - )) - ) : ( + {wallets.map((wallet) => ( } - onClick={onPlaceholderClick} - isSelected + key={getWalletUniqueIdentifier(wallet)} + name={wallet.name} + icon={wallet.icon} + isSelected={getWalletUniqueIdentifier(wallet) === selectedWalletName} + onClick={() => onSelect(wallet)} /> - )} + ))}
); } diff --git a/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletListItem.css.ts b/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletListItem.css.ts index c6ce82f9972..3554aa2c57e 100644 --- a/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletListItem.css.ts +++ b/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletListItem.css.ts @@ -3,28 +3,36 @@ // SPDX-License-Identifier: Apache-2.0 import { style } from '@vanilla-extract/css'; - import { themeVars } from '../../../themes/themeContract.js'; export const container = style({ display: 'flex', + width: '100%', }); export const walletItem = style({ display: 'flex', alignItems: 'center', + justifyContent: 'space-between', + width: '100%', flexGrow: 1, - padding: 8, - gap: 8, + padding: themeVars.spacing.medium, borderRadius: themeVars.radii.large, ':hover': { - backgroundColor: themeVars.backgroundColors.walletItemHover, + backgroundColor: themeVars.backgroundColors.primaryButtonHover, }, }); export const selectedWalletItem = style({ - backgroundColor: themeVars.backgroundColors.walletItemSelected, - boxShadow: '0px 2px 6px rgba(0, 0, 0, 0.05)', + border: `1px solid ${themeVars.borderColors.outlineButton}`, + borderRadius: themeVars.radii.large, +}); + +export const walletName = style({ + display: 'flex', + alignItems: 'center', + flexDirection: 'row', + gap: themeVars.spacing.small, }); export const walletIcon = style({ diff --git a/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletListItem.tsx b/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletListItem.tsx index fb9dde09bb5..1822ca336ab 100644 --- a/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletListItem.tsx +++ b/sdk/dapp-kit/src/components/connect-modal/wallet-list/WalletListItem.tsx @@ -4,9 +4,9 @@ import { clsx } from 'clsx'; import type { ReactNode } from 'react'; - import { Heading } from '../../ui/Heading.js'; import * as styles from './WalletListItem.css.js'; +import { ArrowRightIcon } from '../../icons/ArrowRightIcon.js'; type WalletListItemProps = { name: string; @@ -23,14 +23,17 @@ export function WalletListItem({ name, icon, onClick, isSelected = false }: Wall type="button" onClick={onClick} > - {typeof icon === 'string' ? ( - {`${name} - ) : ( - icon - )} - -
{name}
-
+
+ {typeof icon === 'string' ? ( + {`${name} + ) : ( + icon + )} + +
{name}
+
+
+ ); diff --git a/sdk/dapp-kit/src/components/icons/ArrowRightIcon.tsx b/sdk/dapp-kit/src/components/icons/ArrowRightIcon.tsx new file mode 100644 index 00000000000..a7af8029f47 --- /dev/null +++ b/sdk/dapp-kit/src/components/icons/ArrowRightIcon.tsx @@ -0,0 +1,23 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import type { ComponentProps } from 'react'; + +export function ArrowRightIcon(props: ComponentProps<'svg'>) { + return ( + + + + ); +} diff --git a/sdk/dapp-kit/src/components/icons/ArrowTopRightIcon.tsx b/sdk/dapp-kit/src/components/icons/ArrowTopRightIcon.tsx new file mode 100644 index 00000000000..8b60ddc0ab9 --- /dev/null +++ b/sdk/dapp-kit/src/components/icons/ArrowTopRightIcon.tsx @@ -0,0 +1,23 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import type { ComponentProps } from 'react'; + +export function ArrowTopRightIcon(props: ComponentProps<'svg'>) { + return ( + + + + ); +} diff --git a/sdk/dapp-kit/src/components/icons/BackIcon.tsx b/sdk/dapp-kit/src/components/icons/BackIcon.tsx deleted file mode 100644 index 32bf963b04d..00000000000 --- a/sdk/dapp-kit/src/components/icons/BackIcon.tsx +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import type { ComponentProps } from 'react'; - -// FIXME: Replace this with a 10x10 icon to match the CheckIcon, or alternatively make the CheckIcon bigger -// Right now, the icons don't align on mobile :( -export function BackIcon(props: ComponentProps<'svg'>) { - return ( - - - - ); -} diff --git a/sdk/dapp-kit/src/components/icons/IotaIcon.tsx b/sdk/dapp-kit/src/components/icons/IotaIcon.tsx index 205cd3fc9f8..3bc4b326464 100644 --- a/sdk/dapp-kit/src/components/icons/IotaIcon.tsx +++ b/sdk/dapp-kit/src/components/icons/IotaIcon.tsx @@ -6,13 +6,229 @@ import type { ComponentProps } from 'react'; export function IotaIcon(props: ComponentProps<'svg'>) { return ( - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); diff --git a/sdk/dapp-kit/src/components/styling/StyleMarker.css.ts b/sdk/dapp-kit/src/components/styling/StyleMarker.css.ts index deb0993cf8a..001595fdae9 100644 --- a/sdk/dapp-kit/src/components/styling/StyleMarker.css.ts +++ b/sdk/dapp-kit/src/components/styling/StyleMarker.css.ts @@ -47,3 +47,18 @@ globalStyle(':where(h1, h2, h3, h4, h5, h6)', { fontWeight: 'inherit', margin: 0, }); + +globalStyle('::-webkit-scrollbar', { + width: '14px', +}); + +globalStyle('::-webkit-scrollbar-track', { + borderRadius: themeVars.radii.xlarge, + backgroundColor: 'transparent', +}); + +globalStyle('::-webkit-scrollbar-thumb', { + boxShadow: `inset 0 0 10px 10px ${themeVars.backgroundColors.scrollThumb}`, + borderRadius: themeVars.radii.xlarge, + border: '4px solid transparent', +}); diff --git a/sdk/dapp-kit/src/components/ui/Button.css.ts b/sdk/dapp-kit/src/components/ui/Button.css.ts index 794c8f13ea4..07c43671f4d 100644 --- a/sdk/dapp-kit/src/components/ui/Button.css.ts +++ b/sdk/dapp-kit/src/components/ui/Button.css.ts @@ -4,7 +4,6 @@ import type { RecipeVariants } from '@vanilla-extract/recipes'; import { recipe } from '@vanilla-extract/recipes'; - import { themeVars } from '../../themes/themeContract.js'; export const buttonVariants = recipe({ @@ -22,7 +21,6 @@ export const buttonVariants = recipe({ primary: { backgroundColor: themeVars.backgroundColors.primaryButton, color: themeVars.colors.primaryButton, - boxShadow: themeVars.shadows.primaryButton, ':hover': { backgroundColor: themeVars.backgroundColors.primaryButtonHover, }, @@ -31,15 +29,15 @@ export const buttonVariants = recipe({ borderWidth: 1, borderStyle: 'solid', borderColor: themeVars.borderColors.outlineButton, - color: themeVars.colors.outlineButton, + color: themeVars.colors.primaryButton, ':hover': { backgroundColor: themeVars.backgroundColors.outlineButtonHover, }, }, }, size: { - md: { borderRadius: themeVars.radii.medium, padding: '8px 16px' }, - lg: { borderRadius: themeVars.radii.large, padding: '16px 24px ' }, + md: { borderRadius: themeVars.radii.full, padding: '8px 16px' }, + lg: { borderRadius: themeVars.radii.full, padding: '16px 24px ' }, }, }, defaultVariants: { diff --git a/sdk/dapp-kit/src/components/ui/IconButton.css.ts b/sdk/dapp-kit/src/components/ui/IconButton.css.ts index b5e4f510cf0..9d507bf7c50 100644 --- a/sdk/dapp-kit/src/components/ui/IconButton.css.ts +++ b/sdk/dapp-kit/src/components/ui/IconButton.css.ts @@ -11,7 +11,4 @@ export const container = style({ padding: 8, color: themeVars.colors.iconButton, backgroundColor: themeVars.backgroundColors.iconButton, - ':hover': { - backgroundColor: themeVars.backgroundColors.iconButtonHover, - }, }); diff --git a/sdk/dapp-kit/src/components/ui/Text.css.ts b/sdk/dapp-kit/src/components/ui/Text.css.ts index c3044d3f99f..089c71d488f 100644 --- a/sdk/dapp-kit/src/components/ui/Text.css.ts +++ b/sdk/dapp-kit/src/components/ui/Text.css.ts @@ -4,7 +4,6 @@ import type { RecipeVariants } from '@vanilla-extract/recipes'; import { recipe } from '@vanilla-extract/recipes'; - import { themeVars } from '../../themes/themeContract.js'; export const textVariants = recipe({ @@ -20,6 +19,7 @@ export const textVariants = recipe({ bold: { fontWeight: themeVars.fontWeights.bold }, }, color: { + body: { color: themeVars.colors.body }, muted: { color: themeVars.colors.bodyMuted }, danger: { color: themeVars.colors.bodyDanger }, }, diff --git a/sdk/dapp-kit/src/constants/walletDefaults.ts b/sdk/dapp-kit/src/constants/walletDefaults.ts index be739763898..94338c7ddf4 100644 --- a/sdk/dapp-kit/src/constants/walletDefaults.ts +++ b/sdk/dapp-kit/src/constants/walletDefaults.ts @@ -18,3 +18,8 @@ export const DEFAULT_REQUIRED_FEATURES: (keyof WalletWithRequiredFeatures['featu ]; export const DEFAULT_PREFERRED_WALLETS = [IOTA_WALLET_NAME]; + +const WALLET_CHROME_EXTENSION_ID = 'TODO'; + +export const WALLET_DOWNLOAD_URL = + 'https://chromewebstore.google.com/detail/' + WALLET_CHROME_EXTENSION_ID; diff --git a/sdk/dapp-kit/src/index.ts b/sdk/dapp-kit/src/index.ts index 0868413591e..a267488a395 100644 --- a/sdk/dapp-kit/src/index.ts +++ b/sdk/dapp-kit/src/index.ts @@ -23,6 +23,7 @@ export * from './hooks/wallet/useSignPersonalMessage.js'; export * from './hooks/wallet/useSignTransactionBlock.js'; export * from './hooks/wallet/useSwitchAccount.js'; export * from './hooks/wallet/useWallets.js'; +export * from './themes/darkTheme.js'; export * from './themes/lightTheme.js'; export * from './types.js'; diff --git a/sdk/dapp-kit/src/themes/darkTheme.ts b/sdk/dapp-kit/src/themes/darkTheme.ts new file mode 100644 index 00000000000..e12870b478a --- /dev/null +++ b/sdk/dapp-kit/src/themes/darkTheme.ts @@ -0,0 +1,69 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import type { ThemeVars } from './themeContract.js'; + +export const darkTheme: ThemeVars = { + blurs: { + modalOverlay: 'blur(12px)', + }, + backgroundColors: { + primaryButton: '#253041', + primaryButtonHover: '#313D50', + outlineButtonHover: '#313D50', + modalOverlay: 'rgba(0, 47, 109, 0.72)', + modalPrimary: '#0f141c', + modalSecondary: '#171d26', + iconButton: 'transparent', + dropdownMenu: '#0f141c', + dropdownMenuSeparator: '#bed8ff14', + walletItemSelected: '#171D26', + walletItemHover: 'rgba(190, 216, 255, 0.12)', + scrollThumb: '#3c4656', + }, + borderColors: { + outlineButton: '#8892A1', + }, + colors: { + primaryButton: '#E3EAF6', + outlineButtonHover: '#171D26', + iconButton: '#E3EAF6', + body: '#E3EAF6', + bodyMuted: '#8892A1', + bodyDanger: '#FFB0BE', + }, + radii: { + small: '6px', + medium: '8px', + large: '12px', + xlarge: '16px', + full: '120px', + }, + fontWeights: { + normal: '400', + medium: '500', + bold: '600', + }, + fontSizes: { + small: '14px', + medium: '16px', + large: '18px', + xlarge: '20px', + }, + typography: { + fontFamily: + 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"', + fontStyle: 'normal', + lineHeight: '24px', + letterSpacing: '0.1px', + }, + spacing: { + xxsmall: '4px', + xsmall: '8px', + small: '12px', + medium: '16px', + large: '24px', + xlarge: '32px', + }, +}; diff --git a/sdk/dapp-kit/src/themes/lightTheme.ts b/sdk/dapp-kit/src/themes/lightTheme.ts index c95918bff2e..2b238f47270 100644 --- a/sdk/dapp-kit/src/themes/lightTheme.ts +++ b/sdk/dapp-kit/src/themes/lightTheme.ts @@ -6,42 +6,39 @@ import type { ThemeVars } from './themeContract.js'; export const lightTheme: ThemeVars = { blurs: { - modalOverlay: 'blur(0)', + modalOverlay: 'blur(12px)', }, backgroundColors: { - primaryButton: '#F6F7F9', - primaryButtonHover: '#F0F2F5', - outlineButtonHover: '#F4F4F5', - modalOverlay: 'rgba(24 36 53 / 20%)', - modalPrimary: 'white', - modalSecondary: '#F7F8F8', + primaryButton: '#DDE4F0', + primaryButtonHover: '#002F6D14', + outlineButtonHover: '#002F6D14', + modalOverlay: 'rgba(0, 47, 109, 0.72)', + modalPrimary: '#ffffff', + modalSecondary: '#e3eaf6', iconButton: 'transparent', - iconButtonHover: '#F0F1F2', dropdownMenu: '#FFFFFF', - dropdownMenuSeparator: '#F3F6F8', - walletItemSelected: 'white', - walletItemHover: '#3C424226', + dropdownMenuSeparator: '#002f6d14', + walletItemSelected: '#EFF4FA', + walletItemHover: 'rgba(0, 103, 238, 0.12)', + scrollThumb: '#cad4e2', }, borderColors: { - outlineButton: '#E4E4E7', + outlineButton: '#6E7787', }, colors: { - primaryButton: '#373737', - outlineButton: '#373737', - iconButton: '#000000', - body: '#182435', - bodyMuted: '#767A81', - bodyDanger: '#FF794B', + primaryButton: '#171D26', + outlineButtonHover: '#E3EAF6', + iconButton: '#171D26', + body: '#171D26', + bodyMuted: '#545E6E', + bodyDanger: '#B51431', }, radii: { small: '6px', medium: '8px', large: '12px', xlarge: '16px', - }, - shadows: { - primaryButton: '0px 4px 12px rgba(0, 0, 0, 0.1)', - walletItemSelected: '0px 2px 6px rgba(0, 0, 0, 0.05)', + full: '120px', }, fontWeights: { normal: '400', @@ -58,7 +55,15 @@ export const lightTheme: ThemeVars = { fontFamily: 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"', fontStyle: 'normal', - lineHeight: '1.3', - letterSpacing: '1', + lineHeight: '24px', + letterSpacing: '0.1px', + }, + spacing: { + xxsmall: '4px', + xsmall: '8px', + small: '12px', + medium: '16px', + large: '24px', + xlarge: '32px', }, }; diff --git a/sdk/dapp-kit/src/themes/themeContract.ts b/sdk/dapp-kit/src/themes/themeContract.ts index 56595f8fc56..3c3a0a28ebc 100644 --- a/sdk/dapp-kit/src/themes/themeContract.ts +++ b/sdk/dapp-kit/src/themes/themeContract.ts @@ -18,16 +18,16 @@ const themeContractValues = { modalPrimary: '', modalSecondary: '', iconButton: '', - iconButtonHover: '', dropdownMenu: '', dropdownMenuSeparator: '', + scrollThumb: '', }, borderColors: { outlineButton: '', }, colors: { primaryButton: '', - outlineButton: '', + outlineButtonHover: '', body: '', bodyMuted: '', bodyDanger: '', @@ -38,10 +38,7 @@ const themeContractValues = { medium: '', large: '', xlarge: '', - }, - shadows: { - primaryButton: '', - walletItemSelected: '', + full: '', }, fontWeights: { normal: '', @@ -60,6 +57,14 @@ const themeContractValues = { lineHeight: '', letterSpacing: '', }, + spacing: { + xxsmall: '', + xsmall: '', + small: '', + medium: '', + large: '', + xlarge: '', + }, }; export type ThemeVars = typeof themeContractValues; diff --git a/sdk/dapp-kit/test/components/ConnectButton.test.tsx b/sdk/dapp-kit/test/components/ConnectButton.test.tsx index c1e9ff508bc..d3f172b912b 100644 --- a/sdk/dapp-kit/test/components/ConnectButton.test.tsx +++ b/sdk/dapp-kit/test/components/ConnectButton.test.tsx @@ -14,7 +14,7 @@ describe('ConnectButton', () => { render(, { wrapper }); - const connectButtonEl = screen.getByRole('button', { name: 'Connect Wallet' }); + const connectButtonEl = screen.getByRole('button', { name: 'Connect' }); expect(connectButtonEl).toBeInTheDocument(); const user = userEvent.setup();