LPsLux Proposals
Threshold Cryptography
LP-3310

Safe Multisig Standard

Implemented

Safe (formerly Gnosis Safe) multisig wallet integration for institutional-grade custody on Lux Network.

Category
LRC
Created
2025-12-14

Abstract

This LP documents the integration of Safe (formerly Gnosis Safe) smart contract wallet protocol into the Lux Standard Library. Safe is the most battle-tested and widely-deployed multisig infrastructure in the EVM ecosystem, securing over $100 billion in digital assets. This specification covers the core Safe contracts, module system, guard contracts, signature schemes, and Lux-specific adaptations including FROST threshold signatures and cross-chain Safe via Warp messaging.

Motivation

Multi-signature wallets are essential infrastructure for secure asset management, particularly for:

  1. Institutional Custody: Enterprises, DAOs, and protocols require robust multisig for treasury management
  2. Battle-Tested Security: Safe has undergone multiple audits and secures significant TVL across chains
  3. Modular Architecture: Extensible via modules and guards without modifying core contracts
  4. Standards Compliance: Full support for EIP-712 typed data signing and EIP-1271 contract signatures
  5. Cross-Chain Requirements: Lux's multi-chain architecture demands Safe compatibility across C-Chain, chains, and via Warp messaging

Why Safe over Custom Solutions

CriteriaSafeCustom Implementation
Audits6+ comprehensive auditsNew code = new risks
TVL Secured$100B+Unproven
Ecosystem ToolsSafe{Wallet}, SDK, Transaction ServiceBuild from scratch
CommunityLarge developer communityInternal only
Module SystemBattle-tested extensibilityDesign required

Specification

Core Contracts

The Safe protocol implementation is located at /Users/z/work/lux/standard/src/safe/.

Safe.sol (Main Contract)

The core multisig wallet contract supporting:

  • Threshold Signatures: Configurable M-of-N owner scheme
  • EIP-712 Typed Data: Structured transaction hashing for secure signing
  • Nonce Management: Replay protection via sequential nonces
  • Gas Refunds: Optional transaction fee refunds to relayers
  • Delegate Calls: Execute arbitrary logic via delegatecall operations
contract Safe is
    Singleton,
    NativeCurrencyPaymentFallback,
    ModuleManager,
    GuardManager,
    OwnerManager,
    SignatureDecoder,
    SecuredTokenTransfer,
    ISignatureValidatorConstants,
    FallbackManager,
    StorageAccessible,
    ISafe
{
    string public constant VERSION = "1.4.1";

    function execTransaction(
        address to,
        uint256 value,
        bytes calldata data,
        Enum.Operation operation,
        uint256 safeTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        address gasToken,
        address payable refundReceiver,
        bytes memory signatures
    ) external payable returns (bool success);
}

SafeL2.sol

Layer 2 optimized variant that emits events for all operations, enabling indexing without archive nodes:

  • Additional events for transaction execution
  • Optimized for rollup and L2 deployments
  • Compatible with standard Safe SDK tooling

SafeProxy.sol

Minimal proxy contract (EIP-1167 pattern) for gas-efficient Safe deployments:

contract SafeProxy {
    address internal singleton;

    constructor(address _singleton) {
        singleton = _singleton;
    }

    fallback() external payable {
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), sload(0), 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            switch result
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }
}

SafeProxyFactory.sol

Factory contract for deterministic Safe deployments:

  • CREATE2 for predictable addresses
  • Callback support for post-deployment initialization
  • Batch deployment capabilities

Base Contracts

OwnerManager.sol

Manages the set of Safe owners:

  • Add/remove owners with threshold adjustment
  • Swap owners atomically
  • Linked list storage pattern for gas efficiency

ModuleManager.sol

Module system for extending Safe functionality:

  • Enable/disable modules via Safe transaction
  • Module guard for transaction validation
  • Paginated module enumeration

GuardManager.sol

Transaction guard system:

  • Pre-transaction validation hooks
  • Post-execution verification
  • Interface: ITransactionGuard

FallbackManager.sol

Fallback handler for extended functionality:

  • Token callback handling (ERC-721, ERC-1155, ERC-777)
  • Custom view function support
  • Handler delegation pattern

Signature Schemes

Safe supports multiple signature types via the v parameter encoding:

