Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ Traits are specific attributes of provider accounts you can verify. Examples:

**Coinbase:**
- `coinbase_one_active:eq:true` - Active Coinbase One subscription
- `coinbase_one_billed:eq:true` - User has been billed for Coinbase One
- `coinbase_one_billed:eq:true` - User has an active Coinbase One subscription

**Instagram:**
- `followers_count:gte:5000` - 5000+ followers
Expand Down Expand Up @@ -307,12 +307,12 @@ Users can delete their own airdrop claim:

For more detailed information, see the `/docs` folder:

- **[Getting Started](/docs/index.md)** - Quick overview and contact information
- **[Core Concepts](/docs/core-concepts.md)** - Understanding providers, traits, tokens, and Sybil resistance
- **[Integration Guide](/docs/integration.md)** - Complete implementation walkthrough with code examples
- **[Trait Catalog](/docs/traits.md)** - All available traits for X, Coinbase, Instagram, and TikTok
- **[API Reference](/docs/api.md)** - Complete API endpoint documentation
- **[Security & Privacy](/docs/security.md)** - Security best practices and data handling
- **[Getting Started](docs/index.md)** - Quick overview and contact information
- **[Core Concepts](docs/core-concepts.md)** - Understanding providers, traits, tokens, and Sybil resistance
- **[Integration Guide](docs/integration.md)** - Complete implementation walkthrough with code examples
- **[Trait Catalog](docs/traits.md)** - All available traits for X, Coinbase, Instagram, and TikTok
- **[API Reference](docs/api.md)** - Complete API endpoint documentation
- **[Security & Privacy](docs/security.md)** - Security best practices and data handling

---

Expand Down
2 changes: 1 addition & 1 deletion docs/core-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ The action is returned in the API response:
**Use Cases:**
- **Multiple campaigns**: Run separate airdrops without interference
- **Feature gating**: Different tokens for different premium features
- **Time-based events**: New action per event (e.g., `raffle_jan_2025`, `raffle_feb_2025`)
- **Time-based events**: New action per event (e.g., `raffle_jan_2026`, `raffle_feb_2026`)

### Choosing Action Names

Expand Down
4 changes: 2 additions & 2 deletions docs/integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Even if a wallet has few transactions, Base Verify reveals if the user is high-v

**Example Use Cases:**
- Token-gated airdrops or daily rewards
- Exclusive content access (e.g. creator coins)
- Exclusive content access (e.g., creator coins)
- Identity-based rewards and loyalty programs

---
Expand Down Expand Up @@ -256,7 +256,7 @@ export default async function handler(req, res) {
});
}

