Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wallet-dashboard): Add migration PTB #3908

Merged
merged 42 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
9bbc7a8
feat: fetch stardust basic and nft objects
brancoder Oct 30, 2024
1f67787
feat: organize stardust objects on migratable and unmigratable
brancoder Oct 31, 2024
2ea4ee1
fix: migration constants and variable nameing
brancoder Oct 31, 2024
cbe9762
Merge branch 'toolin-dashboard/fetch-stardust-assets' into tooling-da…
brancoder Oct 31, 2024
6890ecc
Merge branch 'develop' into toolin-dashboard/fetch-stardust-assets
brancoder Oct 31, 2024
c7509fe
fix: variable naming
brancoder Oct 31, 2024
9a27cc3
Merge branch 'toolin-dashboard/fetch-stardust-assets' into tooling-da…
brancoder Oct 31, 2024
7a3eb78
Merge branch 'develop' into toolin-dashboard/fetch-stardust-assets
brancoder Oct 31, 2024
58d038a
Merge branch 'develop' into toolin-dashboard/fetch-stardust-assets
brancoder Nov 4, 2024
63fbe9b
Merge branch 'toolin-dashboard/fetch-stardust-assets' into tooling-da…
brancoder Nov 4, 2024
45bc046
fix: resolve conflicts
brancoder Nov 4, 2024
35af4d3
feat:add migration popup
brancoder Nov 4, 2024
2539673
fix: move constants to core
brancoder Nov 4, 2024
0ec34ba
fix: resolve conflicts
brancoder Nov 4, 2024
61199da
Merge branch 'develop' into tooling-dashboard/filter-stardust-objects
brancoder Nov 4, 2024
c128dfd
Merge branch 'tooling-dashboard/filter-stardust-objects' into tooling…
brancoder Nov 4, 2024
0e985ec
fix: evert adding routes to barrel
brancoder Nov 4, 2024
bf740a1
Merge branch 'tooling-dashboard/filter-stardust-objects' into tooling…
brancoder Nov 4, 2024
00e36f3
feat: add migration ptb
brancoder Nov 5, 2024
5b1b904
fix: remove leftover testing code
brancoder Nov 5, 2024
66cdfc2
Merge branch 'develop' into tooling-dashboard/filter-stardust-objects
brancoder Nov 6, 2024
cca2fc9
fix: remove undefined object check
brancoder Nov 6, 2024
9d0d90f
Merge branch 'tooling-dashboard/filter-stardust-objects' into tooling…
brancoder Nov 6, 2024
9e055e7
Merge branch 'develop' into tooling-dashboard/filter-stardust-objects
marc2332 Nov 6, 2024
bd6c026
fix: add schemas for migration transaction
brancoder Nov 6, 2024
8e5517f
Merge branch 'develop' into tooling-dashboard/filter-stardust-objects
brancoder Nov 7, 2024
c276ef8
Merge branch 'tooling-dashboard/filter-stardust-objects' into tooling…
brancoder Nov 7, 2024
fd17081
fix: change migration popup params and add loader to button
brancoder Nov 7, 2024
ce1a5b1
fix: resolve conflicts
brancoder Nov 7, 2024
4fae582
fix: resolve conflicts
brancoder Nov 7, 2024
f8fac5a
Merge branch 'tooling-dashboard/add-migration-popup' into tooling-das…
brancoder Nov 7, 2024
0e7fe09
remove migration inteface
brancoder Nov 7, 2024
b0b4970
fix: remove empty spaces and add iota coin type constant migration co…
brancoder Nov 7, 2024
2cac6c7
fix: exporting type and eslint
brancoder Nov 7, 2024
29f4b71
fix: migration object schema
brancoder Nov 7, 2024
939597f
fix: add missing exports
brancoder Nov 8, 2024
7ead70e
fix: remove IOTA_COIN_TYPE and use sdk constant IOTA_TYPE_ARG
brancoder Nov 8, 2024
6e3a720
conficts resolved
brancoder Nov 8, 2024
c6fd731
Merge branch 'tooling-epic/dashboard-styling' into tooling-dashboard/…
brancoder Nov 11, 2024
17832cf
fix: move migration types to dedicated file and add literal strings f…
brancoder Nov 12, 2024
31cfe59
Merge branch 'develop' into tooling-dashboard/add-migration-ptb
brancoder Nov 21, 2024
e0848e3
Merge branch 'develop' into tooling-dashboard/add-migration-ptb
brancoder Nov 21, 2024
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
6 changes: 4 additions & 2 deletions apps/core/src/constants/migration.constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';

