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 1 wei 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.

Ensure you have set the gateway URL correctly with Infura or Alchemy when you initialize the portal class.

NativeSendTx.tsx
import React, { FC, useState } from 'react'
import { Button, Text, TextInput, View } from 'react-native'
import { usePortal } from '@portal-hq/core'

/** 
 * In this example, we're using the `ethers` package.
 * This is not included with any of the Portal packages.
 *
 * If you'd like to run this example yourself, you should
 * manually install the `ethers` package.
 */
import { BigNumber } from 'ethers'

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

  const handleSend = async () => {
    // Build a transaction to be sent
    const transaction = {
      data: '',
      to: toAddress,
      value: BigNumber.from('1').toHexString(),
      gasPrice: '0x6000',
      from: await portal.keychain.getAddress(),
    }

    // Use the Portal Web3 Provider to make requests
    const newTxHash = await portal.request({
      method: 'eth_sendTransaction',
      params: transaction,
    })

    setTxHash(newTxHash)
    setToAddress('')
  }

  return (
    <View>
      <Text>Send 1 WEI</Text>
      <TextInput
        onChangeText={setToAddress}
        placeholder="Enter an address"
        value={toAddress}
      />

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

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

export default NativeSendTx

Signing a Solana Transaction

You can use our provider just like you do with ethereum with solana. Simply specifiy 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

  const handleSolSign = async () => {
    try {
      const addresses = await portal.addresses
      const fromPubkey = new PublicKey(addresses.solana)
      const toPubkey = new PublicKey(
        '6UeW7EEKcADrxiMga6ssT6DQMvzoyBnMmb3SF58262qN',
      )
      // Create a list of Program instructions to execute.
      const instructions = [
        SystemProgram.transfer({
          fromPubkey,
          toPubkey,
          lamports: 1_000_000,
        }),
      ]

      // Connect to an RPC endpoint and get the latest blockhash, to include in
      // the transaction.
      const connection = new Connection(
        `https://solana-devnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}`,
        'confirmed',
      )
      const latestBlockhash = await connection.getLatestBlockhash('finalized')

      // Create the "message" of a transaction and compile to `V0Message` format.
      // Create a new Transaction
      const transaction = new Transaction({
        recentBlockhash: latestBlockhash.blockhash,
        feePayer: fromPubkey,
      })

      // Add the instructions to the transaction
      transaction.add(...instructions)

      const signedMessage = await portal!.provider.request({
        method: 'sol_signMessage',
        params: ['message'],
        chainId: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
      })

      console.log('✅ Signed message', signedMessage)

      const signedTx = await portal!.provider.request({
        method: 'sol_signAndSendTransaction',
        params: [
          transaction
            .serialize({ requireAllSignatures: false })
            .toString('base64'),
        ],
        chainId: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
      })
      console.log(signedTx)
    } catch (error) {
      console.error(error)
      console.log('❌ Failed to sign')
      throw error
    }
  }

Raw sign

You can now utlizie 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.

  const handleRawSign = async () => {
    try {
      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
      const signedMessage = await portal!.rawSign(
        '7369676e2074686973',
        'eip155:11155111', // choose the chainId eip155 = secp256k1, solana = ed25519
      )
      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.

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

// Use the Portal Web3 Provider to make requests
const gas = await portal.request({
  method: 'eth_estimateGas',
  params: transaction,
})

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.

Last updated