Introduction

On September 8th, 2025, zkSecurity was commissioned to perform a security audit of Randa-mu’s cross-chain OnlySwaps protocol and the BLS threshold signatures implementation. For the next two weeks, one consultant reviewed the code in search of bugs. A number of observations and findings have been reported to the Randa-mu team. The findings are detailed in the latter section of this report. The codebase was found to be of high quality, accompanied by thorough tests and documentation.

Scope

The scope of the audit was divided into three distinct parts:

  • onlyswaps-solidity: containing the main on-chain logic of the protocol (commit: 7607614bcff8ee2bd843f303db1b32c5f5c3f4e5)
  • bls-solidity: a library implementing BLS verification in Solidity (commit: 9e10df92d631fab9c46f0ce9cd5c445f857bedcb)
  • Parts of the dcipher repo: implementing the off-chain logic written in Rust (commit: e08602de2d1543c508914b2ca9ddbcb647fb7bcf). Specifically focusing on:
    • onlyswaps-verifier: main agent for verifying cross-chain swap fulfillments and creating threshold signatures
    • omnievent: listens to events from chains
    • signer: threshold signing
    • crates/network: wrapping functionality of libp2p used by the signer
    • crates/utils: utility functions for hash_to_curve, dst, and serialize (for the pairing operations)

The distributed key generation codebase was not part of this engagement. Furthermore, the main dependencies such as libp2p and ark are considered to be secure.

Protocol Overview

OnlySwaps is a protocol for cross-chain token swaps that are fulfilled by solvers. An off-chain committee produces BLS threshold signatures verifying that a solver has fulfilled a request from a user in a destination chain. The signature is then submitted in the source chain to release the funds to the solver.

The following figure demonstrates an end-to-end example of OnlySwaps.

Overview

The main functions in the Router.sol contract that handle the on-chain activity are:

  • requestCrossChainSwap: Called by a user in the source chain, it creates and saves a requestId with the passed parameters and emits an event about this request. With this function call, the user also sets the solver reward and sends the funds to the Router.
  • relayTokens: Called by a solver in the destination chain, it fulfills a request by sending the funds to the user-specified address. It further updates the state of the Router in the destination chain and emits an event.
  • rebalanceSolver: When a committee member has a BLS threshold signature that verifies that a requestId has been fulfilled correctly, they can submit the signature by calling this function in the source chain. It first verifies the signature, then updates the state, sends the solver reward to the solver, and emits an event. The BLS signature verification happens by calling the verifySignature function from bls-solidity.
  • updateSolverFeesIfUnfulfilled: A user can call this function in the source chain to update the solver fee to incentivize solvers to fulfill their request. Importantly, this function can only increase the fee.

On the off-chain part, an onlyswaps-verifier agent queries all supported blockchains for detecting new events. When it detects a new event that requires verification (e.g., a receipt event), it calls the evaluate_and_send function. This function is responsible for:

  1. Querying both the source chain and the destination chain to get the transfer params and the transfer receipt, respectively
  2. Calling the reconcile_transfer_params function that checks that the funds were sent and received in the correct chains, that the correct tokens were used, and that the correct amount was passed
  3. Starting the signing process, which by communicating with other committee members using libp2p, generates a BLS threshold signature. The logic of the signing process is in the signer directory
  4. Optionally, when having a correct threshold signature, the committee member can call the rebalanceSolver function in the source chain to verify the signature on-chain and pay back the solver

Threat Model

The OnlySwaps protocol operates with the following trust assumptions and administrative controls:

Administrative Roles

Router Admin:

  • setVerificationFeeBps: Modify the verification fee in basis points
  • setMinimumContractUpgradeDelay: Set the minimum delay for contract upgrades
  • permitDestinationChainId: Allow a new destination chain for swaps
  • blockDestinationChainId: Block a destination chain from receiving swaps
  • setTokenMapping: Configure token mappings across chains
  • removeTokenMapping: Remove existing token mappings
  • withdrawVerificationFee: Withdraw accumulated verification fees

Upgrade Validator Committee:

  • scheduleUpgrade: Schedule a future contract upgrade
  • cancelUpgrade: Cancel a scheduled upgrade
  • setContractUpgradeBlsValidator: Update the BLS validator for contract upgrades
  • setSwapRequestBlsValidator: Update the BLS validator for swap requests

Swap Validator Committee:

  • rebalanceSolver: Reimburse solvers with valid threshold signatures

Trust Assumptions

  • The committee operating the threshold signature scheme maintains an honest majority (at least t honest members out of n total)
  • The Router Admin is a trusted entity that will not act maliciously
  • Solvers are untrusted but economically incentivized to fulfill swaps correctly
  • Users trust the protocol’s verification mechanism to ensure proper swap execution
  • t honest nodes create valid partial and at least one of them aggregate them into a valid signature and submit it on-chain

BLS Threshold Signatures

The OnlySwaps protocol uses BLS (Boneh-Lynn-Shacham) threshold signatures to verify cross-chain swap fulfillments. This cryptographic scheme allows a distributed committee to collectively sign messages while requiring only a threshold number of participants.

