Loading...
Loading...
Verify Seeker device ownership by checking for the Seeker Genesis Token (SGT) with Sign-in-with-Solana and server-side token verification. Use when gating content or rewards to Seeker owners, verifying a user holds an SGT, adding anti-Sybil checks to a Solana mobile app, or implementing one-claim-per-device logic.
npx skill4agent add solana-mobile/solana-mobile-dev-skill seeker-genesis-tokenhasSGTEXPO_PUBLIC_*solana-mobile-walletuseMobileWallet()// POST /api/siws/nonce
const nonce = crypto.randomBytes(16).toString('hex')
await store.put(nonce, { issuedAt: Date.now(), used: false }, { ttlSeconds: 300 })
return { nonce }signInimport { useMobileWallet } from '@wallet-ui/react-native-kit'
const { signIn } = useMobileWallet()
const output = await signIn({
address: account.address.toString(),
chainId: 'solana:mainnet',
domain: 'yourdapp.com',
issuedAt: new Date().toISOString(),
nonce, // from step 1
statement: 'Sign in to verify Seeker ownership',
uri: 'https://yourdapp.com',
version: '1',
})chainIdsolana:mainnetchainsignInnoncedomainversionsolana-mobile-walletoutputnpm install @solana/wallet-standard-utilimport { verifySignIn } from '@solana/wallet-standard-util'
function verifySiws(payload, result) {
return verifySignIn(payload, {
account: { ...result.account, publicKey: new Uint8Array(result.account.publicKey) },
signature: new Uint8Array(result.signature),
signedMessage: new Uint8Array(result.signedMessage),
})
}async function verifySeekerUser({ payload, result }) {
// 1. The nonce must be one we issued, unused, and unexpired.
const record = await store.get(payload.nonce)
if (!record || record.used) throw new Error('Invalid or reused nonce.')
await store.markUsed(payload.nonce)
// 2. The signature must be valid for that payload.
if (!verifySiws(payload, result)) throw new Error('Invalid signature.')
// 3. The domain must be ours, or a signature farmed by another site would pass.
if (payload.domain !== 'yourdapp.com') throw new Error('Wrong domain.')
// 4. Check the SGT against the *signed* address only.
const { hasSGT, mintAddress } = await checkWalletForSGT(payload.address)
return { address: payload.address, hasSGT, mintAddress }
}const { hasSGT, mintAddress } = await verifySeekerUser({ payload, result })
if (!hasSGT) throw new Error('No Seeker Genesis Token found.')
if (await claims.exists(mintAddress)) throw new Error('This device has already claimed.')
await claims.insert({ claimedAt: new Date(), mintAddress })checkWalletForSGTsolana-mobile-walletsignInseeker-domains.skr