Standards

Chapter 7 / 15

FHE Basics

encrypted<T>, fhe_add, fhe_mul — computing on encrypted state.


Covenant's FHE type system lets you write contracts where state is never decrypted on-chain. The encrypted<T> type annotates a field or variable as ciphertext. Operations (fhe_add, fhe_mul) compile to STATICCALL instructions targeting chain precompile addresses — the actual FHE implementation lives in the chain's precompile layer, not in Covenant. Covenant is fully scheme-agnostic (TFHE, BGV, CKKS, …).

private_counter.cov
record PrivateCounter {
    count: encrypted;
    owner: address;

    // Increment without revealing the current count
    action increment() only(self.owner) {
        // fhe_add compiles to STATICCALL → precompile 0x0300
        self.count = fhe_add(self.count, encrypted(1u64));
    }

    action add(n: encrypted) only(self.owner) {
        self.count = fhe_add(self.count, n);
    }

    // Reveal the decrypted value — only owner can call
    view read_count() -> u64 only(self.owner) {
        return decrypt(self.count);
    }
}
encrypted_voting.cov — private ballot tally
record PrivateBallot {
    yes_votes: encrypted;
    no_votes:  encrypted;
    end_time:  time;
    owner:     address;
    voted:     map(address => bool);

    error AlreadyVoted();
    error VotingClosed();

    // Vote without revealing direction
    action vote(choice: encrypted)
        when(block.timestamp < self.end_time)
    {
        if self.voted[msg.sender] {
            revert_with AlreadyVoted();
        }
        self.voted[msg.sender] = true;

        // Homomorphic addition — tally stays encrypted
        self.yes_votes = fhe_add(self.yes_votes, choice);
        self.no_votes  = fhe_add(
            self.no_votes,
            fhe_sub(encrypted(1u64), choice)
        );
    }

    // Owner tallies after vote ends — decrypts locally
    view tally() -> (u64, u64)
        only(self.owner)
        when(block.timestamp >= self.end_time)
    {
        return (decrypt(self.yes_votes), decrypt(self.no_votes));
    }
}

Annotations

encrypted<T>is a first-class type. You cannot accidentally pass it to a function expecting T — the compiler rejects the type mismatch.
fhe_add / fhe_mulcompile to STATICCALL targeting addresses 0x03000x0308. The chain's precompile determines the underlying FHE scheme.
encrypted(v)wraps a plaintext value as a client-side ciphertext before sending to the contract.
decrypt(c)calls the decryption precompile. Only the keyholder can obtain the plaintext — on chains with threshold decryption, this requires a quorum.
Scheme agnosticismCovenant emits the same bytecode regardless of whether the chain uses TFHE, BGV, or CKKS. See LICENSE_CLARIFICATION.md.

Key takeaways