BLS Signature Implementation

The implementation provides both standard BLS signatures and threshold signature capabilities through the following components:

Core BLS Operations: The system implements the standard BLS signature scheme over the BN254 and BLS12-381 curves. Signatures are created by computing σ=H(m)sk where H is a hash-to-curve function that maps messages to elliptic curve points.

Signature verification uses bilinear pairings to check the equation e(σ,g2)=e(H(m),pk), ensuring that only the holder of the private key (or the shared private key) corresponding to the public key could have generated the signature.

Domain Separation: Following RFC 9380 standards, the implementation uses comprehensive domain separation tags (DSTs) to prevent signature reuse across different contexts. The DST format includes:

  • Application identifier (e.g., “swap-v1” for OnlySwaps)
  • Curve specification (BN254G1 or BLS12381G1)
  • Hash function and expansion method (XMD:KECCAK-256 or XMD:SHA-256)
  • Mapping algorithm (SVDW for BN254, SSWU for BLS12-381)
  • Chain ID to prevent cross-chain replay attacks

For OnlySwaps, a typical DST looks like: swap-v1-BN254G1_XMD:KECCAK-256_SVDW_RO_0x[chain_id]_

Threshold Signature Protocol

The threshold signature scheme allows any t out of n committee members to produce a valid signature:

Secret Sharing: The system assumes that private keys have been distributed using Shamir Secret Sharing, where each committee member i holds a share ski of the master secret. The corresponding verification key is vki=gski.

Partial Signature Generation: When a swap needs verification, each committee member independently:

  1. Computes their partial signature: σi=H(m)ski
  2. Broadcasts this partial signature to other committee members via libp2p
  3. Verifies received partial signatures against the sender’s public key

Signature Aggregation: Once t valid partial signatures are collected, any committee member can reconstruct the full signature using Lagrange interpolation:

σ=iSσiλi

where λi are the Lagrange coefficients computed for the set S of participating members.

Network Protocol

The threshold signing protocol operates through a distributed network using libp2p:

  1. Event Detection: The onlyswaps-verifier agent monitors blockchain events through the omnievent system
  2. Verification Request: When a swap receipt is detected, the agent queries both source and destination chains to verify the swap parameters
  3. Partial Signature Exchange: Committee members compute and broadcast their partial signatures using CBOR serialization
  4. Aggregation: When exactly t partial signatures are collected, the full signature is computed
  5. On-chain Submission: The aggregated signature can be submitted to the Router contract’s rebalanceSolver function

Implementation Architecture

The Rust implementation is organized into modular components:

  • BlsPairingSigner: Handles individual BLS signing and verification operations
  • BlsThresholdSigner: Coordinates the distributed threshold signature protocol
  • signer/src/bls/filter.rs: Manages domain separation and DST generation
  • signer/src/bls/aggregation.rs: Implements Lagrange interpolation for signature reconstruction
  • crates/utils: Provides utility functions for hash-to-curve and serialization

The system supports both BN254 and BLS12-381 curves, with flexible configuration for different applications beyond OnlySwaps.

Additional Differential Testing

To ensure the correctness of the BLS implementation in Solidity, beyond manual code review, we performed extensive differential testing between the bls-solidity library and a reference Rust implementation. Each test category was executed with at least one million randomly generated test cases to provide high confidence in the implementation’s correctness.

Testing Methodology

The differential testing framework consists of a Rust program that accepts various test parameters and computes reference outputs for comparison with the Solidity implementation. The testing covered:

BLS Signature Generation and Verification:

  • BN254 curve signatures using Keccak256 hashing
  • BLS12-381 curve signatures using SHA256 hashing
  • Public key generation and signature creation
  • Full signature verification flow

Hash-to-Curve Mapping:

  • Shallue-van de Woestijne (SvdW) mapping for BN254
  • Field element to curve point conversion

Test Suites

Three comprehensive fuzz testing suites were developed:

BLSTestFuzz.sol:

  • testFfiBlsVerifyGenerated: Verifies BLS signature generation and verification on BN254 curve by comparing Rust-generated signatures with Solidity verification
  • testFfiMapToPointBN254: Tests the hash-to-curve mapping function, ensuring field elements map to the same curve points in both implementations

BLS2TestFuzz.sol:

  • Similar test structure to BLSTestFuzz but using the BLS12-381 curve
  • Validates signature operations with SHA256 hashing instead of Keccak256

ModExpFuzz.t.sol:

  • testFfiModExp1: Validates modular exponentiation against the BN254 field modulus
  • testFfiModExpInverse: Tests modular inverse computation
  • testFfiModExpSqrt: Verifies modular square root calculations

Testing Results

All differential tests passed successfully across millions of test cases, providing strong evidence that the Solidity BLS implementation correctly matches the reference Rust implementation using the arkworks library. This extensive testing gives high confidence in the cryptographic correctness of the bls-solidity library used in the OnlySwaps protocol.

Differential Testing Results