v ValueSignature TypeDescription
0Contract SignatureEIP-1271 isValidSignature check
1Approved HashPre-approved via approveHash()
27, 28ECDSAStandard Ethereum signatures
31, 32eth_signLegacy \x19Ethereum Signed Message prefix

EIP-712 Domain Separator

bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH =
    keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");

bytes32 private constant SAFE_TX_TYPEHASH = keccak256(
    "SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"
);

EIP-1271 Contract Signatures

Enables smart contract wallets as Safe owners:

interface ISignatureValidator {
    function isValidSignature(bytes32 _hash, bytes memory _signature)
        external view returns (bytes4 magicValue);
}

// Magic value: 0x1626ba7e
bytes4 constant EIP1271_MAGIC_VALUE = 0x1626ba7e;

Guard Contracts

Example guards included in the implementation:

BaseGuard.sol

Abstract base for custom guards:

abstract contract BaseTransactionGuard is ITransactionGuard {
    function supportsInterface(bytes4 interfaceId) external view virtual returns (bool) {
        return interfaceId == type(ITransactionGuard).interfaceId ||
               interfaceId == type(IERC165).interfaceId;
    }
}

DelegateCallTransactionGuard.sol

Prevents delegate calls to unauthorized targets:

  • Whitelist of allowed delegate call destinations
  • Blocks potential proxy upgrade attacks

OnlyOwnersGuard.sol

Restricts module transactions to owner-only:

  • Prevents external module abuse
  • Additional security layer for sensitive operations

ReentrancyTransactionGuard.sol

Reentrancy protection for Safe transactions:

  • Mutex pattern for transaction execution
  • Guards against callback-based attacks

DebugTransactionGuard.sol

Development and debugging guard:

  • Emits detailed transaction events
  • Gas usage tracking
  • For testing environments only

Library Contracts

MultiSend.sol

Batch transaction execution:

function multiSend(bytes memory transactions) public payable {
    assembly {
        let length := mload(transactions)
        let i := 0x20
        for {} lt(i, length) {} {
            let operation := shr(0xf8, mload(add(transactions, i)))
            let to := shr(0x60, mload(add(transactions, add(i, 0x01))))
            let value := mload(add(transactions, add(i, 0x15)))
            let dataLength := mload(add(transactions, add(i, 0x35)))
            let data := add(transactions, add(i, 0x55))
            // Execute transaction...
        }
    }
}

MultiSendCallOnly.sol

Restricted variant allowing only CALL operations (no DELEGATECALL).

SignMessageLib.sol

EIP-191 message signing support for the Safe:

function signMessage(bytes calldata _data) external authorized {
    bytes32 msgHash = getMessageHash(_data);
    signedMessages[msgHash] = 1;
    emit SignMsg(msgHash);
}

CreateCall.sol

Contract deployment from Safe:

  • CREATE and CREATE2 support
  • Deterministic deployment addresses

Interface Contracts

InterfacePurpose
ISafe.solMain Safe interface
IModuleManager.solModule management
IOwnerManager.solOwner management
IGuardManager.solGuard management
IFallbackManager.solFallback handler
ISignatureValidator.solEIP-1271 validation

Handler Contracts

CompatibilityFallbackHandler.sol

Combined fallback handler supporting:

  • ERC-721 token receiving (onERC721Received)
  • ERC-1155 token receiving (onERC1155Received, onERC1155BatchReceived)
  • ERC-777 token receiving (tokensReceived)
  • EIP-1271 signature validation
  • View function delegation

TokenCallbackHandler.sol

Minimal token callback support.

Dependencies

NPM Package

{
  "name": "@safe-global/safe-smart-account",
  "version": "1.4.1-build.0",
  "license": "LGPL-3.0",
  "devDependencies": {
    "@nomicfoundation/hardhat-toolbox": "^5.0.0",
    "@openzeppelin/contracts": "^3.4.0",
    "@safe-global/mock-contract": "^4.1.0",
    "@safe-global/safe-singleton-factory": "^1.0.24",
    "hardhat": "^2.22.6",
    "solc": "0.7.6"
  }
}

Solidity Version

Safe contracts target Solidity >=0.7.0 <0.9.0 for broad compatibility.

Mock Contracts for Testing

Located in /contracts/test/:

  • ERC20Token.sol - Test ERC-20 token
  • ERC1155Token.sol - Test ERC-1155 token
  • Token.sol - Legacy token interface
  • DelegateCaller.sol - Delegate call testing
  • TestHandler.sol - Fallback handler testing
  • Test4337ModuleAndHandler.sol - ERC-4337 compatibility testing