export const STARDUST_PACKAGE_ID =
'000000000000000000000000000000000000000000000000000000000000107a';
export const STARDUST_BASIC_OUTPUT_TYPE = `${STARDUST_PACKAGE_ID}::basic_output::BasicOutput<0x2::iota::IOTA>`;
export const STARDUST_NFT_OUTPUT_TYPE = `${STARDUST_PACKAGE_ID}::nft_output::NftOutput<0x2::iota::IOTA>`;
export const STARDUST_BASIC_OUTPUT_TYPE = `${STARDUST_PACKAGE_ID}::basic_output::BasicOutput<${IOTA_TYPE_ARG}>`;
export const STARDUST_NFT_OUTPUT_TYPE = `${STARDUST_PACKAGE_ID}::nft_output::NftOutput<${IOTA_TYPE_ARG}>`;
1 change: 1 addition & 0 deletions apps/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ export * from './getExplorerLink';
export * from './stake';
export * from './transaction';
export * from './validation';
export * from './migration';
164 changes: 164 additions & 0 deletions apps/core/src/utils/migration/createMigrationTransaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { IotaClient, IotaObjectData } from '@iota/iota-sdk/client';
import { Transaction } from '@iota/iota-sdk/transactions';
import { STARDUST_PACKAGE_ID } from '../../constants/migration.constants';
import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import {
BasicOutputObject,
BasicOutputObjectSchema,
NftOutputObject,
NftOutputObjectSchema,
} from './types';

type NestedResultType = {
$kind: 'NestedResult';
NestedResult: [number, number];
};

export async function getNativeTokenTypesFromBag(
bagId: string,
client: IotaClient,
): Promise<string[]> {
const nativeTokenDynamicFields = await client.getDynamicFields({
parentId: bagId,
});
const nativeTokenTypes: string[] = [];
for (const nativeToken of nativeTokenDynamicFields.data) {
nativeTokenTypes.push(nativeToken?.name?.value as string);
}

return nativeTokenTypes;
}

export function validateBasicOutputObject(outputObject: IotaObjectData): BasicOutputObject {
if (outputObject.content?.dataType !== 'moveObject') {
throw new Error('Invalid basic output object');
}
const result = BasicOutputObjectSchema.safeParse(outputObject.content.fields);
if (!result.success) {
throw new Error('Invalid basic output object content');
}
return result.data;
}

export function validateNftOutputObject(outputObject: IotaObjectData): NftOutputObject {
if (outputObject.content?.dataType !== 'moveObject') {
throw new Error('Invalid nft output object');
}
const result = NftOutputObjectSchema.safeParse(outputObject.content.fields);
if (!result.success) {
throw new Error('Invalid nft output object content');
}
return result.data;
}

