Cross-Chain Credentials: Multi-Chain Verification
GhostSpeak credentials issued on Solana can be verified on Ethereum, Base, and Polygon via Crossmint’s cross-chain infrastructure. This guide shows how to enable cross-chain sync and verify credentials across multiple blockchains.
Why Cross-Chain? Buyers and platforms on Ethereum/Base/Polygon can verify your Solana-issued credentials without bridging tokens or switching wallets.
How Cross-Chain Works
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ SOLANA (Source of Truth) │
│ • Credential issued on-chain │
│ • Signed by GhostSpeak DID │
│ • Stored in compressed NFT │
│ • Cost: ~0.0002 SOL (~$0.03) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ CROSSMINT SYNC SERVICE │
│ • Indexes Solana credential │
│ • Creates cross-chain verification endpoint │
│ • No token bridging required │
│ • Cost: Free (included in credential issuance) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌──────────────┬──────────────┬──────────────┬──────────────┐
│ ETHEREUM │ BASE │ POLYGON │ ARBITRUM │
│ Verify via │ Verify via │ Verify via │ Verify via │
│ API call │ API call │ API call │ API call │
└──────────────┴──────────────┴──────────────┴──────────────┘
Key Points:
✅ No bridging : Credentials stay on Solana
✅ Universal verification : Any EVM chain can verify
✅ Cost-effective : Issue once on Solana, verify everywhere
✅ W3C compliant : Same credential format across all chains
Enabling Cross-Chain Sync
Step 1: Get Crossmint API Key
Create Project
Navigate to Dashboard → Create New Project → Select “Verifiable Credentials”
Get API Key
Copy your API key from Settings → API Keys → Server-Side Key
Configure Environment
CROSSMINT_API_KEY = sk_live_...
CROSSMINT_ENVIRONMENT = production # or 'staging'
Devnet Testing : Use Crossmint’s staging environment for Solana devnet:CROSSMINT_API_KEY = sk_staging_...
CROSSMINT_ENVIRONMENT = staging
Step 2: Issue Cross-Chain Credential
import { GhostSpeakClient } from '@ghostspeak/sdk'
import { createKeyPairSignerFromBytes } from '@solana/signers'
import fs from 'fs'
async function issueCrossChainCredential () {
console . log ( '=== Cross-Chain Credential Issuance === \n ' )
// Load wallet and agent
const walletPath = process . env . WALLET_PATH ?. replace ( '~' , process . env . HOME || '' )
const keypairBytes = JSON . parse ( fs . readFileSync ( walletPath , 'utf-8' ))
const wallet = await createKeyPairSignerFromBytes ( new Uint8Array ( keypairBytes ))
const agentKeypairBytes = JSON . parse (
fs . readFileSync ( './agent-keypair.json' , 'utf-8' )
)
const agentSigner = await createKeyPairSignerFromBytes (
new Uint8Array ( agentKeypairBytes )
)
const client = new GhostSpeakClient ({
cluster: 'devnet' ,
commitment: 'confirmed' ,
})
console . log ( 'Issuing credential with cross-chain sync enabled... \n ' )
// Issue credential with Crossmint sync
const result = await client . credentials . issueAgentIdentityCredential ({
agentId: agentSigner . address ,
owner: wallet . address ,
name: 'Code Review AI' ,
capabilities: [ 'code-review' , 'security-audit' ],
model: 'gpt-4-turbo' ,
serviceEndpoint: 'https://myagent.example.com' ,
frameworkOrigin: 'langchain' ,
x402Enabled: true ,
registeredAt: Math . floor ( Date . now () / 1000 ),
verifiedAt: Math . floor ( Date . now () / 1000 ),
// Enable cross-chain sync
syncToCrossmint: true ,
crossmintConfig: {
apiKey: process . env . CROSSMINT_API_KEY ! ,
environment: ( process . env . CROSSMINT_ENVIRONMENT as 'production' | 'staging' ) || 'staging' ,
},
// Optional: specify target chains
targetChains: [ 'ethereum' , 'base' , 'polygon' ], // default: all supported chains
})
console . log ( '✅ Credential issued successfully! \n ' )
console . log ( '📜 Solana Credential:' )
console . log ( ' ID:' , result . solanaCredential . credentialId )
console . log ( ' Issuer:' , result . solanaCredential . issuer )
console . log ( ' Subject:' , result . solanaCredential . subject )
if ( result . crossmintSync ) {
console . log ( ' \n 🌉 Cross-Chain Sync:' )
console . log ( ' Crossmint Credential ID:' , result . crossmintSync . credentialId )
console . log ( ' Verification URL:' , result . crossmintSync . url )
console . log ( ' Available on:' , result . crossmintSync . chains . join ( ', ' ))
console . log ( ' \n Chain-Specific Endpoints:' )
result . crossmintSync . chains . forEach (( chain : string ) => {
console . log ( ` ${ chain } : ${ result . crossmintSync . endpoints [ chain ] } ` )
})
// Save credential data
fs . writeFileSync (
'./cross-chain-credential.json' ,
JSON . stringify ({
solana: result . solanaCredential ,
crossmint: result . crossmintSync ,
}, null , 2 )
)
console . log ( ' \n 📁 Credential data saved to: ./cross-chain-credential.json' )
}
return result
}
issueCrossChainCredential (). catch ( console . error )
Expected Output:
=== Cross-Chain Credential Issuance ===
Issuing credential with cross-chain sync enabled...
✅ Credential issued successfully!
📜 Solana Credential:
ID: cred_4Hc7mK2pXvF9...
Issuer: did:sol:devnet:GpvFxus2eecFKcqa2bhxXeRjpstPeCEJNX216TQCcNC9
Subject: did:sol:devnet:4Hc7mK2pXvF9...
🌉 Cross-Chain Sync:
Crossmint Credential ID: cm_cred_xyz123
Verification URL: https://verify.crossmint.com/credentials/cm_cred_xyz123
Available on: ethereum, base, polygon
Chain-Specific Endpoints:
ethereum: https://verify.crossmint.com/ethereum/cm_cred_xyz123
base: https://verify.crossmint.com/base/cm_cred_xyz123
polygon: https://verify.crossmint.com/polygon/cm_cred_xyz123
📁 Credential data saved to: ./cross-chain-credential.json
Verifying Cross-Chain Credentials
Ethereum Verification (ethers.js)
import { ethers } from 'ethers'
async function verifyOnEthereum () {
// Load credential data
const credentialData = JSON . parse (
fs . readFileSync ( './cross-chain-credential.json' , 'utf-8' )
)
const crossmintCredentialId = credentialData . crossmint . credentialId
console . log ( '=== Ethereum Verification === \n ' )
console . log ( 'Verifying credential:' , crossmintCredentialId )
// Call Crossmint verification API
const response = await fetch (
`https://verify.crossmint.com/api/v1/credentials/ ${ crossmintCredentialId } /verify` ,
{
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'X-Chain' : 'ethereum' , // Specify target chain
},
body: JSON . stringify ({
credentialId: crossmintCredentialId ,
}),
}
)
const verification = await response . json ()
console . log ( ' \n ✅ Verification Result: \n ' )
console . log ( 'Valid:' , verification . valid )
console . log ( 'Issuer:' , verification . issuer )
console . log ( 'Subject:' , verification . subject )
console . log ( 'Issuance Date:' , new Date ( verification . issuanceDate ). toISOString ())
if ( verification . valid ) {
console . log ( ' \n 📋 Credential Claims:' )
console . log ( ' Name:' , verification . credentialSubject . name )
console . log ( ' Capabilities:' , verification . credentialSubject . capabilities . join ( ', ' ))
console . log ( ' Model:' , verification . credentialSubject . model )
console . log ( ' Ghost Score:' , verification . credentialSubject . ghostScore )
console . log ( ' Tier:' , verification . credentialSubject . tier )
}
return verification
}
verifyOnEthereum (). catch ( console . error )
Base Verification (wagmi)
import { useAccount , useContractRead } from 'wagmi'
import { base } from 'wagmi/chains'
function VerifyCredentialOnBase ({ credentialId } : { credentialId : string }) {
const { address } = useAccount ()
// Call Crossmint verification endpoint
const { data , isLoading , error } = useContractRead ({
address: '0x...' , // Crossmint verification contract on Base
abi: CROSSMINT_VERIFIER_ABI ,
functionName: 'verifyCredential' ,
args: [ credentialId ],
chainId: base . id ,
})
if ( isLoading ) return < div > Verifying credential ...</ div >
if ( error ) return < div > Verification failed : {error. message } </ div >
return (
< div >
< h3 > Credential Verification ( Base ) </ h3 >
< p > Valid : { data . valid ? '✅' : '❌' }</ p >
< p > Issuer : { data . issuer }</ p >
< p > Subject : { data . subject }</ p >
< p > Ghost Score : { data . credentialSubject . ghostScore }</ p >
</ div >
)
}
Polygon Verification (REST API)
async function verifyOnPolygon ( credentialId : string ) {
console . log ( '=== Polygon Verification === \n ' )
// Use Crossmint REST API
const response = await fetch (
`https://verify.crossmint.com/polygon/api/v1/credentials/ ${ credentialId } ` ,
{
headers: {
'X-API-Key' : process . env . CROSSMINT_API_KEY ! ,
},
}
)
const credential = await response . json ()
console . log ( 'Credential Details:' )
console . log ( ' Valid:' , credential . valid )
console . log ( ' Revoked:' , credential . revoked )
console . log ( ' Issuer DID:' , credential . issuer )
console . log ( ' Subject DID:' , credential . subject )
// Check revocation status on Polygon
const revocationStatus = await fetch (
`https://verify.crossmint.com/polygon/api/v1/revocation/ ${ credentialId } ` ,
{
headers: {
'X-API-Key' : process . env . CROSSMINT_API_KEY ! ,
},
}
)
const revocation = await revocationStatus . json ()
console . log ( ' \n Revocation Status:' )
console . log ( ' Revoked:' , revocation . revoked )
if ( revocation . revoked ) {
console . log ( ' Reason:' , revocation . reason )
console . log ( ' Revoked At:' , new Date ( revocation . revokedAt ). toISOString ())
}
return { credential , revocation }
}
Smart Contract Integration (EVM)
Solidity Contract Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20 ;
import "@crossmint/contracts/interfaces/ICredentialVerifier.sol" ;
contract GhostScoreVerifier {
ICredentialVerifier public verifier;
constructor ( address _verifierAddress ) {
verifier = ICredentialVerifier (_verifierAddress);
}
// Verify agent has minimum Ghost Score
function requireMinimumScore (
string memory credentialId ,
uint256 minimumScore
) public view returns ( bool ) {
// Verify credential is valid
( bool valid, , , bytes memory credentialData) = verifier. verifyCredential (credentialId);
require (valid, "Invalid credential" );
// Decode credential subject
CredentialSubject memory subject = abi . decode (credentialData, (CredentialSubject));
// Check Ghost Score
require (subject.ghostScore >= minimumScore, "Ghost Score too low" );
return true ;
}
// Verify agent has specific capability
function requireCapability (
string memory credentialId ,
string memory capability
) public view returns ( bool ) {
( bool valid, , , bytes memory credentialData) = verifier. verifyCredential (credentialId);
require (valid, "Invalid credential" );
CredentialSubject memory subject = abi . decode (credentialData, (CredentialSubject));
// Check capabilities array
for ( uint i = 0 ; i < subject.capabilities.length; i ++ ) {
if ( keccak256 ( bytes (subject.capabilities[i])) == keccak256 ( bytes (capability))) {
return true ;
}
}
revert ( "Capability not found" );
}
struct CredentialSubject {
string name;
string [] capabilities;
string model;
uint256 ghostScore;
string tier;
}
}
Using the Contract
import { ethers } from 'ethers'
async function verifyAgentOnChain () {
const provider = new ethers . JsonRpcProvider ( 'https://mainnet.base.org' )
const contract = new ethers . Contract (
'0x...' , // GhostScoreVerifier contract address
GHOST_SCORE_VERIFIER_ABI ,
provider
)
const credentialId = 'cm_cred_xyz123'
const minimumScore = 750 // Gold tier
try {
const result = await contract . requireMinimumScore ( credentialId , minimumScore )
console . log ( '✅ Agent verified on-chain!' )
console . log ( ' Credential ID:' , credentialId )
console . log ( ' Minimum Score Required:' , minimumScore )
console . log ( ' Result:' , result )
} catch ( error ) {
console . error ( '❌ Verification failed:' , error . message )
}
}
Cross-Chain Use Cases
Use Case 1: Base Marketplace with Solana Agents
Scenario: Marketplace on Base wants to verify Solana-based AI agents.
// Marketplace contract on Base
async function listAgentOnBase ( agentAddress : string , credentialId : string ) {
// 1. Verify credential via Crossmint
const verification = await fetch (
`https://verify.crossmint.com/base/api/v1/credentials/ ${ credentialId } ` ,
{
headers: { 'X-API-Key' : process . env . CROSSMINT_API_KEY ! },
}
)
const credential = await verification . json ()
if ( ! credential . valid ) {
throw new Error ( 'Invalid credential' )
}
// 2. Check Ghost Score tier
if ( credential . credentialSubject . tier !== 'Gold' && credential . credentialSubject . tier !== 'Platinum' ) {
throw new Error ( 'Agent must be Gold or Platinum tier' )
}
// 3. List agent on Base marketplace
const marketplace = new ethers . Contract (
BASE_MARKETPLACE_ADDRESS ,
MARKETPLACE_ABI ,
signer
)
const tx = await marketplace . listAgent ({
agentAddress ,
credentialId ,
ghostScore: credential . credentialSubject . ghostScore ,
tier: credential . credentialSubject . tier ,
capabilities: credential . credentialSubject . capabilities ,
})
console . log ( 'Agent listed on Base marketplace:' , tx . hash )
}
Use Case 2: Ethereum Gated Access
Scenario: Premium Ethereum dApp requires Gold tier agents.
contract PremiumService {
GhostScoreVerifier public verifier;
modifier onlyGoldTier ( string memory credentialId ) {
require (verifier. requireMinimumScore (credentialId, 750 ), "Gold tier required" );
_ ;
}
function accessPremiumFeature ( string memory credentialId )
public
onlyGoldTier ( credentialId )
{
// Premium feature logic
}
}
Use Case 3: Polygon Reputation Aggregator
Scenario: Aggregate Ghost Scores from multiple agents on Polygon.
async function aggregateGhostScores ( credentialIds : string []) {
const scores = await Promise . all (
credentialIds . map ( async ( id ) => {
const response = await fetch (
`https://verify.crossmint.com/polygon/api/v1/credentials/ ${ id } ` ,
{
headers: { 'X-API-Key' : process . env . CROSSMINT_API_KEY ! },
}
)
const credential = await response . json ()
return {
id ,
score: credential . credentialSubject . ghostScore ,
tier: credential . credentialSubject . tier ,
}
})
)
const totalScore = scores . reduce (( sum , s ) => sum + s . score , 0 )
const averageScore = totalScore / scores . length
console . log ( 'Aggregated Ghost Scores:' )
console . log ( ' Total agents:' , scores . length )
console . log ( ' Average score:' , averageScore )
console . log ( ' Gold+ agents:' , scores . filter ( s => s . score >= 750 ). length )
return { scores , averageScore }
}
Cost Comparison
Operation Solana Ethereum Base Polygon Credential Issuance ~$0.03 N/A N/A N/A Cross-Chain Sync Free Free Free Free Verification (API) Free Free Free Free On-Chain Verification ~$0.001 ~$5-20 ~$0.10 ~$0.05
Cost Optimization : Issue on Solana (cheapest), verify via API on other chains (free). Only use on-chain verification when you need trustless execution.
Troubleshooting
Error: “Crossmint API returned 401 Unauthorized”Solution: Check API key configuration:# Verify API key is set
echo $CROSSMINT_API_KEY
# Should start with sk_live_ (production) or sk_staging_ (devnet)
Ensure environment matches:
Solana devnet → Crossmint staging
Solana mainnet → Crossmint production
Credential not found on target chain
Error: “Credential ID not found on Ethereum”Solution: Wait for indexing (can take 30-60 seconds):await new Promise ( resolve => setTimeout ( resolve , 60000 ))
// Then retry verification
const verification = await verifyOnEthereum ( credentialId )
Verification fails despite valid credential
Error: “Signature verification failed”Solution: Check credential hasn’t been revoked:const revocationStatus = await client . credentials . checkRevocation ( credentialId )
if ( revocationStatus . revoked ) {
console . error ( 'Credential has been revoked:' , revocationStatus . reason )
}
Next Steps
Pro Tip : Enable cross-chain sync by default. It costs nothing extra and makes your credentials universally verifiable across all major blockchains.