Lux-Specific Adaptations

Integration with FROST Threshold Signatures

Safe can be extended to support FROST threshold signatures via a custom module:

interface IFROSTModule {
    function verifyFROSTSignature(
        bytes32 messageHash,
        bytes calldata signature,
        uint8 threshold,
        uint8 totalParties
    ) external view returns (bool);

    function executeFROSTTransaction(
        address to,
        uint256 value,
        bytes calldata data,
        bytes calldata frostSignature
    ) external returns (bool);
}

Benefits:

  • Single 64-byte signature regardless of threshold
  • Off-chain signature aggregation
  • Compatible with LP-321 FROST precompile

Reference: See LP-321 (FROST Threshold Signature Precompile) and LP-104 (FROST Threshold Signatures).

Cross-Chain Safe via Warp Messaging

Enable Safe governance across Lux chains:

interface ICrossChainSafeModule {
    function sendCrossChainTransaction(
        bytes32 destinationChainId,
        address destinationSafe,
        bytes calldata encodedTransaction
    ) external;

    function executeCrossChainTransaction(
        uint32 warpIndex,
        bytes calldata encodedTransaction
    ) external returns (bool);
}

Use Cases:

  • Unified treasury management across chains
  • Cross-chain governance execution
  • Multi-chain protocol administration

Reference: See LP-603 (Warp Messaging Protocol).

Post-Quantum Signature Module

The Post-Quantum Module enables quantum-resistant signatures for Lux Safe, supporting three complementary schemes:

AlgorithmTypeReusableGas CostBest For
ML-DSA-65Lattice✅ Yes~500KSingle-signer wallets
SLH-DSA-128sHash-based✅ Yes~800KLong-term archives
RingtailLattice Threshold✅ Yes~200KMultisig/MPC
Lamport OTSHash-based❌ One-time~800KRemote chains (LP-4105)

Ringtail Threshold Integration

Ringtail is the recommended PQ algorithm for Safe multisig because it natively supports threshold signing:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IRingtailThreshold} from "@luxfi/precompiles/IRingtailThreshold.sol";

/**
 * @title RingtailSafeModule
 * @notice Quantum-safe threshold signatures for Lux Safe
 * @dev Uses Ringtail precompile at 0x020000000000000000000000000000000000000B
 */
contract RingtailSafeModule {
    address constant RINGTAIL_PRECOMPILE = 0x020000000000000000000000000000000000000B;

    // Ringtail group key for this Safe (Ring-LWE public key)
    bytes public ringtailGroupKey;

    // Threshold configuration (t-of-n)
    uint16 public threshold;
    uint16 public totalSigners;

    // Migration phase: CLASSICAL_ONLY → HYBRID → RINGTAIL_ONLY
    enum MigrationPhase { CLASSICAL_ONLY, HYBRID, RINGTAIL_PREFERRED, RINGTAIL_ONLY }
    MigrationPhase public phase;

    /**
     * @notice Register Ringtail group key for threshold signing
     * @param groupKey The Ring-LWE group public key from DKG ceremony
     * @param t Threshold (minimum signers required)
     * @param n Total signers in the group
     */
    function registerRingtailGroup(
        bytes calldata groupKey,
        uint16 t,
        uint16 n
    ) external onlyOwner {
        require(t > 0 && t <= n, "Invalid threshold");
        require(n >= 2, "Need at least 2 signers");
        ringtailGroupKey = groupKey;
        threshold = t;
        totalSigners = n;
    }

    /**
     * @notice Verify Ringtail threshold signature via precompile
     * @param messageHash Hash of the Safe transaction
     * @param signature Aggregated Ringtail threshold signature
     * @return valid True if t-of-n signers produced valid signature
     */
    function verifyRingtailSignature(
        bytes32 messageHash,
        bytes calldata signature
    ) public view returns (bool valid) {
        // Call Ringtail precompile for verification
        (bool success, bytes memory result) = RINGTAIL_PRECOMPILE.staticcall(
            abi.encode(
                uint8(1),           // VERIFY opcode
                ringtailGroupKey,   // Group public key
                messageHash,        // Message
                signature,          // Threshold signature
                threshold           // Required signers
            )
        );
        return success && abi.decode(result, (bool));
    }

    /**
     * @notice Execute Safe transaction with Ringtail signature
     * @dev Wraps Safe.execTransaction with PQ verification
     */
    function execTransactionWithRingtail(
        address to,
        uint256 value,
        bytes calldata data,
        bytes calldata ringtailSig,
        bytes calldata classicalSigs  // For hybrid mode
    ) external returns (bool) {
        bytes32 txHash = safe.getTransactionHash(to, value, data, ...);

        if (phase == MigrationPhase.RINGTAIL_ONLY) {
            // Quantum-only: Ringtail signature required
            require(verifyRingtailSignature(txHash, ringtailSig), "Invalid Ringtail sig");
        } else if (phase == MigrationPhase.HYBRID) {
            // Hybrid: Both classical AND Ringtail required
            require(verifyRingtailSignature(txHash, ringtailSig), "Invalid Ringtail sig");
            // Classical sigs verified by Safe.execTransaction
        }
        // CLASSICAL_ONLY or RINGTAIL_PREFERRED: classical sigs verified by Safe

        return safe.execTransaction(to, value, data, ..., classicalSigs);
    }
}