export async function createMigrationTransaction(
client: IotaClient,
address: string,
basicOutputs: IotaObjectData[] = [],
nftOutputs: IotaObjectData[] = [],
): Promise<Transaction> {
const ptb = new Transaction();

const coinsFromBasicOutputs: NestedResultType[] = [];

// Basic Outputs
for (const basicOutputObject of basicOutputs) {
const validatedOutputObject = validateBasicOutputObject(basicOutputObject);
const basicOutputObjectId = validatedOutputObject.id.id;
const bagId = validatedOutputObject.native_tokens.fields.id.id;
const bagSize = validatedOutputObject.native_tokens.fields.size;
const nativeTokenTypes: string[] =
Number(bagSize) > 0 ? await getNativeTokenTypesFromBag(bagId, client) : [];

const migratableResult = ptb.moveCall({
target: `${STARDUST_PACKAGE_ID}::basic_output::extract_assets`,
typeArguments: [IOTA_TYPE_ARG],
arguments: [ptb.object(basicOutputObjectId)],
});

const balance = migratableResult[0];
let nativeTokensBag = migratableResult[1];

// Convert Balance in Coin
const [coin] = ptb.moveCall({
target: '0x02::coin::from_balance',
typeArguments: [IOTA_TYPE_ARG],
arguments: [ptb.object(balance)],
});

coinsFromBasicOutputs.push(coin);

for (const nativeTokenType of nativeTokenTypes) {
[nativeTokensBag] = ptb.moveCall({
target: '0x107a::utilities::extract_and_send_to',
typeArguments: [nativeTokenType],
arguments: [ptb.object(nativeTokensBag), ptb.pure.address(address)],
});
}

ptb.moveCall({
target: '0x02::bag::destroy_empty',
arguments: [ptb.object(nativeTokensBag)],
});
}

// NFT Outputs
const coinsFromNftOutputs: NestedResultType[] = [];
const nftsFromNftOutputs: NestedResultType[] = [];

for (const nftOutputObject of nftOutputs) {
const validatedOutputObject = validateNftOutputObject(nftOutputObject);
const nftOutputObjectId = validatedOutputObject.id.id;
const bagId = validatedOutputObject.native_tokens.fields.id.id;
const bagSize = validatedOutputObject.native_tokens.fields.size;
const nativeTokenTypes: string[] =
Number(bagSize) > 0 ? await getNativeTokenTypesFromBag(bagId, client) : [];

const migratableResult = ptb.moveCall({
target: `${STARDUST_PACKAGE_ID}::nft_output::extract_assets`,
typeArguments: [IOTA_TYPE_ARG],
arguments: [ptb.object(nftOutputObjectId)],
});

const balance = migratableResult[0];
let nativeTokensBag = migratableResult[1];
const nft = migratableResult[2];

nftsFromNftOutputs.push(nft);

// Convert Balance in Coin
const [coin] = ptb.moveCall({
target: '0x02::coin::from_balance',
typeArguments: [IOTA_TYPE_ARG],
arguments: [ptb.object(balance)],
});
coinsFromNftOutputs.push(coin);

for (const nativeTokenType of nativeTokenTypes) {
[nativeTokensBag] = ptb.moveCall({
target: '0x107a::utilities::extract_and_send_to',
typeArguments: [nativeTokenType],
arguments: [ptb.object(nativeTokensBag), ptb.pure.address(address)],
});
}

ptb.moveCall({
target: '0x02::bag::destroy_empty',
arguments: [ptb.object(nativeTokensBag)],
});
}

const coinOne = coinsFromBasicOutputs.shift() || coinsFromNftOutputs.shift();
const remainingCoins = [...coinsFromBasicOutputs, ...coinsFromNftOutputs];
if (coinOne) {
if (remainingCoins.length > 0) {
ptb.mergeCoins(coinOne, remainingCoins);
}
ptb.transferObjects([coinOne, ...nftsFromNftOutputs], ptb.pure.address(address));
}

return ptb;
}
5 changes: 5 additions & 0 deletions apps/core/src/utils/migration/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './createMigrationTransaction';
export * from './types';
73 changes: 73 additions & 0 deletions apps/core/src/utils/migration/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { z } from 'zod';
import { STARDUST_PACKAGE_ID } from '../../constants';

const ExpirationUnlockConditionSchema = z.object({
type: z.literal(
`${STARDUST_PACKAGE_ID}::expiration_unlock_condition::ExpirationUnlockCondition`,
),
fields: z.object({
owner: z.string(),
return_address: z.string(),
unix_time: z.number(),
}),
});

