LogoLogo
SupportGithubSign InGet Access
  • Introduction
  • GETTING STARTED
    • SDK Quick Start
    • API Quick Start
  • Guides
    • Web
      • Create a wallet
      • Send tokens
      • Sign a transaction
      • Simulate a transaction
      • Back up a wallet
      • Recover a wallet
      • Cross-device sessions
      • Manage wallet lifecycle states
      • Web authentication methods
      • Perform swaps
      • Add custom signature hooks
      • MPC progress callbacks
      • Portal API methods
      • Configure a custom subdomain
      • Eject a wallet
      • Using the EIP-1193 Provider
      • Legacy documentation
        • Back up a wallet
          • Backup Options
        • Recover a wallet
    • iOS
      • Create a wallet
      • Send tokens
      • Sign a transaction
      • Simulate a transaction
      • Back up a wallet
      • Recover a wallet
      • Cross-device sessions
      • Manage wallet lifecycle states
      • Connect with WalletConnect
      • Build a WebView
      • Perform swaps
      • Add custom signature hooks
      • MPC progress callbacks
      • Portal API methods
      • Manage ERC20 tokens
      • Eject a wallet
      • Legacy documentation
        • Back up a wallet
          • Backup Options
          • Passkey + Enclave Storage
        • Recover a wallet
      • Troubleshooting Tips
      • Feature Flags
    • Android
      • Create a wallet
      • Send tokens
      • Sign a transaction
      • Simulate a transaction
      • Back up a wallet
      • Recover a wallet
      • Cross-device sessions
      • Manage wallet lifecycle states
      • Connect with WalletConnect
      • Build a WebView
      • Perform swaps
      • Add custom signature hooks
      • MPC progress callbacks
      • Portal API methods
      • Eject a wallet
      • Legacy documentation
        • Back up a wallet
          • Backup Options
        • Recover a wallet
    • React Native
      • Create a wallet
      • Send tokens
      • Sign a transaction
      • Simulate a transaction
      • Back up a wallet
      • Recover a wallet
      • Cross-device sessions
      • Manage wallet lifecycle states
      • Connect with WalletConnect
      • Build a WebView
      • Perform swaps
      • Add custom signature hooks
      • MPC progress callbacks
      • Portal API methods
      • Eject a wallet
      • Legacy documentation
        • Back up a wallet
          • Backup Options
        • Recover a wallet
    • Enclave MPC API
      • Create a client
      • Create a wallet
      • Send tokens
      • Sign Ethereum transactions
      • Sign Solana transactions
      • Sign Tron transactions
      • Sign Stellar Transaction
      • Concurrent Transactions
      • Back up a wallet
      • Eject a wallet
  • Reference
    • iOS
      • createWallet
      • backupWallet
      • recoverWallet
      • ejectPrivateKeys
      • registerBackupMethod
      • setGDriveConfiguration
      • setPasskeyConfiguration
      • setPasskeyAuthenticationAnchor
      • setPassword
      • availableRecoveryMethods
      • doesWalletExist
      • isWalletBackedUp
      • isWalletOnDevice
      • isWalletRecoverable
      • getBalances
      • getAssets
      • getNftAssets
      • getTransactions
      • sendSol
      • evaluateTransaction
      • buildEip155Transaction
      • buildSolanaTransaction
      • getWalletCapabilities
    • Android
      • Reference Documentation
    • React Native
      • @portal-hq/core
      • Storage adapters
        • Cloud storage
          • @portal-hq/gdrive-storage
          • @portal-hq/icloud-storage
        • Mobile storage
          • @portal-hq/keychain
          • @portal-hq/mobile-key-values
    • Enclave MPC API
      • V1 endpoints
    • Client API
      • V3 endpoints
      • V1 endpoints
    • Custodian API
      • V3 endpoints
      • V1 endpoints
    • Swaps API
      • V3 endpoints
      • V1 endpoints
  • Resources
    • Flutter
      • iOS
      • Android
    • Error codes
      • Overview
      • MPC errors
      • Network errors
      • General errors
      • Encryption errors
      • Portal Connect errors
    • Portal's MPC architecture
    • Authentication and API Keys
    • Self-Managed Backups
    • Alert Webhooks
    • Wallet lifecycle
    • Backup options
      • Password/PIN
      • GDrive
      • iCloud
      • Passkey + Enclave
    • WalletConnect metadata
    • Advanced security scanning
    • Account abstraction
    • Security firewall
    • Eject
    • Security
    • Blockchain support
    • Chain ID formatting
    • Testnet faucets
    • Going to Production
    • Rate Limits
    • Multi-backup migration guide
    • Multi-wallet migration guides
      • Migrating from Android SDK v3.x.x to v4.x.x
      • Migrating from iOS SDK v3.0.x to v3.2.x
  • Support
    • Changelog
      • Android
      • iOS
      • React Native
      • Web
      • Past Releases
        • 2024 Releases
        • 2023 Releases
    • Celo Hackathon Hub
    • Glossary