Migration Phases

Phase 0: CLASSICAL_ONLY   - ECDSA multisig (current state)
Phase 1: HYBRID           - ECDSA + Ringtail both required
Phase 2: RINGTAIL_PREFERRED - Ringtail primary, ECDSA fallback
Phase 3: RINGTAIL_ONLY    - Full quantum resistance

T-Chain MPC Integration

For institutional custody, Safe can delegate to T-Chain MPC signer sets:

contract TChainCustodySafe is RingtailSafeModule {
    // T-Chain signer set ID (registered on T-Chain)
    bytes32 public tChainSignerSet;

    /**
     * @notice Link Safe to T-Chain MPC custody
     * @param signerSetId The T-Chain threshold signer group
     * @param attestation Proof of signer set registration
     */
    function linkTChainCustody(
        bytes32 signerSetId,
        bytes calldata attestation
    ) external onlyOwner {
        // Verify T-Chain attestation (Warp message from T-Chain)
        require(verifyTChainAttestation(signerSetId, attestation));
        tChainSignerSet = signerSetId;

        // Import Ringtail group key from T-Chain
        ringtailGroupKey = getTChainGroupKey(signerSetId);
    }

    // Transactions now require T-Chain MPC threshold signatures
    // using Ringtail for quantum safety
}

Use Cases:

  • DAO Treasuries: Quantum-safe governance with threshold control
  • Bridge Vaults: External asset custody with T-Chain MPC
  • Institutional Wallets: Enterprise custody with quantum resistance
  • Protocol Upgrades: Time-locked upgrades with PQ attestations

Reference: See LP-7324 (Ringtail), LP-4105 (Lamport OTS), LP-7000 (T-Chain).

Formal Verification

Certora Verification

The implementation includes Certora formal verification:

Location: /Users/z/work/lux/standard/src/safe/certora/

Specifications (specs/):

  • Ownership invariants
  • Module security properties
  • Guard correctness
  • Signature validation

Harnesses (harnesses/):

  • SafeHarness.sol - Test harness for Safe contract

Configuration (conf/):

  • Certora verification configurations

Audit History

Safe v1.4.0/1.4.1 audits:

  • Ackee Blockchain (v1.4.0/1.4.1)
  • G0 Group (v1.3.0, v1.2.0, v1.1.1)
  • Runtime Verification (v1.0.0)
  • Alexey Akhunov (v0.0.1)

OpenZeppelin 5.x Migration

Required Updates

Safe currently uses @openzeppelin/contracts@^3.4.0. Migration to OZ 5.x requires:

  1. SafeMath Removal: Native Solidity 0.8.x overflow checks
  2. Access Control Updates: New Ownable and AccessControl patterns
  3. ERC Token Updates: Updated token interfaces and implementations
  4. ReentrancyGuard: Updated modifier patterns

Migration Path

// Before (OZ 3.x / Safe current)
import {SafeMath} from "./external/SafeMath.sol";
using SafeMath for uint256;
uint256 result = a.add(b);

// After (OZ 5.x / Solidity 0.8+)
// Native overflow checking
uint256 result = a + b;

Note: Safe's use of SafeMath is for compatibility with Solidity >=0.7.0. Migration to Solidity 0.8+ removes this dependency.

Rationale

