diff --git a/contracts/deploy-config-example.json b/contracts/deploy-config-example.json index 77032cfed..4fbcc8441 100644 --- a/contracts/deploy-config-example.json +++ b/contracts/deploy-config-example.json @@ -16,11 +16,6 @@ "OptimisticRecipientRegistry": { "challengePeriodSeconds": 9007199254740990, "deposit": "0.001" - }, - "BrightIdUserRegistry": { - "deploy": false, - "context": "clrfund-arbitrum-goerli", - "verifier": "0xdbf0b2ee9887fe11934789644096028ed3febe9c" } }, "arbitrum-sepolia": { @@ -43,8 +38,7 @@ }, "BrightIdUserRegistry": { "context": "clrfund-arbitrum-goerli", - "verifier": "0xdbf0b2ee9887fe11934789644096028ed3febe9c", - "sponsor": "0xC7c81634Dac2de4E7f2Ba407B638ff003ce4534C" + "verifier": "0xdbf0b2ee9887fe11934789644096028ed3febe9c" } } } diff --git a/contracts/tasks/subtasks/user/03-brightidSponsor.ts b/contracts/tasks/subtasks/user/03-brightidSponsor.ts deleted file mode 100644 index 9482a449e..000000000 --- a/contracts/tasks/subtasks/user/03-brightidSponsor.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Deploy an instance of the BrightID sponsor contract - * - */ -import { ContractStorage } from '../../helpers/ContractStorage' -import { Subtask } from '../../helpers/Subtask' -import { EContracts } from '../../../utils/types' - -const subtask = Subtask.getInstance() -const storage = ContractStorage.getInstance() - -/** - * Deploy step registration and task itself - */ -subtask - .addTask('user:deploy-brightid-sponsor', 'Deploy BrightID sponsor contract') - .setAction(async (_, hre) => { - subtask.setHre(hre) - const deployer = await subtask.getDeployer() - - const userRegistryName = subtask.getConfigField( - EContracts.ClrFund, - 'userRegistry' - ) - - if (userRegistryName !== EContracts.BrightIdUserRegistry) { - return - } - - let brightidSponsorContractAddress = subtask.tryGetConfigField( - EContracts.BrightIdUserRegistry, - 'sponsor' - ) - - if (brightidSponsorContractAddress) { - console.log( - `Skip BrightIdSponsor deployment, use ${brightidSponsorContractAddress}` - ) - return - } - - brightidSponsorContractAddress = storage.getAddress( - EContracts.BrightIdSponsor, - hre.network.name - ) - - if (brightidSponsorContractAddress) { - return - } - - const BrightIdSponsorContract = await subtask.deployContract( - EContracts.BrightIdSponsor, - { signer: deployer } - ) - - await storage.register({ - id: EContracts.BrightIdSponsor, - contract: BrightIdSponsorContract, - args: [], - network: hre.network.name, - }) - }) diff --git a/contracts/tasks/subtasks/user/04-brightidUserRegistry.ts b/contracts/tasks/subtasks/user/04-brightidUserRegistry.ts index 6db98dc2c..73f1ce259 100644 --- a/contracts/tasks/subtasks/user/04-brightidUserRegistry.ts +++ b/contracts/tasks/subtasks/user/04-brightidUserRegistry.ts @@ -10,6 +10,10 @@ import { EContracts } from '../../../utils/types' const subtask = Subtask.getInstance() const storage = ContractStorage.getInstance() +// @TODO remove this on the next major release when the sponsor contract is removed +// Hardcode with a dummy address for now +const BRIGHTID_SPONSOR = '0xC7c81634Dac2de4E7f2Ba407B638ff003ce4534C' + /** * Deploy step registration and task itself */ @@ -58,15 +62,7 @@ subtask 'verifier' ) - let sponsor = subtask.tryGetConfigField( - EContracts.BrightIdUserRegistry, - 'sponsor' - ) - if (!sponsor) { - sponsor = storage.mustGetAddress(EContracts.BrightIdSponsor, network) - } - - const args = [encodeBytes32String(context), verifier, sponsor] + const args = [encodeBytes32String(context), verifier, BRIGHTID_SPONSOR] const brightidUserRegistryContract = await subtask.deployContract( EContracts.BrightIdUserRegistry, { signer: deployer, args } diff --git a/vue-app/.env.example b/vue-app/.env.example index b7a9357e4..d0a44456f 100644 --- a/vue-app/.env.example +++ b/vue-app/.env.example @@ -28,20 +28,11 @@ VITE_USER_REGISTRY_TYPE=simple VITE_BRIGHTID_CONTEXT=clrfund-goerli -# These are for interacting with the BrightID api. When the SPONSOR_API_URL and one of the -# SPONSOR_KEY is set, a sponsor request will be sent to the BrightID node when the QR code -# to link users wallet address to BrightID is displayed. SPONSOR_KEY is used to sign the -# sponsor request. The SPONSOR_KEY_FOR_NETLIFY will trigger the netlify serverless function -# to send the sponsor request. The SPONSOR_KEY alone will send the request directly from -# the web app without using the netlify function. # VITE_BRIGHTID_NODE_URL is the BrightID node used to query BrightID status. It needs to # match the BRIGHTID_VERIFIER_ADDR defined in the contract .env file. This address is used # to verify the signature returned from the BrightID verification status for user registration. # The BRIGHTID_VERIFIER_ADDR value is the ethSigningAddress from the node url, # e.g. https://brightid.clr.fund -#VITE_BRIGHTID_SPONSOR_KEY_FOR_NETLIFY= -#VITE_BRIGHTID_SPONSOR_KEY= -#VITE_BRIGHTID_SPONSOR_API_URL=https://brightid.clr.fund/brightid/v6/operations # BrightId node one url, default to clrfund node at https://brightid.clr.fund/brightid/v6 #VITE_BRIGHTID_NODE_URL=https://app.brightid.org/node/v6 diff --git a/vue-app/src/api/bright-id.ts b/vue-app/src/api/bright-id.ts index 0aba9a8ad..10330c6fe 100644 --- a/vue-app/src/api/bright-id.ts +++ b/vue-app/src/api/bright-id.ts @@ -2,7 +2,7 @@ import { Contract, encodeBytes32String, toUtf8Bytes, decodeBase64, encodeBase64 import type { TransactionResponse, Signer } from 'ethers' import { BrightIdUserRegistry } from './abi' -import { brightIdSponsorKey, brightIdNodeUrl } from './core' +import { brightIdNodeUrl } from './core' import nacl from 'tweetnacl' const BRIGHTID_APP_URL = 'https://app.brightid.org' @@ -44,55 +44,6 @@ export interface Verification { app: string } -export interface Sponsorship { - timestamp: number - app: string - appHasAuthorized: boolean - spendRequested: boolean -} - -type AppData = { - id: string - name: string - context?: string - verification: string - verifications?: string[] - verificationsUrl: string - logo?: string - url?: string - assignedSponsorships?: number - unusedSponsorships?: number - testing?: boolean - idAsHex?: boolean - usingBlindSig?: boolean - verificationExpirationLength?: number - sponsorPublicKey?: string - nodeUrl?: string - soulbound: boolean - callbackUrl?: string -} - -type SponsorOperation = { - name: string - app: string - appUserId: string - timestamp: number - v: number - sig?: string -} - -type SponsorData = { - hash?: string - error?: string -} - -export async function selfSponsor(registryAddress: string, signer: Signer): Promise { - const registry = new Contract(registryAddress, BrightIdUserRegistry, signer) - const userAddress = await signer.getAddress() - const transaction = await registry.sponsor(userAddress) - return transaction -} - // This link is for generating QR code export function getBrightIdLink(userAddress: string): string { const deepLink = `brightid://link-verification/${CONTEXT}/${userAddress}` @@ -166,129 +117,3 @@ export async function getBrightId(contextId: string): Promise { } return brightId } - -/** - * Get the unused sponsorship amount - * @param context - the context to retrieve unused sponsorships for - * - * @returns Returns the number of sponsorships available to the specified `context` - */ -async function unusedSponsorships(context: string): Promise { - const endpoint = `${NODE_URL}/apps/${context}` - const response = await fetch(endpoint) - const json = await response.json() - - if (json['errorMessage']) { - throw new Error(JSON.stringify(json)) - } - - const data = json['data'] as AppData - return data.unusedSponsorships || 0 -} - -/** - * Call the BrightID sponsor operation endpoint to put a sponsorship request for the user - * @param userAddress user wallet address - * @returns sponsporship result or error - */ -export async function brightIdSponsor(userAddress: string): Promise { - const endpoint = `${NODE_URL}/operations` - - if (!brightIdSponsorKey) { - return { error: 'BrightId sponsor key not set' } - } - - const sponsorships = await unusedSponsorships(CONTEXT) - if (typeof sponsorships === 'number' && sponsorships < 1) { - return { error: 'BrightID sponsorships not available' } - } - - if (typeof sponsorships !== 'number') { - return { error: 'Invalid BrightID sponsorship' } - } - - const timestamp = Date.now() - - // these fields must be in alphabetical because - // BrightID nodes use 'fast-json-stable-stringify' that sorts fields - const op: SponsorOperation = { - app: CONTEXT, - appUserId: userAddress, - name: 'Sponsor', - timestamp, - v: 6, - } - - const message = JSON.stringify(op) - const arrayedMessage = toUtf8Bytes(message) - const arrayedKey = decodeBase64(brightIdSponsorKey) - const signature = nacl.sign.detached(arrayedMessage, arrayedKey) - op.sig = encodeBase64(signature) - - const res = await fetch(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(op), - }) - const json = await res.json() - - if (json['error']) { - if (canIgnoreError(json.errorNum)) { - // sponsorship already sent recently, ignore this error - return { hash: '0x0' } - } - return { error: json['errorMessage'] } - } else { - return json['data'] - } -} - -/** - * Call the netlify function to invoke the BrightId sponsor api - * @param userAddress user wallet address - * @returns sponsorship data or error - */ -async function netlifySponsor(userAddress: string): Promise { - const res = await fetch('/.netlify/functions/sponsor', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ userAddress }), - }) - - const json = await res.json() - if (res.status === 200) { - return json - } - - if (res.status === 400 && canIgnoreError(json.errorNum)) { - return { hash: '0x0' } - } - - // return the error - return json -} - -/** - * Sponsor a BrightID user using the sponsorship api - * @param userAddress user wallet address - * @returns sponsporship result or error - */ -export async function sponsorUser(userAddress: string): Promise { - if (brightIdSponsorKey) { - return brightIdSponsor(userAddress) - } - - try { - return await netlifySponsor(userAddress) - } catch (err) { - if (err instanceof Error) { - return { error: (err as Error).message } - } else { - return { error: 'Unknown sponsorhip error' } - } - } -} diff --git a/vue-app/src/api/core.ts b/vue-app/src/api/core.ts index 5caa704c6..f120af12f 100644 --- a/vue-app/src/api/core.ts +++ b/vue-app/src/api/core.ts @@ -72,10 +72,8 @@ export const maxDecimals = Number(import.meta.env.VUE_APP_MAX_DECIMAL || 1) // the number of records per batch in the `pending submissions` export file export const exportBatchSize = Number(import.meta.env.VITE_EXPORT_BATCH_SIZE) || 60 -// BrightId sponsorhip stuff, set these parameters to automatically sponsor user using the brightId URL -export const brightIdSponsorKey = import.meta.env.VITE_BRIGHTID_SPONSOR_KEY +// BrightId url for querying user verification export const brightIdNodeUrl = import.meta.env.VITE_BRIGHTID_NODE_URL || 'https://brightid.clr.fund/brightid/v6' -export const brightIdSponsorUrl = import.meta.env.VITE_BRIGHTID_SPONSOR_API_URL // wait for data to sync with the subgraph export const MAX_WAIT_DEPTH = Number(import.meta.env.VITE_MAX_WAIT_DEPTH) || 15 diff --git a/vue-app/src/lambda/sponsor.mts b/vue-app/src/lambda/sponsor.mts deleted file mode 100644 index 5d460e17a..000000000 --- a/vue-app/src/lambda/sponsor.mts +++ /dev/null @@ -1,137 +0,0 @@ -import { decodeBase64, encodeBase64, toUtf8Bytes } from 'ethers' -import nacl from 'tweetnacl' -import type { Handler } from '@netlify/functions' - -const NODE_URL = process.env.VITE_BRIGHTID_NODE_URL || 'https://app.brightid.org/node/v6' - -// these fields must be in alphabetical because -// BrightID nodes use 'fast-json-stable-stringify' that sorts fields -type BrightIDMessage = { - app: string - appUserId: string - name: string - sig?: string - timestamp: number - v: number -} - -/** - * Returns an error object - * @param errorMessage error message - * @returns error object - */ -function makeError(errorMessage: string, errorNum: number) { - const body = JSON.stringify({ error: errorMessage, errorNum }) - return { statusCode: 400, body } -} - -/** - * Returns the result with statusCode and body - * @param result the result - * @returns result object - */ -function makeResult(result: any) { - const body = JSON.stringify(result) - return { statusCode: 200, body } -} - -/** - * Get the unused sponsorship amount - * @param context - the context to retrieve unused sponsorships for - * - * @returns the number of sponsorships available to the specified `context` - */ -async function unusedSponsorships(context: string) { - const endpoint = `${NODE_URL}/apps/${context}` - const response = await fetch(endpoint) - const json = await response.json() - - if (json['errorMessage']) { - throw new Error(JSON.stringify(json)) - } - - const data = json['data'] - return data.unusedSponsorships || 0 -} - -async function handleSponsorRequest(userAddress: string) { - const endpoint = process.env.VITE_BRIGHTID_SPONSOR_API_URL - if (!endpoint) { - throw new Error('Environment variable VITE_BRIGHTID_SPONSOR_API_URL not set') - } - - const brightIdSponsorKey = process.env.VITE_BRIGHTID_SPONSOR_KEY_FOR_NETLIFY - if (!brightIdSponsorKey) { - throw new Error('Environment variable VITE_BRIGHTID_SPONSOR_KEY_FOR_NETLIFY not set') - } - - const CONTEXT = process.env.VITE_BRIGHTID_CONTEXT - if (!CONTEXT) { - throw new Error('Environment variable VITE_BRIGHTID_CONTEXT not set') - } - - const sponsorships = await unusedSponsorships(CONTEXT) - if (typeof sponsorships === 'number' && sponsorships < 1) { - throw new Error('BrightID sponsorships not available') - } - if (typeof sponsorships !== 'number') { - throw new Error('Invalid BrightID sponsorship') - } - - const timestamp = Date.now() - - const op: BrightIDMessage = { - app: CONTEXT, - appUserId: userAddress, - name: 'Sponsor', - timestamp, - v: 6, - } - - const message = JSON.stringify(op) - const arrayedMessage = toUtf8Bytes(message) - const arrayedKey = decodeBase64(brightIdSponsorKey) - const signature = nacl.sign.detached(arrayedMessage, arrayedKey) - op.sig = encodeBase64(signature) - - const res = await fetch(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(op), - }) - const json = await res.json() - - if (json.error) { - return makeError(json.errorMessage, json.errorNum) - } - - if (json.data) { - return makeResult({ hash: json.data.hash }) - } - - return makeError('Unexpected result from the BrightID sponsorship API.', 500) -} - -/** - * Submit a sponsorship request using the BrightID api - * @param event contains user address to sponsor - * @returns sponsor data or error - */ -export const handler: Handler = async event => { - if (!event.body) { - return makeError('Missing request body', 400) - } - - try { - const jsonBody = JSON.parse(event.body) - if (!jsonBody.userAddress) { - return makeError('Missing userAddress in request body: ' + event.body, 400) - } - - return await handleSponsorRequest(jsonBody.userAddress) - } catch (err) { - return makeError(err.message, 500) - } -} diff --git a/vue-app/src/views/Verify.vue b/vue-app/src/views/Verify.vue index de2c99b65..9fa25f2f6 100644 --- a/vue-app/src/views/Verify.vue +++ b/vue-app/src/views/Verify.vue @@ -136,7 +136,7 @@ import { ref, computed, onMounted, watch } from 'vue' import ProgressBar from '@/components/ProgressBar.vue' import QRCode from 'qrcode' -import { getBrightIdLink, getBrightIdUniversalLink, registerUser, selfSponsor, sponsorUser } from '@/api/bright-id' +import { getBrightIdLink, getBrightIdUniversalLink, registerUser } from '@/api/bright-id' import { getProofSnapshot, getProofMerkle, registerUserSnapshot, registerUserMerkle } from '@/api/user' import Transaction from '@/components/Transaction.vue' import Loader from '@/components/Loader.vue' @@ -145,11 +145,11 @@ import { waitForTransaction } from '@/utils/contracts' import { useAppStore, useUserStore } from '@/stores' import { storeToRefs } from 'pinia' import { useRouter } from 'vue-router' -import { UserRegistryType, isBrightIdRequired, brightIdSponsorUrl, userRegistryType } from '@/api/core' +import { UserRegistryType, isBrightIdRequired, userRegistryType } from '@/api/core' import { assert } from '@/utils/assert' interface VerificationStep { - page: 'connect' | 'registration' | 'sponsorship' + page: 'connect' | 'registration' } function getVerificationSteps(): Array {