Guides Android 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 shows how the Portal Provider interacts with the MPC wallet and the blockchain .
The params have a hardcoded address 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.
Here is a quick example of how you can make requests using Portal's web3 provider:
Ensure you have set the gateway URL correctly with Infura or Alchemy when you initialize the portal class.
Copy // Imports...
class MainActivity : AppCompatActivity () {
lateinit var portal: Portal
private fun sendOneWei () {
lifecycleScope. launch {
val params = listOf (
EthTransactionParam (
from = address,
to = toAddress,
gas = "0x6000" ,
gasPrice = null ,
maxFeePerGas = null ,
maxPriorityFeePerGas = null ,
value = "0x ${ BigInteger ( "1" ). toString ( 16 ) } " ,
data = "" ,
),
)
Log. println (Log.INFO, "[PortalEx]" , "Sending 1 wei: $params" )
try {
Log. println (
Log.INFO,
"[PortalEx]" ,
"Sending request for 'eth_SendTransaction' with params $params" ,
)
val result = portal. request (
chainId = ethChainId,
method = PortalRequestMethod.eth_sendTransaction,
params = params
)
Log. println (Log.INFO, "[PortalEx]" , "Transaction hash: ${ result.result } " )
showTestResult (TestCase.Send1Wei, true , "Transaction hash: ${ result.result } " )
} catch (err: Throwable ) {
Log. println (Log.WARN, "[PortalEx]" , "Failed to send transaction: $err" )
showTestResult (TestCase.Send1Wei, false )
} catch (e: Exception ) {
Log. println (Log.WARN, "[PortalEx]" , "Failed to send transaction: $e" )
showTestResult (TestCase.Send1Wei, false )
}
}
}
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.
Copy suspend fun estimateGas () {
try {
// Create the transaction params.
val params = listOf ( TransactionParams (
"" ,
"0x9AeCB4DA6b438830b88C5F40b6Bf36EF3073B350" ,
"0x ${ BigInteger ( "1" ). toString ( 16 ) } " ,
"" ,
portal.address
))
// Attempt to send the transaction.
val response = portal.provider. request (
chainId = "your chain Id"
method = PortalRequestMethod.eth_estimateGas,
params = params
)
Log. d ( "Portal" , "Estimated gas: ${ response.result } " )
} catch (err: Exception ) {
// ❌ Handle errors sending the transaction.
}
}
Signing Solana Transactions
We offer a portal.sendSol
function to make the process of sending sol very simple.
Copy suspend fun sendSolana () {
kotlin. runCatching {
portal. sendSol (
chainId = SOLANA_DEV_NET_CHAIN_ID,
lamports = 1 ,
to = SOLANA_TEST_ADDRESS,
)
}. onSuccess { response ->
Timber. i ( "✅ Send Solana response: $response" )
}. onFailure {
Timber. e ( "❌ Send Solana error: $it" )
}
}
We also offer methods to build your Solana (and Eth) transactions.
Copy suspend fun buildSolanaTransaction () {
val buildTransactionParam = BuildTransactionParam (
to = SOLANA_TEST_ADDRESS,
token = "SOL" ,
amount = "0.001"
)
portal.api. buildSolanaTransaction (SOLANA_DEV_NET_CHAIN_ID, buildTransactionParam). onSuccess { response ->
if (response.error == null ) {
Timber. i ( "✅ Build Transaction response: ${ response.transaction } " )
} else {
Timber. i ( "❌ Build Transaction response: ${ response.error } " )
}
}. onFailure {
Timber. e ( "❌ Build Transaction error: $it" )
}
}
If you would like to construct your own more advanced transaction for solana then here is how you can do it. Add SolanaKt library to your project as it will help you build the transaction. (You can also use any other library of your choice.)
Copy // In your settings.gradle
repositories {
...
maven { url 'https://jitpack.io' }
}
// In your app/build.gradle
dependencies {
...
implementation 'com.github.metaplex-foundation:SolanaKT:{version}'
}
Then build a Solana request and send it using portal.request(chainId, PortalRequestMethod.sol_signAndSendTransaction, listOf(solanaRequest)
Copy // Here is how you can build the solana request using SolanaKt library
suspend fun sendSolana (
solanaChainId: String ,
fromAddress: String ,
toAddress: String ,
lamports: Long
): String {
val recentBlockhashRpcResponse = portal. request (
solanaChainId,
PortalRequestMethod.sol_getLatestBlockhash,
emptyList ()
).result as PortalProviderRpcResponse
val recentBlockhashResult = recentBlockhashRpcResponse.result
val recentBlockhash = Gson (). let {
it. fromJson (it. toJson (recentBlockhashResult), SolGetLatestBlockhashResult:: class .java)
}
val solanaRequest = prepareSolanaRequest (
fromAddress,
toAddress,
lamports,
recentBlockhash. value .blockhash
)
val transactionHash = portal. request (
solanaChainId,
PortalRequestMethod.sol_signAndSendTransaction,
listOf (solanaRequest)
).result as String
return transactionHash
}
suspend fun prepareSolanaRequest (
fromAddress: String ,
toAddress: String ,
lamports: Long ,
recentBlockhash: String
): PortalSolanaRequest {
val fromPublicKey = PublicKey (fromAddress)
val toPublicKey = PublicKey (toAddress)
val transferInstruction = SystemProgram. transfer (
fromPublicKey,
toPublicKey,
lamports
)
Log. i ( "PortalSolana" , "Transfer instruction: $transferInstruction" )
val transaction = Transaction ()
transaction. addInstruction (transferInstruction)
transaction.recentBlockhash = recentBlockhash
transaction.feePayer = fromPublicKey
val message = transaction. compileMessage ()
Log. i ( "PortalSolana" , "Compiled message: $message" )
val header = PortalSolanaHeader (
numRequiredSignatures = message.header.numRequiredSignatures,
numReadonlySignedAccounts = message.header.numReadonlySignedAccounts,
numReadonlyUnsignedAccounts = message.header.numReadonlyUnsignedAccounts
)
val instructions = message.instructions. map { instruction ->
PortalSolanaInstruction (
instruction.programIdIndex,
instruction.accounts,
instruction. data
)
}
val accountKeys = message.accountKeys. map { key -> key. toString () }
return PortalSolanaRequest (
message = PortalSolanaMessage (
accountKeys = accountKeys,
header = header,
recentBlockhash = message.recentBlockhash,
instructions = instructions
)
)
}
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.