From cebb59e9abe9fdfc875b7cef81fed5beec79c155 Mon Sep 17 00:00:00 2001 From: yuetloo Date: Wed, 23 Aug 2023 11:54:21 -0400 Subject: [PATCH] Add merkle user registry --- common/package.json | 1 + common/src/index.ts | 2 + common/src/ipfs.ts | 18 +++ common/src/merkle.ts | 37 ++++++ .../userRegistry/MerkleUserRegistry.sol | 22 +++- contracts/scripts/deploy.ts | 43 ++----- contracts/scripts/deployUserRegistry.ts | 42 ++----- contracts/tasks/index.ts | 1 + contracts/tasks/loadMerkleUsers.ts | 111 ++++++++++++++++++ contracts/tests/userRegistryMerkle.ts | 65 ++++++++++ contracts/utils/deployment.ts | 81 ++++++++++++- vue-app/src/api/abi.ts | 2 + vue-app/src/api/core.ts | 1 + vue-app/src/api/user.ts | 44 +++++-- vue-app/src/components/CallToActionCard.vue | 53 ++++----- vue-app/src/components/Cart.vue | 17 +-- vue-app/src/locales/cn.json | 3 +- vue-app/src/locales/en.json | 3 +- vue-app/src/locales/es.json | 3 +- vue-app/src/views/Verify.vue | 28 +++-- yarn.lock | 10 +- 21 files changed, 457 insertions(+), 130 deletions(-) create mode 100644 common/src/ipfs.ts create mode 100644 common/src/merkle.ts create mode 100644 contracts/tasks/loadMerkleUsers.ts create mode 100644 contracts/tests/userRegistryMerkle.ts diff --git a/common/package.json b/common/package.json index 039734e11..56f45b22d 100644 --- a/common/package.json +++ b/common/package.json @@ -14,6 +14,7 @@ "typescript": "^4.9.3" }, "dependencies": { + "@openzeppelin/merkle-tree": "^1.0.5", "ethers": "^5.7.2" }, "repository": { diff --git a/common/src/index.ts b/common/src/index.ts index a8f361efc..d1c59a047 100644 --- a/common/src/index.ts +++ b/common/src/index.ts @@ -1,2 +1,4 @@ export * from './block' export * from './proof' +export * from './merkle' +export * from './ipfs' diff --git a/common/src/ipfs.ts b/common/src/ipfs.ts new file mode 100644 index 000000000..4254607e8 --- /dev/null +++ b/common/src/ipfs.ts @@ -0,0 +1,18 @@ +import { utils } from 'ethers' + +const IPFS_BASE_URL = 'https://ipfs.io' + +/** + * Get the IPFS content given the IPFS hash + * @param hash The IPFS hash + * @param gatewayUrl The IPFS gateway url + * @returns The IPFS content + */ +export async function getIpfsContent( + hash: string, + gatewayUrl = IPFS_BASE_URL +): Promise { + const url = `${gatewayUrl}/ipfs/${hash}` + const result = utils.fetchJson(url) + return result +} diff --git a/common/src/merkle.ts b/common/src/merkle.ts new file mode 100644 index 000000000..c93c4d2a1 --- /dev/null +++ b/common/src/merkle.ts @@ -0,0 +1,37 @@ +import { StandardMerkleTree } from '@openzeppelin/merkle-tree' + +/** + * Load users into a merkle tree + * @param users Users to load + * @returns user merkle tree + */ +export function loadUserMerkleTree( + users: string[] +): StandardMerkleTree { + const tree = StandardMerkleTree.of( + users.map((user) => [user.toLowerCase()]), + ['address'] + ) + return tree +} + +/** + * Get the merkle proof for the user + * @param userAccount User wallet address to get the proof for + * @param userMerkleTree The merkle tree containing all approved users + * @returns + */ +export function getUserMerkleProof( + userAccount: string, + userMerkleTree: StandardMerkleTree +): string[] | null { + try { + return userMerkleTree.getProof([userAccount.toLowerCase()]) + } catch (err) { + console.log('userAccount', userAccount.toLowerCase()) + console.log('getUserMerkleProof error', err) + return null + } +} + +export { StandardMerkleTree } diff --git a/contracts/contracts/userRegistry/MerkleUserRegistry.sol b/contracts/contracts/userRegistry/MerkleUserRegistry.sol index b805e0831..31d041bf5 100644 --- a/contracts/contracts/userRegistry/MerkleUserRegistry.sol +++ b/contracts/contracts/userRegistry/MerkleUserRegistry.sol @@ -14,24 +14,38 @@ import {MerkleProof} from "../utils/cryptography/MerkleProof.sol"; * the funding round coordinator. */ contract MerkleUserRegistry is Ownable, IUserRegistry { + + // verified users mapping(address => bool) private users; + + // merkle root bytes32 public merkleRoot; + // ipfs hash of the merkle tree file + string public merkleHash; + // Events event UserAdded(address indexed _user); - event UserRemoved(address indexed _user); + event MerkleRootChanged(bytes32 indexed root, string ipfsHash); - function setMerkleRoot(bytes32 root) external onlyOwner { + /** + * @dev Set the merkle root used to verify users + * @param root Merkle root + * @param ipfsHash The IPFS hash of the merkle tree file + */ + function setMerkleRoot(bytes32 root, string calldata ipfsHash) external onlyOwner { require(root != bytes32(0), 'MerkleUserRegistry: Merkle root is zero'); merkleRoot = root; + merkleHash = ipfsHash; + + emit MerkleRootChanged(root, ipfsHash); } /** * @dev Add verified unique user to the registry. */ - function addUser(bytes32[] calldata proof, address _user) + function addUser(address _user, bytes32[] calldata proof) external - onlyOwner { require(merkleRoot != bytes32(0), 'MerkleUserRegistry: Merkle root is not initialized'); require(_user != address(0), 'MerkleUserRegistry: User address is zero'); diff --git a/contracts/scripts/deploy.ts b/contracts/scripts/deploy.ts index 0acd9fef2..e65d93654 100644 --- a/contracts/scripts/deploy.ts +++ b/contracts/scripts/deploy.ts @@ -1,8 +1,12 @@ import { ethers } from 'hardhat' -import { Contract, utils, Wallet } from 'ethers' +import { Contract, Wallet } from 'ethers' import { UNIT } from '../utils/constants' -import { deployMaciFactory } from '../utils/deployment' +import { + deployMaciFactory, + deployUserRegistry, + getBrightIdParams, +} from '../utils/deployment' import { Keypair, PrivKey } from '@clrfund/maci-utils' // Number.MAX_SAFE_INTEGER - 1 @@ -63,35 +67,12 @@ async function main() { await transferOwnershipTx.wait() const userRegistryType = process.env.USER_REGISTRY_TYPE || 'simple' - let userRegistry: Contract - if (userRegistryType === 'simple') { - const SimpleUserRegistry = await ethers.getContractFactory( - 'SimpleUserRegistry', - deployer - ) - userRegistry = await SimpleUserRegistry.deploy() - } else if (userRegistryType === 'brightid') { - const BrightIdUserRegistry = await ethers.getContractFactory( - 'BrightIdUserRegistry', - deployer - ) - - userRegistry = await BrightIdUserRegistry.deploy( - utils.formatBytes32String(process.env.BRIGHTID_CONTEXT || 'clr.fund'), - process.env.BRIGHTID_VERIFIER_ADDR, - process.env.BRIGHTID_SPONSOR - ) - } else if (userRegistryType === 'snapshot') { - const SnapshotUserRegistry = await ethers.getContractFactory( - 'SnapshotUserRegistry', - deployer - ) - userRegistry = await SnapshotUserRegistry.deploy() - } else { - throw new Error('unsupported user registry type') - } - - await userRegistry.deployTransaction.wait() + const brightidParams = getBrightIdParams(userRegistryType) + const userRegistry: Contract = await deployUserRegistry( + userRegistryType, + deployer, + brightidParams + ) console.log( `User registry (${userRegistryType}) deployed: ${userRegistry.address}` ) diff --git a/contracts/scripts/deployUserRegistry.ts b/contracts/scripts/deployUserRegistry.ts index 26bad9f8d..2373d5451 100644 --- a/contracts/scripts/deployUserRegistry.ts +++ b/contracts/scripts/deployUserRegistry.ts @@ -1,5 +1,5 @@ import { ethers } from 'hardhat' -import { Contract, utils } from 'ethers' +import { deployUserRegistry, getBrightIdParams } from '../utils/deployment' async function main() { console.log('*******************') @@ -20,38 +20,14 @@ async function main() { console.log('funding round factory address ', fundingRoundFactory.address) const userRegistryType = process.env.USER_REGISTRY_TYPE || 'simple' - let userRegistry: Contract - if (userRegistryType === 'simple') { - const SimpleUserRegistry = await ethers.getContractFactory( - 'SimpleUserRegistry', - deployer - ) - userRegistry = await SimpleUserRegistry.deploy() - } else if (userRegistryType === 'brightid') { - console.log('deploying brightid user registry') - const BrightIdUserRegistry = await ethers.getContractFactory( - 'BrightIdUserRegistry', - deployer - ) - - userRegistry = await BrightIdUserRegistry.deploy( - utils.formatBytes32String(process.env.BRIGHTID_CONTEXT || 'clr.fund'), - process.env.BRIGHTID_VERIFIER_ADDR, - process.env.BRIGHTID_SPONSOR - ) - console.log('transaction hash', userRegistry.deployTransaction.hash) - } else if (userRegistryType === 'snapshot') { - const SnapshotUserRegistry = await ethers.getContractFactory( - 'SnapshotUserRegistry', - deployer - ) - userRegistry = await SnapshotUserRegistry.deploy() - } else { - throw new Error('unsupported user registry type') - } - await userRegistry.deployTransaction.wait() + const brightidParams = getBrightIdParams(userRegistryType) + const userRegistry = await deployUserRegistry( + userRegistryType, + deployer, + brightidParams + ) console.log( - `Deployed ${userRegistryType} user registry at ${userRegistry.address}` + `deployed ${userRegistryType} user registry at ${userRegistry.address}` ) const setUserRegistryTx = await fundingRoundFactory.setUserRegistry( @@ -59,7 +35,7 @@ async function main() { ) await setUserRegistryTx.wait() console.log( - 'set user registry in funding round factory', + 'set user registry in funding round factory at tx hash', setUserRegistryTx.hash ) diff --git a/contracts/tasks/index.ts b/contracts/tasks/index.ts index 890bc9c20..b71907150 100644 --- a/contracts/tasks/index.ts +++ b/contracts/tasks/index.ts @@ -16,3 +16,4 @@ import './loadUsers' import './tally' import './findStorageSlot' import './setStorageRoot' +import './loadMerkleUsers' diff --git a/contracts/tasks/loadMerkleUsers.ts b/contracts/tasks/loadMerkleUsers.ts new file mode 100644 index 000000000..fbcc59aad --- /dev/null +++ b/contracts/tasks/loadMerkleUsers.ts @@ -0,0 +1,111 @@ +import { task, types } from 'hardhat/config' +import { Contract, utils } from 'ethers' +import fs from 'fs' +import { StandardMerkleTree } from '@clrfund/common' +import { getIpfsHash } from '../utils/ipfs' + +/* + * Load users into the the merkle user registry by generating a merkle tree and + * setting the merkle root of the merkle user registry. It will generate an output + * file with the dump of the merkle tree. This file should be uploaded to the IPFS. + * + * File path can be relative or absolute path. + * The script can only be run by the owner of the user registry + * + * Sample usage: + * + * yarn hardhat load-merkle-users --address-file addresses.txt --user-registry
--network goerli + */ + +/** + * Load users in the file into the simple user registry + * + * @param registry Merkle user registry contract + * @param addressFile The path of the file containing the addresses + * @param output The path for the merkle tree output file + */ +async function loadFile( + registry: Contract, + addressFile: string, + output: string +) { + let content: string | null = null + try { + content = fs.readFileSync(addressFile, 'utf8') + } catch (err) { + console.error('Failed to read file', addressFile, err) + return + } + + const addresses: string[] = [] + content.split(/\r?\n/).forEach(async (address) => { + addresses.push(address) + }) + + const validAddresses: string[] = [] + + for (let i = 0; i < addresses.length; i++) { + const address = addresses[i] + const isValidAddress = Boolean(address) && utils.isAddress(address) + if (isValidAddress) { + console.log('Adding address', address) + try { + validAddresses.push(address) + } catch (err: any) { + if (err.reason) { + console.error('Failed to add address', address, err.reason) + } else { + console.error('Failed to add address', address, err) + } + } + } else { + console.warn('Skipping invalid address', address) + } + } + + if (validAddresses.length > 0) { + const tree = StandardMerkleTree.of( + validAddresses.map((address) => [address]), + ['address'] + ) + + const treeDump = tree.dump() + fs.writeFileSync(output, JSON.stringify(treeDump, null, 4)) + + const ipfsHash = await getIpfsHash(treeDump) + + const tx = await registry.setMerkleRoot(tree.root, ipfsHash) + return tx + } +} + +task('load-merkle-users', 'Bulkload recipients into the simple user registry') + .addParam('userRegistry', 'The merkle user registry contract address') + .addParam( + 'addressFile', + 'The path of the file containing addresses separated by newline' + ) + .addOptionalParam( + 'output', + 'The output json file path for the merkle tree', + undefined, + types.string + ) + .setAction(async ({ userRegistry, addressFile, output }, { ethers }) => { + const registry = await ethers.getContractAt( + 'MerkleUserRegistry', + userRegistry + ) + + console.log('User merkle registry', userRegistry) + console.log('Deployer', await registry.signer.getAddress()) + const timeMs = new Date().getTime() + const outputFile = output ? output : `./merkle_users_${timeMs}.json` + const tx = await loadFile(registry, addressFile, outputFile) + console.log('User merkle root updated at tx hash', tx.hash) + await tx.wait() + + console.log( + `User merkle tree file generated at ${outputFile}, make sure to upload it to IPFS.` + ) + }) diff --git a/contracts/tests/userRegistryMerkle.ts b/contracts/tests/userRegistryMerkle.ts new file mode 100644 index 000000000..997a6440d --- /dev/null +++ b/contracts/tests/userRegistryMerkle.ts @@ -0,0 +1,65 @@ +import { ethers, waffle } from 'hardhat' +import { use, expect } from 'chai' +import { solidity } from 'ethereum-waffle' +import { Contract, utils } from 'ethers' +import { loadUserMerkleTree, getUserMerkleProof } from '@clrfund/common' + +use(solidity) + +describe('Merkle User Registry', () => { + const provider = waffle.provider + const [, deployer, user1, user2] = provider.getWallets() + + const signers = { [user1.address]: user1, [user2.address]: user2 } + const authorizedUsers = [user1.address, user2.address] + let registry: Contract + let tree: any + + beforeEach(async () => { + const MerkleUserRegistry = await ethers.getContractFactory( + 'MerkleUserRegistry', + deployer + ) + registry = await MerkleUserRegistry.deploy() + tree = loadUserMerkleTree(authorizedUsers) + const tx = await registry.setMerkleRoot(tree.root, 'test') + await tx.wait() + }) + + it('rejects zero merkle root', async () => { + await expect( + registry.setMerkleRoot(utils.hexZeroPad('0x0', 32), 'testzero') + ).to.be.revertedWith('MerkleUserRegistry: Merkle root is zero') + }) + + it('should not allow non-owner to set the merkle root', async () => { + const registryAsUser = registry.connect(signers[user1.address]) + await expect( + registryAsUser.setMerkleRoot(utils.hexZeroPad('0x0', 32), 'non owner') + ).to.be.revertedWith('Ownable: caller is not the owner') + }) + + describe('registration', () => { + it('allows valid verified user to register', async () => { + for (const user of authorizedUsers) { + const proof = getUserMerkleProof(user, tree) + + const registryAsUser = registry.connect(signers[user]) + await expect(registryAsUser.addUser(user, proof)) + .to.emit(registryAsUser, 'UserAdded') + .withArgs(user) + expect(await registryAsUser.isVerifiedUser(user)).to.equal(true) + } + }) + + it('rejects unauthorized user', async () => { + const user = ethers.Wallet.createRandom() + const proof = tree.getProof(0) + const registryAsUser = registry.connect(signers[user1.address]) + await expect( + registryAsUser.addUser(user.address, proof) + ).to.be.revertedWith('MerkleUserRegistry: User is not authorized') + expect(await registryAsUser.isVerifiedUser(user.address)).to.equal(false) + }) + }) +}) diff --git a/contracts/utils/deployment.ts b/contracts/utils/deployment.ts index 87b161a82..342de8d81 100644 --- a/contracts/utils/deployment.ts +++ b/contracts/utils/deployment.ts @@ -1,12 +1,53 @@ import { ethers, config } from 'hardhat' import { Libraries } from 'hardhat/types/runtime' -import { Signer, Contract } from 'ethers' +import { Signer, Contract, utils } from 'ethers' import { link } from 'ethereum-waffle' import path from 'path' import { MaciParameters } from './maci' import { readFileSync } from 'fs' +const userRegistryNames: Record = { + simple: 'SimpleUserRegistry', + brightid: 'BrightIdUserRegistry', + snapshot: 'SnapshotUserRegistry', + merkle: 'MerkleUserRegistry', +} + +export interface BrightIdParams { + context: string + verifierAddress: string + sponsor: string +} + +/** + * Return the brightid user registry contructor parameter values + * @param userRegistryType user registry type + * @returns BrightIdParams or null + */ +export function getBrightIdParams( + userRegistryType: string +): BrightIdParams | null { + if (userRegistryType === 'brightid') { + const verifierAddress = process.env.BRIGHTID_VERIFIER_ADDR + const sponsor = process.env.BRIGHTID_SPONSOR + if (!verifierAddress) { + throw new Error('Missing environment variable BRIGHTID_VERIFIER_ADDR') + } + if (!sponsor) { + throw new Error('Missing environment variable BRIGHTID_SPONSOR') + } + + return { + context: process.env.BRIGHTID_CONTEXT || 'clr.fund', + verifierAddress, + sponsor, + } + } else { + return null + } +} + export function linkBytecode( bytecode: string, libraries: { [name: string]: string } @@ -193,3 +234,41 @@ export async function deployMaciFactory( return maciFactory } + +export async function deployUserRegistry( + userRegistryType: string, + deployer: Signer, + brightid: BrightIdParams | null +): Promise { + let userRegistry: Contract + if (userRegistryType === 'brightid') { + if (!brightid) { + throw new Error('Missing BrightId parameter') + } + + const BrightIdUserRegistry = await ethers.getContractFactory( + 'BrightIdUserRegistry', + deployer + ) + + userRegistry = await BrightIdUserRegistry.deploy( + utils.formatBytes32String(brightid.context), + brightid.verifierAddress, + brightid.sponsor + ) + } else { + const userRegistryName = userRegistryNames[userRegistryType] + if (!userRegistryName) { + throw new Error('unsupported user registry type: ' + userRegistryType) + } + + const UserRegistry = await ethers.getContractFactory( + userRegistryName, + deployer + ) + userRegistry = await UserRegistry.deploy() + } + + await userRegistry.deployTransaction.wait() + return userRegistry +} diff --git a/vue-app/src/api/abi.ts b/vue-app/src/api/abi.ts index ac98156d4..76a08cba0 100644 --- a/vue-app/src/api/abi.ts +++ b/vue-app/src/api/abi.ts @@ -6,6 +6,7 @@ import { abi as MACI } from '../../../contracts/build/contracts/maci-contracts/s import { abi as UserRegistry } from '../../../contracts/build/contracts/contracts/userRegistry/IUserRegistry.sol/IUserRegistry.json' import { abi as BrightIdUserRegistry } from '../../../contracts/build/contracts/contracts/userRegistry/BrightIdUserRegistry.sol/BrightIdUserRegistry.json' import { abi as SnapshotUserRegistry } from '../../../contracts/build/contracts/contracts/userRegistry/SnapshotUserRegistry.sol/SnapshotUserRegistry.json' +import { abi as MerkleUserRegistry } from '../../../contracts/build/contracts/contracts/userRegistry/MerkleUserRegistry.sol/MerkleUserRegistry.json' import { abi as SimpleRecipientRegistry } from '../../../contracts/build/contracts/contracts/recipientRegistry/SimpleRecipientRegistry.sol/SimpleRecipientRegistry.json' import { abi as OptimisticRecipientRegistry } from '../../../contracts/build/contracts/contracts/recipientRegistry/OptimisticRecipientRegistry.sol/OptimisticRecipientRegistry.json' import { abi as KlerosGTCR } from '../../../contracts/build/contracts/contracts/recipientRegistry/IKlerosGTCR.sol/IKlerosGTCR.json' @@ -19,6 +20,7 @@ export { MACI, UserRegistry, SnapshotUserRegistry, + MerkleUserRegistry, BrightIdUserRegistry, SimpleRecipientRegistry, OptimisticRecipientRegistry, diff --git a/vue-app/src/api/core.ts b/vue-app/src/api/core.ts index 77be17398..3a4467de9 100644 --- a/vue-app/src/api/core.ts +++ b/vue-app/src/api/core.ts @@ -47,6 +47,7 @@ export enum UserRegistryType { BRIGHT_ID = 'brightid', SIMPLE = 'simple', SNAPSHOT = 'snapshot', + MERKLE = 'merkle', } if (!Object.values(UserRegistryType).includes(userRegistryType as UserRegistryType)) { diff --git a/vue-app/src/api/user.ts b/vue-app/src/api/user.ts index a7b42e34b..e8df769f1 100644 --- a/vue-app/src/api/user.ts +++ b/vue-app/src/api/user.ts @@ -5,8 +5,14 @@ import type { Web3Provider } from '@ethersproject/providers' import { UserRegistry, ERC20 } from './abi' import { factory, ipfsGatewayUrl, provider, operator } from './core' import type { BrightId } from './bright-id' -import { SnapshotUserRegistry } from './abi' -import { getStorageProof, rlpEncodeProof } from '@clrfund/common' +import { SnapshotUserRegistry, MerkleUserRegistry } from './abi' +import { + getUserMerkleProof, + getStorageProof, + rlpEncodeProof, + getIpfsContent, + StandardMerkleTree, +} from '@clrfund/common' //TODO: update anywhere this is called to take factory address as a parameter, default to env. variable export const LOGIN_MESSAGE = `Welcome to ${operator}! @@ -56,11 +62,13 @@ export async function getEtherBalance(walletAddress: string): Promise return await provider.getBalance(walletAddress) } -export async function registerSnapshotUser( - registryAddress: string, - walletAddress: string, - signer: Signer, -): Promise { +/** + * Register a user in the Snapshot user registry + * @param registryAddress The snapshot user registry contract address + * @param signer The signer + * @returns The contract transaction + */ +export async function registerSnapshotUser(registryAddress: string, signer: Signer): Promise { const registry = new Contract(registryAddress, SnapshotUserRegistry, signer) const [tokenAddress, blockHash, storageSlot] = await Promise.all([ registry.token(), @@ -68,8 +76,30 @@ export async function registerSnapshotUser( registry.storageSlot(), ]) + const walletAddress = await signer.getAddress() const proof = await getStorageProof(tokenAddress, blockHash, walletAddress, storageSlot, provider) const proofRlpBytes = rlpEncodeProof(proof.storageProof[0].proof) return registry.addUser(walletAddress, proofRlpBytes) } + +/** + * Register a user in the merkle user registry + * @param registryAddress The merkle user registry + * @param signer The user to be registered + * @returns The contract transaction + */ +export async function registerMerkleUser(registryAddress: string, signer: Signer): Promise { + const registry = new Contract(registryAddress, MerkleUserRegistry, signer) + const merkleHash = await registry.merkleHash() + + const treeRaw = await getIpfsContent(merkleHash, ipfsGatewayUrl) + const tree = StandardMerkleTree.load(treeRaw) + const walletAddress = await signer.getAddress() + const proof = await getUserMerkleProof(walletAddress, tree) + if (!proof) { + throw new Error('User is not authorized') + } + + return registry.addUser(walletAddress, proof) +} diff --git a/vue-app/src/components/CallToActionCard.vue b/vue-app/src/components/CallToActionCard.vue index ae6ba1c39..61b1f7faf 100644 --- a/vue-app/src/components/CallToActionCard.vue +++ b/vue-app/src/components/CallToActionCard.vue @@ -24,26 +24,28 @@
- - 🚀 -
-

{{ $t('callToActionCard.h2_3') }}

-

- {{ $t('callToActionCard.p3') }} -

-
- {{ $t('callToActionCard.link1') }} - {{ $t('callToActionCard.link2') }} -
-
- 🚀 -
-

{{ $t('callToActionCard.h2_3') }}

-

- {{ $t('callToActionCard.p3') }} -

+
+ + 🚀 +
+

{{ $t('callToActionCard.h2_3') }}

+

+ {{ $t('callToActionCard.p3') }} +

+
+ {{ $t('callToActionCard.link1') }} + {{ $t('callToActionCard.link2') }}
- {{ $t('callToActionCard.link1') }} + + 🚀 +
+

{{ $t('callToActionCard.h2_3') }}

+

+ {{ $t('callToActionCard.p3') }} +

+
+ {{ $t('callToActionCard.link1') }} +
@@ -53,7 +55,7 @@ import { computed } from 'vue' import BrightIdWidget from '@/components/BrightIdWidget.vue' import Links from '@/components/Links.vue' -import { userRegistryType, UserRegistryType } from '@/api/core' +import { userRegistryType, UserRegistryType, isBrightIdRequired } from '@/api/core' import { useAppStore, useUserStore } from '@/stores' import { storeToRefs } from 'pinia' @@ -67,16 +69,7 @@ const hasStartedVerification = computed( ) const showUserVerification = computed(() => { return ( - userRegistryType === UserRegistryType.BRIGHT_ID && - currentRound.value && - currentUser.value?.isRegistered !== undefined && - !currentUser.value.isRegistered - ) -}) - -const showUserRegistration = computed(() => { - return ( - userRegistryType === UserRegistryType.SNAPSHOT && + userRegistryType !== UserRegistryType.SIMPLE && currentRound.value && currentUser.value?.isRegistered !== undefined && !currentUser.value.isRegistered diff --git a/vue-app/src/components/Cart.vue b/vue-app/src/components/Cart.vue index b8d1f2c96..a8a602e27 100644 --- a/vue-app/src/components/Cart.vue +++ b/vue-app/src/components/Cart.vue @@ -165,7 +165,10 @@ }}
- {{ $t('cart.link3') }} + + {{ $t('cart.link3') }} + {{ $t('cart.linkRegister') }} +