Powered by GitBook
On this page
  • What are custom signature hooks?
  • Why use custom signature hooks?
  • How do custom signature hooks work?
  • Designing your flow
  • A more real-world example: MFA for large transactions

Was this helpful?

  1. Guides
  2. React Native

Add custom signature hooks

This guide will help you configure custom signature hooks for requests to the Portal Provider to ensure your business logic is properly implemented into Portal's signing flow.

What are custom signature hooks?

Custom signature hooks allow companies to trigger approval business logic based on parameters including transaction value, transaction volume, types of signature, and more. The business logic can be anything that helps the company and/or end user make good decisions during the signing request process.

A couple examples are auto-approvals (eg, auto-approving transactions below a certain value or volume) and two-factor authentication (eg, requiring approval for transactions above a certain limit). Another option are security checks, either internal or external, to prevent malicious activity.

Why use custom signature hooks?

There are a few common reasons developers use custom signature hooks. For example:

  • Approving transactions - Most developers will prompt a user for approval before signing a message from a dApp or Wallet Connect

  • Adding additional authentication - Developers can add extra authentication steps such as MFA to riskier transactions

  • Checking transaction sizes - Developers can perform checks on transaction sizes to trigger specific authentication steps

Custom signature hooks can be passive, invisible to the user, or active, designed into the frontend UX with which customers engage.

A passive flow will generally be used to facilitate company goals, such as ensuring a user isn’t on a deny list. As such, the company can create a custom security check that is activated when a customer signs a transaction without them knowing. Transactions will continue as normal unless the user is found to be denied use of the app, at which time it will be rejected. An active flow is visible to the user and can be used to offer a superior user experience. For example, companies can trigger MFA for transactions above a certain size, an example we will dive into at the end of this guide.

How do custom signature hooks work?

The Portal approval flow is driven by a series of event hooks that take place throughout the lifecycle of a Provider requests. These hooks are as follows:

  • portal_signingRequested - Emitted by the Portal Provider when a new signing request is received by the Provider

  • portal_signingApproved - Emitted by you when signing a given request is approved

  • portal_signingRejected - Emitted by you when signing a given request is rejected

Designing your flow

The expectation of this flow is that your code binds to the portal_signingRequested event using the following pattern:

portal.provider.on('portal_signingRequested', ({ method, params }) => {
    // On approval
    portal.provider.emit('portal_signingApproved', { method, params })
    
    // On rejection
    portal.provider.emit('portal_signingRejected', { method, params })
})

As demonstrated by this example, you can implement any business logic you feel is required to make good decisions about what signing requests to process. You can prompt your user for approval, you can make requests to your backend to run some code remotely, etc. The only restriction of this flow is that when you have determined whether a signing request should be approved or rejected, you should emit either the portal_signingRejected or portal_signingApproved event.

NOTE: When emitting these events, the Provider uses the method and params to determine if we are indeed approving or rejecting the correct request, so it is vital that you pass the method and params to your emit() calls.

A more real-world example: MFA for large transactions

A common use case is to trigger additional approval from users for large transactions. The implementation of this using custom signature hooks might look something like this:

portal.provider.on('portal_signingRequested', async ({ method, params }) => {    
    // Check the transaction size
    const isLargeTransaction = checkIfLargeTransaction(method, params)
    
    // Request MFA approval from user if large transaction
    let isMfaApproved = false
    if (isLargeTransaction) {
      isMfaApproved = await getMfaApproval()
    }
    
    // Approve transaction request if MFA was successful
    if (isMfaApproved) {
        portal.provider.emit('portal_signingApproved', { method, params }) 
    } else {
        portal.provider.emit('portal_signingRejected', { method, params })
    }
})

The outcome would look like something like this:

PreviousPerform swapsNextMPC progress callbacks

Last updated 11 months ago

Was this helpful?