const StorageDepositReturnUnlockConditionSchema = z.object({
type: z.literal(
`${STARDUST_PACKAGE_ID}::storage_deposit_return_unlock_condition::StorageDepositReturnUnlockCondition`,
),
fields: z.object({
return_address: z.string(),
return_amount: z.string(),
}),
});

const TimelockUnlockConditionSchema = z.object({
type: z.literal(`${STARDUST_PACKAGE_ID}::timelock_unlock_condition::TimelockUnlockCondition`),
fields: z.object({
unix_time: z.number(),
}),
});

const CommonOutputObjectSchema = z.object({
id: z.object({
id: z.string(),
}),
balance: z.string(),
native_tokens: z.object({
type: z.literal('0x2::bag::Bag'),
fields: z.object({
id: z.object({
id: z.string(),
}),
size: z.string(),
}),
}),
});

const CommonOutputObjectWithUcSchema = CommonOutputObjectSchema.extend({
expiration_uc: ExpirationUnlockConditionSchema.nullable().optional(),
storage_deposit_return_uc: StorageDepositReturnUnlockConditionSchema.nullable().optional(),
timelock_uc: TimelockUnlockConditionSchema.nullable().optional(),
});

export const BasicOutputObjectSchema = CommonOutputObjectWithUcSchema.extend({
metadata: z.array(z.number()).nullable().optional(),
tag: z.array(z.number()).nullable().optional(),
sender: z.string().nullable().optional(),
});

export const NftOutputObjectSchema = CommonOutputObjectWithUcSchema;

export type ExpirationUnlockCondition = z.infer<typeof ExpirationUnlockConditionSchema>;
export type StorageDepositReturnUnlockCondition = z.infer<
typeof StorageDepositReturnUnlockConditionSchema
>;
export type TimelockUnlockCondition = z.infer<typeof TimelockUnlockConditionSchema>;
export type CommonOutputObject = z.infer<typeof CommonOutputObjectSchema>;
export type CommonOutputObjectWithUc = z.infer<typeof CommonOutputObjectWithUcSchema>;
export type BasicOutputObject = z.infer<typeof BasicOutputObjectSchema>;
export type NftOutputObject = z.infer<typeof NftOutputObjectSchema>;
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ function MigratePopup({
<Loader className="h-4 w-4 animate-spin" />
) : null
}
iconAfterText
/>
</div>
);
Expand Down
9 changes: 7 additions & 2 deletions apps/wallet-dashboard/hooks/useMigrationTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

import { useIotaClient } from '@iota/dapp-kit';
import { IotaObjectData } from '@iota/iota-sdk/client';
import { Transaction } from '@iota/iota-sdk/transactions';
import { useQuery } from '@tanstack/react-query';
import { createMigrationTransaction } from '@iota/core';

export function useMigrationTransaction(
address: string,
Expand All @@ -16,7 +16,12 @@ export function useMigrationTransaction(
// eslint-disable-next-line @tanstack/query/exhaustive-deps
queryKey: ['migration-transaction', address],
queryFn: async () => {
const transaction = new Transaction();
const transaction = await createMigrationTransaction(
client,
address,
basicOutputObjects,
nftOutputObjects,
);
transaction.setSender(address);
await transaction.build({ client });
return transaction;
Expand Down
1 change: 0 additions & 1 deletion apps/wallet-dashboard/lib/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

export * from './transactions.interface';
export * from './timelock.interface';
export * from './migration.interface';
export * from './vesting.interface';
export * from './appRoute.interface';
export * from './dialogView.interface';
47 changes: 0 additions & 47 deletions apps/wallet-dashboard/lib/interfaces/migration.interface.ts

This file was deleted.

2 changes: 1 addition & 1 deletion apps/wallet-dashboard/lib/utils/migration.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { CommonOutputObjectWithUc } from '@iota/core';
import { IotaObjectData } from '@iota/iota-sdk/client';
import { CommonOutputObjectWithUc } from '../interfaces/migration.interface';

export type StardustMigrationGroupedObjects = {
migratable: IotaObjectData[];
Expand Down
Loading