Gnosis Safe integration
How does the Blowfish API work with Gnosis Safe based wallets
Blowfish's API has full support for simulating transactions and messages for Gnosis Safe based wallets, including overriding signature thresholds.
Transactions
For simulating transactions of m-of-n without the appropriate signatures in place we have created Gnosis Safe preset simulatorConfig
option. When passed in the request payload the EVM simulator will automatically override the signature threshold variable from m-of-n
to 1-of-n
. If the specified tx.from
account is one of the signers for the Safe the transaction will simulate successfully as the tx.from
will count as one signature.
If the signer does not have enough funds to submit transactions the nativeBalance
override can be used to boost the Signers ETH balance during simulation.
Here is an example using a Gnosis Safe on Optimism where the signer has 0 ETH.
{
"txObjects": [
{
// Signer #1 with 0 ETH balance
"from": "0x57f4055164D42D1ee320cF7909E997E6828cDb65",
// Gnosis Safe contract
"to": "0x2cb5a1589f16655036caf137443ccb50dee3333a",
// Encoded Gnosis Safe transaction calldata for sending 0.001 ETH
"data": "0x6a761202000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa960450000000000000000000000000000000000000000000000000003328b944c4000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008200000000000000000000000057f4055164d42d1ee320cf7909e997e6828cdb650000000000000000000000000000000000000000000000000000000000000000011e9850743972b1f25f660f6ec3bf4ee52c3c4f1a753581680b6aee92c574e5205c34b66a53ef53a9a25757f1e85b5cfa32be2e807b6f9eb92b33bffbdb3967c41c000000000000000000000000000000000000000000000000000000000000"
}
],
// Simulate state changes and scan from the point-of-view of the Safe
"userAccount": "0x2cb5a1589f16655036caf137443ccb50dee3333a",
"metadata": {
"origin": "https://examples.blowfish.tools"
},
"simulatorConfig": {
"stateOverrides": {
"nativeBalances": [
{
// Set Signer #1's balance to 0.1 ETH during simulation
// so that they would have enough funds to pay for the transaction
"address": "0x57f4055164D42D1ee320cF7909E997E6828cDb65",
"value": "100000000000000000"
}
],
"storage": []
},
"presets": [
{
// Enable the GNOSIS_SAFE preset
"kind": "GNOSIS_SAFE",
// Specify the Safe address so that the signature threshold can be overriden
"walletAddress": "0x2cb5a1589f16655036caf137443ccb50dee3333a"
}
]
}
}
curl --request POST \
--url 'https://api.blowfish.xyz/optimism/v0/mainnet/scan/transactions?language=en' \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: YOUR_API_KEY_HERE' \
--header 'X-Api-Version: 2023-06-05' \
--header 'accept: application/json' \
--data '
{
"txObjects": [
{
"from": "0x57f4055164D42D1ee320cF7909E997E6828cDb65",
"to": "0x2cb5a1589f16655036caf137443ccb50dee3333a",
"data": "0x6a761202000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa960450000000000000000000000000000000000000000000000000003328b944c4000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008200000000000000000000000057f4055164d42d1ee320cf7909e997e6828cdb650000000000000000000000000000000000000000000000000000000000000000011e9850743972b1f25f660f6ec3bf4ee52c3c4f1a753581680b6aee92c574e5205c34b66a53ef53a9a25757f1e85b5cfa32be2e807b6f9eb92b33bffbdb3967c41c000000000000000000000000000000000000000000000000000000000000"
}
],
"userAccount": "0x2cb5a1589f16655036caf137443ccb50dee3333a",
"metadata": {
"origin": "https://examples.blowfish.tools"
},
"simulatorConfig": {
"stateOverrides": {
"nativeBalances": [
{
"address": "0x57f4055164D42D1ee320cF7909E997E6828cDb65",
"value": "100000000000000000"
}
],
"storage": []
},
"presets": [
{
"kind": "GNOSIS_SAFE",
"walletAddress": "0x2cb5a1589f16655036caf137443ccb50dee3333a"
}
]
}
}
'
{
"action" : "NONE",
"requestId" : "d70f18e18a0826c7b8fa372196016207",
"simulationResults" : {
"aggregated" : {
"error" : null,
"expectedStateChanges" : {
"0x2cb5a1589f16655036caf137443ccb50dee3333a" : [
{
"humanReadableDiff" : "Send 0.0009 ETH",
"rawInfo" : {
"data" : {
"amount" : {
"after" : "0",
"before" : "900000000000000"
},
"asset" : {
"address" : "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"decimals" : 18,
"imageUrl" : "https://d1ts37qlq4uz4s.cloudfront.net/evm__evm%3A%3Aethereum__evm%3A%3Aethereum%3A%3Amainnet__0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png",
"name" : "Ether",
"price" : {
"dollarValuePerToken" : 2644.45,
"source" : "Coingecko",
"updatedAt" : 1723555200
},
"symbol" : "ETH",
"verified" : true
},
"counterparty" : {
"address" : "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
"kind" : "ACCOUNT"
}
},
"kind" : "NATIVE_ASSET_TRANSFER"
}
}
],
"0xd8da6bf26964af9d7eed9e03e53415d37aa96045" : [
{
"humanReadableDiff" : "Receive 0.0009 ETH",
"rawInfo" : {
"data" : {
"amount" : {
"after" : "238959654892529998",
"before" : "238059654892529998"
},
"asset" : {
"address" : "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"decimals" : 18,
"imageUrl" : "https://d1ts37qlq4uz4s.cloudfront.net/evm__evm%3A%3Aethereum__evm%3A%3Aethereum%3A%3Amainnet__0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png",
"name" : "Ether",
"price" : {
"dollarValuePerToken" : 2644.45,
"source" : "Coingecko",
"updatedAt" : 1723555200
},
"symbol" : "ETH",
"verified" : true
},
"counterparty" : {
"address" : "0x2cb5a1589f16655036caf137443ccb50dee3333a",
"kind" : "ACCOUNT"
}
},
"kind" : "NATIVE_ASSET_TRANSFER"
}
}
]
},
"userAccount" : "0x2cb5a1589f16655036caf137443ccb50dee3333a"
},
"perTransaction" : [
{
"decodedCalldata" : null,
"decodedLogs" : [
null,
null
],
"error" : null,
"gas" : {
"gasLimit" : "64147"
},
"logs" : [
{
"address" : "0x2cb5a1589f16655036caf137443ccb50dee3333a",
"data" : "0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa960450000000000000000000000000000000000000000000000000003328b944c40000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008200000000000000000000000057f4055164d42d1ee320cf7909e997e6828cdb650000000000000000000000000000000000000000000000000000000000000000011e9850743972b1f25f660f6ec3bf4ee52c3c4f1a753581680b6aee92c574e5205c34b66a53ef53a9a25757f1e85b5cfa32be2e807b6f9eb92b33bffbdb3967c41c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000400000000000000000000000057f4055164d42d1ee320cf7909e997e6828cdb650000000000000000000000000000000000000000000000000000000000000001",
"topics" : [
"0x66753cd2356569ee081232e3be8909b950e0a76c1f8460c3a5e3c2be32b11bed"
]
},
{
"address" : "0x2cb5a1589f16655036caf137443ccb50dee3333a",
"data" : "0x1e8de1c5625f533121e5ef0b20d3a2ed00e0df952ac86407fb8cb9008f2a48d80000000000000000000000000000000000000000000000000000000000000000",
"topics" : [
"0x442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e"
]
}
],
"protocol" : null
}
]
},
"warnings" : []
}
Messages
Gnosis Safe Transaction message
When simulating a Gnosis Transaction in EIP712 message using the Blowfish message scanning endpoint the only caveat is that the userAccount
needs to be set to one of the Safe signer accounts. The signer account does not need to have any balance. The message simulator detect that it's a Gnosis transaction simulate the transaction from the point-of-view of the Gnosis Safe. No further configuration is needed.
Here is an example using a Gnosis Safe on Optimism where the signer has 0 ETH.
{
"message": {
"kind": "SIGN_TYPED_DATA",
"data": {
"types": {
"SafeTx": [
{
"name": "to",
"type": "address"
},
{
"name": "value",
"type": "uint256"
},
{
"name": "data",
"type": "bytes"
},
{
"name": "operation",
"type": "uint8"
},
{
"name": "safeTxGas",
"type": "uint256"
},
{
"name": "baseGas",
"type": "uint256"
},
{
"name": "gasPrice",
"type": "uint256"
},
{
"name": "gasToken",
"type": "address"
},
{
"name": "refundReceiver",
"type": "address"
},
{
"name": "nonce",
"type": "uint256"
}
],
"EIP712Domain": [
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
]
},
"domain": {
"chainId": "0xa",
"verifyingContract": "0x2cb5a1589f16655036caf137443ccb50dee3333a"
},
"primaryType": "SafeTx",
"message": {
"to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
"value": "900000000000000",
"data": "0x",
"operation": "0",
"safeTxGas": "0",
"baseGas": "0",
"gasPrice": "0",
"gasToken": "0x0000000000000000000000000000000000000000",
"refundReceiver": "0x0000000000000000000000000000000000000000",
"nonce": "4"
}
}
},
// NOTE: The user account needs to be the address of one of the signers
// the account does not need to have any ETH balance
// the simulation results & security scanning will still be performed
// on the Safe account in the message
"userAccount": "0x57f4055164D42D1ee320cF7909E997E6828cDb65",
"metadata": {
"origin": "https://examples.blowfish.tools"
}
}
curl --request POST \
--url 'https://api.blowfish.xyz/optimism/v0/mainnet/scan/message?language=en' \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: YOUR_API_KEY_HERE' \
--header 'X-Api-Version: 2023-06-05' \
--header 'accept: application/json' \
--data '
{
"message": {
"kind": "SIGN_TYPED_DATA",
"data": {
"types": {
"SafeTx": [
{
"name": "to",
"type": "address"
},
{
"name": "value",
"type": "uint256"
},
{
"name": "data",
"type": "bytes"
},
{
"name": "operation",
"type": "uint8"
},
{
"name": "safeTxGas",
"type": "uint256"
},
{
"name": "baseGas",
"type": "uint256"
},
{
"name": "gasPrice",
"type": "uint256"
},
{
"name": "gasToken",
"type": "address"
},
{
"name": "refundReceiver",
"type": "address"
},
{
"name": "nonce",
"type": "uint256"
}
],
"EIP712Domain": [
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
]
},
"domain": {
"chainId": "0xa",
"verifyingContract": "0x2cb5a1589f16655036caf137443ccb50dee3333a"
},
"primaryType": "SafeTx",
"message": {
"to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
"value": "900000000000000",
"data": "0x",
"operation": "0",
"safeTxGas": "0",
"baseGas": "0",
"gasPrice": "0",
"gasToken": "0x0000000000000000000000000000000000000000",
"refundReceiver": "0x0000000000000000000000000000000000000000",
"nonce": "4"
}
}
},
"userAccount": "0x57f4055164D42D1ee320cF7909E997E6828cDb65",
"metadata": {
"origin": "https://examples.blowfish.tools"
}
}
'
{
"action" : "NONE",
"requestId" : "9b35e8dbd995d15a7822c88b239e4625",
"simulationResults" : {
"error" : null,
"expectedStateChanges" : [
{
"humanReadableDiff" : "Send 0.0009 ETH",
"rawInfo" : {
"data" : {
"amount" : {
"after" : "0",
"before" : "900000000000000"
},
"asset" : {
"address" : "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"decimals" : 18,
"imageUrl" : "https://d1ts37qlq4uz4s.cloudfront.net/evm__evm%3A%3Aethereum__evm%3A%3Aethereum%3A%3Amainnet__0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png",
"name" : "Ether",
"price" : {
"dollarValuePerToken" : 2647.2,
"source" : "Coingecko",
"updatedAt" : 1723554601
},
"symbol" : "ETH",
"verified" : true
},
"contract" : {
"address" : "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"kind" : "ACCOUNT"
}
},
"kind" : "NATIVE_ASSET_TRANSFER"
}
}
],
"protocol" : null
},
"warnings" : []
}
General EIP-712 messages with Gnosis Safe
When scanning general EIP-712 messages with a Gnosis Safe the underlying message that the dapp is prompting signature for should be sent to the message scanner endpoint. The userAccount
property should be set to the Safe wallet account.
Here is an example using a Gnosis Safe on Optimism using Permit2 with the Uniswap Universal Router
{
"message": {
"kind": "SIGN_TYPED_DATA",
"data": {
"types": {
"PermitSingle": [
{
"name": "details",
"type": "PermitDetails"
},
{
"name": "spender",
"type": "address"
},
{
"name": "sigDeadline",
"type": "uint256"
}
],
"PermitDetails": [
{
"name": "token",
"type": "address"
},
{
"name": "amount",
"type": "uint160"
},
{
"name": "expiration",
"type": "uint48"
},
{
"name": "nonce",
"type": "uint48"
}
],
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
]
},
"domain": {
"name": "Permit2",
"chainId": "10",
"verifyingContract": "0x000000000022d473030f116ddee9f6b43ac78ba3"
},
"primaryType": "PermitSingle",
"message": {
"details": {
"token": "0x0b2c639c533813f4aa9d7837caf62653d097ff85",
"amount": "1461501637330902918203684832716283019655932542975",
"expiration": "2726145248",
"nonce": "0"
},
"spender": "0xcb1355ff08ab38bbce60111f1bb2b784be25d7e8",
"sigDeadline": "2723555048"
}
}
},
// NOTE: The userAccount should be the address of the Gnosis Safe
"userAccount": "0x2cb5a1589f16655036caf137443CcB50dEe3333a",
"metadata": {
"origin": "https://examples.blowfish.xyz"
}
}
curl --request POST \
--url 'https://api.blowfish.xyz/optimism/v0/mainnet/scan/message?language=en' \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: YOUR_API_KEY_HERE' \
--header 'X-Api-Version: 2023-06-05' \
--header 'accept: application/json' \
--data '
{
"message": {
"kind": "SIGN_TYPED_DATA",
"data": {
"types": {
"PermitSingle": [
{
"name": "details",
"type": "PermitDetails"
},
{
"name": "spender",
"type": "address"
},
{
"name": "sigDeadline",
"type": "uint256"
}
],
"PermitDetails": [
{
"name": "token",
"type": "address"
},
{
"name": "amount",
"type": "uint160"
},
{
"name": "expiration",
"type": "uint48"
},
{
"name": "nonce",
"type": "uint48"
}
],
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
]
},
"domain": {
"name": "Permit2",
"chainId": "10",
"verifyingContract": "0x000000000022d473030f116ddee9f6b43ac78ba3"
},
"primaryType": "PermitSingle",
"message": {
"details": {
"token": "0x0b2c639c533813f4aa9d7837caf62653d097ff85",
"amount": "1461501637330902918203684832716283019655932542975",
"expiration": "2726145248",
"nonce": "0"
},
"spender": "0xcb1355ff08ab38bbce60111f1bb2b784be25d7e8",
"sigDeadline": "2723555048"
}
}
},
"userAccount": "0x2cb5a1589f16655036caf137443CcB50dEe3333a",
"metadata": {
"origin": "https://examples.blowfish.xyz"
}
}
'
{
"action" : "WARN",
"requestId" : "93d65d5c81d3bc53d354d706af22b0d2",
"simulationResults" : {
"error" : null,
"expectedStateChanges" : [
{
"humanReadableDiff" : "Permit to transfer any amount of your USDC within 31 years",
"rawInfo" : {
"data" : {
"amount" : "1461501637330902918203684832716283019655932542975",
"asset" : {
"address" : "0x0b2c639c533813f4aa9d7837caf62653d097ff85",
"decimals" : 6,
"imageUrl" : "https://d1ts37qlq4uz4s.cloudfront.net/evm__evm%3A%3Aoptimism__evm%3A%3Aoptimism%3A%3Amainnet__0x0b2c639c533813f4aa9d7837caf62653d097ff85.png",
"lists" : [
"COINGECKO",
"UNISWAP",
"KLEROS_TOKENS"
],
"name" : "USDCoin",
"price" : {
"dollarValuePerToken" : 1.001,
"source" : "Defillama",
"updatedAt" : 1723554082
},
"symbol" : "USDC",
"verified" : true
},
"contract" : {
"address" : "0x0b2c639c533813f4aa9d7837caf62653d097ff85",
"kind" : "ACCOUNT"
},
"deadline" : 2726145248,
"nonce" : "0",
"owner" : {
"address" : "0x2cb5a1589f16655036caf137443ccb50dee3333a",
"kind" : "ACCOUNT"
},
"spender" : {
"address" : "0xcb1355ff08ab38bbce60111f1bb2b784be25d7e8",
"kind" : "ACCOUNT"
}
},
"kind" : "ERC20_PERMIT"
}
}
],
"protocol" : null
},
"warnings" : [
{
"data" : null,
"kind" : "PERMIT_UNLIMITED_ALLOWANCE",
"message" : "You are allowing this dApp to withdraw funds from your account in the future.",
"severity" : "WARNING"
}
]
}
Updated about 2 months ago