Becoming an Issuer
# Becoming an Issuer
## Issuer Setup
An issuer needs:
1. A funded wallet account
2. ODIS quota for identifier obfuscation
3. Verification infrastructure
4. Optional: Data Encryption Key (DEK) for enhanced authentication
## Step 1: Set Up Issuer Account
```typescript
import { createWalletClient, http, parseEther } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { celoAlfajores } from "viem/chains";
// Issuer private key - KEEP SECURE
const ISSUER_PRIVATE_KEY = process.env.ISSUER_PRIVATE_KEY;
const account = privateKeyToAccount(ISSUER_PRIVATE_KEY);
// Create Viem client
const walletClient = createWalletClient({
account,
transport: http(),
chain: celoAlfajores
});
const issuerAddress = account.address;
console.log("Issuer Address:", issuerAddress);
```
## Step 2: Authentication Methods
Self Connect supports multiple authentication methods for ODIS:
**Wallet Key Authentication**
```typescript
import { OdisUtils } from "@celo/identity";
import { AuthSigner } from "@celo/identity/lib/odis/query";
const authSigner: AuthSigner = {
authenticationMethod: OdisUtils.Query.AuthenticationMethod.WALLET_KEY,
sign191: ({ message, account }) => walletClient.signMessage({
message,
account
})
};
```
**Encryption Key (DEK) Authentication**
```typescript
const authSigner: AuthSigner = {
authenticationMethod: OdisUtils.Query.AuthenticationMethod.ENCRYPTION_KEY,
rawKey: process.env.DEK_PRIVATE_KEY
};
```
## Step 3: Configure ODIS Service Context
```typescript
import { OdisContextName } from "@celo/identity/lib/odis/query";
const serviceContext = OdisUtils.Query.getServiceContext(
OdisContextName.ALFAJORES // or OdisContextName.MAINNET
);
console.log("ODIS Endpoint:", serviceContext.odisUrl);
console.log("ODIS Public Key:", serviceContext.odisPubKey);
```
## ODIS Quota Management
**Check Current Quota**
```typescript
const { remainingQuota } = await OdisUtils.Quota.getPnpQuotaStatus(
issuerAddress,
authSigner,
serviceContext
);
console.log("Remaining ODIS Quota:", remainingQuota);
```
**Purchase Quota**
```typescript
import { getContract } from "viem";
import { stableTokenABI, odisPaymentsABI } from "@celo/abis";
// Contract addresses (Alfajores testnet)
const STABLE_TOKEN_ADDRESS = "0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1"; // cUSD
const ODIS_PAYMENTS_ADDRESS = "0x645170cdB6B5c1bc80847bb728dBa56C50a20a49";
// Amount to pay (0.01 cUSD = 10 queries)
const ONE_CENT_CUSD = parseEther("0.01");
// Approve ODIS Payments to spend cUSD
const stableToken = getContract({
address: STABLE_TOKEN_ADDRESS,
abi: stableTokenABI,
client: walletClient
});
const approveHash = await stableToken.write.approve([
ODIS_PAYMENTS_ADDRESS,
ONE_CENT_CUSD
]);
await walletClient.waitForTransactionReceipt({ hash: approveHash });
// Pay for quota
const odisPayments = getContract({
address: ODIS_PAYMENTS_ADDRESS,
abi: odisPaymentsABI,
client: walletClient
});
const paymentHash = await odisPayments.write.payInCUSD([
issuerAddress,
ONE_CENT_CUSD
]);
await walletClient.waitForTransactionReceipt({ hash: paymentHash });
console.log("ODIS quota purchased successfully");
```
## Verification Responsibilities
As an issuer, you must verify user ownership of identifiers. Implementation depends on identifier type:
**Phone Number Verification**
```typescript
// Example using Twilio
import twilio from "twilio";
const client = twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
async function verifyPhoneNumber(phoneNumber: string): Promise<boolean> {
// Send verification code
await client.verify.v2
.services(TWILIO_VERIFY_SERVICE_SID)
.verifications.create({ to: phoneNumber, channel: "sms" });
// User enters code (from your UI)
const verificationCode = await getUserInput();
// Check verification
const verification = await client.verify.v2
.services(TWILIO_VERIFY_SERVICE_SID)
.verificationChecks.create({ to: phoneNumber, code: verificationCode });
return verification.status === "approved";
}
```
**Twitter Verification**
```typescript
// Example using Twitter OAuth
async function verifyTwitterHandle(handle: string, userAddress: string): Promise<boolean> {
// Implement OAuth flow
const oauth = await initiateTwitterOAuth(userAddress);
// User authenticates with Twitter
const twitterUser = await completeOAuthFlow(oauth);
// Verify handle matches
return twitterUser.username === handle;
}
```
**Email Verification**
```typescript
// Example verification flow
async function verifyEmail(email: string, userAddress: string): Promise<boolean> {
// Generate verification token
const token = generateSecureToken();
// Store token with expiry
await storeVerificationToken(email, userAddress, token);
// Send verification email
await sendEmail(email, {
subject: "Verify your email",
body: `Click here to verify: ${BASE_URL}/verify/${token}`
});
// User clicks link, returns true if token is valid
return await checkTokenVerified(token);
}
```
On this page
Issuer Setup
An issuer needs:
- A funded wallet account
- ODIS quota for identifier obfuscation
- Verification infrastructure
- Optional: Data Encryption Key (DEK) for enhanced authentication
Step 1: Set Up Issuer Account
import { createWalletClient, http, parseEther } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { celoAlfajores } from "viem/chains";
// Issuer private key - KEEP SECURE
const ISSUER_PRIVATE_KEY = process.env.ISSUER_PRIVATE_KEY;
const account = privateKeyToAccount(ISSUER_PRIVATE_KEY);
// Create Viem client
const walletClient = createWalletClient({
account,
transport: http(),
chain: celoAlfajores
});
const issuerAddress = account.address;
console.log("Issuer Address:", issuerAddress);
Step 2: Authentication Methods
Self Connect supports multiple authentication methods for ODIS:
Wallet Key Authentication
import { OdisUtils } from "@celo/identity";
import { AuthSigner } from "@celo/identity/lib/odis/query";
const authSigner: AuthSigner = {
authenticationMethod: OdisUtils.Query.AuthenticationMethod.WALLET_KEY,
sign191: ({ message, account }) => walletClient.signMessage({
message,
account
})
};
Encryption Key (DEK) Authentication
const authSigner: AuthSigner = {
authenticationMethod: OdisUtils.Query.AuthenticationMethod.ENCRYPTION_KEY,
rawKey: process.env.DEK_PRIVATE_KEY
};
Step 3: Configure ODIS Service Context
import { OdisContextName } from "@celo/identity/lib/odis/query";
const serviceContext = OdisUtils.Query.getServiceContext(
OdisContextName.ALFAJORES // or OdisContextName.MAINNET
);
console.log("ODIS Endpoint:", serviceContext.odisUrl);
console.log("ODIS Public Key:", serviceContext.odisPubKey);
ODIS Quota Management
Check Current Quota
const { remainingQuota } = await OdisUtils.Quota.getPnpQuotaStatus(
issuerAddress,
authSigner,
serviceContext
);
console.log("Remaining ODIS Quota:", remainingQuota);
Purchase Quota
import { getContract } from "viem";
import { stableTokenABI, odisPaymentsABI } from "@celo/abis";
// Contract addresses (Alfajores testnet)
const STABLE_TOKEN_ADDRESS = "0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1"; // cUSD
const ODIS_PAYMENTS_ADDRESS = "0x645170cdB6B5c1bc80847bb728dBa56C50a20a49";
// Amount to pay (0.01 cUSD = 10 queries)
const ONE_CENT_CUSD = parseEther("0.01");
// Approve ODIS Payments to spend cUSD
const stableToken = getContract({
address: STABLE_TOKEN_ADDRESS,
abi: stableTokenABI,
client: walletClient
});
const approveHash = await stableToken.write.approve([
ODIS_PAYMENTS_ADDRESS,
ONE_CENT_CUSD
]);
await walletClient.waitForTransactionReceipt({ hash: approveHash });
// Pay for quota
const odisPayments = getContract({
address: ODIS_PAYMENTS_ADDRESS,
abi: odisPaymentsABI,
client: walletClient
});
const paymentHash = await odisPayments.write.payInCUSD([
issuerAddress,
ONE_CENT_CUSD
]);
await walletClient.waitForTransactionReceipt({ hash: paymentHash });
console.log("ODIS quota purchased successfully");
Verification Responsibilities
As an issuer, you must verify user ownership of identifiers. Implementation depends on identifier type:
Phone Number Verification
// Example using Twilio
import twilio from "twilio";
const client = twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
async function verifyPhoneNumber(phoneNumber: string): Promise<boolean> {
// Send verification code
await client.verify.v2
.services(TWILIO_VERIFY_SERVICE_SID)
.verifications.create({ to: phoneNumber, channel: "sms" });
// User enters code (from your UI)
const verificationCode = await getUserInput();
// Check verification
const verification = await client.verify.v2
.services(TWILIO_VERIFY_SERVICE_SID)
.verificationChecks.create({ to: phoneNumber, code: verificationCode });
return verification.status === "approved";
}
Twitter Verification
// Example using Twitter OAuth
async function verifyTwitterHandle(handle: string, userAddress: string): Promise<boolean> {
// Implement OAuth flow
const oauth = await initiateTwitterOAuth(userAddress);
// User authenticates with Twitter
const twitterUser = await completeOAuthFlow(oauth);
// Verify handle matches
return twitterUser.username === handle;
}
Email Verification
// Example verification flow
async function verifyEmail(email: string, userAddress: string): Promise<boolean> {
// Generate verification token
const token = generateSecureToken();
// Store token with expiry
await storeVerificationToken(email, userAddress, token);
// Send verification email
await sendEmail(email, {
subject: "Verify your email",
body: `Click here to verify: ${BASE_URL}/verify/${token}`
});
// User clicks link, returns true if token is valid
return await checkTokenVerified(token);
} Was this page helpful?
Thanks for your feedback!