// Now safe to verify signature with Base Verify API
// Now safe to check signature with Base Verify API
const response = await fetch('https://verify.base.dev/v1/base_verify_token', {
method: 'POST',
headers: {
Expand Down
2 changes: 1 addition & 1 deletion docs/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ The user signs this message, proving they control the wallet and agree to check
Users can delete their verifications at any time:
- Removes all stored provider data
- Invalidates future token generation
- Your app's stored tokens become meaningless (user can't re-verify with same account)
- Your app's stored tokens become completely invalid (user can't re-verify with same account)

---

Expand Down
2 changes: 1 addition & 1 deletion env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ NEXT_PUBLIC_ONCHAINKIT_API_KEY="your_onchainkit_api_key_here"
NEXT_PUBLIC_BASE_VERIFY_API_URL="https://verify.base.dev/v1"
NEXT_PUBLIC_BASE_VERIFY_WEBAPP_URL="https://verify.base.dev"
BASE_VERIFY_SECRET_KEY="sk_live_your_secret_key_here_64_character_hex_string"
NEXT_PUBLIC_BASE_VERIFY_PUBLISHER_KEY="pk_live_your_publisher_key_here_64_character_hex_string"
NEXT_PUBLIC_BASE_VERIFY_PUBLIC_KEY="pk_live_your_publisher_key_here_64_character_hex_string"

# Your application's URL
NEXT_PUBLIC_APP_URL="https://baseverifydemo.com"
Expand Down
2 changes: 1 addition & 1 deletion lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const config = {
baseVerifySecretKey: process.env.BASE_VERIFY_SECRET_KEY,

// Publisher key for client-side API calls (public, requires origin validation)
baseVerifyPublisherKey: process.env.NEXT_PUBLIC_BASE_VERIFY_PUBLISHER_KEY,
baseVerifyPublicKey: process.env.NEXT_PUBLIC_BASE_VERIFY_PUBLIC_KEY,

// Current app URL for redirects
appUrl: process.env.NEXT_PUBLIC_APP_URL || 'https://baseverifydemo.com',
Expand Down
2 changes: 1 addition & 1 deletion lib/signature-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function buildSIWEMessage(options: SIWEOptions): { message: string; nonce: strin
domain = new URL(config.appUrl).hostname,
address,
uri = config.appUrl,
chainId = 8453, // Base chain
chainId = config.claimChainId || 84532,
action,
provider,
traits = {},
Expand Down
9 changes: 5 additions & 4 deletions lib/trait-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,21 @@ function parseTraitsFromMessage(message: string): { provider: string; traits: Tr
.map(line => line.replace(/^- /, '').trim())
.filter(line => line.length > 0);

// Find provider URN
const providerUrn = resources.find(r => r.match(/^urn:verify:provider:[^:]+$/));
if (!providerUrn) {
// Find the first trait URN to extract the provider
const traitUrn = resources.find(r => r.startsWith('urn:verify:provider:'));
if (!traitUrn) {
return null;
}

const providerMatch = providerUrn.match(/^urn:verify:provider:([^:]+)$/);
const providerMatch = traitUrn.match(/^urn:verify:provider:([^:]+):/);
if (!providerMatch) {
return null;
}

const provider = providerMatch[1];
const traits: TraitRequirement = {};


// Parse trait URNs for this provider
const traitPattern = new RegExp(`^urn:verify:provider:${provider}:([^:]+):([^:]+):(.+)$`);

Expand Down
2 changes: 1 addition & 1 deletion pages/api/verify-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
try {
// Extract user data from verification response
const addressMatch = message.match(/0x[a-fA-F0-9]{40}/);
const walletAddress = addressMatch ? addressMatch[0] : '';
const walletAddress = addressMatch ? addressMatch[0].toLowerCase() : '';

console.log('Extracted wallet address:', walletAddress);

Expand Down
6 changes: 3 additions & 3 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function Home({ initialUsers, error }: Props) {
if (success === 'true' && address && isConnected && !isVerifying && !verificationResult) {
console.log('Auto-verifying after successful redirect...');
setIsAutoVerification(true);
handleVerify(true);
handleVerify(true, router.query.code as string, router.query.state as string);

// Clean up the URL to remove the success parameter
const { success: _, code: __, state: ___, ...cleanQuery } = router.query;
Expand Down Expand Up @@ -219,7 +219,7 @@ export default function Home({ initialUsers, error }: Props) {
setShowVerifyModal(true);
}

const handleVerify = async (isAutoVerifyFromSuccess = false) => {
const handleVerify = async (isAutoVerifyFromSuccess = false, passedCode?: string, passedState?: string) => {
if (!address || !signMessage) {
setVerificationError('Please connect your wallet to claim')
return
Expand Down Expand Up @@ -262,7 +262,7 @@ export default function Home({ initialUsers, error }: Props) {
verifySignatureCache.set(signature);
}

const { code, state } = router.query;
const code = passedCode || router.query.code; const state = passedState || router.query.state;
const storedCodeVerifier = sessionStorage.getItem('pkce_code_verifier');
const storedState = sessionStorage.getItem('pkce_state');

Expand Down
5 changes: 2 additions & 3 deletions pages/onchain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ type OnchainToken = {
signature: string
}

const ACTION = 'my_app_airdrop_2026'
const ACTION = 'claim_demo_x_airdrop'

// Read-only client for the dedup pre-check.
const publicClient = createPublicClient({ chain: baseSepolia, transport: http() })
Expand Down Expand Up @@ -96,8 +96,7 @@ export default function OnchainPage() {
if (cachedSignature) {
if (cachedSignature.address.toLowerCase() !== address.toLowerCase()) {
verifySignatureCache.clear()
} else if (!cachedSignature.message.includes('urn:verify:provider:coinbase') ||
!cachedSignature.message.includes(`urn:verify:action:${ACTION}`)) {
} else if (!cachedSignature.message.includes(`urn:verify:action:${ACTION}`)) {
verifySignatureCache.clear()
}
}
Expand Down