> ## Documentation Index
> Fetch the complete documentation index at: https://docs.portalhq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Sign a transaction

> Want to implement your own style, but with all the functionality Portal offers? Use these functions to implement your own custom web3 UI for your users.

This example screen displays how the **Portal Provider** interacts with the **MPC wallet** and **the blockchain**.

The **Provider** has an input box to set a `toAddress` for the transaction of sending 0.001 ETH from our **MPC wallet**.  The **Provider** then receives a *signed transaction* from our **mobile MPC library** and submits that to chain using the configured RPC url.

<Warning>
  Ensure you have set the gateway URL correctly with [Infura](https://www.infura.io/) or [Alchemy](https://www.alchemy.com/) when you initialize the `Portal` class.
</Warning>

### Improving signing performance

You can reduce signing latency for EVM transactions by enabling **presignatures**. The SDK will then pre-compute part of the MPC signing flow in the background and use that data when the user signs, without any change to your code. Enable it via the `usePresignatures` feature flag when creating your `Portal` instance. See [Feature flags](/sdks/react-native/guide/feature-flags#usepresignatures) for details.

```typescript theme={null}
import React, { FC, useState } from 'react'
import { Button, Text, TextInput, View } from 'react-native'
import { usePortal, PortalRequestMethod } from '@portal-hq/core'

const NativeSendTx = () => {
  const portal = usePortal()
  const [toAddress, setToAddress] = useState('')
  const [txHash, setTxHash] = useState('')

  const handleSend = async () => {
    try {
      // Build the transaction using the simplified API
      const { transaction } = await portal.api.buildTransaction(
        toAddress,      // recipient address
        'NATIVE',       // token type (native MON)
        '0.001',        // amount
        'monad-testnet' // network
      )

      // Send the transaction
      const newTxHash = await portal.request(
        PortalRequestMethod.EthSendTransaction,
        [transaction],
        'eip155:10143',
        { signatureApprovalMemo: 'test' } // Optional signature approval memo to use for the request
      )

      setTxHash(newTxHash)
      setToAddress('')
    } catch (error) {
      console.error('Failed to send transaction:', error)
    }
  }

  return (
    <View>
      <Text>Send 0.001 MON</Text>
      <TextInput
        onChangeText={setToAddress}
        placeholder="Enter recipient address"
        value={toAddress}
      />

      <View style={{ marginTop: 10 }}>
        <Button
          disabled={!portal || !toAddress || !toAddress.length}
          onPress={handleSend}
          title="Send MON"
        />
      </View>

      {txHash && txHash.length > 0 && (
        <View>
          <Text>Successfully sent transaction!</Text>
          <Text>Transaction Hash: {txHash}</Text>
        </View>
      )}
    </View>
  )
}

export default NativeSendTx
```

### Signing a User Operation

If your client uses [Account Abstraction](../../../resources/account-abstraction), you can sign an [ERC-4337 User Operation](https://eips.ethereum.org/EIPS/eip-4337) using the `eth_signUserOperation` method. This signs the User Operation without submitting it on-chain, returning the signature directly.

```typescript theme={null}
import React from 'react'
import { usePortal, PortalRequestMethod } from '@portal-hq/core'

const SignUserOperation = () => {
  const portal = usePortal()

  const handleSignUserOperation = async () => {
    const chainId = 'eip155:11155111' // Ethereum Sepolia

    const address = await portal.address
    if (!address) return

    const userOp = {
      sender: address,
      nonce: '0x0',
      callData: '0x',
      callGasLimit: '0x5208',
      verificationGasLimit: '0x5208',
      preVerificationGas: '0x5208',
      maxFeePerGas: '0x1',
      maxPriorityFeePerGas: '0x1',
    }

    try {
      const signature = await portal.request(
        PortalRequestMethod.EthSignUserOperation,
        [userOp],
        chainId,
      )

      console.log('✅ Signature:', signature)
    } catch (error) {
      console.error('❌ Failed to sign UserOperation:', error)
    }
  }

  return null
}

export default SignUserOperation
```

**User Operation Parameters:**

| Name                   | Type     | Description                                                                   |
| ---------------------- | -------- | ----------------------------------------------------------------------------- |
| `sender`               | `string` | The address of the smart contract account                                     |
| `nonce`                | `string` | Anti-replay parameter (hex-encoded)                                           |
| `callData`             | `string` | The data to pass to the `sender` during the main execution call (hex-encoded) |
| `callGasLimit`         | `string` | Gas limit for the main execution call (hex-encoded)                           |
| `verificationGasLimit` | `string` | Gas limit for the verification step (hex-encoded)                             |
| `preVerificationGas`   | `string` | Gas paid for pre-verification (hex-encoded)                                   |
| `maxFeePerGas`         | `string` | Maximum fee per unit of gas (hex-encoded)                                     |
| `maxPriorityFeePerGas` | `string` | Maximum priority fee per unit of gas (hex-encoded)                            |

You can also pass a signature approval memo and control gas sponsorship:

```typescript theme={null}
const signature = await portal.request(
  PortalRequestMethod.EthSignUserOperation,
  [userOp],
  chainId,
  {
    signatureApprovalMemo: 'Approve UserOp',
    sponsorGas: true,
  },
)
```

## Signing a Solana Transaction

You can use our provider just like you do with Ethereum with Solana. Simply specify the method and the chainId for Solana. This example uses `solana/web3.js` to construct a transaction object to sign and pass to the multi-chain provider.

#### Solana Provider Methods

* `sol_signMessage`
* `sol_signTransaction`
* `sol_signAndSendTransaction`
* `sol_signAndConfirmTransaction`

```typescript theme={null}
import React, { useState } from 'react'
import { Button, Text, TextInput, View } from 'react-native'
import { usePortal, PortalRequestMethod } from '@portal-hq/core'

const SolanaSendTx = () => {
  const portal = usePortal()
  const [toAddress, setToAddress] = useState('')
  const [txHash, setTxHash] = useState('')

  const handleSend = async () => {
    try {
      const chainId = 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1' // Solana Devnet

      // Build the transaction using the simplified API
      const { transaction } = await portal.api.buildTransaction(
        toAddress,         // recipient address
        'NATIVE',          // token type (native SOL)
        '0.001',           // amount in SOL
        chainId            // CAIP-2 Chain, or friendly name like "solana-devnet"
      )

      // Send the transaction
      const newTxHash = await portal.request(
        PortalRequestMethod.SolSignAndSendTransaction,
        [transaction], // transaction is already base64 encoded
        chainId,
        { signatureApprovalMemo: "test" } // Optional signature approval memo to use for the request
      )

      setTxHash(newTxHash)
      setToAddress('')
    } catch (error) {
      console.error('Failed to send transaction:', error)
    }
  }

  return (
    <View>
      <Text>Send 0.001 SOL</Text>
      <TextInput
        onChangeText={setToAddress}
        placeholder="Enter recipient address"
        value={toAddress}
      />

      <View style={{ marginTop: 10 }}>
        <Button
          disabled={!portal || !toAddress || !toAddress.length}
          onPress={handleSend}
          title="Send SOL"
        />
      </View>

      {txHash?.length > 0 && (
        <View>
          <Text>Successfully sent transaction!</Text>
          <Text>Transaction Hash: {txHash}</Text>
        </View>
      )}
    </View>
  )
}

export default SolanaSendTx
```

## Raw sign

You can now utilize our SDK to generate raw signatures that are generated using the underlying key share without adding any chain specific formatting to the signature. This effectively unlocks your ability to use the Portal SDK with any chain that uses `SECP256K1` or `ED25519`.

```typescript theme={null}
import { PortalRequestMethod } from '@portal-hq/core'

const handleRawSign = async () => {
  try {
    const signedMessage = await portal.request(
      PortalRequestMethod.RawSign,
      ['74657374'], // Must be in hex format, e.g. "test" is "74657374" in hex.
      'eip155:10143', // choose the chainId eip155 = secp256k1, solana = ed25519, monad = secp256k1
      { signatureApprovalMemo: 'Optional memo for this signature' } // Optional
    )
    console.log('✅ Signed message', signedMessage)
  } catch (error) {
    console.error(error)
    console.log('❌ Failed to sign')
    throw error
  }
}
```

## Estimating Gas

By default, Portal will estimate and populate the `gas` property in a `transaction` object if the property is undefined.

To estimate the `gas` value manually use the `eth_estimateGas` RPC call and pass in your transaction as the parameter.

```javascript theme={null}
import { PortalRequestMethod } from '@portal-hq/core'

// Build a transaction to be sent
const transaction = {
  from: await portal.keychain.getAddress(),
  to: toAddress,
  value: BigNumber.from('1').toHexString(), // Example value
}

// Use the Portal Web3 Provider to make requests
const gas = await portal.request(
  PortalRequestMethod.EthEstimateGas,
  [transaction],
  'eip155:10143',
  { signatureApprovalMemo: 'test' } // Optional signature approval memo to use for the request
)

console.log(gas) // "0x5208"
```

And now you are signing transactions with Portal! 🚀

Next, we'll explore how to simulate a transaction so that you can create smoother experiences for your users.
