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

hotfix(mobile): race condition fix #764

Merged
merged 6 commits into from
Mar 18, 2024
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
9 changes: 9 additions & 0 deletions packages/@core-js/src/utils/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ export class State<TData extends DefaultStateData> {
this.storeIfNeeded();
};

public setAsync = async (
updater: Partial<TData> | ((data: TData) => Partial<TData>),
) => {
const newData = typeof updater === 'function' ? updater(this.data) : updater;
this.data = { ...this.data, ...newData };
this.emit();
await this.storeIfNeeded();
};

private emit() {
this.subscribers.forEach((subscriber) => subscriber(this.data));
}
Expand Down
2 changes: 1 addition & 1 deletion packages/mobile/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 433
versionName "4.0.0"
versionName "4.0.1"
missingDimensionStrategy 'react-native-camera', 'general'
missingDimensionStrategy 'store', 'play'
}
Expand Down
4 changes: 2 additions & 2 deletions packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1294,7 +1294,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 4.0.0;
MARKETING_VERSION = 4.0.1;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
Expand Down Expand Up @@ -1328,7 +1328,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 4.0.0;
MARKETING_VERSION = 4.0.1;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
Expand Down
6 changes: 1 addition & 5 deletions packages/mobile/src/hooks/useMigration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@ export const useMigration = () => {
if (isNewSecurityFlow) {
const keychainService = await AsyncStorage.getItem('keychainService');

if (!keychainService) {
throw new Error();
}

jsonstr = await SecureStore.getItemAsync('biometry_' + keychainItemName, {
keychainService,
keychainService: keychainService || 'TKProtected',
});
} else {
jsonstr = await EncryptedStorage.getItem(keychainItemName);
Expand Down
2 changes: 1 addition & 1 deletion packages/mobile/src/navigation/MainStack/MainStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const MainStack: FC = () => {
const showLockScreen =
tk.lockEnabled && !isUnlocked && hasWallet && !attachedScreen.pathname;

const isMigrated = useExternalState(tk.walletsStore, (state) => state.isMigrated);
const isMigrated = useExternalState(tk.migrationStore, (state) => state.isMigrated);

const root = useMemo(() => {
if (hasWallet) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,43 +41,45 @@ export const BackupPhraseScreen = memo(() => {
return (
<Screen>
<Screen.Header />
<View style={styles.container}>
<Text type="h2" textAlign="center">
{t('recovery_phrase.title')}
</Text>
<Spacer y={4} />
<Text type="body1" color="textSecondary" textAlign="center">
{t('recovery_phrase.caption')}
</Text>
<Spacer y={16} />
<Screen.ScrollView>
<View style={styles.container}>
<Text type="h2" textAlign="center">
{t('recovery_phrase.title')}
</Text>
<Spacer y={4} />
<Text type="body1" color="textSecondary" textAlign="center">
{t('recovery_phrase.caption')}
</Text>
<Spacer y={16} />

<View style={styles.centered}>
<View style={styles.columns}>
<View style={styles.leftColumn}>
{leftColumn.map((word, index) => (
<View style={styles.line} key={`${word}-${index}`}>
<Text type="body2" color="textSecondary" style={styles.num}>
{index + 1}.
</Text>
<Text type="body1">{word}</Text>
</View>
))}
</View>
<View>
{rightColumn.map((word, index) => (
<View style={styles.line} key={`${word}-${index + 1 + 12}`}>
<Text type="body2" color="textSecondary" style={styles.num}>
{index + 1 + 12}.
</Text>
<Text type="body1">{word}</Text>
</View>
))}
<View style={styles.centered}>
<View style={styles.columns}>
<View style={styles.leftColumn}>
{leftColumn.map((word, index) => (
<View style={styles.line} key={`${word}-${index}`}>
<Text type="body2" color="textSecondary" style={styles.num}>
{index + 1}.
</Text>
<Text type="body1">{word}</Text>
</View>
))}
</View>
<View>
{rightColumn.map((word, index) => (
<View style={styles.line} key={`${word}-${index + 1 + 12}`}>
<Text type="body2" color="textSecondary" style={styles.num}>
{index + 1 + 12}.
</Text>
<Text type="body1">{word}</Text>
</View>
))}
</View>
</View>
</View>
</View>
</View>

<View style={[styles.buttonContainer, { paddingBottom: safeArea.bottom + 32 }]}>
<Screen.ButtonSpacer />
</Screen.ScrollView>
<Screen.ButtonContainer>
{lastBackupAt !== null && !params.isBackupAgain ? (
<Button
title={t('recovery_phrase.copy_button')}
Expand All @@ -94,15 +96,14 @@ export const BackupPhraseScreen = memo(() => {
}
/>
)}
</View>
</Screen.ButtonContainer>
</Screen>
);
});

const styles = StyleSheet.create({
container: {
paddingHorizontal: 16,
flex: 1,
},
centered: {
justifyContent: 'center',
Expand All @@ -111,7 +112,7 @@ const styles = StyleSheet.create({
columns: {
flexDirection: 'row',
maxWidth: 310,
paddingTop: 16,
paddingVertical: 16,
},
line: {
width: 151,
Expand All @@ -129,8 +130,4 @@ const styles = StyleSheet.create({
position: 'relative',
top: 3,
},
buttonContainer: {
marginTop: 16,
marginHorizontal: 32,
},
});
45 changes: 33 additions & 12 deletions packages/mobile/src/wallet/Tonkeeper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ export interface WalletsStoreState {
selectedIdentifier: string;
biometryEnabled: boolean;
lockEnabled: boolean;
}

export interface MigrationState {
isMigrated: boolean;
}

Expand Down Expand Up @@ -77,6 +80,9 @@ export class Tonkeeper {
selectedIdentifier: '',
biometryEnabled: false,
lockEnabled: true,
});

public migrationStore = new State<MigrationState>({
isMigrated: false,
});

Expand All @@ -99,6 +105,11 @@ export class Tonkeeper {
storage: this.storage,
key: 'walletsStore',
});

this.migrationStore.persist({
storage: this.storage,
key: 'migrationStore',
});
}

public get wallet() {
Expand All @@ -122,12 +133,18 @@ export class Tonkeeper {
await Promise.all([
this.walletsStore.rehydrate(),
this.tonPrice.rehydrate(),
this.migrationStore.rehydrate(),
this.biometry.detectTypes(),
]);

// @ts-ignore moved to migrationStore, may be set on some clients. So we need to migrate it
if (this.walletsStore.data.isMigrated) {
this.setMigrated();
}

this.tonPrice.load();

if (!this.walletsStore.data.isMigrated) {
if (!this.migrationStore.data.isMigrated) {
this.migrationData = await this.getMigrationData();
}

Expand Down Expand Up @@ -237,7 +254,9 @@ export class Tonkeeper {
return indexA - indexB;
});

this.walletsStore.set(({ wallets }) => ({ wallets: [...wallets, ...sortedWallets] }));
await this.walletsStore.setAsync(({ wallets }) => ({
wallets: [...wallets, ...sortedWallets],
}));
const walletsInstances = await Promise.all(
sortedWallets.map((wallet) => this.createWalletInstance(wallet)),
);
Expand Down Expand Up @@ -334,7 +353,9 @@ export class Tonkeeper {
version,
};

this.walletsStore.set(({ wallets }) => ({ wallets: [...wallets, config] }));
await this.walletsStore.setAsync(({ wallets }) => ({
wallets: [...wallets, config],
}));
const wallet = await this.createWalletInstance(config);
this.setWallet(wallet);

Expand All @@ -348,7 +369,7 @@ export class Tonkeeper {
Array.from(this.wallets.keys()).find((item) => item !== identifier) ?? '',
) ?? null;

this.walletsStore.set(({ wallets }) => ({
await this.walletsStore.setAsync(({ wallets }) => ({
wallets: wallets.filter((w) => w.identifier !== identifier),
selectedIdentifier: nextWallet?.identifier ?? '',
}));
Expand All @@ -358,7 +379,7 @@ export class Tonkeeper {
this.wallets.delete(identifier);

if (this.wallets.size === 0) {
this.walletsStore.set({ biometryEnabled: false });
await this.walletsStore.setAsync({ biometryEnabled: false });
this.vault.destroy();
}

Expand All @@ -369,7 +390,7 @@ export class Tonkeeper {
}

public async removeAllWallets() {
this.walletsStore.set({
await this.walletsStore.setAsync({
wallets: [],
selectedIdentifier: '',
biometryEnabled: false,
Expand Down Expand Up @@ -441,7 +462,7 @@ export class Tonkeeper {
},
);

this.walletsStore.set({ wallets: updatedWallets });
await this.walletsStore.setAsync({ wallets: updatedWallets });

identifiers.forEach((identifier) => {
const currentConfig = updatedWallets.find(
Expand Down Expand Up @@ -496,7 +517,7 @@ export class Tonkeeper {

public setMigrated() {
console.log('migrated');
this.walletsStore.set({ isMigrated: true });
this.migrationStore.set({ isMigrated: true });
}

public saveLastBackupTimestampAll(identifiers: string[], dismissSetup = false) {
Expand All @@ -512,22 +533,22 @@ export class Tonkeeper {
public async enableBiometry(passcode: string) {
await this.vault.setupBiometry(passcode);

this.walletsStore.set({ biometryEnabled: true });
await this.walletsStore.setAsync({ biometryEnabled: true });
}

public async disableBiometry() {
try {
await this.vault.removeBiometry();
} catch {}

this.walletsStore.set({ biometryEnabled: false });
await this.walletsStore.setAsync({ biometryEnabled: false });
}

public async enableLock() {
this.walletsStore.set({ lockEnabled: true });
await this.walletsStore.setAsync({ lockEnabled: true });
}

public async disableLock() {
this.walletsStore.set({ lockEnabled: false });
await this.walletsStore.setAsync({ lockEnabled: false });
}
}
Loading