Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
2243dff
fix(wallet-dashboard): Allow viewing vesting schedule rewards after l…
msarcev Oct 28, 2025
328503b
update overviewhint to show action
evavirseda Mar 20, 2026
f2c6ce7
Update the vesting popup in the wallet
evavirseda Mar 20, 2026
10dc396
fix format
evavirseda Mar 20, 2026
08adbe6
Update dashboard
evavirseda Mar 20, 2026
5795aeb
fix lint
evavirseda Mar 20, 2026
cc1c3ed
first steps
evavirseda Mar 23, 2026
4e73ee3
bring back investors ui
evavirseda Mar 23, 2026
7886dab
cleanup
evavirseda Mar 23, 2026
6e57a43
cleanup
evavirseda Mar 23, 2026
5444e50
cleanup
evavirseda Mar 23, 2026
9e42d2b
format
evavirseda Mar 23, 2026
40e5f2d
Merge branch 'tooling-wallet/update-vesting-ui' into tooling-wallet/c…
evavirseda Mar 23, 2026
4b9b7eb
fix layout
evavirseda Mar 24, 2026
bac8954
add collect summary
evavirseda Mar 24, 2026
8ee249e
fix
evavirseda Mar 24, 2026
9e42028
remove debris
evavirseda Mar 24, 2026
0665667
cleanup and add stake epoch to join stakes
evavirseda Mar 24, 2026
0d01bb9
update summary
evavirseda Mar 24, 2026
5c05dd3
remove duplicated
evavirseda Mar 24, 2026
538624f
feat(wallet,dashboard): update vesting UI after sunset (#10803)
evavirseda Mar 24, 2026
f1c0a12
fix dialog content
evavirseda Mar 24, 2026
f82b84a
Merge branch 'feature/sunset-vesting' into tooling-wallet/collect-ves…
evavirseda Mar 24, 2026
9eab1f3
remove useref
evavirseda Mar 24, 2026
70a2cba
cleanup
evavirseda Mar 25, 2026
cee3ef8
fix tests
evavirseda Mar 25, 2026
b27d4d5
update with constants
evavirseda Mar 25, 2026
f11208d
cleanup
evavirseda Mar 25, 2026
af77349
move NestedResultType to types
evavirseda Mar 26, 2026
8abeb1a
apply suggestions
evavirseda Mar 26, 2026
cd498f4
add suggestion
evavirseda Mar 27, 2026
fe0e23d
remove unused hook
evavirseda Mar 29, 2026
c8e45b5
feat(wallet,dashboard): update vesting UI after sunset (#10803)
evavirseda Mar 24, 2026
ac5e5e7
Merge branch 'feature/sunset-vesting' into tooling-wallet/collect-ves…
evavirseda Mar 30, 2026
7d293d0
Merge branch 'feature/sunset-vesting' into tooling-wallet/collect-ves…
evavirseda Apr 1, 2026
69c0599
feat(wallet-dashboard): handle inactive validator edge case (#10961)
evavirseda Apr 2, 2026
9f01e0c
add inline comments
evavirseda Apr 6, 2026
2f057c3
apply suggestions
evavirseda Apr 6, 2026
9ad4bab
apply suggestion to improve logic
evavirseda Apr 6, 2026
f496b97
apply suggestions
evavirseda Apr 7, 2026
44af2c8
show deleted objects in collect summary
evavirseda Apr 8, 2026
f99b9ec
use object changes
evavirseda Apr 8, 2026
3de8fa2
improve comments
evavirseda Apr 8, 2026
791c916
use standard transaction dialog instead collect summary dialog
evavirseda Apr 9, 2026
68eceff
revert change
evavirseda Apr 9, 2026
8ed1c60
disable the Collect button while isSendingTransaction
evavirseda Apr 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/core/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './gasSummary';
export * from './transactionExecute';
export * from './validators';
export * from './schema';
export * from './nestedResult';
7 changes: 7 additions & 0 deletions apps/core/src/types/nestedResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) 2026 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export type NestedResultType = {
$kind: 'NestedResult';
NestedResult: [number, number];
};
6 changes: 1 addition & 5 deletions apps/core/src/utils/migration/createMigrationTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ import {
NftOutputObject,
NftOutputObjectSchema,
} from './types';

type NestedResultType = {
$kind: 'NestedResult';
NestedResult: [number, number];
};
import { NestedResultType } from '../../types';

export async function getNativeTokensFromBag(bagId: string, client: IotaClient) {
const nativeTokenDynamicFields = await client.getDynamicFields({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright (c) 2026 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Transaction } from '@iota/iota-sdk/transactions';
import {
IOTA_TYPE_ARG,
IOTA_FRAMEWORK_ADDRESS,
IOTA_CLOCK_OBJECT_ID,
IOTA_SYSTEM_ADDRESS,
} from '@iota/iota-sdk/utils';
import { NestedResultType } from '../../types';

// Timelocked stake: fields.staked_iota.fields.{pool_id, stake_activation_epoch}
export interface TimelockedStakeObjectInput {
Comment thread
evavirseda marked this conversation as resolved.
Outdated
objectId: string;
content: {
dataType: 'moveObject';
fields: {
staked_iota: {
fields: {
pool_id: string;
stake_activation_epoch: string;
};
};
};
};
}

// Regular stake: fields.{pool_id, stake_activation_epoch}
export interface RegularStakeObjectInput {
Comment thread
evavirseda marked this conversation as resolved.
Outdated
objectId: string;
content: {
dataType: 'moveObject';
fields: {
pool_id: string;
stake_activation_epoch: string;
};
};
}

interface CreateCollectAllTimelocksTransactionOptions {
address: string;
timelockObjectIds: string[];
timelockedStakedObjects?: TimelockedStakeObjectInput[];
existingStakedObjects?: RegularStakeObjectInput[];
}

export function createCollectAllTimelocksTransaction({
address,
timelockObjectIds,
timelockedStakedObjects = [],
existingStakedObjects = [],
}: CreateCollectAllTimelocksTransactionOptions) {
const ptb = new Transaction();
const coins: NestedResultType[] = [];

// Unlock regular timelocks and convert to coins
for (const objectId of timelockObjectIds) {
const [unlock] = ptb.moveCall({
target: `${IOTA_FRAMEWORK_ADDRESS}::timelock::unlock_with_clock`,
typeArguments: [`${IOTA_FRAMEWORK_ADDRESS}::balance::Balance<${IOTA_TYPE_ARG}>`],
arguments: [ptb.object(objectId), ptb.object(IOTA_CLOCK_OBJECT_ID)],
});

const [coin] = ptb.moveCall({
target: `${IOTA_FRAMEWORK_ADDRESS}::coin::from_balance`,
typeArguments: [IOTA_TYPE_ARG],
arguments: [ptb.object(unlock)],
});

coins.push(coin);
}
Comment thread
evavirseda marked this conversation as resolved.

// Unlock timelock stakes and group by (pool_id, stake_activation_epoch)
const stakedIotaByKey = new Map<string, NestedResultType[]>();

for (const stakedObject of timelockedStakedObjects) {
const [unlockedStakedIota] = ptb.moveCall({
target: `${IOTA_SYSTEM_ADDRESS}::timelocked_staking::unlock_with_clock`,
arguments: [ptb.object(stakedObject.objectId), ptb.object(IOTA_CLOCK_OBJECT_ID)],
});

const poolKey = extractPoolKey(stakedObject);
if (poolKey) {
if (!stakedIotaByKey.has(poolKey)) {
stakedIotaByKey.set(poolKey, []);
}
stakedIotaByKey.get(poolKey)!.push(unlockedStakedIota);
} else {
ptb.transferObjects([unlockedStakedIota], ptb.pure.address(address));
}
}

for (const [poolKey, stakedIotaObjects] of stakedIotaByKey.entries()) {
const existingStake = findExistingStakeForKey(existingStakedObjects, poolKey);

if (existingStake) {
// Join all unlocked stakes into the existing regular stake
for (const stake of stakedIotaObjects) {
ptb.moveCall({
target: `${IOTA_SYSTEM_ADDRESS}::staking_pool::join_staked_iota`,
arguments: [ptb.object(existingStake.objectId), stake],
});
}
} else if (stakedIotaObjects.length === 1) {
ptb.transferObjects([stakedIotaObjects[0]], ptb.pure.address(address));
} else {
// Join all into the first one, then transfer
const [first, ...rest] = stakedIotaObjects;
for (const stake of rest) {
ptb.moveCall({
target: `${IOTA_SYSTEM_ADDRESS}::staking_pool::join_staked_iota`,
arguments: [first, stake],
});
}
ptb.transferObjects([first], ptb.pure.address(address));
}
}

// Transfer all collected coins
if (coins.length > 0) {
ptb.transferObjects(coins, ptb.pure.address(address));
}

return ptb;
}

function extractPoolKey(stakedObject: TimelockedStakeObjectInput): string | null {
const stakedIotaFields = stakedObject.content.fields.staked_iota?.fields;
if (stakedIotaFields?.pool_id && stakedIotaFields?.stake_activation_epoch) {
return `${stakedIotaFields.pool_id}:${stakedIotaFields.stake_activation_epoch}`;
}
return null;
}

function findExistingStakeForKey(
existingStakes: RegularStakeObjectInput[],
poolKey: string,
): RegularStakeObjectInput | undefined {
return existingStakes.find(
(s) => `${s.content.fields.pool_id}:${s.content.fields.stake_activation_epoch}` === poolKey,
);
}
Comment thread
evavirseda marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Transaction } from '@iota/iota-sdk/transactions';
import { IOTA_TYPE_ARG, IOTA_FRAMEWORK_ADDRESS, IOTA_CLOCK_OBJECT_ID } from '@iota/iota-sdk/utils';
import { createCollectAllTimelocksTransaction } from './createCollectAllTimelocksTransaction';

interface CreateUnlockTimelockedObjectTransactionOptions {
address: string;
Expand All @@ -13,25 +12,8 @@ export function createUnlockTimelockedObjectsTransaction({
address,
objectIds,
}: CreateUnlockTimelockedObjectTransactionOptions) {
const ptb = new Transaction();
const coins: { $kind: 'NestedResult'; NestedResult: [number, number] }[] = [];

for (const objectId of objectIds) {
const [unlock] = ptb.moveCall({
target: `${IOTA_FRAMEWORK_ADDRESS}::timelock::unlock_with_clock`,
typeArguments: [`${IOTA_FRAMEWORK_ADDRESS}::balance::Balance<${IOTA_TYPE_ARG}>`],
arguments: [ptb.object(objectId), ptb.object(IOTA_CLOCK_OBJECT_ID)],
});

// Convert Balance to Coin
const [coin] = ptb.moveCall({
target: `${IOTA_FRAMEWORK_ADDRESS}::coin::from_balance`,
typeArguments: [IOTA_TYPE_ARG],
arguments: [ptb.object(unlock)],
});

coins.push(coin);
}
ptb.transferObjects(coins, ptb.pure.address(address));
return ptb;
return createCollectAllTimelocksTransaction({
address,
timelockObjectIds: objectIds,
});
}
1 change: 1 addition & 0 deletions apps/core/src/utils/transaction/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ export * from './createTokenTransferTransaction';
export * from './getObjectDisplayLookup';
export * from './createNftSendValidationSchema';
export * from './createUnlockTimelockedObjectsTransaction';
export * from './createCollectAllTimelocksTransaction';
export * from './isMigrationTransaction';
export * from './isUnlockTimelockedObjectTransaction';
Loading
Loading