Skip to main content
The Attestable contract provides attester management and signature verification functionality for Cross-Chain Transfer Protocol messages. It implements an m-of-n multisig mechanism where m attesters must sign messages for them to be considered valid. Contract: src/roles/Attestable.sol

Key Concepts

  • Attesters: Authorized signers who can attest to message validity
  • Signature Threshold: Minimum number of signatures required (m in m/n multisig)
  • Attester Manager: Role responsible for managing attesters and threshold

State Variables

signatureThreshold

uint256 public signatureThreshold
Number of signatures from distinct attesters required for a message to be received (m in m/n multisig).

enabledAttesters

EnumerableSet.AddressSet private enabledAttesters
Set of enabled attesters (message signers). The length represents n in m/n multisig.

Functions

enableAttester

function enableAttester(address newAttester) public onlyAttesterManager
Enables an attester to sign messages. Parameters:
  • newAttester: Address of the attester to enable
Requirements:
  • Caller must be the attester manager
  • New attester must be non-zero address
  • Attester must not already be enabled
Emits: AttesterEnabled(address indexed attester) Source: Attestable.sol:100

disableAttester

function disableAttester(address attester) external onlyAttesterManager
Disables an attester from signing messages. Parameters:
  • attester: Address of the attester to disable
Requirements:
  • Caller must be the attester manager
  • Must have more than one enabled attester
  • Number of enabled attesters must exceed signature threshold
  • Attester must currently be enabled
Emits: AttesterDisabled(address indexed attester) Source: Attestable.sol:138

setSignatureThreshold

function setSignatureThreshold(uint256 newSignatureThreshold) external onlyAttesterManager
Updates the threshold of signatures required to attest to a message (m in m/n multisig). Parameters:
  • newSignatureThreshold: New signature threshold value
Requirements:
  • Caller must be the attester manager
  • New threshold must be non-zero
  • New threshold must not exceed number of enabled attesters
  • New threshold must be different from current threshold
Emits: SignatureThresholdUpdated(uint256 oldSignatureThreshold, uint256 newSignatureThreshold) Source: Attestable.sol:161

updateAttesterManager

function updateAttesterManager(address newAttesterManager) external onlyOwner
Transfers attester manager control to a new address. Parameters:
  • newAttesterManager: Address of the new attester manager
Requirements:
  • Caller must be the owner
  • New attester manager must be non-zero address
Emits: AttesterManagerUpdated(address indexed previousAttesterManager, address indexed newAttesterManager) Source: Attestable.sol:125

isEnabledAttester

function isEnabledAttester(address attester) public view returns (bool)
Checks if an address is an enabled attester. Parameters:
  • attester: Address to check
Returns: true if the address is an enabled attester, false otherwise Source: Attestable.sol:109

getNumEnabledAttesters

function getNumEnabledAttesters() public view returns (uint256)
Returns the number of enabled attesters. Returns: Count of enabled attesters Source: Attestable.sol:117

getEnabledAttester

function getEnabledAttester(uint256 index) external view returns (address)
Gets the enabled attester at a specific index. Parameters:
  • index: Index of the attester in the set
Returns: Address of the attester at the given index Source: Attestable.sol:180

attesterManager

function attesterManager() external view returns (address)
Returns the address of the current attester manager. Returns: Attester manager address Source: Attestable.sol:171

Signature Verification

Internal Verification

The contract includes internal signature verification logic:
function _verifyAttestationSignatures(
    bytes calldata _message,
    bytes calldata _attestation
) internal view
Verifies that an attestation is valid according to these rules:
  1. Attestation length must equal signatureLength * signatureThreshold (65 bytes per signature)
  2. Recovered addresses must be in increasing order (prevents duplicates)
  3. All signers must be enabled attesters
  4. Uses ECDSA signature recovery
Source: Attestable.sol:227

Modifiers

onlyAttesterManager

modifier onlyAttesterManager()
Restricts function access to the attester manager only. Reverts: “Caller not attester manager” if caller is not the attester manager Source: Attestable.sol:77

Events

AttesterEnabled

event AttesterEnabled(address indexed attester)
Emitted when an attester is enabled.

AttesterDisabled

event AttesterDisabled(address indexed attester)
Emitted when an attester is disabled.

SignatureThresholdUpdated

event SignatureThresholdUpdated(
    uint256 oldSignatureThreshold,
    uint256 newSignatureThreshold
)
Emitted when the signature threshold is updated.

AttesterManagerUpdated

event AttesterManagerUpdated(
    address indexed previousAttesterManager,
    address indexed newAttesterManager
)
Emitted when the attester manager is updated.

Usage Example

// Enable a new attester (as attester manager)
attestable.enableAttester(0x123...);

// Update signature threshold to require 2 signatures
attestable.setSignatureThreshold(2);

// Check if an address is an enabled attester
bool isEnabled = attestable.isEnabledAttester(0x123...);

// Get total number of attesters
uint256 count = attestable.getNumEnabledAttesters();

Security Considerations

  • The signature threshold must always be less than or equal to the number of enabled attesters
  • Cannot disable attesters if it would reduce the count below the threshold
  • Must maintain at least one enabled attester at all times
  • Signatures must be provided in ascending order by signer address to prevent duplicates
  • Uses ECDSA signature recovery from OpenZeppelin for security

Build docs developers (and LLMs) love