Brahma Console Integration [for dApp developers]

An integration guide to ensure both general smart contract wallet support, as well as Brahma Connect, 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;
    };
    

    Please note, that the above code can be translated to any library that is to be used.

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.

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

The following are the steps required to check if Brahma Connect is the connected wallet -

A. 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"));
}

B. Check for presence on window.ethereum

const isBrahmaConnectInjected = window.ethereum['isConsole Kernel']

C. For validating a successful connection, both conditions must be verified

Last updated