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

feat: implement eth sendrawtransaction handler #1994

Merged
merged 16 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from 14 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
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { handleError } from '@core/error/handlers'
import { IConnectedDapp } from '@auxiliary/wallet-connect/interface'
import { CallbackParameters } from '@auxiliary/wallet-connect/types'
import { signAndSendTransactionFromEvm } from '@core/wallet/actions'
import { sendAndPersistTransactionFromEvm, signEvmTransaction } from '@core/wallet/actions'
import { selectedAccount } from '@core/account/stores'
import { ExplorerEndpoint, IChain, getDefaultExplorerUrl } from '@core/network'
import { DappInfo, TransactionAssetSection } from '@ui'
Expand All @@ -13,6 +13,7 @@
import {
calculateEstimatedGasFeeFromTransactionData,
calculateMaxGasFeeFromTransactionData,
getHexEncodedTransaction,
getMethodNameForEvmTransaction,
} from '@core/layer-2'
import { getTokenFromSelectedAccountTokens } from '@core/token/stores'
Expand All @@ -28,17 +29,23 @@
import { BASE_TOKEN_ID } from '@core/token/constants'
import { checkActiveProfileAuthAsync } from '@core/profile/actions'
import { LedgerAppName } from '@core/ledger'
import { DappVerification } from '@auxiliary/wallet-connect/enums'
import { DappVerification, RpcMethod } from '@auxiliary/wallet-connect/enums'
import { LegacyTransaction } from '@ethereumjs/tx'

export let preparedTransaction: EvmTransactionData
export let chain: IChain
export let dapp: IConnectedDapp
export let signAndSend: boolean
export let verifiedState: DappVerification
export let method: RpcMethod.EthSendTransaction | RpcMethod.EthSignTransaction | RpcMethod.EthSendRawTransaction
export let callback: (params: CallbackParameters) => void

const { id } = chain.getConfiguration()
$: localeKey = signAndSend ? (isSmartContractCall ? 'smartContractCall' : 'sendTransaction') : 'signTransaction'
$: localeKey =
method === RpcMethod.EthSignTransaction
? 'signTransaction'
: isSmartContractCall
? 'smartContractCall'
: 'sendTransaction'
Tuditi marked this conversation as resolved.
Show resolved Hide resolved

let nft: Nft | undefined
let tokenTransfer: TokenTransferData | undefined
Expand Down Expand Up @@ -79,6 +86,30 @@
}
}

async function getSignedTransaction(): Promise<string> {
if (preparedTransaction?.v && preparedTransaction?.s && preparedTransaction?.r) {
const transaction = LegacyTransaction.fromTxData(preparedTransaction)
return getHexEncodedTransaction(transaction)
} else {
return await signEvmTransaction(preparedTransaction, chain, $selectedAccount)
}
}

async function signOrSend(): Promise<void> {
const signedTransaction = await getSignedTransaction()
if (method === RpcMethod.EthSignTransaction) {
callback({ result: signedTransaction })
return
}
const transactionHash = await sendAndPersistTransactionFromEvm(
preparedTransaction,
signedTransaction,
chain,
$selectedAccount
)
callback({ result: transactionHash })
}

