Skip to content

Commit 85aa4d1

Browse files
committed
Support switching between mainnet and testnet
1 parent 6e94d4a commit 85aa4d1

File tree

8 files changed

+233
-90
lines changed

8 files changed

+233
-90
lines changed

packages/mobile-app/app/menu/debug/oreowallet.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ export default function MenuDebugOreowallet() {
7373
onPress={async () => {
7474
if (!account.data) return;
7575

76+
const result = await OreowalletServerApi.removeAccount(
77+
Network.MAINNET,
78+
await getAccountInfo(account.data.name),
79+
);
80+
console.log(JSON.stringify(result));
81+
}}
82+
title="Remove Account"
83+
/>
84+
<Button
85+
onPress={async () => {
86+
if (!account.data) return;
87+
7688
const result = await OreowalletServerApi.getTransactions(
7789
Network.MAINNET,
7890
await getAccountInfo(account.data.name),
Lines changed: 137 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,130 @@
11
import { StatusBar } from "expo-status-bar";
2-
import { Button, Modal, StyleSheet, Text, View } from "react-native";
3-
import { useRouter } from "expo-router";
2+
import {
3+
Button,
4+
Modal,
5+
StyleSheet,
6+
Text,
7+
View,
8+
ActivityIndicator,
9+
} from "react-native";
410
import { useState } from "react";
511
import { useFacade } from "@/data/facades";
612
import { SettingsKey } from "@/data/settings/db";
713
import { Network } from "@/data/constants";
14+
import { AccountFormat } from "@ironfish/sdk";
815

916
export default function MenuNetwork() {
10-
const router = useRouter();
11-
1217
const [modalVisible, setModalVisible] = useState(false);
18+
const [isChangingNetwork, setIsChangingNetwork] = useState(false);
1319

1420
const facade = useFacade();
15-
1621
const setAppSetting = facade.setAppSetting.useMutation();
22+
const getAccounts = facade.getAccounts.useQuery();
23+
const exportAccount = facade.exportAccount.useMutation();
24+
const importAccount = facade.importAccount.useMutation();
25+
const networkSetting = facade.getAppSettings.useQuery();
26+
27+
const currentNetwork =
28+
networkSetting.data?.[SettingsKey.Network] ?? Network.MAINNET;
29+
const targetNetwork =
30+
currentNetwork === Network.MAINNET ? Network.TESTNET : Network.MAINNET;
31+
32+
const handleNetworkChange = async (network: Network) => {
33+
setIsChangingNetwork(true);
34+
try {
35+
// First update the network setting
36+
await setAppSetting.mutateAsync({
37+
key: SettingsKey.Network,
38+
value: network,
39+
});
40+
41+
// Re-import all accounts for the new network
42+
const accounts = getAccounts.data ?? [];
43+
for (const account of accounts) {
44+
// Export the account first
45+
const encodedAccount = await exportAccount.mutateAsync({
46+
name: account.name,
47+
format: AccountFormat.Base64Json,
48+
});
49+
// Then import it for the new network
50+
try {
51+
await importAccount.mutateAsync({
52+
account: encodedAccount,
53+
name: account.name,
54+
});
55+
} catch (error) {
56+
console.error(error);
57+
}
58+
}
59+
} finally {
60+
setIsChangingNetwork(false);
61+
setModalVisible(false);
62+
}
63+
};
64+
65+
if (getAccounts.isLoading || networkSetting.isLoading) {
66+
return (
67+
<View style={styles.container}>
68+
<ActivityIndicator size="large" />
69+
<Text style={styles.loadingText}>Loading...</Text>
70+
</View>
71+
);
72+
}
1773

1874
return (
1975
<View style={styles.container}>
2076
<Modal animationType="slide" visible={modalVisible}>
2177
<View style={styles.container}>
22-
<Text>Switch to Testnet?</Text>
23-
<Text>
24-
Switching networks requires a blockchain rescan, which may take time
25-
based on your last sync.
26-
</Text>
27-
<Button
28-
title="Yes, Change Network"
29-
onPress={() => {
30-
setAppSetting.mutate({
31-
key: SettingsKey.Network,
32-
value: Network.TESTNET,
33-
});
34-
setModalVisible(false);
35-
}}
36-
/>
37-
<Button
38-
title="I changed my mind"
39-
onPress={() => setModalVisible(false)}
40-
/>
78+
{isChangingNetwork ? (
79+
<>
80+
<ActivityIndicator size="large" />
81+
<Text style={styles.loadingText}>
82+
Changing network and re-importing accounts...
83+
</Text>
84+
</>
85+
) : (
86+
<>
87+
<Text>Switch to {targetNetwork}?</Text>
88+
<Text>
89+
Switching networks will upload your accounts to the{" "}
90+
{targetNetwork} server. It may take a few minutes for your
91+
accounts to sync.
92+
</Text>
93+
<Button
94+
title="Yes, Change Network"
95+
onPress={() => handleNetworkChange(targetNetwork)}
96+
/>
97+
<Button
98+
title="I changed my mind"
99+
onPress={() => setModalVisible(false)}
100+
/>
101+
</>
102+
)}
41103
</View>
42104
</Modal>
43-
<Button title="Back" onPress={() => router.dismiss()} />
44-
<Text>Mainnet</Text>
45-
<Text>
46-
The live blockchain network where real transactions with value occur.
47-
</Text>
48-
<Text>Testnet</Text>
49-
<Text>A separate environment for testing without real asset risks.</Text>
105+
<View style={styles.networkOption}>
106+
<View style={styles.networkInfo}>
107+
<Text style={styles.networkTitle}>Mainnet</Text>
108+
<Text style={styles.networkDescription}>
109+
The live blockchain network where real transactions with value
110+
occur.
111+
</Text>
112+
{currentNetwork === Network.MAINNET && (
113+
<Text style={styles.selectedText}>(Selected)</Text>
114+
)}
115+
</View>
116+
</View>
117+
<View style={styles.networkOption}>
118+
<View style={styles.networkInfo}>
119+
<Text style={styles.networkTitle}>Testnet</Text>
120+
<Text style={styles.networkDescription}>
121+
A separate environment for testing without real asset risks.
122+
</Text>
123+
{currentNetwork === Network.TESTNET && (
124+
<Text style={styles.selectedText}>(Selected)</Text>
125+
)}
126+
</View>
127+
</View>
50128
<Button title="Change Network" onPress={() => setModalVisible(true)} />
51129
<StatusBar style="auto" />
52130
</View>
@@ -60,4 +138,32 @@ const styles = StyleSheet.create({
60138
alignItems: "center",
61139
justifyContent: "center",
62140
},
141+
networkOption: {
142+
width: "100%",
143+
padding: 16,
144+
borderBottomWidth: 1,
145+
borderBottomColor: "#eee",
146+
},
147+
networkInfo: {
148+
alignItems: "center",
149+
},
150+
networkTitle: {
151+
fontSize: 18,
152+
fontWeight: "bold",
153+
marginBottom: 8,
154+
},
155+
networkDescription: {
156+
textAlign: "center",
157+
color: "#666",
158+
marginBottom: 8,
159+
},
160+
selectedText: {
161+
color: "#007AFF",
162+
fontWeight: "500",
163+
},
164+
loadingText: {
165+
marginTop: 16,
166+
fontSize: 16,
167+
color: "#666",
168+
},
63169
});

packages/mobile-app/data/facades/chain/demoHandlers.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ export const chainDemoHandlers = f.facade<ChainHandlers>({
1919
};
2020
},
2121
),
22-
getNetworkInfo: f.handler.query(async () => {
23-
return { networkId: 0 };
24-
}),
2522
isValidPublicAddress: f.handler.query(
2623
async ({ address }: { address: string }) => {
2724
return isValidPublicAddress(address);

packages/mobile-app/data/facades/chain/oreowalletHandlers.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@ import { f } from "data-facade";
22
import { Asset, ChainHandlers } from "./types";
33

44
import { isValidPublicAddress } from "ironfish-native-module";
5-
import { Network } from "../../constants";
65
import { oreoWallet } from "../../wallet/oreowalletWallet";
6+
import { SettingsManager } from "@/data/settings/manager";
7+
import { SettingsKey } from "@/data/settings/db";
78

89
export const chainHandlers = f.facade<ChainHandlers>({
910
getAsset: f.handler.query(
1011
async ({ assetId }: { assetId: string }): Promise<Asset | null> => {
11-
const asset = await oreoWallet.getAsset(Network.MAINNET, assetId);
12+
const network = await SettingsManager.db().getOrDefault(
13+
SettingsKey.Network,
14+
);
15+
const asset = await oreoWallet.getAsset(network, assetId);
1216

1317
if (!asset) {
1418
return null;
@@ -35,10 +39,6 @@ export const chainHandlers = f.facade<ChainHandlers>({
3539
};
3640
},
3741
),
38-
getNetworkInfo: f.handler.query(async () => {
39-
// TODO: Implement network switching
40-
return { networkId: 0 };
41-
}),
4242
isValidPublicAddress: f.handler.query(({ address }: { address: string }) => {
4343
return isValidPublicAddress(address);
4444
}),

packages/mobile-app/data/facades/chain/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export type Asset = {
2525

2626
export type ChainHandlers = {
2727
getAsset: Query<(args: { assetId: string }) => Asset | null>;
28-
getNetworkInfo: Query<() => { networkId: number }>;
2928
isValidPublicAddress: Query<(args: { address: string }) => boolean>;
3029
requestFaucetTokens: Mutation<
3130
(args: { address: string; email: string }) => boolean

packages/mobile-app/data/facades/chain/walletServerHandlers.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ export const chainHandlers = f.facade<ChainHandlers>({
3535
};
3636
},
3737
),
38-
getNetworkInfo: f.handler.query(async () => {
39-
// TODO: Implement network switching
40-
return { networkId: 0 };
41-
}),
4238
isValidPublicAddress: f.handler.query(({ address }: { address: string }) => {
4339
return isValidPublicAddress(address);
4440
}),

0 commit comments

Comments
 (0)