Guides
Safe Auth

Integration with SafeAuth

This guide demonstrates creating an externally-owned account using your email or social media account. Once authenticated, you can sign transactions and interact with your Safe accounts.

The SafeAuthPack is an authentication system that utilizes the Web3Auth (opens in a new tab) MPC technology. It was developed in collaboration with Web3Auth to create a smooth onboarding experience for web2 users across different dapps.

Prerequisites

Install dependencies

yarn add @safe-global/auth-kit @web3auth/safeauth-embed

Create a SafeAuthPack instance

We will use the SafeAuthPack exported from the @safe-global/auth-kit package.

Create an instance of the SafeAuthPack (opens in a new tab) using the required SafeAuthConfig configuration object.

Supported networks:

  • Production: Ethereum, Polygon, BSC, Avalanche, Optimism, Celo, Arbitrum, Gnosis chain
  • Test: Sepolia, Polygon Mumbai, BSC Testnet, Avalanche Testnet, Arbitrum Testnet, Optimism Testnet
import {
  SafeAuthPack,
  SafeAuthConfig,
  SafeAuthInitOptions,
} from '@safe-global/auth-kit'
 
const safeAuthConfig: SafeAuthConfig = {
  txServiceUrl: 'https://safe-transaction-mainnet.safe.global',
}
const safeAuthInitOptions: SafeAuthInitOptions = {
  enableLogging: true,
  showWidgetButton: false,
  chainConfig: {
    chainId: '0x1',
    rpcTarget: `${rpcUrl}`
  },
}
 
// You can also pass the SafeAuthConfig as a parameter to the SafeAuthPack constructor if you are using a custom txServiceUrl domain
// e.g. const safeAuthConfig: SafeAuthConfig = {
//   txServiceUrl: 'https://safe-transaction-mainnet.safe.global'
// }
const safeAuthPack = new SafeAuthPack(safeAuthConfig)
await safeAuthPack.init(safeAuthInitOptions)

Sign in to an Ethereum account

After creating your SafeAuthPack instance, initiate the authentication process by calling the signIn() method. Typically, this method is called when the user clicks a "Sign In" button on the web page.

After successfully signing in, you will create a new Ethereum Wallet. This wallet will be used for all future logins and can be shared across different applications.

// The signIn() method returns the user's Ethereum address and the associated Safe addresses
// The `await` will last until the user is authenticated. Therefore, it will be active while the authentication popup is being displayed.
const authKitSignData = await safeAuthPack.signIn()

The returned authKitSignData data contains the following properties:

AuthKitSignInData {
  eoa: string // The safe signer
  safes?: string[] // The list of associated Safe addresses in the chain
}

The signOut() method removes the current session.

await safeAuthPack.signOut()

After the user is authenticated, call getProvider() to get the Ethereum provider instance. This is a EIP-1193 (opens in a new tab) compatible provider you can wrap using your favorite library (web3, ethers).

safeAuthPack.getProvider()

We offer two methods for listening to events, subscribe() and unsubscribe().

const accountChangedHandler = (accounts: string[]) => {
  console.log('Signer accounts:', accounts)
}
 
safeAuthPack.subscribe('accountsChanged', accountChangedHandler)
safeAuthPack.unsubscribe('accountsChanged', accountChangedHandler)

The SafeAuthPack instantiation will return the list of associated Safe addresses as part of the response from the signIn() method when the txServiceUrl is provided.

const safeAuthPack = new SafeAuthPack()

Signing and executing transactions using the SafeAuthPack and Protocol Kit

The SafeAuthPack can be used with the Protocol Kit to establish a connection to a Safe. This connection is made using the provider and signer associated with the authenticated account.

After connecting, you can use any of the methods provided in the Protocol Kit (opens in a new tab).

import { ethers } from 'ethers'
import { EthersAdapter } from '@safe-global/protocol-kit'
 
// Wrap EIP-1193 provider with ethers
const provider = new ethers.BrowserProvider(safeAuthPack.getProvider())
const signer = provider.getSigner()
 
// Create the Safe EthersAdapter
const ethAdapter = new EthersAdapter({
  ethers,
  signerOrProvider: signer || provider,
})
 
// Instantiate the protocolKit
const protocolKit = await Safe.create({
  ethAdapter,
  safeAddress,
})
 
// Create a Safe transaction with the provided parameters
const safeTransactionData: MetaTransactionData = {
  to: `${ethAddress}`,
  data: '0x',
  value: ethers.parseUnits('0.0001', 'ether').toString(),
}
 
const safeTransaction = await protocolKit.createTransaction({
  transactions: [safeTransactionData],
})
 
// Sign the transaction if the Safe have several owners
// safeTransaction = await protocolKit1.signTransaction(safeTransaction)
// safeTransaction = await protocolKit2.signTransaction(safeTransaction)
 
// Execute the transaction
await protocolKit.executeTransaction(safeTransaction)

Sign messages using the SafeAuthPack

You can also sign any arbitrary message or transaction as a regular Signing Account with your favorite web3 library:

// Using web3
const web3 = new Web3(safeAuthPack.getProvider())
 
await web3.eth.sendTransaction(tx)
await web3.eth.signTransaction(tx)
const message = 'hello world'
const address = '0x...'
await web3.eth.personal.sign(message, address)
 
// Using ethers
const provider = new ethers.BrowserProvider(safeAuthPack.getProvider())
const signer = provider.getSigner()
 
await signer.sendTransaction(tx)
await signer.signTransaction(tx)
await signer.signMessage(message)

Examples