async function onConfirmClick(): Promise<void> {
try {
await checkActiveProfileAuthAsync(LedgerAppName.Ethereum)
Expand All @@ -89,15 +120,11 @@
try {
busy = true
modifyPopupState({ preventClose: true })
const response = await signAndSendTransactionFromEvm(
preparedTransaction,
chain,
$selectedAccount,
signAndSend
)

await signOrSend()

modifyPopupState({ preventClose: false }, true)
busy = false
callback({ result: response })
openPopup({
id: PopupId.SuccessfulDappInteraction,
props: {
Expand All @@ -112,7 +139,7 @@
}
}

$: setMethodName(preparedTransaction)
$: void setMethodName(preparedTransaction)
async function setMethodName(preparedTransaction: EvmTransactionData): Promise<void> {
const result = await getMethodNameForEvmTransaction(preparedTransaction)
methodName = result?.startsWith('0x') ? undefined : result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { SupportedNamespaces } from '@auxiliary/wallet-connect/types'
import { Text } from '@bloomwalletio/ui'
import { getPermissionForMethod } from '@auxiliary/wallet-connect/utils'
import { RpcMethod } from '@auxiliary/wallet-connect/enums'
import { SelectionOption } from '@core/utils/interfaces'

export let checkedMethods: string[]
Expand Down Expand Up @@ -37,7 +38,7 @@
}
checkedMethods[method.method] = true

const permission = getPermissionForMethod(method.method)
const permission = getPermissionForMethod(method.method as RpcMethod)
if (!permission || addedPermission[permission]) {
continue
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
import {
createEvmTransactionFromSendFlowParameters,
createStardustOutputFromSendFlowParameters,
signAndSendTransactionFromEvm,
sendAndPersistTransactionFromEvm,
signAndSendStardustTransaction,
signEvmTransaction,
} from '@core/wallet/actions'
import { sendFlowParameters } from '@core/wallet/stores'
import { getNetworkIdFromSendFlowParameters, validateSendConfirmation } from '@core/wallet/utils'
Expand Down Expand Up @@ -104,7 +105,9 @@
busy = true
modifyPopupState({ preventClose: true })
if (isSourceNetworkLayer2) {
await signAndSendTransactionFromEvm(preparedTransaction, chain, $selectedAccount, true)
const signedTransaction = await signEvmTransaction(preparedTransaction, chain, $selectedAccount)
MarkNerdi marked this conversation as resolved.
Show resolved Hide resolved

await sendAndPersistTransactionFromEvm(preparedTransaction, signedTransaction, chain, $selectedAccount)
} else {
await signAndSendStardustTransaction(preparedOutput, $selectedAccount)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export const GENERAL_SUPPORTED_METHODS = ['wallet_watchAsset']
import { RpcMethod } from '../enums'

export const GENERAL_SUPPORTED_METHODS = [RpcMethod.WalletWatchAsset]
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { DappPermission } from '../enums/dapp-permission.enum'
import { RpcMethod } from '../enums/rpc-method.enum'

export const METHODS_FOR_PERMISSION = {
[DappPermission.SignData]: [
'eth_sign',
'personal_sign',
'eth_signTypedData',
'eth_signTypedData_v3',
'eth_signTypedData_v4',
RpcMethod.EthSign,
RpcMethod.PersonalSign,
RpcMethod.EthSignTypedData,
RpcMethod.EthSignTypedDataV3,
RpcMethod.EthSignTypedDataV4,
],
[DappPermission.SignTransaction]: ['eth_signTransaction'],
[DappPermission.SendTransaction]: ['eth_sendTransaction', 'eth_sendRawTransaction'],
[DappPermission.SignTransaction]: [RpcMethod.EthSignTransaction],
[DappPermission.SendTransaction]: [RpcMethod.EthSendTransaction, RpcMethod.EthSendRawTransaction],
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './dapp-permission.enum'
export * from './dapp-verification.enum'
export * from './rpc-method.enum'
export * from './wallet-connect-events.enum'
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export enum RpcMethod {
EthSign = 'eth_sign',
PersonalSign = 'personal_sign',
EthSignTypedData = 'eth_signTypedData',
EthSignTypedDataV3 = 'eth_signTypedData_v3',
EthSignTypedDataV4 = 'eth_signTypedData_v4',
EthSignTransaction = 'eth_signTransaction',
EthSendTransaction = 'eth_sendTransaction',
EthSendRawTransaction = 'eth_sendRawTransaction',
WalletWatchAsset = 'wallet_watchAsset',
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { EvmTransactionData } from '@core/layer-2'
import { switchToRequiredAccount } from '@auxiliary/wallet-connect/utils'
import { getSdkError } from '@walletconnect/utils'
import { Platform } from '@core/app'
import { DappVerification } from '../enums'
import { DappVerification, RpcMethod } from '../enums'

export async function handleEthTransaction(
evmTransactionData: EvmTransactionData & { from: string },
dapp: IConnectedDapp,
chain: IChain,
signAndSend: boolean,
method: RpcMethod.EthSendTransaction | RpcMethod.EthSignTransaction | RpcMethod.EthSendRawTransaction,
responseCallback: (params: CallbackParameters) => void,
verifiedState: DappVerification
): Promise<void> {
Expand All @@ -28,7 +28,7 @@ export async function handleEthTransaction(
return
}

if (!nonce || !gasPrice || !gasLimit) {
if (nonce === undefined || !gasPrice || !gasLimit) {
try {
const { nonce, gasPrice, gasLimit } = await buildEvmTransactionData(
chain,
Expand Down Expand Up @@ -61,7 +61,7 @@ export async function handleEthTransaction(
chain,
dapp,
preparedTransaction: evmTransactionData,
signAndSend,
method,
verifiedState,
callback: responseCallback,
onCancel: () => responseCallback({ error: getSdkError('USER_REJECTED') }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import { handleEthSignTypedData } from './eth_signTypedData.handler'
import { handleEthTransaction } from './eth_transaction.handler'
import { handleSignMessage } from './sign_message.handler'
import { handleWatchAsset } from '@auxiliary/wallet-connect/handlers'
import { DappVerification } from '../enums'
import { DappVerification, RpcMethod } from '../enums'
import { EvmTransactionData, getEvmTransactionFromHexString } from '@core/layer-2'

export function onSessionRequest(event: Web3WalletTypes.SessionRequest): void {
// We need to call this here, because if the dapp requests too fast after approval, we won't have the dapp in the store yet
setConnectedDapps()
const { topic, params, id, verifyContext } = event
const { request, chainId } = params
const method = request.method
const method = request.method as RpcMethod

const dapp = getConnectedDappByOrigin(verifyContext.verified.origin)
const verifiedState: DappVerification = verifyContext.verified.isScam
Expand Down Expand Up @@ -58,23 +59,28 @@ export function onSessionRequest(event: Web3WalletTypes.SessionRequest): void {
return
}

const signAndSend = method === 'eth_sendTransaction'

switch (method) {
case 'eth_sendTransaction':
case 'eth_signTransaction':
void handleEthTransaction(request.params[0], dapp, chain, signAndSend, returnResponse, verifiedState)
case RpcMethod.EthSendTransaction:
case RpcMethod.EthSignTransaction:
case RpcMethod.EthSendRawTransaction: {
const evmTransactionData: EvmTransactionData & { from: string } =
method === RpcMethod.EthSendRawTransaction
? getEvmTransactionFromHexString(request.params[0])
Tuditi marked this conversation as resolved.
Show resolved Hide resolved
: request.params[0]

void handleEthTransaction(evmTransactionData, dapp, chain, method, returnResponse, verifiedState)
break
case 'eth_sign':
case 'personal_sign':
}
case RpcMethod.EthSign:
case RpcMethod.PersonalSign:
void handleSignMessage(request.params, dapp, method, chain, returnResponse, verifiedState)
break
case 'eth_signTypedData':
case 'eth_signTypedData_v3':
case 'eth_signTypedData_v4':
case RpcMethod.EthSignTypedData:
case RpcMethod.EthSignTypedDataV3:
case RpcMethod.EthSignTypedDataV4:
void handleEthSignTypedData(request.params, method, dapp, chain, returnResponse, verifiedState)
break
case 'wallet_watchAsset':
case RpcMethod.WalletWatchAsset:
void handleWatchAsset(request.params, dapp, chain, returnResponse)
break
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import { CallbackParameters } from '../types'
import { switchToRequiredAccount } from '../utils'
import { getSdkError } from '@walletconnect/utils'
import { Platform } from '@core/app'
import { DappVerification } from '../enums'
import { DappVerification, RpcMethod } from '../enums'
import { parseSiweMessage, validateSiwe } from '@core/layer-2'
import { showNotification } from '@auxiliary/notification'
import { localize } from '@core/i18n'

export async function handleSignMessage(
params: unknown,
dapp: IConnectedDapp,
method: 'personal_sign' | 'eth_sign',
method: RpcMethod.PersonalSign | RpcMethod.EthSign,
chain: IChain,
responseCallback: (params: CallbackParameters) => void,
verifiedState: DappVerification
Expand All @@ -26,8 +26,8 @@ export async function handleSignMessage(

// Type for `eth_sign` params: [ address, hexMessage ]
// Type for `personal_sign` params: [ hexMessage, address ]
const hexMessage = method === 'personal_sign' ? params[0] : params[1]
const accountAddress = method === 'personal_sign' ? params[1] : params[0]
const hexMessage = method === RpcMethod.PersonalSign ? params[0] : params[1]
const accountAddress = method === RpcMethod.PersonalSign ? params[1] : params[0]

if (typeof hexMessage !== 'string') {
responseCallback({ error: getSdkError('INVALID_METHOD') })
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { METHODS_FOR_PERMISSION } from '../constants'
import { DappPermission } from '../enums'
import { DappPermission, RpcMethod } from '../enums'

export function getPermissionForMethod(method: string): DappPermission | undefined {
export function getPermissionForMethod(method: RpcMethod): DappPermission | undefined {
for (const permission of Object.values(DappPermission)) {
const supportedMethods = METHODS_FOR_PERMISSION[permission] ?? []

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Converter } from '@core/utils'
import { LegacyTransaction, TransactionFactory } from '@ethereumjs/tx'
import { EvmTransactionData } from '../types'

export function getEvmTransactionFromHexString(transactionHex: string): EvmTransactionData & { from: string } {
const transaction = TransactionFactory.fromSerializedData(Converter.hexToBytes(transactionHex)) as LegacyTransaction
const sender = transaction.getSenderAddress().toString()

const { nonce, gasPrice, gasLimit, to, value, data, v, r, s, type } = transaction

return {
nonce: Converter.bigIntToHex(nonce),
gasPrice: Converter.bigIntToHex(gasPrice),
gasLimit: Converter.bigIntToHex(gasLimit),
to,
value: Converter.bigIntToHex(value),
data: Converter.bytesToHex(data),
r: Converter.bigIntToHex(r),
v: Converter.bigIntToHex(v),
s: Converter.bigIntToHex(s),
type,
from: sender,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { HEX_PREFIX } from '@core/utils'
import { LegacyTransaction } from '@ethereumjs/tx'

export function getHexEncodedTransaction(transaction: LegacyTransaction): string {
const serializedTransaction = transaction.serialize()
const hexEncodedTransaction = HEX_PREFIX + Buffer.from(serializedTransaction).toString('hex')
return hexEncodedTransaction
}
2 changes: 2 additions & 0 deletions packages/shared/src/lib/core/layer-2/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export * from './getAmountFromEvmTransactionValue'
export * from './getEvmTokenMetadata'
export * from './getErc20TransferSmartContractData'
export * from './getErc721TransferSmartContractData'
export * from './getEvmTransactionFromHexString'
export * from './getHexEncodedTransaction'
export * from './getMethodNameForEvmTransaction'
export * from './lookupMethodSignature'
export * from './parseLayer2Metadata'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LegacyTransaction, TransactionFactory, TypedTxData } from '@ethereumjs/tx'
import { prepareEvmTransaction } from '@core/layer-2/utils'
import { getHexEncodedTransaction, prepareEvmTransaction } from '@core/layer-2/utils'
import { EvmChainId, getEvmTransactionOptions } from '@core/network'
import { removeLeadingZeros } from '@core/utils/array'
import { ECDSASignature } from '@ethereumjs/util'
Expand Down Expand Up @@ -36,12 +36,6 @@ function createSignedTransaction(
return signedTransaction
}

function getHexEncodedTransaction(transaction: LegacyTransaction): string {
const serializedTransaction = transaction.serialize()
const hexEncodedTransaction = HEX_PREFIX + Buffer.from(serializedTransaction).toString('hex')
return hexEncodedTransaction
}

function padHexString(str: string): string {
return str.length % 2 !== 0 ? '0' + str : str
}
4 changes: 2 additions & 2 deletions packages/shared/src/lib/core/utils/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ export class Converter {
return prefix ? HEX_PREFIX + number.toString(16) : number.toString(16)
}

public static bigIntToHex(bigInt: bigint, prefix = true): string {
bigInt = BigInt(bigInt)
public static bigIntToHex(bigInt: bigint | undefined, prefix = true): string {
bigInt = BigInt(bigInt ?? 0)
return prefix ? HEX_PREFIX + bigInt.toString(16) : bigInt.toString(16)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ export * from './createEvmChainToEvmChainTransaction'
export * from './createEvmChainToStardustNetworkTransaction'
export * from './createEvmTransactionFromSendFlowParameters'
export * from './createStardustOutputFromSendFlowParameters'
export * from './signAndSendTransactionFromEvm'
export * from './sendAndPersistTransactionFromEvm'
export * from './signAndSendStardustTransaction'
Loading