Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Optimize switch chain logic
  • Loading branch information
Ethella committed Aug 22, 2025
commit 55037b4fae6a303c264d311bcae8e0d056cc780d
147 changes: 141 additions & 6 deletions MagicExpoRNExample/app/(tabs)/explore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import { GestureHandlerRootView, ScrollView } from 'react-native-gesture-handler
import { Card } from 'react-native-paper';
import { ethers } from 'ethers';
import { styles } from '../../styles/styles';
import {MagicService} from "@/hooks/magic";
import { useMagic } from "@/hooks/magic";

export default function CryptoScreen() {
const [publicAddress, updatePublicAddress] = React.useState('');
const [toAddress, onChangeToAddress] = React.useState('YOUR_PUBLIC_TO_ADDRESS');
const [toAddress, onChangeToAddress] = React.useState('');
const [transactionHash, updateTransactionHash] = React.useState('');
const [ciphertexts, setCiphertexts] = React.useState('');
const magic = MagicService.magic;
const provider = MagicService.provider;
const [chainId, setChainId] = React.useState('137');
const [solanaAddress, setSolanaAddress] = React.useState('');
const { magic, provider } = useMagic();

/** GetAccount */
const getAccount = async () => {
Expand Down Expand Up @@ -42,13 +43,18 @@ export default function CryptoScreen() {
/** sendTransaction */
const sendTransaction = async () => {
try {
if (!toAddress) {
Alert.alert('Error', 'Please enter a recipient address');
return;
}

const signer = await provider.getSigner();
const tx = await signer.sendTransaction({
from: publicAddress,
to: toAddress,
value: ethers.parseEther('0.1'),
value: ethers.parseEther('0.001'),
});
updateTransactionHash(tx.hash);
Alert.alert('Success', `Transaction sent: ${tx.hash}`);
} catch (err) {
console.error(err);
Alert.alert('Transaction Error', (err as Error).message);
Expand All @@ -74,6 +80,85 @@ export default function CryptoScreen() {
}
};

/**
* switchEVMChain
* */
const switchNetwork = async (chainId: number) => {
try {
const res = await magic.evm.switchEVMChain(chainId);
if (typeof res.network === 'string') {
Alert.alert('Success', `Switched to ${res.network}`);
} else {
Alert.alert('Success', `Switched to chain ${res.network.chainId} and rpc url ${res.network.rpcUrl}`);
}
console.log('Switch result:', res);
} catch (err) {
console.log(err);
Alert.alert('Error', `Failed to switch to chain ${chainId}`);
}
};

/**
* getSolanaPublicAddress
* */
const getSolanaPublicAddress = async () => {
try {
const res = await magic.solana.getPublicAddress();
setSolanaAddress(res);
Alert.alert('Solana Address', res);
} catch (err) {
console.log(err);
Alert.alert('Error', 'Failed to get Solana address');
}
};

/**
* Solana Transaction
*/
const sendSolanaTransaction = async () => {
try {
// Example Solana transaction
const transaction = await magic.solana.createTransaction({
to: solanaAddress || '11111111111111111111111111111111', // Default to system program
value: '0.001', // SOL amount
});

const signature = await magic.solana.signTransaction(transaction);
Alert.alert('Solana Transaction', `Signed: ${signature}`);
} catch (err) {
console.log(err);
Alert.alert('Error', 'Failed to send Solana transaction');
}
};

/**
* GDKMS
*/
const encrypt = async () => {
try {
const res = await magic.gdkms.encryptWithPrivateKey('asdf');
Alert.alert('Encrypted', res);
setCiphertexts(res);
} catch (err) {
console.log(err);
Alert.alert('Error', 'Failed to encrypt');
}
};

const decrypt = React.useCallback(async () => {
try {
if (!ciphertexts) {
Alert.alert('Error', 'No ciphertext to decrypt');
return;
}
const message = await magic.gdkms.decryptWithPrivateKey(ciphertexts);
Alert.alert('Decrypted', message);
} catch (err) {
console.log(err);
Alert.alert('Error', 'Failed to decrypt');
}
}, [ciphertexts, magic.gdkms]);

return (
<View style={styles.container}>
<GestureHandlerRootView style={{ flex: 1 }}>
Expand All @@ -87,6 +172,7 @@ export default function CryptoScreen() {
style={styles.TextInputContainer}
onChangeText={onChangeToAddress}
value={toAddress}
placeholder="Enter recipient address"
/>
<Text style={styles.publicAddress}>Transaction Hash: {transactionHash}</Text>
</View>
Expand All @@ -113,6 +199,55 @@ export default function CryptoScreen() {
<Button onPress={personalSign} title="Personal Sign" />
</View>
</Card>

{/* Switch EVM Chain */}
<Card>
<Card.Title title="Switch EVM Chain" />
<View style={styles.loginContainer}>
<View style={styles.emailContainer}>
<Text>Chain ID:</Text>
<TextInput
style={styles.TextInputContainer}
onChangeText={(chainId) => setChainId(chainId)}
value={chainId}
placeholder="Enter chain ID"
keyboardType="numeric"
/>
</View>
</View>
<View style={styles.actionContainer}>
<Button onPress={() => switchNetwork(Number(chainId))} title="Switch Chain" />
</View>
</Card>

{/* Solana */}
<Card>
<Card.Title title="Solana" />
<View style={styles.loginContainer}>
<Text style={styles.publicAddress}>
Solana Address: {solanaAddress}
</Text>
</View>
<View style={styles.actionContainer}>
<Button onPress={getSolanaPublicAddress} title="Get Solana Address" />
<Button onPress={sendSolanaTransaction} title="Send Solana Transaction" />
</View>
</Card>

{/* GDKMS */}
<Card>
<Card.Title title="Encrypt" />
<View style={styles.actionContainer}>
<Button onPress={encrypt} title="Encrypt" />
</View>
</Card>

<Card>
<Card.Title title="Decrypt" />
<View style={styles.actionContainer}>
<Button onPress={decrypt} title="Decrypt" />
</View>
</Card>
</ScrollView>
</GestureHandlerRootView>
</View>
Expand Down
65 changes: 2 additions & 63 deletions MagicExpoRNExample/app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { MagicService } from "@/hooks/magic";

export default function LoginScreen() {
const [email, onChangeEmail] = React.useState("[email protected]");
const [chainId, onChangeChainId] = React.useState("137");
const [recoveryEmail, onChangerecoveryEmail] =
React.useState("[email protected]");
const [phoneNumber, onChangePhoneNumber] = React.useState("+18888888888");
Expand Down Expand Up @@ -84,17 +83,6 @@ export default function LoginScreen() {
}
};

/** Magic Connect w/ UI */
const ConnectWithUI = async () => {
try {
const account = await magic.wallet.connectWithUI();
Alert.alert(`Your Public address is: ${account[0]}`);
} catch (err) {
const e = err as Error;
Alert.alert(e.message);
}
};

/**
* getInfo()
* */
Expand All @@ -103,22 +91,6 @@ export default function LoginScreen() {
Alert.alert(JSON.stringify(res));
};

/**
* switchEVMChain
* */
const switchNetwork = async (chainId: number) => {
const res = await magic.evm.switchEVMChain(chainId);
Alert.alert(JSON.stringify(res));
};

/**
* getSolanaPublicAddress
* */
const getSolanaPublicAddress = async () => {
const res = await magic.solana.getPublicAddress();
Alert.alert(JSON.stringify(res));
};

/**
* IsLoggedIn
* */
Expand Down Expand Up @@ -202,34 +174,7 @@ export default function LoginScreen() {
<Card.Title title="Is Logged In" />
<TouchableButton handler={() => isLoggedIn()} title="isLoggedIn" />
</Card>
{/* Solana Address */}
<Card>
<Card.Title title="Solana Address" />
<TouchableButton
handler={() => getSolanaPublicAddress()}
title="get Solana address"
/>
</Card>
{/* Switch EVM Chain */}
<Card>
<Card.Title title="Switch EVM Chain" />
<View style={styles.loginContainer}>
<View style={styles.emailContainer}>
<Text>Chain ID:</Text>
<TextInput
style={styles.TextInputContainer}
onChangeText={(chainId) => onChangeChainId(chainId)}
value={chainId}
/>
</View>
</View>
<View style={styles.margin10}>
<TouchableButton
handler={() => switchNetwork(Number(chainId))}
title="switchEVMChain"
/>
</View>
</Card>

{/* metaData */}
<Card>
<Card.Title title="Metadata (getInfo)" />
Expand Down Expand Up @@ -257,13 +202,7 @@ export default function LoginScreen() {
/>
</View>
</Card>
<Card>
<Card.Title title="Connect With UI" />
<TouchableButton
handler={() => ConnectWithUI()}
title="Connect With UI"
/>
</Card>

</ScrollView>
</GestureHandlerRootView>
</View>
Expand Down
2 changes: 1 addition & 1 deletion MagicExpoRNExample/constants/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ export const ENV = {
PROD: 'PROD'
}

export const API_KEY = 'YOUR_PUBLISHABLE_KEY';
export const API_KEY = 'pk_live_667E736A88BC7612';
13 changes: 10 additions & 3 deletions MagicExpoRNExample/hooks/magic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ const customOptimismOptions = {
};

export class MagicService {
private static _magic: Magic | null = null;
private static _magic: any = null;
private static _provider: ethers.BrowserProvider | null = null;

public static get magic(): Magic {
public static get magic(): any {
if (!this._magic) {
this._magic = new Magic(API_KEY, {
extensions: [
Expand All @@ -38,10 +38,17 @@ export class MagicService {
public static get provider(): ethers.BrowserProvider {
if (!this._provider) {
this._provider = new ethers.BrowserProvider(
// cast as any if necessary; Magic’s rpcProvider type is slightly different
MagicService.magic.rpcProvider as any
);
}
return this._provider;
}
}

// React hook to use Magic service
export function useMagic() {
return {
magic: MagicService.magic,
provider: MagicService.provider,
};
}