Update wallet code for token transfer using android embedded webview (#36)
Part of https://www.notion.so/Integrate-eSIM-buy-flow-into-app-18aa6b22d47280d4a77cf1b27e2ba193 Co-authored-by: AdityaSalunkhe21 <adityasalunkhe2204@gmail.com> Co-authored-by: pranavjadhav007 <jadhavpranav89@gmail.com> Reviewed-on: LaconicNetwork/laconic-wallet-web#36
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
REACT_APP_WALLET_CONNECT_PROJECT_ID=
|
REACT_APP_WALLET_CONNECT_PROJECT_ID=
|
||||||
|
|
||||||
REACT_APP_DEFAULT_GAS_PRICE=0.025
|
|
||||||
# Reference: https://github.com/cosmos/cosmos-sdk/issues/16020
|
# Reference: https://github.com/cosmos/cosmos-sdk/issues/16020
|
||||||
REACT_APP_GAS_ADJUSTMENT=2
|
REACT_APP_GAS_ADJUSTMENT=2
|
||||||
REACT_APP_LACONICD_RPC_URL=https://laconicd-sapo.laconic.com
|
REACT_APP_LACONICD_RPC_URL=https://laconicd-sapo.laconic.com
|
||||||
|
|||||||
Vendored
+21
@@ -14,10 +14,31 @@ declare global {
|
|||||||
|
|
||||||
// Called when accounts are ready for use
|
// Called when accounts are ready for use
|
||||||
onAccountsReady?: () => void;
|
onAccountsReady?: () => void;
|
||||||
|
|
||||||
|
// Called when transfer is successfully completed
|
||||||
|
onTransferComplete?: (result: string) => void;
|
||||||
|
|
||||||
|
// Called when transfer fails
|
||||||
|
onTransferError?: (error: string) => void;
|
||||||
|
|
||||||
|
// Called when transfer is cancelled
|
||||||
|
onTransferCancelled?: () => void;
|
||||||
|
|
||||||
|
// Called when account is created
|
||||||
|
onAccountCreated?: (account: string) => void;
|
||||||
|
|
||||||
|
// Called when account creation fails
|
||||||
|
onAccountError?: (error: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handles incoming signature requests from Android
|
// Handles incoming signature requests from Android
|
||||||
receiveSignRequestFromAndroid?: (message: string) => void;
|
receiveSignRequestFromAndroid?: (message: string) => void;
|
||||||
|
|
||||||
|
// Handles incoming transfer requests from Android
|
||||||
|
receiveTransferRequestFromAndroid?: (to: string, amount: string, namespace: String, chainId: string, memo: string) => void;
|
||||||
|
|
||||||
|
// Handles account creation requests from Android
|
||||||
|
receiveGetOrCreateAccountFromAndroid?: (chainId: string) => void;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,35 +58,14 @@ const useGetOrCreateAccounts = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const autoCreateAccounts = async () => {
|
|
||||||
const defaultChainId = networksData[0]?.chainId;
|
|
||||||
|
|
||||||
if (!defaultChainId) {
|
|
||||||
console.log('useGetOrCreateAccounts: No default chainId found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const accounts = await getOrCreateAccountsForChain(defaultChainId);
|
|
||||||
|
|
||||||
// Only notify Android when we actually have accounts
|
|
||||||
if (accounts.length > 0 && window.Android?.onAccountsReady) {
|
|
||||||
window.Android.onAccountsReady();
|
|
||||||
} else {
|
|
||||||
console.log('No accounts created or Android bridge not available');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('message', handleCreateAccounts);
|
window.addEventListener('message', handleCreateAccounts);
|
||||||
|
|
||||||
const isAndroidWebView = !!(window.Android);
|
|
||||||
|
|
||||||
if (isAndroidWebView) {
|
|
||||||
autoCreateAccounts();
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('message', handleCreateAccounts);
|
window.removeEventListener('message', handleCreateAccounts);
|
||||||
};
|
};
|
||||||
}, [networksData, getAccountsData, getOrCreateAccountsForChain]);
|
}, [networksData, getAccountsData, getOrCreateAccountsForChain]);
|
||||||
|
|
||||||
|
return { getOrCreateAccountsForChain };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useGetOrCreateAccounts;
|
export default useGetOrCreateAccounts;
|
||||||
|
|||||||
@@ -4,17 +4,38 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
|||||||
|
|
||||||
import { useAccounts } from '../context/AccountsContext';
|
import { useAccounts } from '../context/AccountsContext';
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
import { useNetworks } from '../context/NetworksContext';
|
||||||
|
import useAccountsData from "../hooks/useAccountsData";
|
||||||
|
|
||||||
import { StackParamsList } from '../types';
|
import { StackParamsList } from '../types';
|
||||||
import useGetOrCreateAccounts from './useGetOrCreateAccounts';
|
import useGetOrCreateAccounts from './useGetOrCreateAccounts';
|
||||||
|
import { retrieveAccountsForNetwork } from '../utils/accounts';
|
||||||
|
|
||||||
export const useWebViewHandler = () => {
|
export const useWebViewHandler = () => {
|
||||||
// Navigation and context hooks
|
// Navigation and context hooks
|
||||||
const navigation = useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
const navigation = useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||||
const { selectedNetwork } = useNetworks();
|
const { selectedNetwork } = useNetworks();
|
||||||
const { accounts, currentIndex } = useAccounts();
|
const { accounts, currentIndex } = useAccounts();
|
||||||
|
const { getAccountsData } = useAccountsData();
|
||||||
|
const { getOrCreateAccountsForChain } = useGetOrCreateAccounts();
|
||||||
|
|
||||||
// Initialize accounts
|
// Initialize accounts
|
||||||
useGetOrCreateAccounts();
|
const handleGetOrCreateAccount = useCallback(async (chainId: string) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const accountsData = await getOrCreateAccountsForChain(chainId);
|
||||||
|
|
||||||
|
if (!accountsData || accountsData.length === 0) {
|
||||||
|
window.Android?.onAccountError?.('Failed to create/retrieve account');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Android?.onAccountCreated?.(JSON.stringify(accountsData[0]));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Account operation error:', error);
|
||||||
|
window.Android?.onAccountError?.(`Operation failed: ${error}`);
|
||||||
|
}
|
||||||
|
}, [getOrCreateAccountsForChain, getAccountsData]);
|
||||||
|
|
||||||
// Core navigation handler
|
// Core navigation handler
|
||||||
const navigateToSignRequest = useCallback((message: string) => {
|
const navigateToSignRequest = useCallback((message: string) => {
|
||||||
@@ -27,12 +48,14 @@ export const useWebViewHandler = () => {
|
|||||||
|
|
||||||
if (!accounts?.length) {
|
if (!accounts?.length) {
|
||||||
window.Android?.onSignatureError?.('No accounts available');
|
window.Android?.onSignatureError?.('No accounts available');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentAccount = accounts[currentIndex];
|
const currentAccount = accounts[currentIndex];
|
||||||
if (!currentAccount) {
|
if (!currentAccount) {
|
||||||
window.Android?.onSignatureError?.('Current account not found');
|
window.Android?.onSignatureError?.('Current account not found');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +66,7 @@ export const useWebViewHandler = () => {
|
|||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
window.Android?.onSignatureError?.('Invalid signing path');
|
window.Android?.onSignatureError?.('Invalid signing path');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,12 +94,67 @@ export const useWebViewHandler = () => {
|
|||||||
}
|
}
|
||||||
}, [selectedNetwork, accounts, currentIndex, navigation]);
|
}, [selectedNetwork, accounts, currentIndex, navigation]);
|
||||||
|
|
||||||
|
// Handle incoming transfer requests
|
||||||
|
const navigateToTransfer = useCallback(async (to: string, amount: string, namespace: String, chainId: string, memo: string) => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: Pass the account info for transferring tokens
|
||||||
|
// Get first account
|
||||||
|
const [chainAccount] = await retrieveAccountsForNetwork(
|
||||||
|
`${namespace}:${chainId}`,
|
||||||
|
'0'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!chainAccount) {
|
||||||
|
console.error('Accounts not found');
|
||||||
|
if (window.Android?.onTransferError) {
|
||||||
|
window.Android.onTransferError('Accounts not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = `/transfer/${namespace}/${chainId}/${chainAccount.address}/${to}/${amount}`;
|
||||||
|
|
||||||
|
navigation.reset({
|
||||||
|
index: 0,
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'ApproveTransfer',
|
||||||
|
path: path,
|
||||||
|
params: {
|
||||||
|
namespace: namespace,
|
||||||
|
chainId: `${namespace}:${chainId}`,
|
||||||
|
transaction: {
|
||||||
|
from: chainAccount.address,
|
||||||
|
to: to,
|
||||||
|
value: amount
|
||||||
|
},
|
||||||
|
accountInfo: chainAccount,
|
||||||
|
memo: memo
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (window.Android?.onTransferError) {
|
||||||
|
window.Android.onTransferError(`Navigation error: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [navigation]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Assign the function to the window object
|
// Assign the function to the window object
|
||||||
window.receiveSignRequestFromAndroid = navigateToSignRequest;
|
window.receiveSignRequestFromAndroid = navigateToSignRequest;
|
||||||
|
|
||||||
|
window.receiveTransferRequestFromAndroid = navigateToTransfer;
|
||||||
|
|
||||||
|
window.receiveGetOrCreateAccountFromAndroid = handleGetOrCreateAccount;
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.receiveSignRequestFromAndroid = undefined;
|
window.receiveSignRequestFromAndroid = undefined;
|
||||||
|
window.receiveTransferRequestFromAndroid = undefined;
|
||||||
|
window.receiveGetOrCreateAccountFromAndroid = undefined;
|
||||||
};
|
};
|
||||||
}, [navigateToSignRequest]); // Only the function reference as dependency
|
}, [navigateToSignRequest, navigateToTransfer, handleGetOrCreateAccount]); // Only the function reference as dependency
|
||||||
};
|
};
|
||||||
|
|||||||
+190
-106
@@ -2,11 +2,11 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|||||||
import { Image, ScrollView, View } from 'react-native';
|
import { Image, ScrollView, View } from 'react-native';
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Button,
|
|
||||||
Text,
|
Text,
|
||||||
Appbar,
|
Appbar,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from 'react-native-paper';
|
} from 'react-native-paper';
|
||||||
|
import JSONbig from 'json-bigint';
|
||||||
import { providers, BigNumber } from 'ethers';
|
import { providers, BigNumber } from 'ethers';
|
||||||
import { Deferrable } from 'ethers/lib/utils';
|
import { Deferrable } from 'ethers/lib/utils';
|
||||||
|
|
||||||
@@ -41,28 +41,29 @@ import { COSMOS, EIP155, IS_NUMBER_REGEX } from '../utils/constants';
|
|||||||
import TxErrorDialog from '../components/TxErrorDialog';
|
import TxErrorDialog from '../components/TxErrorDialog';
|
||||||
import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data';
|
import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data';
|
||||||
import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData';
|
import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData';
|
||||||
|
import { Button } from '@mui/material';
|
||||||
|
import { LoadingButton } from '@mui/lab';
|
||||||
|
|
||||||
export const MEMO = 'Sending signed tx from Laconic Wallet';
|
export const MEMO = 'Sending signed tx from Laconic Wallet';
|
||||||
// Reference: https://ethereum.org/en/developers/docs/gas/#what-is-gas-limit
|
// Reference: https://ethereum.org/en/developers/docs/gas/#what-is-gas-limit
|
||||||
const ETH_MINIMUM_GAS = 21000;
|
const ETH_MINIMUM_GAS = 21000;
|
||||||
|
|
||||||
type SignRequestProps = NativeStackScreenProps<
|
type ApproveTransferProps = NativeStackScreenProps<StackParamsList, 'ApproveTransfer'>
|
||||||
StackParamsList,
|
|
||||||
'ApproveTransfer'
|
|
||||||
>;
|
|
||||||
|
|
||||||
const ApproveTransfer = ({ route }: SignRequestProps) => {
|
const ApproveTransfer = ({ route }: ApproveTransferProps) => {
|
||||||
const { networksData } = useNetworks();
|
const { networksData } = useNetworks();
|
||||||
const { web3wallet } = useWalletConnect();
|
const { web3wallet } = useWalletConnect();
|
||||||
|
|
||||||
|
// Extract data from route params or path
|
||||||
const requestSession = route.params.requestSessionData;
|
const requestSession = route.params.requestSessionData;
|
||||||
const requestName = requestSession.peer.metadata.name;
|
const requestName = requestSession?.peer.metadata.name;
|
||||||
const requestIcon = requestSession.peer.metadata.icons[0];
|
const requestIcon = requestSession?.peer.metadata.icons[0];
|
||||||
const requestURL = requestSession.peer.metadata.url;
|
const requestURL = requestSession?.peer.metadata.url;
|
||||||
const transaction = route.params.transaction;
|
const transaction = route.params.transaction;
|
||||||
const requestEvent = route.params.requestEvent;
|
const requestEvent = route.params.requestEvent;
|
||||||
const chainId = requestEvent.params.chainId;
|
const chainId = requestEvent?.params.chainId || route.params.chainId;
|
||||||
const requestMethod = requestEvent.params.request.method;
|
const requestMethod = requestEvent?.params.request.method;
|
||||||
|
const txMemo = route.params.memo || MEMO;
|
||||||
|
|
||||||
const [account, setAccount] = useState<Account>();
|
const [account, setAccount] = useState<Account>();
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
@@ -205,6 +206,58 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
[navigation, requestedNetwork],
|
[navigation, requestedNetwork],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//TODO: Handle ETH transactions
|
||||||
|
const handleIntent = async () => {
|
||||||
|
if (!account) {
|
||||||
|
throw new Error('Account is not valid');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Sending transaction request:', {
|
||||||
|
from: account.address,
|
||||||
|
to: transaction.to,
|
||||||
|
amount: transaction.value,
|
||||||
|
denom: requestedNetwork!.nativeDenom,
|
||||||
|
memo: txMemo,
|
||||||
|
gas: cosmosGasLimit,
|
||||||
|
fees: fees
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!requestedNetwork) {
|
||||||
|
throw new Error('Network not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cosmosStargateClient) {
|
||||||
|
throw new Error('Cosmos stargate client not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await cosmosStargateClient.signAndBroadcast(
|
||||||
|
account.address,
|
||||||
|
[sendMsg],
|
||||||
|
{
|
||||||
|
amount: [
|
||||||
|
{
|
||||||
|
amount: fees,
|
||||||
|
denom: requestedNetwork.nativeDenom!,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
gas: cosmosGasLimit,
|
||||||
|
},
|
||||||
|
txMemo,
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Transaction result:', result);
|
||||||
|
|
||||||
|
// Convert BigInt values to strings before sending to Android
|
||||||
|
const serializedResult = JSONbig.stringify(result);
|
||||||
|
|
||||||
|
// Send the result back to Android and close dialog
|
||||||
|
if (window.Android?.onTransferComplete) {
|
||||||
|
window.Android.onTransferComplete(serializedResult);
|
||||||
|
} else {
|
||||||
|
alert(`Transaction: ${serializedResult}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Set loading to false when gas values for requested chain are fetched
|
// Set loading to false when gas values for requested chain are fetched
|
||||||
// If requested chain is EVM compatible, the cosmos gas values will be undefined and vice-versa, hence the condition checks only one of them at the same time
|
// If requested chain is EVM compatible, the cosmos gas values will be undefined and vice-versa, hence the condition checks only one of them at the same time
|
||||||
@@ -246,6 +299,7 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
requestedNetwork,
|
requestedNetwork,
|
||||||
ethMaxFee,
|
ethMaxFee,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
retrieveData(transaction.from!);
|
retrieveData(transaction.from!);
|
||||||
}, [retrieveData, transaction]);
|
}, [retrieveData, transaction]);
|
||||||
@@ -267,78 +321,82 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
throw new Error('account not found');
|
throw new Error('account not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ethGasLimit && ethGasLimit.lt(ETH_MINIMUM_GAS)) {
|
if (requestEvent) {
|
||||||
throw new Error(`Atleast ${ETH_MINIMUM_GAS} gas limit is required`);
|
// Handle WalletConnect request
|
||||||
}
|
if (ethGasLimit && ethGasLimit.lt(ETH_MINIMUM_GAS)) {
|
||||||
|
throw new Error(`Atleast ${ETH_MINIMUM_GAS} gas limit is required`);
|
||||||
|
}
|
||||||
|
|
||||||
if (ethMaxFee && ethMaxPriorityFee && ethMaxFee.lte(ethMaxPriorityFee)) {
|
if (ethMaxFee && ethMaxPriorityFee && ethMaxFee.lte(ethMaxPriorityFee)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Max fee per gas (${ethMaxFee.toNumber()}) cannot be lower than or equal to max priority fee per gas (${ethMaxPriorityFee.toNumber()})`,
|
`Max fee per gas (${ethMaxFee.toNumber()}) cannot be lower than or equal to max priority fee per gas (${ethMaxPriorityFee.toNumber()})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let options: WalletConnectRequests;
|
||||||
|
|
||||||
|
switch (requestMethod) {
|
||||||
|
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
||||||
|
if (
|
||||||
|
ethMaxFee === undefined ||
|
||||||
|
ethMaxPriorityFee === undefined ||
|
||||||
|
ethGasPrice === undefined
|
||||||
|
) {
|
||||||
|
throw new Error('Gas values not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
options = {
|
||||||
|
type: 'eth_sendTransaction',
|
||||||
|
provider: provider!,
|
||||||
|
ethGasLimit: BigNumber.from(ethGasLimit),
|
||||||
|
ethGasPrice: ethGasPrice ? ethGasPrice.toHexString() : null,
|
||||||
|
maxFeePerGas: ethMaxFee,
|
||||||
|
maxPriorityFeePerGas: ethMaxPriorityFee,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case COSMOS_METHODS.COSMOS_SEND_TOKENS:
|
||||||
|
if (!cosmosStargateClient) {
|
||||||
|
throw new Error('Cosmos stargate client not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
options = {
|
||||||
|
type: 'cosmos_sendTokens',
|
||||||
|
signingStargateClient: cosmosStargateClient,
|
||||||
|
cosmosFee: {
|
||||||
|
amount: [
|
||||||
|
{
|
||||||
|
amount: fees,
|
||||||
|
denom: requestedNetwork!.nativeDenom!,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
gas: cosmosGasLimit,
|
||||||
|
},
|
||||||
|
sendMsg,
|
||||||
|
memo: txMemo,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('Invalid method');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await approveWalletConnectRequest(
|
||||||
|
requestEvent,
|
||||||
|
account!,
|
||||||
|
namespace,
|
||||||
|
requestedNetwork!.chainId,
|
||||||
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { topic } = requestEvent;
|
||||||
|
await web3wallet!.respondSessionRequest({ topic, response });
|
||||||
|
navigation.navigate('Home');
|
||||||
|
} else {
|
||||||
|
await handleIntent();
|
||||||
}
|
}
|
||||||
|
|
||||||
let options: WalletConnectRequests;
|
|
||||||
|
|
||||||
switch (requestMethod) {
|
|
||||||
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
|
||||||
if (
|
|
||||||
ethMaxFee === undefined ||
|
|
||||||
ethMaxPriorityFee === undefined ||
|
|
||||||
ethGasPrice === undefined
|
|
||||||
) {
|
|
||||||
throw new Error('Gas values not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
options = {
|
|
||||||
type: 'eth_sendTransaction',
|
|
||||||
provider: provider!,
|
|
||||||
ethGasLimit: BigNumber.from(ethGasLimit),
|
|
||||||
ethGasPrice: ethGasPrice ? ethGasPrice.toHexString() : null,
|
|
||||||
maxFeePerGas: ethMaxFee,
|
|
||||||
maxPriorityFeePerGas: ethMaxPriorityFee,
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case COSMOS_METHODS.COSMOS_SEND_TOKENS:
|
|
||||||
if (!cosmosStargateClient) {
|
|
||||||
throw new Error('Cosmos stargate client not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
options = {
|
|
||||||
type: 'cosmos_sendTokens',
|
|
||||||
signingStargateClient: cosmosStargateClient,
|
|
||||||
// StdFee object
|
|
||||||
cosmosFee: {
|
|
||||||
// This amount is total fees required for transaction
|
|
||||||
amount: [
|
|
||||||
{
|
|
||||||
amount: fees,
|
|
||||||
denom: requestedNetwork!.nativeDenom!,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
gas: cosmosGasLimit,
|
|
||||||
},
|
|
||||||
sendMsg,
|
|
||||||
memo: MEMO,
|
|
||||||
};
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error('Invalid method');
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await approveWalletConnectRequest(
|
|
||||||
requestEvent,
|
|
||||||
account,
|
|
||||||
namespace,
|
|
||||||
requestedNetwork!.chainId,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { topic } = requestEvent;
|
|
||||||
await web3wallet!.respondSessionRequest({ topic, response });
|
|
||||||
navigation.navigate('Home');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (window.Android?.onTransferError) {
|
||||||
|
window.Android.onTransferError(`Transaction Failed: ${error}`);
|
||||||
|
}
|
||||||
if (!(error instanceof Error)) {
|
if (!(error instanceof Error)) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@@ -350,14 +408,31 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const rejectRequestHandler = async () => {
|
const rejectRequestHandler = async () => {
|
||||||
const response = rejectWalletConnectRequest(requestEvent);
|
setIsTxLoading(true);
|
||||||
const { topic } = requestEvent;
|
try {
|
||||||
await web3wallet!.respondSessionRequest({
|
if (requestEvent) {
|
||||||
topic,
|
const response = rejectWalletConnectRequest(requestEvent);
|
||||||
response,
|
const { topic } = requestEvent;
|
||||||
});
|
await web3wallet!.respondSessionRequest({
|
||||||
|
topic,
|
||||||
|
response,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
navigation.navigate('Home');
|
if (window.Android?.onTransferCancelled) {
|
||||||
|
window.Android.onTransferCancelled();
|
||||||
|
} else {
|
||||||
|
navigation.navigate('Home');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof Error)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTxError(error.message);
|
||||||
|
setIsTxErrorDialogOpen(true);
|
||||||
|
}
|
||||||
|
setIsTxLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -472,7 +547,7 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
const gasEstimation = await cosmosStargateClient.simulate(
|
const gasEstimation = await cosmosStargateClient.simulate(
|
||||||
transaction.from!,
|
transaction.from!,
|
||||||
[sendMsg],
|
[sendMsg],
|
||||||
MEMO,
|
txMemo,
|
||||||
);
|
);
|
||||||
|
|
||||||
setCosmosGasLimit(
|
setCosmosGasLimit(
|
||||||
@@ -490,7 +565,7 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
getCosmosGas();
|
getCosmosGas();
|
||||||
}, [cosmosStargateClient, isSufficientFunds, sendMsg, transaction]);
|
}, [cosmosStargateClient, isSufficientFunds, sendMsg, transaction,txMemo]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (balance && !isSufficientFunds) {
|
if (balance && !isSufficientFunds) {
|
||||||
@@ -508,16 +583,18 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ScrollView contentContainerStyle={styles.appContainer}>
|
<ScrollView contentContainerStyle={styles.appContainer}>
|
||||||
<View style={styles.dappDetails}>
|
{requestSession && (
|
||||||
{requestIcon && (
|
<View style={styles.dappDetails}>
|
||||||
<Image
|
{requestIcon && (
|
||||||
style={styles.dappLogo}
|
<Image
|
||||||
source={requestIcon ? { uri: requestIcon } : undefined}
|
style={styles.dappLogo}
|
||||||
/>
|
source={requestIcon ? { uri: requestIcon } : undefined}
|
||||||
)}
|
/>
|
||||||
<Text>{requestName}</Text>
|
)}
|
||||||
<Text variant="bodyMedium">{requestURL}</Text>
|
<Text>{requestName}</Text>
|
||||||
</View>
|
<Text variant="bodyMedium">{requestURL}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
<View style={styles.dataBoxContainer}>
|
<View style={styles.dataBoxContainer}>
|
||||||
<Text style={styles.dataBoxLabel}>From</Text>
|
<Text style={styles.dataBoxLabel}>From</Text>
|
||||||
<View style={styles.dataBox}>
|
<View style={styles.dataBox}>
|
||||||
@@ -545,6 +622,12 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
transaction.value?.toString(),
|
transaction.value?.toString(),
|
||||||
).toString()}
|
).toString()}
|
||||||
/>
|
/>
|
||||||
|
{namespace === COSMOS && (
|
||||||
|
<DataBox
|
||||||
|
label="Memo"
|
||||||
|
data={txMemo}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{namespace === EIP155 ? (
|
{namespace === EIP155 ? (
|
||||||
<>
|
<>
|
||||||
@@ -638,17 +721,18 @@ const ApproveTransfer = ({ route }: SignRequestProps) => {
|
|||||||
)}
|
)}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
<View style={styles.buttonContainer}>
|
<View style={styles.buttonContainer}>
|
||||||
<Button
|
<LoadingButton
|
||||||
mode="contained"
|
variant="contained"
|
||||||
onPress={acceptRequestHandler}
|
onClick={acceptRequestHandler}
|
||||||
loading={isTxLoading}
|
loading={isTxLoading}
|
||||||
disabled={!balance || !fees}>
|
disabled={!balance || !fees}
|
||||||
|
id="approve-transaction-button">
|
||||||
{isTxLoading ? 'Processing' : 'Yes'}
|
{isTxLoading ? 'Processing' : 'Yes'}
|
||||||
</Button>
|
</LoadingButton>
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
variant="contained"
|
||||||
onPress={rejectRequestHandler}
|
onClick={rejectRequestHandler}
|
||||||
buttonColor="#B82B0D">
|
color="error">
|
||||||
No
|
No
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
+4
-2
@@ -21,9 +21,11 @@ export type StackParamsList = {
|
|||||||
requestSessionData?: SessionTypes.Struct;
|
requestSessionData?: SessionTypes.Struct;
|
||||||
};
|
};
|
||||||
ApproveTransfer: {
|
ApproveTransfer: {
|
||||||
|
chainId?: string;
|
||||||
transaction: PopulatedTransaction;
|
transaction: PopulatedTransaction;
|
||||||
requestEvent: Web3WalletTypes.SessionRequest;
|
requestEvent?: Web3WalletTypes.SessionRequest;
|
||||||
requestSessionData: SessionTypes.Struct;
|
requestSessionData?: SessionTypes.Struct;
|
||||||
|
memo?: string;
|
||||||
};
|
};
|
||||||
InvalidPath: undefined;
|
InvalidPath: undefined;
|
||||||
WalletConnect: undefined;
|
WalletConnect: undefined;
|
||||||
|
|||||||
+19
-3
@@ -41,10 +41,10 @@ export const DEFAULT_NETWORKS: NetworksFormData[] = [
|
|||||||
isDefault: true,
|
isDefault: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
chainId: 'theta-testnet-001',
|
chainId: 'provider',
|
||||||
networkName: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].name,
|
networkName: COSMOS_TESTNET_CHAINS['cosmos:provider'].name,
|
||||||
namespace: COSMOS,
|
namespace: COSMOS,
|
||||||
rpcUrl: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].rpc,
|
rpcUrl: COSMOS_TESTNET_CHAINS['cosmos:provider'].rpc,
|
||||||
blockExplorerUrl: '',
|
blockExplorerUrl: '',
|
||||||
nativeDenom: 'uatom',
|
nativeDenom: 'uatom',
|
||||||
addressPrefix: 'cosmos',
|
addressPrefix: 'cosmos',
|
||||||
@@ -52,6 +52,22 @@ export const DEFAULT_NETWORKS: NetworksFormData[] = [
|
|||||||
gasPrice: '0.025',
|
gasPrice: '0.025',
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
//TODO: Add network from android app
|
||||||
|
{
|
||||||
|
chainId: 'nyx',
|
||||||
|
networkName: 'Nym',
|
||||||
|
namespace: COSMOS,
|
||||||
|
rpcUrl: 'https://rpc.nymtech.net',
|
||||||
|
blockExplorerUrl: 'https://explorer.nymtech.net',
|
||||||
|
nativeDenom: 'unym',
|
||||||
|
addressPrefix: 'n',
|
||||||
|
coinType: '118',
|
||||||
|
|
||||||
|
// Ref: https://nym.com/docs/operators/nodes/validator-setup#apptoml-configuration
|
||||||
|
gasPrice: '0.025',
|
||||||
|
isDefault: true,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const CHAINID_DEBOUNCE_DELAY = 250;
|
export const CHAINID_DEBOUNCE_DELAY = 250;
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ export const COSMOS_TESTNET_CHAINS: Record<
|
|||||||
namespace: string;
|
namespace: string;
|
||||||
}
|
}
|
||||||
> = {
|
> = {
|
||||||
'cosmos:theta-testnet-001': {
|
'cosmos:provider': {
|
||||||
chainId: 'theta-testnet-001',
|
chainId: 'provider',
|
||||||
name: 'Cosmos Hub Testnet',
|
name: 'Cosmos Hub Testnet',
|
||||||
rpc: 'https://rpc-t.cosmos.nodestake.top',
|
rpc: 'https://rpc-rs.cosmos.nodestake.top',
|
||||||
namespace: 'cosmos',
|
namespace: 'cosmos',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user