Use Case

Farcaster is a social media platform that allows users to post messages to a decentralized network. Although it’s not a DeFi protocol with a lot of funds at stake, it’s still important to make sure that messages are valid and usernames are unique.

It is a basic requirement for a social media platform to make sure that usernames are unique, but if there’s a way to circumvent this invariant, it could break the platform.

Explanation

Check that a username is unique and that the owner of the username is the same as the one who registered it.

Code Example

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {Assertion} from "../../lib/credible-std/src/Assertion.sol";
import {PhEvm} from "../../lib/credible-std/src/PhEvm.sol";

interface IFarcaster {
    function register(string calldata username, address owner) external;
    function isRegistered(string calldata username) external view returns (bool);
    function getUsernameOwner(string calldata username) external view returns (address);
}

contract FarcasterUsernameAssertion is Assertion {
    IFarcaster public farcaster = IFarcaster(address(0xbeef));

    function fnSelectors() external pure override returns (bytes4[] memory assertions) {
        assertions = new bytes4[](1);
        assertions[0] = this.assertUniqueUsername.selector;
    }

    function assertUniqueUsername() external {
        PhEvm.CallInputs[] memory callInputs = ph.getCallInputs(address(farcaster), farcaster.register.selector);
        if (callInputs.length == 0) {
            return;
        }

        for (uint256 i = 0; i < callInputs.length; i++) {
            bytes memory data = callInputs[i].input;

            // Decode registration parameters
            (string memory username, address owner) = abi.decode(stripSelector(data), (string, address));

            // Check pre-registration state
            ph.forkPreState();
            require(!farcaster.isRegistered(username), "Username already registered");

            // Check post-registration state
            ph.forkPostState();
            require(farcaster.isRegistered(username), "Registration failed");

            require(farcaster.getUsernameOwner(username) == owner, "Owner mismatch");
        }
    }

    function stripSelector(bytes memory input) internal pure returns (bytes memory) {
        bytes memory paramData = new bytes(32);
        for (uint256 i = 4; i < input.length; i++) {
            paramData[i - 4] = input[i];
        }
        return paramData;
    }
}