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 (users without an active Civic Pass).
For each blockchain supported by Civic Pass, we provide you with a library that is tailor-made to the programming model of the chain.
Ethereum + EVMs
Integrating a Civic Pass check in your Solana on-chain program is very easy.
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 (e.g. the trader in a defi application). A Civic Pass must have been already issued to this wallet.
  • gateway_token : The address of the Civic Pass (token). This address can be accessed in the dApp through the useGateway hook on the Civic React Component once the user has passed Gatekeeper verification.
  • gatekeeper_network The gatekeeper network on which the Civic Pass has been issued. See here for test keys or contact us to discuss.
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_network

Error handling

If something goes wrong or the token is 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.
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 gatekeeperNetworkSlotId)
Gated(gatewayTokenContract, gatekeeperNetworkSlotId) {
function myFunction() external gated {
  • The gatewayTokenContract address is 0xF65b6396dF6B7e2D8a6270E3AB6c7BB08BAEF22E
  • You will receive the gatekeeperNetworkSlotId when you complete your onboarding with Civic (see here).


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