Design Decisions

  1. Proxy Pattern: Minimal proxy reduces deployment costs (~60,000 gas vs ~2,000,000)
  2. Linked List Storage: Gas-efficient owner/module iteration
  3. Modular Architecture: Extend without modifying audited core
  4. Guard System: Pre/post transaction hooks without core changes
  5. EIP-712 Signing: Human-readable transaction signing in wallets

Security Model

  • Threshold Enforcement: Cannot bypass M-of-N requirement
  • Nonce Sequencing: Prevents replay and front-running
  • Module Isolation: Modules cannot modify owner/threshold
  • Guard Composition: Stack multiple guards for defense in depth

Backwards Compatibility

Gnosis Safe Compatibility

Full compatibility with existing Safe ecosystem:

  • Safe{Wallet} UI
  • Safe Transaction Service
  • Safe SDK (ethers-lib, web3-lib, core-sdk)
  • Safe Apps

LP-42 Alignment

Complements LP-42 (Multi-Signature Wallet Standard):

  • Safe implements ILuxMultiSig interface patterns
  • Extended with Safe-specific module/guard system
  • Additional signature schemes beyond basic threshold

Test Cases

Unit Tests

Located at /Users/z/work/lux/standard/src/safe/test/:

cd /Users/z/work/lux/standard/src/safe
npm run build
npm run test

Test Coverage

ComponentCoverage
Safe.sol95%+
ModuleManager95%+
GuardManager95%+
OwnerManager95%+
Proxy100%

Example Test

describe("Safe", () => {
  it("should execute transaction with threshold signatures", async () => {
    const safe = await deploySafe([owner1, owner2, owner3], 2);

    const tx = buildSafeTransaction({
      to: recipient.address,
      value: parseEther("1"),
      nonce: await safe.nonce()
    });

    const sig1 = await signTypedData(owner1, safe.address, tx);
    const sig2 = await signTypedData(owner2, safe.address, tx);

    await safe.execTransaction(
      tx.to, tx.value, tx.data, tx.operation,
      tx.safeTxGas, tx.baseGas, tx.gasPrice,
      tx.gasToken, tx.refundReceiver,
      buildSignatureBytes([sig1, sig2])
    );

    expect(await provider.getBalance(recipient.address)).to.equal(parseEther("1"));
  });
});

Reference Implementation

Repository: https://github.com/luxfi/standard Local Path: /Users/z/work/lux/standard/

Contracts

ContractDescription
src/safe/contracts/Safe.solSafe core contract
src/safe/contracts/base/ModuleManager.solModule management
src/safe/contracts/base/OwnerManager.solOwner management

Build and Test

cd /Users/z/work/lux/standard

# Build all contracts
forge build

# Run tests
forge test -vvv

# Gas report
forge test --gas-report

Security Considerations

Signature Security

  • Replay Protection: Nonces prevent transaction replay
  • Chain ID Binding: EIP-712 domain includes chain ID
  • Signature Ordering: Signatures must be sorted by owner address
  • Contract Signatures: EIP-1271 validation for smart contract owners

Module Security

  • Module Risk: Modules have unlimited access; only add trusted, audited modules
  • Module Guard: Additional validation layer for module transactions
  • Disable Mechanism: Owners can disable compromised modules

Guard Security

  • Guard Bypass: Cannot bypass guard checks without removing guard
  • Guard Composition: Multiple guards can be stacked via delegation
  • Gas Considerations: Guards consume gas; malicious guards could cause DoS

Upgrade Considerations

  • Singleton Upgrade: Change singleton storage slot to upgrade
  • Migration Libraries: SafeMigration.sol for controlled upgrades
  • State Preservation: Proxy pattern preserves all Safe state

License

LGPL-3.0-only

The LGPL-3.0 license permits:

  • Linking Safe contracts with proprietary code
  • Modifications must remain open source
  • Derivative works of Safe contracts must use LGPL-3.0

References

Standards

  • LP-40 - Wallet standards
  • LP-42 - Multisig standard
  • LP-3320 - Lamport OTS for quantum-safe signing
  • LP-3337 - Account abstraction
  • LP-3500 - Post-quantum signatures
  • LP-4105 - Lamport OTS implementation
  • LP-4200 - Complete PQC ecosystem
  • LP-7000 - T-Chain MPC custody
  • LP-7324 - Ringtail threshold signatures

External Resources

Copyright and related rights waived via CC0.