forked from cerc-io/laconic-wallet
Add new page for approving eth transactions (#63)
* Display funds on signRequest page * Format balance value * Display upto 18 digits * Use useMemo for provider * Display balance in wei * Make UI changes * Make review changes * Add page to approve eth transactions * Update approve transaction page ui * Update balance unit display --------- Co-authored-by: Shreerang Kale <shreerangkale@gmail.com>
This commit is contained in:
parent
3cd4c51515
commit
0fa793bead
18
src/App.tsx
18
src/App.tsx
@ -25,6 +25,7 @@ import { StackParamsList } from './types';
|
|||||||
import { web3wallet } from './utils/wallet-connect/WalletConnectUtils';
|
import { web3wallet } from './utils/wallet-connect/WalletConnectUtils';
|
||||||
import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Lib';
|
import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Lib';
|
||||||
import { getSignParamsMessage } from './utils/wallet-connect/Helpers';
|
import { getSignParamsMessage } from './utils/wallet-connect/Helpers';
|
||||||
|
import ApproveTransaction from './screens/ApproveTransaction';
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<StackParamsList>();
|
const Stack = createNativeStackNavigator<StackParamsList>();
|
||||||
|
|
||||||
@ -65,10 +66,9 @@ const App = (): React.JSX.Element => {
|
|||||||
web3wallet!.engine.signClient.session.get(topic);
|
web3wallet!.engine.signClient.session.get(topic);
|
||||||
switch (request.method) {
|
switch (request.method) {
|
||||||
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
||||||
navigation.navigate('SignRequest', {
|
navigation.navigate('ApproveTransaction', {
|
||||||
network: 'eth',
|
network: 'eth',
|
||||||
address: request.params[0].from,
|
transaction: request.params[0],
|
||||||
message: JSON.stringify(request.params[0], undefined, 2),
|
|
||||||
requestEvent,
|
requestEvent,
|
||||||
requestSessionData,
|
requestSessionData,
|
||||||
});
|
});
|
||||||
@ -167,9 +167,7 @@ const App = (): React.JSX.Element => {
|
|||||||
name="SignRequest"
|
name="SignRequest"
|
||||||
component={SignRequest}
|
component={SignRequest}
|
||||||
options={{
|
options={{
|
||||||
headerTitle: () => (
|
headerTitle: () => <Text variant="titleLarge">Sign Request</Text>,
|
||||||
<Text variant="titleLarge">Sign this message?</Text>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@ -203,6 +201,14 @@ const App = (): React.JSX.Element => {
|
|||||||
title: 'New session',
|
title: 'New session',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Stack.Screen
|
||||||
|
name="ApproveTransaction"
|
||||||
|
component={ApproveTransaction}
|
||||||
|
options={{
|
||||||
|
title: 'Approve transaction',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
<PairingModal
|
<PairingModal
|
||||||
visible={modalVisible}
|
visible={modalVisible}
|
||||||
|
17
src/components/DataBox.tsx
Normal file
17
src/components/DataBox.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from 'react-native';
|
||||||
|
|
||||||
|
import styles from '../styles/stylesheet';
|
||||||
|
|
||||||
|
const DataBox = ({ label, data }: { label: string; data: string }) => {
|
||||||
|
return (
|
||||||
|
<View style={styles.dataBoxContainer}>
|
||||||
|
<Text style={styles.dataBoxLabel}>{label}</Text>
|
||||||
|
<View style={styles.dataBox}>
|
||||||
|
<Text style={styles.dataBoxData}>{data}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DataBox;
|
228
src/screens/ApproveTransaction.tsx
Normal file
228
src/screens/ApproveTransaction.tsx
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { Image, ScrollView, View } from 'react-native';
|
||||||
|
import { ActivityIndicator, Button, Text, Appbar } from 'react-native-paper';
|
||||||
|
import { PopulatedTransaction, providers, BigNumber } from 'ethers';
|
||||||
|
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
import {
|
||||||
|
NativeStackNavigationProp,
|
||||||
|
NativeStackScreenProps,
|
||||||
|
} from '@react-navigation/native-stack';
|
||||||
|
import { getHeaderTitle } from '@react-navigation/elements';
|
||||||
|
|
||||||
|
import { Account, StackParamsList } from '../types';
|
||||||
|
import AccountDetails from '../components/AccountDetails';
|
||||||
|
import styles from '../styles/stylesheet';
|
||||||
|
import { retrieveSingleAccount } from '../utils/accounts';
|
||||||
|
import {
|
||||||
|
approveWalletConnectRequest,
|
||||||
|
rejectWalletConnectRequest,
|
||||||
|
} from '../utils/wallet-connect/WalletConnectRequests';
|
||||||
|
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
|
import {
|
||||||
|
TEIP155Chain,
|
||||||
|
EIP155_CHAINS,
|
||||||
|
} from '../utils/wallet-connect/EIP155Data';
|
||||||
|
import DataBox from '../components/DataBox';
|
||||||
|
|
||||||
|
type SignRequestProps = NativeStackScreenProps<
|
||||||
|
StackParamsList,
|
||||||
|
'ApproveTransaction'
|
||||||
|
>;
|
||||||
|
|
||||||
|
const ApproveTransaction = ({ route }: SignRequestProps) => {
|
||||||
|
const requestSession = route.params?.requestSessionData;
|
||||||
|
const requestName = requestSession?.peer?.metadata?.name;
|
||||||
|
const requestIcon = requestSession?.peer?.metadata?.icons[0];
|
||||||
|
const requestURL = requestSession?.peer?.metadata?.url;
|
||||||
|
|
||||||
|
const [account, setAccount] = useState<Account>();
|
||||||
|
const [transactionData, setTransactionData] =
|
||||||
|
useState<PopulatedTransaction>();
|
||||||
|
const [network, setNetwork] = useState<string>('');
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [balance, setBalance] = useState<string>('');
|
||||||
|
|
||||||
|
const requestParams = route.params!.requestEvent;
|
||||||
|
const chainId = requestParams?.params.chainId;
|
||||||
|
const provider = useMemo(() => {
|
||||||
|
return new providers.JsonRpcProvider(
|
||||||
|
EIP155_CHAINS[chainId as TEIP155Chain].rpc,
|
||||||
|
);
|
||||||
|
}, [chainId]);
|
||||||
|
|
||||||
|
const navigation =
|
||||||
|
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||||
|
|
||||||
|
const retrieveData = async (
|
||||||
|
requestNetwork: string,
|
||||||
|
requestAddress: string,
|
||||||
|
) => {
|
||||||
|
const requestAccount = await retrieveSingleAccount(
|
||||||
|
requestNetwork,
|
||||||
|
requestAddress,
|
||||||
|
);
|
||||||
|
if (!requestAccount) {
|
||||||
|
navigation.navigate('InvalidPath');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestAccount !== account) {
|
||||||
|
setAccount(requestAccount);
|
||||||
|
}
|
||||||
|
if (requestNetwork !== network) {
|
||||||
|
setNetwork(requestNetwork);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.params?.transaction) {
|
||||||
|
setTransactionData(route.params?.transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const gasFees = useMemo(() => {
|
||||||
|
if (!transactionData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return BigNumber.from(transactionData?.gasLimit)
|
||||||
|
.mul(BigNumber.from(transactionData?.gasPrice))
|
||||||
|
.toString();
|
||||||
|
}, [transactionData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
route.params &&
|
||||||
|
retrieveData(route.params?.network, route.params?.transaction.from!);
|
||||||
|
}, [route]);
|
||||||
|
|
||||||
|
const acceptRequestHandler = async () => {
|
||||||
|
const { requestEvent } = route.params || {};
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
throw new Error('account not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!requestEvent) {
|
||||||
|
throw new Error('Request event not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await approveWalletConnectRequest(
|
||||||
|
requestEvent,
|
||||||
|
account,
|
||||||
|
network,
|
||||||
|
'',
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { topic } = requestEvent;
|
||||||
|
await web3wallet!.respondSessionRequest({ topic, response });
|
||||||
|
|
||||||
|
navigation.navigate('Laconic');
|
||||||
|
};
|
||||||
|
|
||||||
|
const rejectRequestHandler = async () => {
|
||||||
|
if (route.params?.requestEvent) {
|
||||||
|
const response = rejectWalletConnectRequest(route.params?.requestEvent);
|
||||||
|
const { topic } = route.params?.requestEvent;
|
||||||
|
await web3wallet!.respondSessionRequest({
|
||||||
|
topic,
|
||||||
|
response,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
navigation.navigate('Laconic');
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const getAccountBalance = async (account: Account) => {
|
||||||
|
const fetchedBalance = await provider.getBalance(account.address);
|
||||||
|
setBalance(fetchedBalance.toString());
|
||||||
|
};
|
||||||
|
|
||||||
|
if (account) {
|
||||||
|
getAccountBalance(account);
|
||||||
|
}
|
||||||
|
}, [account, provider]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
navigation.setOptions({
|
||||||
|
// eslint-disable-next-line react/no-unstable-nested-components
|
||||||
|
header: ({ options, back }) => {
|
||||||
|
const title = getHeaderTitle(options, 'Approve Transaction');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Appbar.Header>
|
||||||
|
{back && (
|
||||||
|
<Appbar.BackAction
|
||||||
|
onPress={async () => {
|
||||||
|
await rejectRequestHandler();
|
||||||
|
navigation.navigate('Laconic');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Appbar.Content title={title} />
|
||||||
|
</Appbar.Header>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [navigation, route.name]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isLoading ? (
|
||||||
|
<View style={styles.spinnerContainer}>
|
||||||
|
<ActivityIndicator size="large" color="#0000ff" />
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<ScrollView style={styles.appContainer}>
|
||||||
|
<View style={styles.dappDetails}>
|
||||||
|
{requestIcon && (
|
||||||
|
<Image
|
||||||
|
style={styles.dappLogo}
|
||||||
|
source={requestIcon ? { uri: requestIcon } : undefined}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Text>{requestName}</Text>
|
||||||
|
<Text variant="bodyMedium">{requestURL}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.dataBoxContainer}>
|
||||||
|
<Text style={styles.dataBoxLabel}>From</Text>
|
||||||
|
<View style={styles.dataBox}>
|
||||||
|
<AccountDetails account={account} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<DataBox
|
||||||
|
label="Balance (Wei)"
|
||||||
|
data={balance !== '' ? `${balance}` : 'Loading balance...'}
|
||||||
|
/>
|
||||||
|
{transactionData && (
|
||||||
|
<View style={styles.approveTransaction}>
|
||||||
|
<DataBox label="To" data={transactionData.to!} />
|
||||||
|
<DataBox
|
||||||
|
label="Amount (Wei)"
|
||||||
|
data={BigNumber.from(
|
||||||
|
transactionData.value?.toString(),
|
||||||
|
).toString()}
|
||||||
|
/>
|
||||||
|
<DataBox label="Gas Fees (Wei)" data={gasFees!} />
|
||||||
|
<DataBox label="Data" data={transactionData.data!} />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<View style={styles.buttonContainer}>
|
||||||
|
<Button mode="contained" onPress={acceptRequestHandler}>
|
||||||
|
Yes
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
mode="contained"
|
||||||
|
onPress={rejectRequestHandler}
|
||||||
|
buttonColor="#B82B0D">
|
||||||
|
No
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ApproveTransaction;
|
@ -185,7 +185,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
// eslint-disable-next-line react/no-unstable-nested-components
|
// eslint-disable-next-line react/no-unstable-nested-components
|
||||||
header: ({ options, back }) => {
|
header: ({ options, back }) => {
|
||||||
const title = getHeaderTitle(options, route.name);
|
const title = getHeaderTitle(options, 'Sign Request');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Appbar.Header>
|
<Appbar.Header>
|
||||||
@ -212,7 +212,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
<ActivityIndicator size="large" color="#0000ff" />
|
<ActivityIndicator size="large" color="#0000ff" />
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View style={styles.appContainer}>
|
<ScrollView style={styles.appContainer}>
|
||||||
<View style={styles.dappDetails}>
|
<View style={styles.dappDetails}>
|
||||||
{requestIcon && (
|
{requestIcon && (
|
||||||
<Image
|
<Image
|
||||||
@ -224,7 +224,6 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
<Text variant="bodyMedium">{requestURL}</Text>
|
<Text variant="bodyMedium">{requestURL}</Text>
|
||||||
</View>
|
</View>
|
||||||
<AccountDetails account={account} />
|
<AccountDetails account={account} />
|
||||||
|
|
||||||
{isCosmosSignDirect || isEthSendTransaction ? (
|
{isCosmosSignDirect || isEthSendTransaction ? (
|
||||||
<View style={styles.requestDirectMessage}>
|
<View style={styles.requestDirectMessage}>
|
||||||
<ScrollView nestedScrollEnabled>
|
<ScrollView nestedScrollEnabled>
|
||||||
@ -248,7 +247,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
No
|
No
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</ScrollView>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -18,7 +18,8 @@ const styles = StyleSheet.create({
|
|||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
accountContainer: {
|
accountContainer: {
|
||||||
marginTop: 12,
|
padding: 8,
|
||||||
|
paddingBottom: 0,
|
||||||
},
|
},
|
||||||
addAccountButton: {
|
addAccountButton: {
|
||||||
marginTop: 24,
|
marginTop: 24,
|
||||||
@ -129,6 +130,9 @@ const styles = StyleSheet.create({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
padding: 8,
|
padding: 8,
|
||||||
},
|
},
|
||||||
|
approveTransaction: {
|
||||||
|
height: '40%',
|
||||||
|
},
|
||||||
buttonContainer: {
|
buttonContainer: {
|
||||||
marginTop: 50,
|
marginTop: 50,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
@ -216,6 +220,34 @@ const styles = StyleSheet.create({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
|
dataBoxContainer: {
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
dataBoxLabel: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: 3,
|
||||||
|
color: 'black',
|
||||||
|
},
|
||||||
|
dataBox: {
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#ccc',
|
||||||
|
padding: 10,
|
||||||
|
borderRadius: 5,
|
||||||
|
},
|
||||||
|
dataBoxData: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: 'black',
|
||||||
|
},
|
||||||
|
transactionText: {
|
||||||
|
padding: 8,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: 'black',
|
||||||
|
},
|
||||||
|
balancePadding: {
|
||||||
|
padding: 8,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default styles;
|
export default styles;
|
||||||
|
10
src/types.ts
10
src/types.ts
@ -1,3 +1,5 @@
|
|||||||
|
import { PopulatedTransaction } from 'ethers';
|
||||||
|
|
||||||
import { SignClientTypes, SessionTypes } from '@walletconnect/types';
|
import { SignClientTypes, SessionTypes } from '@walletconnect/types';
|
||||||
import { Web3WalletTypes } from '@walletconnect/web3wallet';
|
import { Web3WalletTypes } from '@walletconnect/web3wallet';
|
||||||
|
|
||||||
@ -13,6 +15,14 @@ export type StackParamsList = {
|
|||||||
requestSessionData?: SessionTypes.Struct;
|
requestSessionData?: SessionTypes.Struct;
|
||||||
}
|
}
|
||||||
| undefined;
|
| undefined;
|
||||||
|
ApproveTransaction:
|
||||||
|
| {
|
||||||
|
network: string;
|
||||||
|
transaction: PopulatedTransaction;
|
||||||
|
requestEvent?: Web3WalletTypes.SessionRequest;
|
||||||
|
requestSessionData?: SessionTypes.Struct;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
InvalidPath: undefined;
|
InvalidPath: undefined;
|
||||||
WalletConnect: undefined;
|
WalletConnect: undefined;
|
||||||
AddSession: undefined;
|
AddSession: undefined;
|
||||||
|
@ -10,16 +10,16 @@ import { signDirectMessage, signEthMessage } from '../sign-message';
|
|||||||
import { Account } from '../../types';
|
import { Account } from '../../types';
|
||||||
import { getMnemonic, getPathKey } from '../misc';
|
import { getMnemonic, getPathKey } from '../misc';
|
||||||
import { getCosmosAccounts } from '../accounts';
|
import { getCosmosAccounts } from '../accounts';
|
||||||
import { TEIP155Chain, EIP155_CHAINS } from './EIP155Data';
|
|
||||||
|
|
||||||
export async function approveWalletConnectRequest(
|
export async function approveWalletConnectRequest(
|
||||||
requestEvent: SignClientTypes.EventArguments['session_request'],
|
requestEvent: SignClientTypes.EventArguments['session_request'],
|
||||||
account: Account,
|
account: Account,
|
||||||
network: string,
|
network: string,
|
||||||
message: string,
|
message?: string,
|
||||||
|
provider?: providers.JsonRpcProvider,
|
||||||
) {
|
) {
|
||||||
const { params, id } = requestEvent;
|
const { params, id } = requestEvent;
|
||||||
const { request, chainId } = params;
|
const { request } = params;
|
||||||
|
|
||||||
const path = (await getPathKey(network, account.counterId)).path;
|
const path = (await getPathKey(network, account.counterId)).path;
|
||||||
const mnemonic = await getMnemonic();
|
const mnemonic = await getMnemonic();
|
||||||
@ -27,7 +27,24 @@ export async function approveWalletConnectRequest(
|
|||||||
const address = cosmosAccount.data.address;
|
const address = cosmosAccount.data.address;
|
||||||
|
|
||||||
switch (request.method) {
|
switch (request.method) {
|
||||||
|
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
||||||
|
if (!provider) {
|
||||||
|
throw new Error('JSON RPC provider not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const privKey = (await getPathKey('eth', account.counterId)).privKey;
|
||||||
|
const wallet = new Wallet(privKey);
|
||||||
|
const sendTransaction = request.params[0];
|
||||||
|
const connectedWallet = await wallet.connect(provider);
|
||||||
|
const hash = await connectedWallet.sendTransaction(sendTransaction);
|
||||||
|
const receipt = typeof hash === 'string' ? hash : hash?.hash;
|
||||||
|
return formatJsonRpcResult(id, receipt);
|
||||||
|
|
||||||
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
|
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
|
||||||
|
if (!message) {
|
||||||
|
throw new Error('Message to be signed not found');
|
||||||
|
}
|
||||||
|
|
||||||
const ethSignature = await signEthMessage(message, account.counterId);
|
const ethSignature = await signEthMessage(message, account.counterId);
|
||||||
return formatJsonRpcResult(id, ethSignature);
|
return formatJsonRpcResult(id, ethSignature);
|
||||||
|
|
||||||
@ -69,18 +86,6 @@ export async function approveWalletConnectRequest(
|
|||||||
signature: cosmosAminoSignature.signature.signature,
|
signature: cosmosAminoSignature.signature.signature,
|
||||||
});
|
});
|
||||||
|
|
||||||
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
|
||||||
const provider = new providers.JsonRpcProvider(
|
|
||||||
EIP155_CHAINS[chainId as TEIP155Chain].rpc,
|
|
||||||
);
|
|
||||||
|
|
||||||
const privKey = (await getPathKey('eth', account.counterId)).privKey;
|
|
||||||
const wallet = new Wallet(privKey);
|
|
||||||
const sendTransaction = request.params[0];
|
|
||||||
const connectedWallet = await wallet.connect(provider);
|
|
||||||
const hash = await connectedWallet.sendTransaction(sendTransaction);
|
|
||||||
const receipt = typeof hash === 'string' ? hash : hash?.hash;
|
|
||||||
return formatJsonRpcResult(id, receipt);
|
|
||||||
default:
|
default:
|
||||||
throw new Error(getSdkError('INVALID_METHOD').message);
|
throw new Error(getSdkError('INVALID_METHOD').message);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user