# Gnosis Safe

> Execute multi-signature transactions through Gnosis Safe (Safe) using Nethereum (.NET/C#). Use this skill whenever the user asks about multi-sig wallets, Gnosis Safe, Safe transactions, SafeAccount, multi-signature signing, MultiSend, or executing contract calls through a Safe with C# or .NET.

- Skill: `nethereum/gnosis-safe` (Agent Skill)
- Install (CLI): `npx skillmds@latest add nethereum/gnosis-safe`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nethereum/gnosis-safe/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: nethereum (https://skillmd.com/u/nethereum)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/nethereum/gnosis-safe

---


# Gnosis Safe: Multi-Sig Transactions

Gnosis Safe (now "Safe") is the most widely used multi-signature wallet on Ethereum. Nethereum's `Nethereum.GnosisSafe` package provides three levels of integration: `SafeAccount` (transparent — any contract service auto-routes through Safe), `ChangeContractHandlerToSafeExecTransaction` (per-service switching), and manual transaction building with EIP-712 signatures.

NuGet: `Nethereum.GnosisSafe`

```bash
dotnet add package Nethereum.GnosisSafe
```

## SafeAccount (Recommended)

The simplest approach — create a `SafeAccount` and every contract service created from that `web3` instance automatically executes through the Safe:

```csharp
var safeAccount = new SafeAccount(safeAddress, chainId, privateKey);
var web3 = new Web3(safeAccount, rpcUrl);

// This ERC-20 transfer executes through the Safe
var erc20 = new ERC20ContractService(web3.Eth, tokenAddress);
var receipt = await erc20.TransferRequestAndWaitForReceiptAsync(to, amount);
```

`SafeAccount` implements `IContractServiceConfigurableAccount`, which swaps the contract handler to `SafeExecTransactionContractHandler`. The call data gets packed into a Safe `execTransaction`, signed with EIP-712, and submitted.

## Per-Service Handler Swap

Switch a specific contract service to Safe execution without changing the entire `web3`:

```csharp
var erc20 = new ERC20ContractService(web3.Eth, contractAddress);
erc20.ChangeContractHandlerToSafeExecTransaction(safeAddress, privateKey);
// Only this service goes through Safe; others execute directly
```

This is useful when some operations should go through the Safe and others shouldn't.

## Manual Transaction Building

For full control or multi-owner signature collection:

```csharp
var safeService = new GnosisSafeService(web3, safeAddress);

var transactionData = new EncodeTransactionDataFunction
{
    To = targetAddress,
    Value = 0,
    Data = encodedCallData,
    Operation = 0,  // 0 = Call, 1 = DelegateCall
    SafeTxGas = 0,
    BaseGas = 0,
    SafeGasPrice = 0,
    GasToken = AddressUtil.ZERO_ADDRESS,
    RefundReceiver = AddressUtil.ZERO_ADDRESS
};

var execTx = await safeService.BuildTransactionAsync(
    transactionData, chainId, false, privateKey1, privateKey2);

var receipt = await safeService.ExecTransactionRequestAndWaitForReceiptAsync(execTx);
```

`BuildTransactionAsync` fetches the nonce, hashes the data with EIP-712, signs with each key, orders signatures by signer address (required by Safe), and returns a ready-to-submit function.

## Off-Chain Signature Collection

When owners can't sign in the same session, compute the hash and collect signatures separately:

```csharp
// Compute hash
transactionData.SafeNonce = await safeService.NonceQueryAsync();
var safeHashes = GnosisSafeService.GetSafeHashes(transactionData, chainId, safeAddress);

// Each owner signs independently
var signature = await safeService.SignEncodedTransactionDataAsync(
    transactionData, chainId, convertToSafeVFormat: true);
```

Safe uses a custom V format (V+4 from standard Ethereum). The `convertToSafeVFormat: true` parameter handles this automatically.

### Combine and Execute

```csharp
var signatures = new List<SafeSignature>
{
    new SafeSignature { Address = owner1Address, Signature = signature1 },
    new SafeSignature { Address = owner2Address, Signature = signature2 }
};

var combinedSignatures = safeService.GetCombinedSignaturesInOrder(signatures);
```

Safe requires signatures ordered by signer address (ascending). `GetCombinedSignaturesInOrder` handles sorting and concatenation.

## MultiSend

Batch multiple actions in a single Safe transaction using typed `MultiSendFunctionInput<T>`:

```csharp
var input1 = new MultiSendFunctionInput<TransferFunction>(
    new TransferFunction { To = recipient1, Value = amount1 }, tokenAddress);

var input2 = new MultiSendFunctionInput<TransferFunction>(
    new TransferFunction { To = recipient2, Value = amount2 }, tokenAddress);

var execTx = await safeService.BuildMultiSendTransactionAsync(
    transactionData, chainId, privateKey, false, input1, input2);
var receipt = await safeService.ExecTransactionRequestAndWaitForReceiptAsync(execTx);
```

`MultiSendFunctionInput<T>` implements `IMultiSendInput` and encodes call data automatically. `BuildMultiSendTransactionAsync` sets Operation to DelegateCall (required by MultiSend).

## Query Safe Configuration

```csharp
var owners = await safeService.GetOwnersQueryAsync();
var threshold = await safeService.GetThresholdQueryAsync();
var nonce = await safeService.NonceQueryAsync();
var version = await safeService.VersionQueryAsync();
var isOwner = await safeService.IsOwnerQueryAsync(address);

Console.WriteLine($"Safe v{version}: {owners.Count} owners, threshold {threshold}, nonce {nonce}");
```

## Gas Parameters

| Parameter | Purpose |
|-----------|---------|
| `SafeTxGas` | Gas for the internal Safe transaction. Set 0 to let the Safe estimate. |
| `BaseGas` | Gas overhead independent of execution. Usually 0. |
| `SafeGasPrice` | Gas price for refund calculation. Set 0 for no refund. |
| `GasToken` | Token to pay gas refunds in. `address(0)` = ETH. |
| `RefundReceiver` | Address receiving the gas refund. `address(0)` = tx.origin. |

For most use cases, setting all gas parameters to 0 is correct — the Safe uses Ethereum's standard gas mechanism.

## When to Use Which Approach

| Approach | When |
|----------|------|
| `SafeAccount` | All contract calls should go through Safe — simplest |
| `ChangeContractHandlerToSafeExecTransaction` | Only specific services should use Safe |
| Manual `BuildTransactionAsync` | Need multi-owner signature collection or custom gas params |
| `BuildMultiSendTransactionAsync` | Batch multiple actions in one Safe tx |

For full documentation, see: https://docs.nethereum.com/docs/defi/guide-gnosis-safe

