npm i @ethsign/sign-protocol-evm -S

Example Usage: Actually Met IRL

Actually Met IRL is a smart contract that only creates an attestation on EVM Sign Protocol for two people meeting in real life if both parties consent to this fact. In this example, we will demonstrate how to:

  • Link your smart contract to an existing on-chain Sign Protocol deployment and schema

  • Create an attestation programmatically

You can find the complete Foundry repository here, a deployment of the example contract on Base Sepolia here, and the corresponding SignScan page here.

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

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ISP } from "@ethsign/sign-protocol-evm/src/interfaces/ISP.sol";
import { Attestation } from "@ethsign/sign-protocol-evm/src/models/Attestation.sol";
import { DataLocation } from "@ethsign/sign-protocol-evm/src/models/DataLocation.sol";

contract ActuallyMetIRL is Ownable {
    ISP public spInstance;
    uint64 public schemaId;
    mapping(address partyA => address partyB) public metIRLMapping;

    error ConfirmationAddressMismatch();

    event DidMeetIRL(address partyA, address partyB, uint64 attestationId);

    constructor() Ownable(_msgSender()) { }

    function setSPInstance(address instance) external onlyOwner {
        spInstance = ISP(instance);

    function setSchemaID(uint64 schemaId_) external onlyOwner {
        schemaId = schemaId_;

    function claimMetIRL(address partyB) external {
        metIRLMapping[_msgSender()] = partyB;

    function confirmMetIRL(address partyA, bytes memory data) external returns (uint64) {
        address partyB = _msgSender();
        if (metIRLMapping[partyA] == partyB) {
            // B has confirm A's claim of having met them IRL
            // We now make an attestation of having actually met IRL
            bytes[] memory recipients = new bytes[](2);
            recipients[0] = abi.encode(partyA);
            recipients[1] = abi.encode(partyB);
            Attestation memory a = Attestation({
                schemaId: schemaId,
                linkedAttestationId: 0,
                attestTimestamp: 0,
                revokeTimestamp: 0,
                attester: address(this),
                validUntil: 0,
                dataLocation: DataLocation.ONCHAIN,
                revoked: false,
                recipients: recipients,
                data: data // SignScan assumes this is from `abi.encode(...)`
            uint64 attestationId = spInstance.attest(a, "", "", "");
            emit DidMeetIRL(partyA, partyB, attestationId);
            return attestationId;
        } else {
            revert ConfirmationAddressMismatch();

NOTE: data is expecting a value from abi.encode(). If your relevant schema is storing data on-chain, you need to encode the actual data you are trying to store. If you are storing data off-chain, such as IPFS, you will need to upload the data, in JSON format, to the storage provider first. Next, you will encode the resulting CID string and pass in this value as data.

Last updated