Brahma Console Integration [for dApp developers]

An integration guide to ensure both general smart contract wallet support, as well as Brahma Connect support, an advanced dApp connection and execution workflow.

Brahma Console dApp connection methods natively works with any dApp, unless there are signature schemes (such as auth signatures) in the user journey, which require extra attention.

The flagship connection method in Console is Brahma Connect, which allows user to batch and chain multiple actions across dApps in a single transaction, loading them in a secure iFrame directly in Console thanks to simulating the state on a forked RPC. This method doesn't support the collection of signatures, and therefore it requires the dApp whitelisting Brahma Connect as a method to bypass it, see Additional Support for Brahma Connect [Ideal].

Wallet Connect is a backup connection method, which requires #id-1.-signature-support-for-smart-contract-wallets, as well as Wallet Connect as an available connection method in the dApp (demo of Wallet Connect flow in Console).

Signature support for smart contract wallets [Must have]

As Consoles are Safe smart contract wallets, they do not support EIP-191 signatures, and even EIP-712 signatures are required to be transformed into a SafeMessage type to be compatible, which are then verified using EIP-1271 which is the smart contract signature verification standard. The digest generation & signing is handled by Console, but the integrating DApps must support EIP-1271 to verify these signatures (https://eips.ethereum.org/EIPS/eip-1271).

Below are some code snippets that can be used for the implementation. Alternatively, multiple npm packages make it easy for dApps to add support for EIP 1271.

Packages

https://github.com/etherspot/eip1271-verification-util/

https://github.com/AmbireTech/signature-validator/

https://docs.sequence.xyz/wallet/guides/sign-message/#verifying-message-signatures

Code Snippets for manual validation

Using viem
const viem = require("viem");
const chains = require("viem/chains");

const EIP_1271_MAGIC_VALUE = "0x1626ba7e";
const EIP_1271_ABI = [
  {
    constant: true,
    inputs: [
      {
        name: "_hash",
        type: "bytes32"
      },
      {
        name: "_sig",
        type: "bytes"
      }
    ],
    name: "isValidSignature",
    outputs: [
      {
        name: "magicValue",
        type: "bytes4"
      }
    ],
    payable: false,
    stateMutability: "view",
    type: "function"
  }
];

function parse(hex) {
  return decodeURIComponent("%" + hex.match(/.{1,2}/g).join("%"));
}

const validate1271Signature = async (signer, digest, signature) => {
  const publicClient = viem.createPublicClient({
    chain: chains["desired_chain"],
    transport: viem.http()
  });

  const parsedDigest = parse(digest);

  const magicValue = await publicClient.readContract({
    address: signer,
    abi: EIP_1271_ABI,
    functionName: "isValidSignature",
    args: [
      viem.keccak256(
        viem.encodePacked(
          ["string"],
          [
            "\\x19Ethereum Signed Message:\\n" +
              parsedDigest.length +
              parsedDigest
          ]
        )
      ),
      signature
    ]
  });

  return magicValue === EIP_1271_MAGIC_VALUE;
};
Using ethers
const ethers = require("ethers");

const EIP_1271_MAGIC_VALUE = "0x1626ba7e";
const EIP_1271_ABI = [
  {
    constant: true,
    inputs: [
      {
        name: "_hash",
        type: "bytes32"
      },
      {
        name: "_sig",
        type: "bytes"
      }
    ],
    name: "isValidSignature",
    outputs: [
      {
        name: "magicValue",
        type: "bytes4"
      }
    ],
    payable: false,
    stateMutability: "view",
    type: "function"
  }
];

function parse(hex) {
  return decodeURIComponent("%" + hex.match(/.{1,2}/g).join("%"));
}

const validate1271Signature = async (signer, digest, signature) => {
  const provider = new ethers.providers.JsonRpcProvider("<rpc_url>");
  const verifyingContract = new ethers.Contract(signer, EIP_1271_ABI, provider);

  const parsedDigest = parse(digest);

  const magicValue = await verifyingContract.isValidSignature(
    ethers.utils.keccak256(
      ethers.utils.solidityPack(
        ["string"],
        ["\\x19Ethereum Signed Message:\\n" + parsedDigest.length + parsedDigest]
      )
    ),
    signature
  );

  return magicValue === EIP_1271_MAGIC_VALUE;
};

Additional Support for Brahma Connect [Ideal]

Brahma Connect is a new dApp connection mechanism which allows users to perform multiple sequential interactions on a single or multiple dApps in a single interaction and transaction.

For example: approving > swapping and then LPing in a separate protocol, all in 1 txn

Brahma Connect is powered by a suite of forked nodes that propagate the simulated state to dApps. dApps that support connect allow Brahma users to perform operations and strategy with the best possible UX and increase the dApp usage. More on the Brahma Connect UX here.

1. Adding Browser/Injected Wallet Support in your dApp Connect Kit [Must Have]

If you are using RainbowKit

Add support for browser/injected wallet with a single line of code change

Custom Wallet List — RainbowKit

If you are using web3-onboard by BlockNative

web3-onboard has 6963 support out of the box, but if disable6963Support is set to true on your project for any reason, refer to the following guide to add support for injected wallet: https://onboard.blocknative.com/docs/wallets/injected

If you are using Web3Modal by WalletConnect

Web3Modal supports injected wallets by default, but if your project is in custom configuration mode, injected wallet support can be added with a single line of code:

new InjectedConnector({ chains, options: { shimDisconnect: true } }),

https://docs.walletconnect.com/web3modal/vue/wagmi1/about/implementation

2. For dApps with signatures in their flow (e.g., auth signatures)

Connect doesn’t support signatures, and therefore if a dApp has an authentication signature or similar signature steps, it needs to bypass the signature step for users connecting through Brahma Connect:

Detect if Brahma Connect is the connected wallet

Check if Brahma Connect has announced itself as an EIP-6963 provider:

const brahmaConnectUUID = 'f3c205d4-5785-4f34-b019-2472b4e03a7a';
const providers: EIP6963ProviderDetail[];

let isBrahmaConnectDetected;

function onPageLoad() {
  window.addEventListener(
    "eip6963:announceProvider",
    (event: EIP6963AnnounceProviderEvent) => {
      providers.push(event.detail);
      
      if(
	      !isBrahmaConnectDetected && 
	      event.detail?.info?.uuid === brahmaConnectUUID
	     ) {
		     isBrahmaConnectDetected = true
	     }
    }
  );

  window.dispatchEvent(new Event("eip6963:requestProvider"));
}

Last updated