diff --git a/.github/workflows/_vercel_deploy.yml b/.github/workflows/_vercel_deploy.yml
index d85f6664fff..ffdb8fda37f 100644
--- a/.github/workflows/_vercel_deploy.yml
+++ b/.github/workflows/_vercel_deploy.yml
@@ -68,14 +68,16 @@ jobs:
secrets: inherit
with:
isProd: false
+ isStaging: false
- wallet-dashboard-prod:
- name: Vercel Wallet Dashboard Production
+ wallet-dashboard-staging:
+ name: Vercel Wallet Dashboard Staging
if: github.ref_name == 'develop'
uses: ./.github/workflows/apps_wallet_dashboard_deploy.yml
secrets: inherit
with:
- isProd: true
+ isProd: false
+ isStaging: true
apps-backend-preview:
name: Vercel apps-backend Preview
diff --git a/.github/workflows/apps_wallet_dashboard_deploy.yml b/.github/workflows/apps_wallet_dashboard_deploy.yml
index d3ae3f5f284..f533776b7bb 100644
--- a/.github/workflows/apps_wallet_dashboard_deploy.yml
+++ b/.github/workflows/apps_wallet_dashboard_deploy.yml
@@ -3,14 +3,25 @@ name: Deploy for Wallet Dashboard
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.WALLET_DASHBOARD_VERCEL_PROJECT_ID }}
+ WALLET_DASHBOARD_VERCEL_PROJECT_STAGING_URL: ${{ secrets.WALLET_DASHBOARD_VERCEL_PROJECT_STAGING_URL }}
on:
workflow_dispatch:
+ inputs:
+ isProd:
+ type: boolean
+ required: true
+ isStaging:
+ type: boolean
+ required: true
workflow_call:
inputs:
isProd:
type: boolean
required: true
+ isStaging:
+ type: boolean
+ required: true
jobs:
deploy:
@@ -64,8 +75,11 @@ jobs:
id: deploy_url
if: ${{ inputs.isProd == false }}
run: echo "DEPLOY_URL=$(cat vercel_output.txt | awk 'END{print}')" >> $GITHUB_OUTPUT
+ - name: Alias Staging deploy
+ if: ${{ inputs.isStaging }}
+ run: vercel alias ${{ steps.deploy_url.outputs.DEPLOY_URL }} $WALLET_DASHBOARD_VERCEL_PROJECT_STAGING_URL --token=${{ secrets.VERCEL_TOKEN }} --scope=${{ secrets.VERCEL_SCOPE }}
- name: Comment on pull request
- if: ${{ inputs.isProd == false }}
+ if: ${{ inputs.isProd == false && inputs.isStaging == false }}
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/Cargo.lock b/Cargo.lock
index c0b1c6e29bf..550aa462be0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -803,7 +803,7 @@ dependencies = [
"proc-macro2 1.0.86",
"quote 1.0.37",
"syn 1.0.109",
- "synstructure",
+ "synstructure 0.12.6",
]
[[package]]
@@ -5500,6 +5500,124 @@ dependencies = [
"cc",
]
+[[package]]
+name = "icu_collections"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_locid_transform_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+
+[[package]]
+name = "icu_normalizer"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "utf16_iter",
+ "utf8_iter",
+ "write16",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+
+[[package]]
+name = "icu_properties"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locid_transform",
+ "icu_properties_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+
+[[package]]
+name = "icu_provider"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_provider_macros",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_provider_macros"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
+dependencies = [
+ "proc-macro2 1.0.86",
+ "quote 1.0.37",
+ "syn 2.0.77",
+]
+
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -5508,12 +5626,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
-version = "0.5.0"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
dependencies = [
- "unicode-bidi",
- "unicode-normalization",
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
]
[[package]]
@@ -8275,7 +8404,7 @@ version = "0.8.0-alpha"
dependencies = [
"proc-macro2 1.0.86",
"syn 1.0.109",
- "synstructure",
+ "synstructure 0.12.6",
]
[[package]]
@@ -8876,7 +9005,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
dependencies = [
"cfg-if",
- "windows-targets 0.52.6",
+ "windows-targets 0.48.5",
]
[[package]]
@@ -8972,6 +9101,12 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+[[package]]
+name = "litemap"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
+
[[package]]
name = "lock_api"
version = "0.4.12"
@@ -10066,7 +10201,7 @@ dependencies = [
"proc-macro2 1.0.86",
"quote 1.0.37",
"syn 1.0.109",
- "synstructure",
+ "synstructure 0.12.6",
]
[[package]]
@@ -11040,9 +11175,9 @@ dependencies = [
[[package]]
name = "passkey-authenticator"
-version = "0.2.0"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4017d27e98940a98358b43a3fe19cb3d7b7c821c3b35634d8087230e92445579"
+checksum = "f9b065ce31354bcf23a333003c77f0d71f00eb95761b3390a069546e078a7a5b"
dependencies = [
"async-trait",
"coset",
@@ -11054,9 +11189,9 @@ dependencies = [
[[package]]
name = "passkey-client"
-version = "0.2.0"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f14d42b14749cc7927add34a9932b3b3cc5349a633384850baa67183061439dd"
+checksum = "5080bfafe23d139ae8be8b907453aee0b8af3ca7cf25d1f8d7bfcf7b079d3412"
dependencies = [
"ciborium",
"coset",
@@ -11072,14 +11207,16 @@ dependencies = [
[[package]]
name = "passkey-types"
-version = "0.2.0"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "499cff8432e71c5f8784d9645aac0f9fca604d67f59b68a606170b5e229c6538"
+checksum = "77144664f6aac5f629d7efa815f5098a054beeeca6ccafee5ec453fd2b0c53f9"
dependencies = [
"bitflags 2.6.0",
"ciborium",
"coset",
"data-encoding",
+ "getrandom 0.2.15",
+ "hmac",
"indexmap 2.5.0",
"rand 0.8.5",
"serde",
@@ -11087,6 +11224,7 @@ dependencies = [
"sha2 0.10.8",
"strum 0.25.0",
"typeshare",
+ "zeroize",
]
[[package]]
@@ -14337,6 +14475,17 @@ dependencies = [
"unicode-xid 0.2.6",
]
+[[package]]
+name = "synstructure"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2 1.0.86",
+ "quote 1.0.37",
+ "syn 2.0.77",
+]
+
[[package]]
name = "sysinfo"
version = "0.31.4"
@@ -14710,6 +14859,16 @@ dependencies = [
"crunchy",
]
+[[package]]
+name = "tinystr"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
[[package]]
name = "tinytemplate"
version = "1.2.1"
@@ -15338,7 +15497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
"cfg-if",
- "rand 0.8.5",
+ "rand 0.7.3",
"static_assertions",
]
@@ -15466,12 +15625,6 @@ dependencies = [
"version_check",
]
-[[package]]
-name = "unicode-bidi"
-version = "0.3.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
-
[[package]]
name = "unicode-ident"
version = "1.0.13"
@@ -15563,9 +15716,9 @@ dependencies = [
[[package]]
name = "url"
-version = "2.5.2"
+version = "2.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
dependencies = [
"form_urlencoded",
"idna",
@@ -15585,6 +15738,18 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+[[package]]
+name = "utf16_iter"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
[[package]]
name = "utf8parse"
version = "0.2.2"
@@ -16218,6 +16383,18 @@ dependencies = [
"windows-sys 0.48.0",
]
+[[package]]
+name = "write16"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+
+[[package]]
+name = "writeable"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+
[[package]]
name = "ws_stream_wasm"
version = "0.7.4"
@@ -16328,6 +16505,30 @@ dependencies = [
"time",
]
+[[package]]
+name = "yoke"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
+dependencies = [
+ "proc-macro2 1.0.86",
+ "quote 1.0.37",
+ "syn 2.0.77",
+ "synstructure 0.13.1",
+]
+
[[package]]
name = "yup-oauth2"
version = "8.3.2"
@@ -16376,6 +16577,27 @@ dependencies = [
"syn 2.0.77",
]
+[[package]]
+name = "zerofrom"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
+dependencies = [
+ "proc-macro2 1.0.86",
+ "quote 1.0.37",
+ "syn 2.0.77",
+ "synstructure 0.13.1",
+]
+
[[package]]
name = "zeroize"
version = "1.8.1"
@@ -16397,6 +16619,28 @@ dependencies = [
"syn 2.0.77",
]
+[[package]]
+name = "zerovec"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+dependencies = [
+ "proc-macro2 1.0.86",
+ "quote 1.0.37",
+ "syn 2.0.77",
+]
+
[[package]]
name = "zip"
version = "0.6.6"
diff --git a/Cargo.toml b/Cargo.toml
index 277ac3a5492..9dbfc1385f2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -284,9 +284,9 @@ object_store = { version = "0.10", features = ["aws", "gcp", "azure", "http"] }
once_cell = "1.18.0"
p256 = { version = "0.13.2", features = ["ecdsa"] }
parking_lot = "0.12.1"
-passkey-authenticator = { version = "0.2.0" }
-passkey-client = { version = "0.2.0" }
-passkey-types = { version = "0.2.0" }
+passkey-authenticator = "0.4.0"
+passkey-client = "0.4.0"
+passkey-types = "0.4.0"
pretty_assertions = "1.3.0"
proc-macro2 = "1.0.47"
prometheus = "0.13.3"
diff --git a/apps/core/src/components/stake/StakedCard.tsx b/apps/core/src/components/stake/StakedCard.tsx
index 58bc5c82f20..92f6b0e8184 100644
--- a/apps/core/src/components/stake/StakedCard.tsx
+++ b/apps/core/src/components/stake/StakedCard.tsx
@@ -37,7 +37,7 @@ export function StakedCard({
// For inactive validator, show principal + rewards
const [principalStaked, symbol] = useFormatCoin(
- inactiveValidator ? principal + rewards : principal,
+ inactiveValidator ? BigInt(principal) + rewards : principal,
IOTA_TYPE_ARG,
);
diff --git a/apps/core/src/components/transaction/info/UnstakeTransactionInfo.tsx b/apps/core/src/components/transaction/info/UnstakeTransactionInfo.tsx
index bb69cf69226..94e120821d3 100644
--- a/apps/core/src/components/transaction/info/UnstakeTransactionInfo.tsx
+++ b/apps/core/src/components/transaction/info/UnstakeTransactionInfo.tsx
@@ -27,21 +27,21 @@ export function UnstakeTransactionInfo({
renderExplorerLink,
}: UnstakeTransactionInfoProps) {
const json = event.parsedJson as {
- principal_amount?: number;
- reward_amount?: number;
+ principal_amount?: string;
+ reward_amount?: string;
validator_address?: string;
};
- const principalAmount = json?.principal_amount || 0;
- const rewardAmount = json?.reward_amount || 0;
+ const principalAmount = json?.principal_amount || '0';
+ const rewardAmount = json?.reward_amount || '0';
const validatorAddress = json?.validator_address;
- const totalAmount = Number(principalAmount) + Number(rewardAmount);
+ const totalAmount = BigInt(principalAmount) + BigInt(rewardAmount);
const [formatPrinciple, symbol] = useFormatCoin(principalAmount, IOTA_TYPE_ARG);
const [formatRewards] = useFormatCoin(rewardAmount || 0, IOTA_TYPE_ARG);
return (
{validatorAddress &&
}
- {totalAmount && (
+ {totalAmount !== 0n && (
)}
diff --git a/apps/core/src/hooks/useCountdownByTimestamp.ts b/apps/core/src/hooks/useCountdownByTimestamp.ts
index 3b79b69b915..18a5a598292 100644
--- a/apps/core/src/hooks/useCountdownByTimestamp.ts
+++ b/apps/core/src/hooks/useCountdownByTimestamp.ts
@@ -9,7 +9,17 @@ import {
MILLISECONDS_PER_SECOND,
} from '../constants';
-export function useCountdownByTimestamp(initialTimestamp: number | null): string {
+interface FormatCountdownOptions {
+ showSeconds?: boolean;
+ showMinutes?: boolean;
+ showHours?: boolean;
+ showDays?: boolean;
+}
+
+export function useCountdownByTimestamp(
+ initialTimestamp: number | null,
+ options?: FormatCountdownOptions,
+): string {
const [timeRemainingMs, setTimeRemainingMs] = useState(0);
useEffect(() => {
@@ -22,11 +32,19 @@ export function useCountdownByTimestamp(initialTimestamp: number | null): string
return () => clearInterval(interval);
}, [initialTimestamp]);
- const formattedCountdown = formatCountdown(timeRemainingMs);
+ const formattedCountdown = formatCountdown(timeRemainingMs, options);
return formattedCountdown;
}
-function formatCountdown(totalMilliseconds: number) {
+function formatCountdown(
+ totalMilliseconds: number,
+ {
+ showSeconds = true,
+ showMinutes = true,
+ showHours = true,
+ showDays = true,
+ }: FormatCountdownOptions = {},
+) {
const days = Math.floor(totalMilliseconds / MILLISECONDS_PER_DAY);
const hours = Math.floor((totalMilliseconds % MILLISECONDS_PER_DAY) / MILLISECONDS_PER_HOUR);
const minutes = Math.floor(
@@ -36,11 +54,11 @@ function formatCountdown(totalMilliseconds: number) {
(totalMilliseconds % MILLISECONDS_PER_MINUTE) / MILLISECONDS_PER_SECOND,
);
- const timeUnits = [];
- if (days > 0) timeUnits.push(`${days}d`);
- if (hours > 0) timeUnits.push(`${hours}h`);
- if (minutes > 0) timeUnits.push(`${minutes}m`);
- if (seconds > 0 || timeUnits.length === 0) timeUnits.push(`${seconds}s`);
+ const timeUnits: string[] = [];
+ if (showDays && days > 0) timeUnits.push(`${days}d`);
+ if (showHours && hours > 0) timeUnits.push(`${hours}h`);
+ if (showMinutes && minutes > 0) timeUnits.push(`${minutes}m`);
+ if (showSeconds && (seconds > 0 || timeUnits.length === 0)) timeUnits.push(`${seconds}s`);
return timeUnits.join(' ');
}
diff --git a/apps/core/src/utils/getStakeIotaByIotaId.ts b/apps/core/src/utils/getStakeIotaByIotaId.ts
index 5bdaa2a01ee..ba2e8837c99 100644
--- a/apps/core/src/utils/getStakeIotaByIotaId.ts
+++ b/apps/core/src/utils/getStakeIotaByIotaId.ts
@@ -10,7 +10,7 @@ export function getStakeIotaByIotaId(allDelegation: DelegatedStake[], stakeIotaI
allDelegation.reduce((acc, curr) => {
const total = BigInt(
curr.stakes.find(({ stakedIotaId }) => stakedIotaId === stakeIotaId)?.principal ||
- 0,
+ 0n,
);
return total + acc;
}, 0n) || 0n
diff --git a/apps/explorer/src/components/network/NetworkSelector.tsx b/apps/explorer/src/components/network/NetworkSelector.tsx
index 34da8635f62..deb20e35e9b 100644
--- a/apps/explorer/src/components/network/NetworkSelector.tsx
+++ b/apps/explorer/src/components/network/NetworkSelector.tsx
@@ -99,11 +99,12 @@ export function NetworkSelector(): JSX.Element {
key={idx}
onClick={() => handleNetworkSwitch(network.id)}
hideBottomBorder
+ isHighlighted={network === selectedNetwork}
>
diff --git a/apps/explorer/src/pages/transaction-result/programmable-transaction-view/utils.ts b/apps/explorer/src/pages/transaction-result/programmable-transaction-view/utils.ts
index 92e2e32d939..e239ce3a86e 100644
--- a/apps/explorer/src/pages/transaction-result/programmable-transaction-view/utils.ts
+++ b/apps/explorer/src/pages/transaction-result/programmable-transaction-view/utils.ts
@@ -24,10 +24,11 @@ export function flattenIotaArguments(data: (IotaArgument | IotaArgument[])[]): s
} else if ('NestedResult' in value) {
return `NestedResult(${value.NestedResult[0]}, ${value.NestedResult[1]})`;
}
+ } else if (typeof value === 'string') {
+ return value;
} else {
throw new Error('Not a correct flattenable data');
}
- return '';
})
.join(', ');
}
diff --git a/apps/ui-kit/package.json b/apps/ui-kit/package.json
index ff323347fea..89e9785c466 100644
--- a/apps/ui-kit/package.json
+++ b/apps/ui-kit/package.json
@@ -66,7 +66,7 @@
"tailwindcss": "^3.3.3",
"typescript": "^5.5.3",
"vite": "^5.3.3",
- "vite-plugin-dts": "^3.9.1",
+ "vite-plugin-dts": "^4.3.0",
"vite-tsconfig-paths": "^4.2.0"
}
}
diff --git a/apps/ui-kit/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx b/apps/ui-kit/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx
index 569054372f2..d15da2af5de 100644
--- a/apps/ui-kit/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx
+++ b/apps/ui-kit/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx
@@ -57,6 +57,10 @@ interface KeyValueProps {
* Reverse the KeyValue (optional).
*/
isReverse?: boolean;
+ /**
+ * Text shown on value hover.
+ */
+ valueHoverTitle?: string;
}
export function KeyValueInfo({
@@ -72,6 +76,7 @@ export function KeyValueInfo({
onCopyError,
fullwidth,
isReverse = false,
+ valueHoverTitle,
}: KeyValueProps): React.JSX.Element {
const flexDirectionClass = isReverse ? 'flex-row-reverse' : 'flex-row';
async function handleCopyClick(event: React.MouseEvent
) {
@@ -119,6 +124,7 @@ export function KeyValueInfo({
})}
>
): React.JSX.Element {
function handleKeyDown(event: React.KeyboardEvent) {
if ((event.key === 'Enter' || event.key === ' ') && !isDisabled && onClick) {
@@ -63,7 +68,10 @@ export function ListItem({
className={cx(
'relative flex flex-row items-center justify-between px-md py-sm text-neutral-10 dark:text-neutral-92',
!isDisabled && onClick ? 'cursor-pointer' : 'cursor-default',
- { 'state-layer': !isDisabled },
+ {
+ 'bg-shader-primary-dark-16 dark:bg-shader-inverted-dark-16': isHighlighted,
+ 'state-layer': !isDisabled,
+ },
)}
>
{children}
diff --git a/apps/ui-kit/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx b/apps/ui-kit/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx
index 6a0c8d5df45..86160c55771 100644
--- a/apps/ui-kit/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx
+++ b/apps/ui-kit/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx
@@ -18,16 +18,21 @@ export interface LoadingIndicatorProps {
* The text to display next to the loading indicator.
*/
text?: string;
+ /**
+ * The 'data-testid' attribute value (used in e2e tests)
+ */
+ testId?: string;
}
export function LoadingIndicator({
color = 'text-primary-30 dark:text-primary-80',
size = 'w-5 h-5',
text,
+ testId,
}: LoadingIndicatorProps): React.JSX.Element {
return (
-
+
{text && {text}}
);
diff --git a/apps/wallet-dashboard/app/(protected)/assets/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/page.tsx
index ed96fe0a519..7007cc86c5c 100644
--- a/apps/wallet-dashboard/app/(protected)/assets/page.tsx
+++ b/apps/wallet-dashboard/app/(protected)/assets/page.tsx
@@ -29,7 +29,7 @@ export default function AssetsDashboardPage(): React.JSX.Element {
const [selectedAsset, setSelectedAsset] = useState(null);
const [selectedCategory, setSelectedCategory] = useState(AssetCategory.Visual);
const account = useCurrentAccount();
- const { data, isFetching, fetchNextPage, hasNextPage } = useGetOwnedObjects(
+ const { data, isFetching, fetchNextPage, hasNextPage, refetch } = useGetOwnedObjects(
account?.address,
undefined,
OBJECTS_PER_REQ,
@@ -79,7 +79,11 @@ export default function AssetsDashboardPage(): React.JSX.Element {
fetchNextPage={fetchNextPage}
/>
{selectedAsset && (
- setSelectedAsset(null)} asset={selectedAsset} />
+ setSelectedAsset(null)}
+ asset={selectedAsset}
+ refetchAssets={refetch}
+ />
)}
diff --git a/apps/wallet-dashboard/app/(protected)/components/top-nav/TopNav.tsx b/apps/wallet-dashboard/app/(protected)/components/top-nav/TopNav.tsx
index 2fe8eba32f3..3b802387ca8 100644
--- a/apps/wallet-dashboard/app/(protected)/components/top-nav/TopNav.tsx
+++ b/apps/wallet-dashboard/app/(protected)/components/top-nav/TopNav.tsx
@@ -1,18 +1,43 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
+import { SettingsDialog, useSettingsDialog } from '@/components';
import { Badge, BadgeType, Button, ButtonType } from '@iota/apps-ui-kit';
+import { ConnectButton, useIotaClientContext } from '@iota/dapp-kit';
+import { getNetwork, Network } from '@iota/iota-sdk/client';
import { ThemeSwitcher } from '@iota/core';
-import { ConnectButton } from '@iota/dapp-kit';
import { Settings } from '@iota/ui-icons';
export function TopNav() {
+ const { network } = useIotaClientContext();
+ const { name: networkName } = getNetwork(network);
+ const {
+ isSettingsDialogOpen,
+ settingsDialogView,
+ setSettingsDialogView,
+ onCloseSettingsDialogClick,
+ onOpenSettingsDialogClick,
+ } = useSettingsDialog();
+
return (
-
+
+
- } type={ButtonType.Ghost} />
+ }
+ type={ButtonType.Ghost}
+ onClick={onOpenSettingsDialogClick}
+ />
);
}
diff --git a/apps/wallet-dashboard/app/(protected)/home/page.tsx b/apps/wallet-dashboard/app/(protected)/home/page.tsx
index 61f55475702..d435d44f630 100644
--- a/apps/wallet-dashboard/app/(protected)/home/page.tsx
+++ b/apps/wallet-dashboard/app/(protected)/home/page.tsx
@@ -8,6 +8,7 @@ import {
TransactionsOverview,
StakingOverview,
MigrationOverview,
+ SupplyIncreaseVestingOverview,
} from '@/components';
import { useFeature } from '@growthbook/growthbook-react';
import { Feature } from '@iota/core';
@@ -18,12 +19,13 @@ function HomeDashboardPage(): JSX.Element {
const account = useCurrentAccount();
const stardustMigrationEnabled = useFeature
(Feature.StardustMigration).value;
+ const supplyIncreaseVestingEnabled = useFeature(Feature.SupplyIncreaseVesting).value;
return (
{connectionStatus === 'connected' && account && (
<>
-
+
@@ -31,12 +33,10 @@ function HomeDashboardPage(): JSX.Element {
{stardustMigrationEnabled &&
}
-
+
-
- Vesting
-
+ {supplyIncreaseVestingEnabled &&
}
diff --git a/apps/wallet-dashboard/app/(protected)/migrations/page.tsx b/apps/wallet-dashboard/app/(protected)/migrations/page.tsx
index acc5dd6f40c..b042465c49e 100644
--- a/apps/wallet-dashboard/app/(protected)/migrations/page.tsx
+++ b/apps/wallet-dashboard/app/(protected)/migrations/page.tsx
@@ -6,9 +6,8 @@
import { useState, useMemo, useCallback } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import clsx from 'clsx';
-import MigratePopup from '@/components/Popup/Popups/MigratePopup';
-import { useGetStardustMigratableObjects, usePopups } from '@/hooks';
-import { summarizeMigratableObjectValues, summarizeUnmigratableObjectValues } from '@/lib/utils';
+import { useGetStardustMigratableObjects } from '@/hooks';
+import { summarizeMigratableObjectValues, summarizeTimelockedObjectValues } from '@/lib/utils';
import {
Button,
ButtonSize,
@@ -25,15 +24,14 @@ import { useCurrentAccount, useIotaClient } from '@iota/dapp-kit';
import { STARDUST_BASIC_OUTPUT_TYPE, STARDUST_NFT_OUTPUT_TYPE, useFormatCoin } from '@iota/core';
import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import { StardustOutputMigrationStatus } from '@/lib/enums';
-import { MigrationObjectsPanel } from '@/components';
+import { MigrationObjectsPanel, MigrationDialog } from '@/components';
function MigrationDashboardPage(): JSX.Element {
const account = useCurrentAccount();
const address = account?.address || '';
- const { openPopup, closePopup } = usePopups();
const queryClient = useQueryClient();
const iotaClient = useIotaClient();
-
+ const [isMigrationDialogOpen, setIsMigrationDialogOpen] = useState(false);
const [selectedStardustObjectsCategory, setSelectedStardustObjectsCategory] = useState<
StardustOutputMigrationStatus | undefined
>(undefined);
@@ -43,8 +41,8 @@ function MigrationDashboardPage(): JSX.Element {
const {
migratableBasicOutputs,
migratableNftOutputs,
- unmigratableBasicOutputs,
- unmigratableNftOutputs,
+ timelockedBasicOutputs,
+ timelockedNftOutputs,
} = stardustMigrationObjects || {};
const {
@@ -56,9 +54,9 @@ function MigrationDashboardPage(): JSX.Element {
nftOutputs: migratableNftOutputs,
address,
});
- const { totalUnmigratableObjects } = summarizeUnmigratableObjectValues({
- basicOutputs: unmigratableBasicOutputs,
- nftOutputs: unmigratableNftOutputs,
+ const { totalTimelockedObjects } = summarizeTimelockedObjectValues({
+ basicOutputs: timelockedBasicOutputs,
+ nftOutputs: timelockedNftOutputs,
});
const hasMigratableObjects =
@@ -108,7 +106,7 @@ function MigrationDashboardPage(): JSX.Element {
const TIMELOCKED_ASSETS_CARDS: MigrationDisplayCardProps[] = [
{
- title: `${totalUnmigratableObjects}`,
+ title: `${totalTimelockedObjects}`,
subtitle: 'Time-locked',
icon: Clock,
},
@@ -125,23 +123,16 @@ function MigrationDashboardPage(): JSX.Element {
selectedStardustObjectsCategory === StardustOutputMigrationStatus.TimeLocked
) {
return [
- ...stardustMigrationObjects.unmigratableBasicOutputs,
- ...stardustMigrationObjects.unmigratableNftOutputs,
+ ...stardustMigrationObjects.timelockedBasicOutputs,
+ ...stardustMigrationObjects.timelockedNftOutputs,
];
}
}
return [];
}, [selectedStardustObjectsCategory, stardustMigrationObjects]);
- function openMigratePopup(): void {
- openPopup(
-
,
- );
+ function openMigrationDialog(): void {
+ setIsMigrationDialogOpen(true);
}
function handleCloseDetailsPanel() {
@@ -157,6 +148,19 @@ function MigrationDashboardPage(): JSX.Element {
)}
>
+ {isMigrationDialogOpen && (
+
+ )}
}
@@ -212,7 +216,7 @@ function MigrationDashboardPage(): JSX.Element {
disabled={
selectedStardustObjectsCategory ===
StardustOutputMigrationStatus.TimeLocked ||
- !totalUnmigratableObjects
+ !totalTimelockedObjects
}
onClick={() =>
setSelectedStardustObjectsCategory(
diff --git a/apps/wallet-dashboard/app/(protected)/staking/page.tsx b/apps/wallet-dashboard/app/(protected)/staking/page.tsx
index e33b1253b95..87f69eccffa 100644
--- a/apps/wallet-dashboard/app/(protected)/staking/page.tsx
+++ b/apps/wallet-dashboard/app/(protected)/staking/page.tsx
@@ -3,7 +3,6 @@
'use client';
-import { StartStaking } from '@/components/staking-overview/StartStaking';
import {
Button,
ButtonSize,
@@ -16,7 +15,15 @@ import {
Title,
TitleSize,
} from '@iota/apps-ui-kit';
-import { StakeDialog, StakeDialogView } from '@/components';
+import {
+ StakeDialog,
+ StakeDialogView,
+ UnstakeDialog,
+ useUnstakeDialog,
+ UnstakeDialogView,
+ useStakeDialog,
+ StartStaking,
+} from '@/components';
import {
ExtendedDelegatedStake,
formatDelegatedStake,
@@ -32,8 +39,8 @@ import { useCurrentAccount, useIotaClient, useIotaClientQuery } from '@iota/dapp
import { IotaSystemStateSummary } from '@iota/iota-sdk/client';
import { Info } from '@iota/ui-icons';
import { useMemo } from 'react';
-import { useStakeDialog } from '@/components/Dialogs/Staking/hooks/useStakeDialog';
import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
+import { IotaSignAndExecuteTransactionOutput } from '@iota/wallet-standard';
function StakingDashboardPage(): React.JSX.Element {
const account = useCurrentAccount();
@@ -52,6 +59,14 @@ function StakingDashboardPage(): React.JSX.Element {
handleCloseStakeDialog,
handleNewStake,
} = useStakeDialog();
+ const {
+ isOpen: isUnstakeDialogOpen,
+ openUnstakeDialog,
+ defaultDialogProps,
+ handleClose: handleCloseUnstakeDialog,
+ setView: setUnstakeDialogView,
+ setTxDigest,
+ } = useUnstakeDialog();
const { data: delegatedStakeData, refetch: refetchDelegatedStakes } = useGetDelegatedStake({
address: account?.address || '',
@@ -100,6 +115,34 @@ function StakingDashboardPage(): React.JSX.Element {
.then(() => refetchDelegatedStakes());
}
+ function handleUnstakeClick() {
+ setStakeDialogView(undefined);
+ openUnstakeDialog();
+ }
+
+ function handleUnstakeDialogBack() {
+ setStakeDialogView(StakeDialogView.Details);
+ handleCloseUnstakeDialog();
+ }
+
+ function handleOnUnstakeBack(view: UnstakeDialogView): (() => void) | undefined {
+ if (view === UnstakeDialogView.Unstake) {
+ return handleUnstakeDialogBack;
+ }
+ }
+
+ function handleOnUnstakeSuccess(tx: IotaSignAndExecuteTransactionOutput): void {
+ setUnstakeDialogView(UnstakeDialogView.TransactionDetails);
+ iotaClient
+ .waitForTransaction({
+ digest: tx.digest,
+ })
+ .then((tx) => {
+ refetchDelegatedStakes();
+ setTxDigest(tx.digest);
+ });
+ }
+
return (
@@ -171,16 +214,27 @@ function StakingDashboardPage(): React.JSX.Element {
-
+ {isDialogStakeOpen && (
+
+ )}
+
+ {isUnstakeDialogOpen && selectedStake && (
+
+ )}
) : (
diff --git a/apps/wallet-dashboard/app/(protected)/vesting/page.tsx b/apps/wallet-dashboard/app/(protected)/vesting/page.tsx
index b925d57e97c..e29d94a7fef 100644
--- a/apps/wallet-dashboard/app/(protected)/vesting/page.tsx
+++ b/apps/wallet-dashboard/app/(protected)/vesting/page.tsx
@@ -6,22 +6,15 @@
import {
Banner,
StakeDialog,
- StakeDialogView,
- TimelockedUnstakePopup,
useStakeDialog,
VestingScheduleDialog,
+ UnstakeDialog,
+ StakeDialogView,
} from '@/components';
-import { useGetCurrentEpochStartTimestamp, useNotifications, usePopups } from '@/hooks';
-import {
- buildSupplyIncreaseVestingSchedule,
- formatDelegatedTimelockedStake,
- getLatestOrEarliestSupplyIncreaseVestingPayout,
- getVestingOverview,
- groupTimelockedStakedObjects,
- isTimelockedUnlockable,
- mapTimelockObjects,
- TimelockedStakedObjectsGrouped,
-} from '@/lib/utils';
+import { UnstakeDialogView } from '@/components/Dialogs/unstake/enums';
+import { useUnstakeDialog } from '@/components/Dialogs/unstake/hooks';
+import { useGetSupplyIncreaseVestingObjects, useNotifications } from '@/hooks';
+import { groupTimelockedStakedObjects, TimelockedStakedObjectsGrouped } from '@/lib/utils';
import { NotificationType } from '@/stores/notificationStore';
import { useFeature } from '@growthbook/growthbook-react';
import {
@@ -44,13 +37,9 @@ import {
} from '@iota/apps-ui-kit';
import {
Theme,
- TIMELOCK_IOTA_TYPE,
useFormatCoin,
useGetActiveValidatorsInfo,
- useGetAllOwnedObjects,
- useGetTimelockedStakedObjects,
useTheme,
- useUnlockTimelockedObjectsTransaction,
useCountdownByTimestamp,
Feature,
} from '@iota/core';
@@ -63,29 +52,22 @@ import {
import { IotaValidatorSummary } from '@iota/iota-sdk/client';
import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import { Calendar, StarHex } from '@iota/ui-icons';
-import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { StakedTimelockObject } from '@/components';
+import { IotaSignAndExecuteTransactionOutput } from '@iota/wallet-standard';
-function VestingDashboardPage(): JSX.Element {
+export default function VestingDashboardPage(): JSX.Element {
+ const [timelockedObjectsToUnstake, setTimelockedObjectsToUnstake] =
+ useState
(null);
const account = useCurrentAccount();
- const queryClient = useQueryClient();
+ const address = account?.address || '';
const iotaClient = useIotaClient();
const router = useRouter();
const { data: system } = useIotaClientQuery('getLatestIotaSystemState');
const [isVestingScheduleDialogOpen, setIsVestingScheduleDialogOpen] = useState(false);
const { addNotification } = useNotifications();
- const { openPopup, closePopup } = usePopups();
- const { data: currentEpochMs } = useGetCurrentEpochStartTimestamp();
const { data: activeValidators } = useGetActiveValidatorsInfo();
- const { data: timelockedObjects } = useGetAllOwnedObjects(account?.address || '', {
- StructType: TIMELOCK_IOTA_TYPE,
- });
-
- const { data: timelockedStakedObjects, isLoading: istimelockedStakedObjectsLoading } =
- useGetTimelockedStakedObjects(account?.address || '');
-
const { mutateAsync: signAndExecuteTransaction } = useSignAndExecuteTransaction();
const { theme } = useTheme();
@@ -96,16 +78,19 @@ function VestingDashboardPage(): JSX.Element {
const supplyIncreaseVestingEnabled = useFeature(Feature.SupplyIncreaseVesting).value;
- const timelockedMapped = mapTimelockObjects(timelockedObjects || []);
- const timelockedstakedMapped = formatDelegatedTimelockedStake(timelockedStakedObjects || []);
+ const {
+ nextPayout,
+ supplyIncreaseVestingPortfolio,
+ supplyIncreaseVestingSchedule,
+ supplyIncreaseVestingMapped,
+ supplyIncreaseVestingStakedMapped,
+ isTimelockedStakedObjectsLoading,
+ unlockAllSupplyIncreaseVesting,
+ refreshStakeList,
+ } = useGetSupplyIncreaseVestingObjects(address);
const timelockedStakedObjectsGrouped: TimelockedStakedObjectsGrouped[] =
- groupTimelockedStakedObjects(timelockedstakedMapped || []);
-
- const vestingSchedule = getVestingOverview(
- [...timelockedMapped, ...timelockedstakedMapped],
- Number(currentEpochMs),
- );
+ groupTimelockedStakedObjects(supplyIncreaseVestingStakedMapped || []);
const {
isDialogStakeOpen,
@@ -118,37 +103,30 @@ function VestingDashboardPage(): JSX.Element {
handleNewStake,
} = useStakeDialog();
- const nextPayout = getLatestOrEarliestSupplyIncreaseVestingPayout(
- [...timelockedMapped, ...timelockedstakedMapped],
- Number(currentEpochMs),
- false,
- );
-
- const lastPayout = getLatestOrEarliestSupplyIncreaseVestingPayout(
- [...timelockedMapped, ...timelockedstakedMapped],
- Number(currentEpochMs),
- true,
- );
-
- const vestingPortfolio =
- lastPayout && buildSupplyIncreaseVestingSchedule(lastPayout, Number(currentEpochMs));
+ const {
+ isOpen: isUnstakeDialogOpen,
+ openUnstakeDialog,
+ defaultDialogProps,
+ setTxDigest,
+ setView: setUnstakeDialogView,
+ } = useUnstakeDialog();
const formattedLastPayoutExpirationTime = useCountdownByTimestamp(
Number(nextPayout?.expirationTimestampMs),
);
const [formattedTotalVested, vestedSymbol] = useFormatCoin(
- vestingSchedule.totalVested,
+ supplyIncreaseVestingSchedule.totalVested,
IOTA_TYPE_ARG,
);
const [formattedTotalLocked, lockedSymbol] = useFormatCoin(
- vestingSchedule.totalLocked,
+ supplyIncreaseVestingSchedule.totalLocked,
IOTA_TYPE_ARG,
);
const [formattedAvailableClaiming, availableClaimingSymbol] = useFormatCoin(
- vestingSchedule.availableClaiming,
+ supplyIncreaseVestingSchedule.availableClaiming,
IOTA_TYPE_ARG,
);
@@ -164,54 +142,33 @@ function VestingDashboardPage(): JSX.Element {
}
const [totalStakedFormatted, totalStakedSymbol] = useFormatCoin(
- vestingSchedule.totalStaked,
+ supplyIncreaseVestingSchedule.totalStaked,
IOTA_TYPE_ARG,
);
const [totalEarnedFormatted, totalEarnedSymbol] = useFormatCoin(
- vestingSchedule.totalEarned,
+ supplyIncreaseVestingSchedule.totalEarned,
IOTA_TYPE_ARG,
);
- const unlockedTimelockedObjects = timelockedMapped?.filter((timelockedObject) =>
- isTimelockedUnlockable(timelockedObject, Number(currentEpochMs)),
- );
- const unlockedTimelockedObjectIds: string[] =
- unlockedTimelockedObjects.map((timelocked) => timelocked.id.id) || [];
- const { data: unlockAllTimelockedObjects } = useUnlockTimelockedObjectsTransaction(
- account?.address || '',
- unlockedTimelockedObjectIds,
- );
-
function handleOnSuccess(digest: string): void {
+ setTimelockedObjectsToUnstake(null);
+
iotaClient
.waitForTransaction({
digest,
})
- .then(() => {
- queryClient.invalidateQueries({
- queryKey: ['get-timelocked-staked-objects', account?.address],
- });
- queryClient.invalidateQueries({
- queryKey: [
- 'get-all-owned-objects',
- account?.address,
- {
- StructType: TIMELOCK_IOTA_TYPE,
- },
- ],
- });
- });
+ .then(refreshStakeList);
}
const handleCollect = () => {
- if (!unlockAllTimelockedObjects?.transactionBlock) {
+ if (!unlockAllSupplyIncreaseVesting?.transactionBlock) {
addNotification('Failed to create a Transaction', NotificationType.Error);
return;
}
signAndExecuteTransaction(
{
- transaction: unlockAllTimelockedObjects.transactionBlock,
+ transaction: unlockAllSupplyIncreaseVesting.transactionBlock,
},
{
onSuccess: (tx) => {
@@ -228,34 +185,29 @@ function VestingDashboardPage(): JSX.Element {
};
function handleUnstake(delegatedTimelockedStake: TimelockedStakedObjectsGrouped): void {
- const validatorInfo = getValidatorByAddress(delegatedTimelockedStake.validatorAddress);
- if (!account || !validatorInfo) {
- addNotification('Cannot create transaction', NotificationType.Error);
- return;
- }
-
- openPopup(
- ,
- );
+ setTimelockedObjectsToUnstake(delegatedTimelockedStake);
+ openUnstakeDialog(UnstakeDialogView.TimelockedUnstake);
}
- function openReceiveTokenPopup(): void {
+ function openReceiveTokenDialog(): void {
setIsVestingScheduleDialogOpen(true);
}
+ function handleOnSuccessUnstake(tx: IotaSignAndExecuteTransactionOutput): void {
+ setUnstakeDialogView(UnstakeDialogView.TransactionDetails);
+ iotaClient.waitForTransaction({ digest: tx.digest }).then((tx) => {
+ refreshStakeList();
+ setTxDigest(tx.digest);
+ });
+ }
+
useEffect(() => {
if (!supplyIncreaseVestingEnabled) {
router.push('/');
}
}, [router, supplyIncreaseVestingEnabled]);
- if (istimelockedStakedObjectsLoading) {
+ if (isTimelockedStakedObjectsLoading) {
return (
@@ -264,152 +216,177 @@ function VestingDashboardPage(): JSX.Element {
}
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {vestingPortfolio && (
-
- )}
-
-
-
- {timelockedstakedMapped.length === 0 ? (
-
handleNewStake()}
- buttonText="Stake"
- />
- ) : null}
-
-
- {timelockedstakedMapped.length !== 0 ? (
-
+ <>
+
+
- {
- setStakeDialogView(StakeDialogView.SelectValidator);
- }}
- />
- }
- />
-
-
-
+
+
-
-
- {system &&
- timelockedStakedObjectsGrouped?.map(
- (timelockedStakedObject) => {
- return (
-
- );
- },
- )}
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {supplyIncreaseVestingPortfolio && (
+
+ )}
+
+ {supplyIncreaseVestingMapped.length === 0 ? (
+
handleNewStake()}
+ buttonText="Stake"
+ />
+ ) : null}
- ) : null}
-
-
+
+ {supplyIncreaseVestingMapped.length !== 0 ? (
+
+
+ {
+ setStakeDialogView(StakeDialogView.SelectValidator);
+ }}
+ />
+ }
+ />
+
+
+
+
+ {system &&
+ timelockedStakedObjectsGrouped?.map(
+ (timelockedStakedObject) => {
+ return (
+
+ );
+ },
+ )}
+
+
+
+
+ ) : null}
+
+ {isDialogStakeOpen && (
+
+ )}
+
+ {isUnstakeDialogOpen && timelockedObjectsToUnstake && (
+
+ )}
+
+ >
);
}
-
-export default VestingDashboardPage;
diff --git a/apps/wallet-dashboard/app/globals.css b/apps/wallet-dashboard/app/globals.css
index 04c6811f389..69ca23f41f2 100644
--- a/apps/wallet-dashboard/app/globals.css
+++ b/apps/wallet-dashboard/app/globals.css
@@ -24,7 +24,6 @@ body {
'balance'
'staking'
'coins'
- 'vesting'
'activity';
&
@@ -36,13 +35,29 @@ body {
height: 200px;
}
}
- .home-page-grid-container:has(.with-migration) {
+ .home-page-grid-container:has(.with-vesting):not(:has(.with-migration)) {
+ grid-template-areas:
+ 'balance'
+ 'staking'
+ 'vesting'
+ 'coins'
+ 'activity';
+ }
+ .home-page-grid-container:has(.with-migration):not(:has(.with-vesting)) {
grid-template-areas:
'balance'
'staking'
'migration'
'coins'
+ 'activity';
+ }
+ .home-page-grid-container:has(.with-migration):has(.with-vesting) {
+ grid-template-areas:
+ 'balance'
+ 'staking'
+ 'migration'
'vesting'
+ 'coins'
'activity';
}
@@ -53,28 +68,56 @@ body {
'balance balance'
'staking staking'
'coins coins'
+ 'activity activity';
+ }
+ .home-page-grid-container:has(.with-vesting):not(:has(.with-migration)) {
+ grid-template-areas:
+ 'balance balance'
+ 'staking staking'
'vesting vesting'
+ 'coins coins'
'activity activity';
}
- .home-page-grid-container:has(.with-migration) {
+ .home-page-grid-container:has(.with-migration):not(:has(.with-vesting)) {
grid-template-areas:
'balance balance'
'staking migration'
'coins coins'
+ 'activity activity';
+ }
+ .home-page-grid-container:has(.with-migration):has(.with-vesting) {
+ grid-template-areas:
+ 'balance balance'
+ 'staking migration'
'vesting vesting'
+ 'coins coins'
'activity activity';
}
}
@screen md {
.home-page-grid-container {
+ min-height: 700px;
+ height: calc(100vh - 140px);
@apply grid-cols-3;
+ grid-template-areas:
+ 'balance staking staking'
+ 'coins activity activity';
+ }
+ .home-page-grid-container:has(.with-vesting):not(:has(.with-migration)) {
grid-template-areas:
'balance staking staking'
'coins vesting vesting'
'coins activity activity';
}
- .home-page-grid-container:has(.with-migration) {
+
+ .home-page-grid-container:has(.with-migration):not(:has(.with-vesting)) {
+ grid-template-areas:
+ 'balance staking migration'
+ 'coins activity activity';
+ }
+
+ .home-page-grid-container:has(.with-migration):has(.with-vesting) {
grid-template-areas:
'balance staking migration'
'coins vesting vesting'
diff --git a/apps/wallet-dashboard/app/page.tsx b/apps/wallet-dashboard/app/page.tsx
index e3036bf38c2..3392c231d31 100644
--- a/apps/wallet-dashboard/app/page.tsx
+++ b/apps/wallet-dashboard/app/page.tsx
@@ -63,8 +63,10 @@ function HomeDashboardPage(): JSX.Element {
-
+
© IOTA Foundation {CURRENT_YEAR}
+
+ {process.env.NEXT_PUBLIC_DASHBOARD_REV}
>
diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx
index ec3d8d38d11..c71390a33aa 100644
--- a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx
+++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx
@@ -4,17 +4,20 @@
import React, { useState } from 'react';
import { Dialog } from '@iota/apps-ui-kit';
import { FormikProvider, useFormik } from 'formik';
-import { useCurrentAccount } from '@iota/dapp-kit';
+import { useIotaClient, useCurrentAccount } from '@iota/dapp-kit';
import { createNftSendValidationSchema } from '@iota/core';
import { DetailsView, SendView } from './views';
import { IotaObjectData } from '@iota/iota-sdk/client';
import { AssetsDialogView } from './constants';
import { useCreateSendAssetTransaction, useNotifications } from '@/hooks';
import { NotificationType } from '@/stores/notificationStore';
+import { TransactionDetailsView } from '../SendToken';
+import { DialogLayout } from '../layout';
interface AssetsDialogProps {
onClose: () => void;
asset: IotaObjectData;
+ refetchAssets: () => void;
}
interface FormValues {
@@ -25,12 +28,14 @@ const INITIAL_VALUES: FormValues = {
to: '',
};
-export function AssetDialog({ onClose, asset }: AssetsDialogProps): JSX.Element {
+export function AssetDialog({ onClose, asset, refetchAssets }: AssetsDialogProps): JSX.Element {
const [view, setView] = useState
(AssetsDialogView.Details);
const account = useCurrentAccount();
+ const [digest, setDigest] = useState('');
const activeAddress = account?.address ?? '';
const objectId = asset?.objectId ?? '';
const { addNotification } = useNotifications();
+ const iotaClient = useIotaClient();
const validationSchema = createNftSendValidationSchema(activeAddress, objectId);
const { mutation: sendAsset } = useCreateSendAssetTransaction(objectId);
@@ -44,10 +49,16 @@ export function AssetDialog({ onClose, asset }: AssetsDialogProps): JSX.Element
async function onSubmit(values: FormValues) {
try {
- await sendAsset.mutateAsync(values.to);
+ const executed = await sendAsset.mutateAsync(values.to);
+
+ const tx = await iotaClient.waitForTransaction({
+ digest: executed.digest,
+ });
+
+ setDigest(tx.digest);
+ refetchAssets();
addNotification('Transfer transaction successful', NotificationType.Success);
- onClose();
- setView(AssetsDialogView.Details);
+ setView(AssetsDialogView.TransactionDetails);
} catch {
addNotification('Transfer transaction failed', NotificationType.Error);
}
@@ -66,16 +77,26 @@ export function AssetDialog({ onClose, asset }: AssetsDialogProps): JSX.Element
}
return (
);
}
diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/constants/AssetsDialogView.ts b/apps/wallet-dashboard/components/Dialogs/Assets/constants/AssetsDialogView.ts
index 88cb34c15b1..242da33c2c3 100644
--- a/apps/wallet-dashboard/components/Dialogs/Assets/constants/AssetsDialogView.ts
+++ b/apps/wallet-dashboard/components/Dialogs/Assets/constants/AssetsDialogView.ts
@@ -4,4 +4,5 @@
export enum AssetsDialogView {
Details = 'Details',
Send = 'Send',
+ TransactionDetails = 'TransactionDetails',
}
diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx
index 3929ba0062f..8520d62a484 100644
--- a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx
+++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx
@@ -13,7 +13,7 @@ import {
} from '@iota/apps-ui-kit';
import Link from 'next/link';
import { formatAddress } from '@iota/iota-sdk/utils';
-import { DialogLayout, DialogLayoutBody, DialogLayoutFooter } from '../../layout';
+import { DialogLayoutBody, DialogLayoutFooter } from '../../layout';
import { IotaObjectData } from '@iota/iota-sdk/client';
import { ExplorerLink } from '@/components/ExplorerLink';
import { useCurrentAccount } from '@iota/dapp-kit';
@@ -55,7 +55,7 @@ export function DetailsView({ onClose, asset, onSend }: DetailsViewProps) {
}
return (
-
+ <>
@@ -195,6 +195,6 @@ export function DetailsView({ onClose, asset, onSend }: DetailsViewProps) {
)}
-
+ >
);
}
diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx
index b5757582c7b..3d0350478ac 100644
--- a/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx
+++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx
@@ -4,7 +4,7 @@
import React from 'react';
import { AddressInput, useNftDetails } from '@iota/core';
import { useFormikContext } from 'formik';
-import { DialogLayout, DialogLayoutFooter, DialogLayoutBody } from '../../layout';
+import { DialogLayoutFooter, DialogLayoutBody } from '../../layout';
import {
Button,
ButtonHtmlType,
@@ -33,7 +33,7 @@ export function SendView({ asset, onClose, onBack }: SendViewProps) {
const { nftName, nftImageUrl } = useNftDetails(objectId, senderAddress);
return (
-
+ <>
@@ -65,6 +65,6 @@ export function SendView({ asset, onClose, onBack }: SendViewProps) {
onClick={submitForm}
/>
-
+ >
);
}
diff --git a/apps/wallet-dashboard/components/Dialogs/MigrationDialog.tsx b/apps/wallet-dashboard/components/Dialogs/MigrationDialog.tsx
new file mode 100644
index 00000000000..f1f661c2db4
--- /dev/null
+++ b/apps/wallet-dashboard/components/Dialogs/MigrationDialog.tsx
@@ -0,0 +1,176 @@
+// Copyright (c) 2024 IOTA Stiftung
+// SPDX-License-Identifier: Apache-2.0
+
+import React from 'react';
+import { VirtualList } from '@/components';
+import { useCurrentAccount, useSignAndExecuteTransaction } from '@iota/dapp-kit';
+import { IotaObjectData } from '@iota/iota-sdk/client';
+import { useMigrationTransaction } from '@/hooks/useMigrationTransaction';
+import {
+ Button,
+ Dialog,
+ Header,
+ InfoBox,
+ InfoBoxStyle,
+ InfoBoxType,
+ KeyValueInfo,
+ LoadingIndicator,
+ Panel,
+ Title,
+ TitleSize,
+} from '@iota/apps-ui-kit';
+import { useGroupedMigrationObjectsByExpirationDate } from '@/hooks';
+import { Loader, Warning } from '@iota/ui-icons';
+import { DialogLayout, DialogLayoutBody, DialogLayoutFooter } from './layout';
+import { MigrationObjectDetailsCard } from '../migration/migration-object-details-card';
+import { Collapsible, useFormatCoin } from '@iota/core';
+import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
+import { summarizeMigratableObjectValues } from '@/lib/utils';
+import toast from 'react-hot-toast';
+
+interface MigrationDialogProps {
+ basicOutputObjects: IotaObjectData[] | undefined;
+ nftOutputObjects: IotaObjectData[] | undefined;
+ onSuccess: (digest: string) => void;
+ setOpen: (bool: boolean) => void;
+ open: boolean;
+ isTimelocked: boolean;
+}
+
+export function MigrationDialog({
+ basicOutputObjects = [],
+ nftOutputObjects = [],
+ onSuccess,
+ open,
+ setOpen,
+ isTimelocked,
+}: MigrationDialogProps): JSX.Element {
+ const account = useCurrentAccount();
+ const {
+ data: migrateData,
+ isPending: isMigrationPending,
+ isError: isMigrationError,
+ } = useMigrationTransaction(account?.address || '', basicOutputObjects, nftOutputObjects);
+
+ const {
+ data: resolvedObjects = [],
+ isLoading,
+ error: isGroupedMigrationError,
+ } = useGroupedMigrationObjectsByExpirationDate(
+ [...basicOutputObjects, ...nftOutputObjects],
+ isTimelocked,
+ );
+
+ const { mutateAsync: signAndExecuteTransaction, isPending: isSendingTransaction } =
+ useSignAndExecuteTransaction();
+ const { totalNotOwnedStorageDepositReturnAmount } = summarizeMigratableObjectValues({
+ basicOutputs: basicOutputObjects,
+ nftOutputs: nftOutputObjects,
+ address: account?.address || '',
+ });
+
+ const [gasFee, gasFeeSymbol] = useFormatCoin(migrateData?.gasBudget, IOTA_TYPE_ARG);
+ const [totalStorageDepositReturnAmountFormatted, totalStorageDepositReturnAmountSymbol] =
+ useFormatCoin(totalNotOwnedStorageDepositReturnAmount.toString(), IOTA_TYPE_ARG);
+
+ async function handleMigrate(): Promise
{
+ if (!migrateData) return;
+ signAndExecuteTransaction(
+ {
+ transaction: migrateData.transaction,
+ },
+ {
+ onSuccess: (tx) => {
+ onSuccess(tx.digest);
+ },
+ },
+ )
+ .then(() => {
+ toast.success('Migration transaction has been sent');
+ })
+ .catch(() => {
+ toast.error('Migration transaction was not sent');
+ });
+ }
+
+ return (
+
+ );
+}
diff --git a/apps/wallet-dashboard/components/Dialogs/SendToken/SendTokenDialog.tsx b/apps/wallet-dashboard/components/Dialogs/SendToken/SendTokenDialog.tsx
index 913e362e27d..a33f018a09c 100644
--- a/apps/wallet-dashboard/components/Dialogs/SendToken/SendTokenDialog.tsx
+++ b/apps/wallet-dashboard/components/Dialogs/SendToken/SendTokenDialog.tsx
@@ -13,7 +13,7 @@ import { INITIAL_VALUES } from './constants';
import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import { useTransferTransactionMutation } from '@/hooks';
-interface SendCoinPopupProps {
+interface SendCoinDialogProps {
coin: CoinBalance;
activeAddress: string;
setOpen: (bool: boolean) => void;
@@ -30,7 +30,7 @@ function SendTokenDialogBody({
coin,
activeAddress,
setOpen,
-}: SendCoinPopupProps): React.JSX.Element {
+}: SendCoinDialogProps): React.JSX.Element {
const [step, setStep] = useState(FormStep.EnterValues);
const [selectedCoin, setSelectedCoin] = useState(coin);
const [formData, setFormData] = useState(INITIAL_VALUES);
@@ -125,7 +125,7 @@ function SendTokenDialogBody({
);
}
-export function SendTokenDialog(props: SendCoinPopupProps) {
+export function SendTokenDialog(props: SendCoinDialogProps) {
return (