Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { decryptAES } from 'adena-module';
import { StorageMigration012 } from './storage-migration-v012';

const mockStorageData = {
NETWORKS: [],
CURRENT_CHAIN_ID: 'test7.2',
CURRENT_NETWORK_ID: 'test7.2',
SERIALIZED: 'U2FsdGVkX19eI8kOCI/T9o1Ru0b2wdj5rHxmG4QbLQ0yZH4kDa8/gg6Ac2JslvEm',
ENCRYPTED_STORED_PASSWORD: '',
CURRENT_ACCOUNT_ID: '',
ACCOUNT_NAMES: {},
ESTABLISH_SITES: {},
ADDRESS_BOOK: '',
ACCOUNT_TOKEN_METAINFOS: {},
QUESTIONNAIRE_EXPIRED_DATE: null,
WALLET_CREATION_GUIDE_CONFIRM_DATE: null,
ADD_ACCOUNT_GUIDE_CONFIRM_DATE: null,
ACCOUNT_GRC721_COLLECTIONS: {},
ACCOUNT_GRC721_PINNED_PACKAGES: {},
};

describe('serialized wallet migration V012', () => {
it('version', () => {
const migration = new StorageMigration012();
expect(migration.version).toBe(12);
});

it('up success', async () => {
const mockData = {
version: 7,
data: mockStorageData,
};
const migration = new StorageMigration012();
const result = await migration.up(mockData);

expect(result.data.CURRENT_CHAIN_ID).toEqual('test8');
expect(result.data.CURRENT_NETWORK_ID).toEqual('test8');
});

it('up password success', async () => {
const mockData = {
version: 1,
data: mockStorageData,
};
const password = '123';
const migration = new StorageMigration012();
const result = await migration.up(mockData);

expect(result.version).toBe(12);
expect(result.data).not.toBeNull();
expect(result.data.ACCOUNT_GRC721_COLLECTIONS).toEqual({});
expect(result.data.ACCOUNT_GRC721_PINNED_PACKAGES).toEqual({});

const serialized = result.data.SERIALIZED;
const decrypted = await decryptAES(serialized, password);
const wallet = JSON.parse(decrypted);

expect(wallet.accounts).toHaveLength(0);
expect(wallet.keyrings).toHaveLength(0);

expect(result.data.CURRENT_CHAIN_ID).toEqual('test8');
expect(result.data.CURRENT_NETWORK_ID).toEqual('test8');
});

it('up failed throw error', async () => {
const mockData: any = {
version: 1,
data: { ...mockStorageData, SERIALIZED: null },
};
const migration = new StorageMigration012();

await expect(migration.up(mockData)).rejects.toThrow(
'Storage Data does not match version V011',
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { StorageModel } from '@common/storage';
import { Migration } from '@migrates/migrator';
import CHAIN_DATA from '@resources/chains/chains.json';
import {
CurrentChainIdModelV011,
CurrentNetworkIdModelV011,
NetworksModelV011,
StorageModelDataV011,
} from '../v011/storage-model-v011';
import {
CurrentChainIdModelV012,
CurrentNetworkIdModelV012,
NetworksModelV012,
StorageModelDataV012,
} from './storage-model-v012';

export class StorageMigration012 implements Migration<StorageModelDataV012> {
public readonly version = 12;

async up(
current: StorageModel<StorageModelDataV011>,
): Promise<StorageModel<StorageModelDataV012>> {
if (!this.validateModelV011(current.data)) {
throw new Error('Storage Data does not match version V011');
}
const previous: StorageModelDataV011 = current.data;
return {
version: this.version,
data: {
...previous,
CURRENT_CHAIN_ID: this.migrateCurrentChainId(previous.CURRENT_CHAIN_ID),
CURRENT_NETWORK_ID: this.migrateCurrentNetworkId(previous.CURRENT_NETWORK_ID),
NETWORKS: this.migrateNetwork(previous.NETWORKS),
},
};
}

private validateModelV011(currentData: StorageModelDataV011): boolean {
const storageDataKeys = [
'NETWORKS',
'CURRENT_CHAIN_ID',
'CURRENT_NETWORK_ID',
'SERIALIZED',
'ENCRYPTED_STORED_PASSWORD',
'CURRENT_ACCOUNT_ID',
'ESTABLISH_SITES',
'ADDRESS_BOOK',
'ACCOUNT_TOKEN_METAINFOS',
'ACCOUNT_GRC721_COLLECTIONS',
'ACCOUNT_GRC721_PINNED_PACKAGES',
];
const currentDataKeys = Object.keys(currentData);
const hasKeys = storageDataKeys.every((dataKey) => {
return currentDataKeys.includes(dataKey);
});

if (!hasKeys) {
return false;
}
if (!Array.isArray(currentData.NETWORKS)) {
return false;
}
if (typeof currentData.CURRENT_CHAIN_ID !== 'string') {
return false;
}
if (typeof currentData.CURRENT_NETWORK_ID !== 'string') {
return false;
}
if (typeof currentData.SERIALIZED !== 'string') {
return false;
}
if (typeof currentData.ENCRYPTED_STORED_PASSWORD !== 'string') {
return false;
}
if (typeof currentData.CURRENT_ACCOUNT_ID !== 'string') {
return false;
}
if (currentData.ACCOUNT_NAMES && typeof currentData.ACCOUNT_NAMES !== 'object') {
return false;
}
if (currentData.ESTABLISH_SITES && typeof currentData.ESTABLISH_SITES !== 'object') {
return false;
}
return true;
}

private migrateCurrentChainId(currentChainId: CurrentChainIdModelV011): CurrentChainIdModelV012 {
if (currentChainId === 'test7.2') {
return 'test8';
}
return currentChainId;
}

private migrateCurrentNetworkId(
currentNetworkId: CurrentNetworkIdModelV011,
): CurrentNetworkIdModelV012 {
if (currentNetworkId === 'test7.2') {
return 'test8';
}
return currentNetworkId;
}

private migrateNetwork(networks: NetworksModelV011): NetworksModelV012 {
const defaultNetworks = CHAIN_DATA.filter((network) => network.default);
const customNetworks = networks
.filter((network) => !network.default)
.map((network) => {
const providedNetwork = CHAIN_DATA.find((data) => data.chainId === network.id);
if (providedNetwork) {
return {
...providedNetwork,
chainName: network.chainName,
networkName: network.networkName,
rpcUrl: network.rpcUrl,
};
}
return {
...network,
indexerUrl: '',
};
});
return [...defaultNetworks, ...customNetworks];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
export type StorageModelV012 = {
version: 12;
data: StorageModelDataV012;
};

export type StorageModelDataV012 = {
NETWORKS: NetworksModelV012;
CURRENT_CHAIN_ID: CurrentChainIdModelV012;
CURRENT_NETWORK_ID: CurrentNetworkIdModelV012;
SERIALIZED: SerializedModelV012;
ENCRYPTED_STORED_PASSWORD: EncryptedStoredPasswordModelV012;
CURRENT_ACCOUNT_ID: CurrentAccountIdModelV012;
ACCOUNT_NAMES: AccountNamesModelV012;
ESTABLISH_SITES: EstablishSitesModelV012;
ADDRESS_BOOK: AddressBookModelV012;
ACCOUNT_TOKEN_METAINFOS: AccountTokenMetainfoModelV012;
QUESTIONNAIRE_EXPIRED_DATE: QuestionnaireExpiredDateModelV012;
WALLET_CREATION_GUIDE_CONFIRM_DATE: WalletCreationGuideConfirmDateModelV012;
ADD_ACCOUNT_GUIDE_CONFIRM_DATE: AddAccountGuideConfirmDateModelV012;
ACCOUNT_GRC721_COLLECTIONS: AccountGRC721CollectionsV012;
ACCOUNT_GRC721_PINNED_PACKAGES: AccountGRC721PinnedPackagesV012;
};

export type NetworksModelV012 = {
id: string;
default: boolean;
main: boolean;
chainId: string;
chainName: string;
networkId: string;
networkName: string;
addressPrefix: string;
rpcUrl: string;
indexerUrl: string;
gnoUrl: string;
apiUrl: string;
linkUrl: string;
deleted?: boolean;
}[];

export type CurrentChainIdModelV012 = string;

export type CurrentNetworkIdModelV012 = string;

export type SerializedModelV012 = string;

export type QuestionnaireExpiredDateModelV012 = number | null;

export type WalletCreationGuideConfirmDateModelV012 = number | null;

export type AddAccountGuideConfirmDateModelV012 = number | null;

export type WalletModelV012 = {
accounts: AccountDataModelV012[];
keyrings: KeyringDataModelV012[];
currentAccountId?: string;
};

type AccountDataModelV012 = {
id?: string;
index: number;
type: 'HD_WALLET' | 'PRIVATE_KEY' | 'LEDGER' | 'WEB3_AUTH' | 'AIRGAP';
name: string;
keyringId: string;
hdPath?: number;
publicKey: number[];
addressBytes?: number[];
};

export type KeyringDataModelV012 = {
id?: string;
type: 'HD_WALLET' | 'PRIVATE_KEY' | 'LEDGER' | 'WEB3_AUTH' | 'AIRGAP';
publicKey?: number[];
privateKey?: number[];
seed?: number[];
mnemonic?: string;
addressBytes?: number[];
};

export type EncryptedStoredPasswordModelV012 = string;

export type CurrentAccountIdModelV012 = string;

type AccountId = string;
type NetworkId = string;

export type AccountNamesModelV012 = { [key in AccountId]: string };

export type EstablishSitesModelV012 = {
[key in AccountId]: {
hostname: string;
chainId: string;
account: string;
name: string;
favicon: string | null;
establishedTime: string;
}[];
};

export type AddressBookModelV012 = string;

export type AccountTokenMetainfoModelV012 = {
[key in string]: {
main: boolean;
tokenId: string;
networkId: string;
display: boolean;
type: 'gno-native' | 'grc20' | 'ibc-native' | 'ibc-tokens';
name: string;
symbol: string;
decimals: number;
description?: string;
websiteUrl?: string;
image: string;
denom?: string;
pkgPath?: string;
originChain?: string;
originDenom?: string;
originType?: string;
path?: string;
channel?: string;
port?: string;
}[];
};

export type AccountGRC721CollectionsV012 = {
[key in AccountId]: {
[key in NetworkId]: {
tokenId: string;
networkId: string;
display: boolean;
type: 'grc721';
packagePath: string;
name: string;
symbol: string;
image: string | null;
isTokenUri: boolean;
isMetadata: boolean;
}[];
};
};

export type AccountGRC721PinnedPackagesV012 = {
[key in AccountId]: { [key in NetworkId]: string[] };
};
Loading