Skip to main content

Polygon ID State Replication: Smart contracts reference

LightweightStateV2

It's a modified state contract meant for the destination chains. It checks a TSS from the Rarimo validators instead of a ZKP to transit the state and only holds a partial history. The relayer service lazily updates the contract whenever a user wants to submit ZKP on a particular chain.

Deployments

ChainChainIDAddress
Sepolia111551110x4EaEdFbD2156f5D77b11c4fE073032c4D90Dd315 (Etherscan)

Interface

contract LightweightStateV2 is ILightweightStateV2, UUPSSignableUpgradeable, Signers {
address public override sourceStateContract;

uint256 internal _currentGistRoot;

mapping(uint256 => GistRootData) internal _gistsRootData;

mapping(uint256 => IdentityInfo) internal _identitiesInfo;

function signedTransitGISTData(
uint256 prevGist_,
GistRootData calldata gistData_,
bytes calldata proof_
) external override {
// ...

emit SignGISTDataTransited(gistData_.root, prevGist_);
}

function signedTransitStateData(
uint256 prevState_,
StateData calldata stateData_,
bytes calldata proof_
) external override {
// ...

emit SignStateDataTransited(stateData_.id, stateData_.state, prevState_);
}
}

  • sourceStateContract — address of the state contract on the source chain (Rarimo);
  • _currentGistRoot — the latest transferred GIST root;
  • _gistsRootData — mapping of the GIST roots to their data. Data stores two roots, one that replaced the other, timestamp and block number;
  • _identitiesInfo — mapping of the identity IDs to the information about this identity. Information has the latest identity state and the history of the states with timestamps;
  • signedTransitGISTData() — adds the GIST root to the history and changes the current root variable if the proposed one is newer;
  • signedTransitStateData(...) — adds the state to the history and sets the last state of the identity to the provided one if it's newer;

Both functions require proof of change — TSS of Rarimo signature providers. Also, core functions emit events, which contain the data about changes:

  • SignGISTDataTransited contains the data about roots, the previous one and the provided one;
  • SignStateDataTransited contains the provided identity state, previous state, and identity ID;

See LightweightStateV2.sol for the full implementation.

QueryVerifier

It's an implementation of a verifier contract that inherits Iden3 ZKPVerifier that mints an SBT. It is configured to work with the KYCAge demo credential.

Deployments

ChainChainIDAddress
Sepolia111551110xb95734f64f242E976E155594E1E15D39700Cbb26 (Etherscan)

Interface

contract QueryVerifier is IQueryVerifier, ZKPVerifier {
uint256 public constant AGE_VERIFY_REQUEST_ID = 1;

IVerifiedSBT public override sbtContract;

mapping(address => uint256) public override addressToUserId;

mapping(uint256 => VerificationInfo) internal _verificationsInfo;

function _beforeProofSubmit(
uint64,
uint256[] memory inputs_,
ICircuitValidator
) internal override {
require(
!isUserVerified(_getIdentityId(inputs_)),
"QueryVerifier: identity with this identifier has already been verified"
);
require(
addressToUserId[msg.sender] == 0,
"QueryVerifier: current address has already been used to verify another identity"
);
}

function _afterProofSubmit(
uint64,
uint256[] memory inputs_,
ICircuitValidator
) internal override {
uint256 tokenId_ = sbtContract.nextTokenId();
uint256 userId_ = _getIdentityId(inputs_);

_verificationsInfo[userId_] = VerificationInfo(msg.sender, tokenId_);
addressToUserId[msg.sender] = userId_;

sbtContract.mint(msg.sender);

emit Verified(userId_, msg.sender, tokenId_);
}
}
  • AGE_VERIFY_REQUEST_ID — hard-coded KYC demo request ID;
  • sbtContract — address of the SBT contract, which will be called to mint Soulbound token;
  • addressToUserId — mapping of addresses to user's identity ID;
  • _verificationsInfo — mapping of user's identity ID to the verification information, which contains the prover address and minted token ID;
  • _beforeProofSubmit — hook that performs some necessary security checks;
  • _afterProofSubmit — another hook, which mints the SBT in case of valid verification;

The function that verifies the ZKP - verify(...) is inherited from the ZKPVerifier contract.

See QueryVerifier.sol for the full implementation.

VerifiedSBT

The SBT is awarded to users who complete the Polygon ID x Rarimo demo.

Deployments

ChainChainIDAddress
Sepolia111551110x8A55c9CfDCbCeDcA1A543295a3F54351C335A20f (Etherscan)

Interface

contract VerifiedSBT is
IVerifiedSBT,
UUPSUpgradeable,
ERC721EnumerableUpgradeable,
OwnableUpgradeable
{
address public override verifier;
uint256 public override nextTokenId;
string public override tokensURI;

// ...

function mint(address recipientAddr_) external override onlyVerifier {}

function _beforeTokenTransfer(
address from_,
address to_,
uint256 firstTokenId_,
uint256 batchSize_
) internal override {
// ...
}
}
  • verifier — address of the verifier's (QueryVerifier) contract;
  • nextTokenId — nonce that is incremented each time a new SBT is created;
  • tokensURI — token's picture URI;
  • onlyVerifier() — modifier that allows calls only by the verifier contract;
  • mint(address) — mints the SBT for the given address;
  • _beforeTokenTransfer(...) — prevents the token from being transferred (only burn and mint are available);

See VerifiedSBT.sol for the full implementation.