Civic Docs

On-Chain Integration

Your on-chain smart contract has to check for a valid Civic Pass during program execution and reject transactions from non-compliant users (i.e. those without an active Civic Pass).
For each blockchain supported by Civic Pass, we provide you with a library that is tailored to the programming model of that chain.
Ethereum + EVMs
How to: Gate a Solana program with Civic Pass
Import the solana_gateway Rust crate from and call Gateway::verify_gateway_token_account_info
For your program to be able to call the integration library, the following parameters must be passed as inputs to your dApp's transaction:
  • userWallet : The wallet account for the dApp user. A Civic Pass must have already been issued to this wallet.
  • gateway_token : The address of the Civic Pass. This address can be accessed in the dApp through the useGateway hook on the Civic React Component once the user has passed verification.
  • gatekeeper_network : The Gatekeeper Network on which the Civic Pass has been issued.
See Gatekeeper Networks for the list of available networks.
use solana_gateway::Gateway;
// This check happens before the dApp transaction is processed
fn process() -> ProgramResult {
// The owner of the gateway token
let user_wallet: AccountInfo;
// The gateway token presented by the owner
let gateway_token: AccountInfo;
// The gatekeeper network key
let gatekeeper_network: Pubkey;
// Check the token is valid. An error here means the token
// is not valid for the user's wallet on the gateway network.
&gateway_token_account_info, &userWallet.key, &gatekeeper

Error handling

If something goes wrong or the token it invalid, the Gateway call will return a GatewayError. The possible values can be seen in For error cases, the dApp smart contract should reject the transaction.
How to: Gate a smart contract with Civic Pass
Integrating a Civic Pass check in your EVM smart contract is simple:
First, import the contract dependencies:
npm install
This tutorial assumes Hardhat. If you are using foundry, please see tips here (external link). The steps below can also be followed by copying the contract code directly here. Contact us on our Discord if you have difficulty.
Then, in your smart contract, inherit the Gated contract, and add the 'gated' modifier to any function. The function can only be called by a msg.sender that has a valid gateway token.
import "";
// Your contract
contract MyContract is Gated {
constructor(address gatewayTokenContract, uint256 gatekeeperNetwork)
Gated(gatewayTokenContract, gatekeeperNetwork) {
function myFunction() external gated {
The gateway token contract address is 0xF65b6396dF6B7e2D8a6270E3AB6c7BB08BAEF22E
You can find the correct gatekeeperNetwork in the list of available Gatekeeper Networks.


If you want more control over the verification process on-chain, you can use the following code instead of the Gated contract:
import "";
IGatewayTokenVerifier verifier = IGatewayTokenVerifier(gatewayTokenContract);
if (!verifier.verifyToken(addressToVerify, gatekeeperNetwork)) {
// some logic