mirror of
https://github.com/anonklub/anonklub.git
synced 2026-01-10 10:27:54 -05:00
:twisted_rightward_arrows: Merge staging (#224)
* Update test workflow * 📦 Build `@anonklub/*` to commonjs (#223) * Build @anonklub/query to commonJS * Build @anonklub/proof to ES2022 * format * refactor: do not use `baseUrl` to build @anonklub/query It is tricky to use a lib that defines module aliases * refactor: do not use `baseUrl` to build @anonklub/proof It is tricky to use a lib that defines module aliases * Build @anonklub/cli to ES2022 * format, lint * fix: do not build as ESM * add comment * Update package.json files * fix folder name and import path * Fix path to coverageDirectory in jest preset * name test workflow * trigger ? * rename workflow * use jest projects Necessary to have 1 root coverage folder that coveralls needs
This commit is contained in:
@@ -12,6 +12,7 @@ parserOptions:
|
||||
'infra/tsconfig.json',
|
||||
'test/tsconfig.json',
|
||||
'ui/tsconfig.json',
|
||||
'tsconfig.json',
|
||||
]
|
||||
|
||||
rules:
|
||||
@@ -20,7 +21,7 @@ rules:
|
||||
overrides:
|
||||
- files: ['apis/query/test/unit/DuneRepository.test.ts']
|
||||
rules:
|
||||
'@typescript-eslint/no-dynamic-delete': warn
|
||||
'@typescript-eslint/no-dynamic-delete': off
|
||||
- files: ['*.ts']
|
||||
rules:
|
||||
'@typescript-eslint/dot-notation': off # because using noPropertyAccessFromIndexSignature tsc option
|
||||
@@ -77,7 +78,7 @@ overrides:
|
||||
'apis/prove/src/mq/*.ts',
|
||||
'circuits/circom/scripts/*.ts',
|
||||
'@anonklub/cli/src/index.ts',
|
||||
'@anonklub/cli/src/cli/index.ts',
|
||||
'@anonklub/cli/src/Cli/index.ts',
|
||||
'ui/src/lib/config.ts',
|
||||
]
|
||||
rules:
|
||||
@@ -102,3 +103,6 @@ overrides:
|
||||
- files: ['shared/src/index.ts']
|
||||
rules:
|
||||
'sort/exports': 'off'
|
||||
- files: ['@anonklub/cli/src/Prompt/index.ts']
|
||||
rules:
|
||||
'@typescript-eslint/naming-convention': off
|
||||
|
||||
2
.github/workflows/static-analysis.yml
vendored
2
.github/workflows/static-analysis.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Static Analysis
|
||||
name: static analysis
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
|
||||
14
.github/workflows/test.yml
vendored
14
.github/workflows/test.yml
vendored
@@ -1,25 +1,29 @@
|
||||
name: Test
|
||||
name: test
|
||||
on:
|
||||
# push trigger required to get coveralls monitoring of default branch
|
||||
# pull_request required to get PR comments
|
||||
# pull_request trigger required to get PR comments
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [main, staging]
|
||||
paths:
|
||||
[
|
||||
'{apis,circuits,@anonklub}/**/*.{js,json,ts,tsx,graphql}',
|
||||
'@anonklub/**/*.ts',
|
||||
'apis/**/*.{js,json,ts,graphql}',
|
||||
'test/**/*.{js,ts}',
|
||||
'contracts/**/*.sol',
|
||||
]
|
||||
push:
|
||||
branches: [main, staging]
|
||||
paths:
|
||||
[
|
||||
'{apis,circuits,@anonklub}/**/*.{js,json,ts,tsx,graphql}',
|
||||
'@anonklub/**/*.ts',
|
||||
'apis/**/*.{js,json,ts,graphql}',
|
||||
'test/**/*.{js,ts}',
|
||||
'contracts/**/*.sol',
|
||||
]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
@@ -2,28 +2,50 @@
|
||||
"name": "@anonklub/cli",
|
||||
"version": "1.0.0",
|
||||
"description": "CLI to build zk proofs of ethereum address ownership.",
|
||||
"main": "index.js",
|
||||
"keywords": [],
|
||||
"repository": "https://github.com/anonklub/anonklub/tree/main/%40anonklub/cli",
|
||||
"homepage": "https://github.com/anonklub/anonklub",
|
||||
"bugs": "https://github.com/anonklub/anonklub/issues",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/types/index.d.ts",
|
||||
"bin": {
|
||||
"akcli": "dist/index.js"
|
||||
},
|
||||
"keywords": [
|
||||
"zk",
|
||||
"ethereum",
|
||||
"cli",
|
||||
"proof",
|
||||
"zk-snark",
|
||||
"address",
|
||||
"ownership",
|
||||
"snarkjs",
|
||||
"circom",
|
||||
"privacy"
|
||||
],
|
||||
"author": "sripwoud <me@sripwoud.xyz>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anonklub/proof": "workspace:^",
|
||||
"@anonklub/proof": "1.0.0",
|
||||
"@anonklub/query": "1.0.0",
|
||||
"@noble/secp256k1": "^1.7.0",
|
||||
"delay": "^5.0.0",
|
||||
"ethers": "^5.7.2",
|
||||
"inquirer": "^8",
|
||||
"inquirer-fuzzy-path": "^2.3.0",
|
||||
"ora": "^5",
|
||||
"snarkjs": "^0.7.0",
|
||||
"terminal-link": "2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/inquirer": "^9.0.3",
|
||||
"@types/yargs": "^17.0.24",
|
||||
"snarkjs": "^0.7.0"
|
||||
"@types/yargs": "^17.0.24"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm run clean && tsc --project tsconfig.compile.json",
|
||||
"clean": "rm -rf dist",
|
||||
"dev": "tsnd --cls --exit-child --ignore-watch node_modules --quiet --respawn --rs --transpile-only src/index.ts",
|
||||
"start": "tsnd --cls --exit-child src/index.ts",
|
||||
"prepack": "npm run build",
|
||||
"start": "node dist/index.js",
|
||||
"typecheck": "tsc"
|
||||
}
|
||||
}
|
||||
|
||||
122
@anonklub/cli/src/Cli/index.ts
Normal file
122
@anonklub/cli/src/Cli/index.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { execSync } from 'child_process'
|
||||
import terminalLink from 'terminal-link'
|
||||
import { ProofRequest } from '@anonklub/proof'
|
||||
import { AnonSetResponse, AnonymitySet } from '@anonklub/query'
|
||||
import { Prompt } from '../Prompt/index.js'
|
||||
import { AnonSetLocation, AnonSetType, ProofAction } from '../types.js'
|
||||
import { CliI } from './interface'
|
||||
|
||||
class Cli implements CliI {
|
||||
anonSet: AnonymitySet
|
||||
prompt: Prompt
|
||||
anonSetResponse: AnonSetResponse | undefined
|
||||
|
||||
constructor({ anonSet: AnonymitySet, prompt: Prompt }) {
|
||||
this.anonSet = AnonymitySet
|
||||
this.prompt = Prompt
|
||||
}
|
||||
|
||||
async run() {
|
||||
const proveOrVerify = await this.prompt.askProveOrVerify()
|
||||
|
||||
if (proveOrVerify === ProofAction.PROVE) {
|
||||
await this.prove()
|
||||
} else if (proveOrVerify === ProofAction.VERIFY) {
|
||||
await this.verify()
|
||||
}
|
||||
}
|
||||
|
||||
async prove() {
|
||||
const location = await this.prompt.askAnonSetLocation()
|
||||
|
||||
if (location === AnonSetLocation.ONCHAIN) {
|
||||
const anonSetType = await this.prompt.askAnonSetType()
|
||||
if (anonSetType === AnonSetType.ERC20_BALANCE) {
|
||||
const { min, tokenAddress } = await this.prompt.askErc20AnonSetInputs()
|
||||
this.anonSetResponse = await this.anonSet.fromErc20Balance({
|
||||
min,
|
||||
tokenAddress,
|
||||
})
|
||||
} else if (anonSetType === AnonSetType.ETH_BALANCE) {
|
||||
const { min } = await this.prompt.askEthAnonSetInput()
|
||||
this.anonSetResponse = await this.anonSet.fromEthBalance({ min })
|
||||
} else if (anonSetType === AnonSetType.CRYPTO_PUNK) {
|
||||
this.anonSetResponse = await this.anonSet.fromCryptoPunkOwners()
|
||||
} else if (anonSetType === AnonSetType.ENS) {
|
||||
const { choice, id } = await this.prompt.askEnsAnonSetInputs()
|
||||
this.anonSetResponse = await this.anonSet.fromEnsProposalVoters({
|
||||
choice,
|
||||
id,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const path = await this.prompt.askAddressesFile()
|
||||
const { default: addresses } = await import(path)
|
||||
this.anonSetResponse = { data: addresses }
|
||||
}
|
||||
|
||||
if (
|
||||
this.anonSetResponse === undefined ||
|
||||
this.anonSetResponse?.data?.length === 0
|
||||
)
|
||||
throw new Error('No addresses found')
|
||||
if (this.anonSetResponse?.error !== undefined)
|
||||
throw this.anonSetResponse.error
|
||||
|
||||
const message = await this.prompt.askMessage()
|
||||
const rawSignature = await this.prompt.askRawSignature(message)
|
||||
|
||||
const proofRequest = new ProofRequest({
|
||||
addresses: this.anonSetResponse.data,
|
||||
message,
|
||||
rawSignature,
|
||||
url: 'http://anonklub.xyz:3000', // TODO: make this configurable
|
||||
})
|
||||
|
||||
const { jobId } = await proofRequest.submit()
|
||||
this.logProofRequestResult(jobId)
|
||||
}
|
||||
|
||||
async verify() {
|
||||
const proofPath = await this.prompt.askProofFile()
|
||||
const publicSignalsPath = await this.prompt.askPublicSignalsFile()
|
||||
// TODO: no need to ask for this file, include it in the pkg
|
||||
const verificationKeyPath = await this.prompt.askVerificationKeyFile()
|
||||
|
||||
console.log({ proofPath, publicSignalsPath, verificationKeyPath })
|
||||
|
||||
// FIXME: use groth16 module from snarkjs directly instead, otherwise we need to install snarkjs globally for this exact command to work
|
||||
const result = execSync(
|
||||
`snarkjs groth16 verify ${verificationKeyPath} ${publicSignalsPath} ${proofPath}`,
|
||||
)
|
||||
console.log(result.toString())
|
||||
}
|
||||
|
||||
logProofRequestResult(jobId: string) {
|
||||
console.log('Proof Request submitted successfully and being now processed.')
|
||||
console.log(
|
||||
`Your job id is ${jobId}. Do not share this id. You'll need it to access your proof file.`,
|
||||
)
|
||||
console.log(
|
||||
`Your proof input is already available at ${terminalLink(
|
||||
'input.json',
|
||||
`/proofs/${jobId}/input.json`, // TODO: prepend Proof API URL
|
||||
)}`,
|
||||
)
|
||||
console.log('Wait 5-10 min and your results will be available at:')
|
||||
console.log(
|
||||
terminalLink('proof.json', `/proofs/${jobId}/proof.json`), // // TODO: prepend Proof API URL
|
||||
)
|
||||
console.log(
|
||||
terminalLink(
|
||||
'publicSignals.json',
|
||||
`/proofs/${jobId}/publicSignals.json`, // TODO: prepend Proof API URL
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const cli = new Cli({
|
||||
anonSet: new AnonymitySet(),
|
||||
prompt: new Prompt(),
|
||||
})
|
||||
13
@anonklub/cli/src/Cli/interface.ts
Normal file
13
@anonklub/cli/src/Cli/interface.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { AnonSetResponse, AnonymitySet } from '@anonklub/query'
|
||||
import { Prompt } from '../Prompt/index.js'
|
||||
|
||||
export interface CliI {
|
||||
anonSet: AnonymitySet
|
||||
prompt: Prompt
|
||||
anonSetResponse: AnonSetResponse | undefined
|
||||
|
||||
logProofRequestResult: (jobId: string) => void
|
||||
run: () => Promise<void>
|
||||
prove: () => Promise<void>
|
||||
verify: () => Promise<void>
|
||||
}
|
||||
173
@anonklub/cli/src/Prompt/index.ts
Normal file
173
@anonklub/cli/src/Prompt/index.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import { ethers } from 'ethers'
|
||||
import inquirer, { DistinctQuestion, QuestionCollection } from 'inquirer'
|
||||
import { join } from 'path'
|
||||
import {
|
||||
EnsProposalVotersRequest,
|
||||
Erc20BalanceAnonSetRequest,
|
||||
EthBalanceAnonSetRequest,
|
||||
Request,
|
||||
} from '@anonklub/query'
|
||||
import { AnonSetLocation, AnonSetType, ProofAction } from '../types.js'
|
||||
import { PromptI } from './interface'
|
||||
|
||||
const excludeRegex =
|
||||
/(coverage|dist|Library|node_modules|turbo|package|tsconfig|\/\.\w+)/
|
||||
|
||||
export class Prompt implements PromptI {
|
||||
askFile = (fileName: string) =>
|
||||
this.prompt<string>({
|
||||
depthLimit: 6,
|
||||
excludeFilter: (path: string) => !path.endsWith('.json'),
|
||||
excludePath: (path: string) => excludeRegex.test(path),
|
||||
itemType: 'file',
|
||||
message: `What is the path to your ${fileName} .json file?`,
|
||||
name: 'file',
|
||||
// TODO: fix fuzzy search
|
||||
rootPath: join(process.env['HOME'] as string),
|
||||
// @ts-expect-error ???
|
||||
type: 'fuzzypath',
|
||||
})
|
||||
|
||||
prompt =
|
||||
<T>(questions: QuestionCollection | DistinctQuestion) =>
|
||||
async (): Promise<T> => {
|
||||
if (questions instanceof Array)
|
||||
return (await inquirer.prompt(questions as QuestionCollection)) as T
|
||||
|
||||
const { name } = questions as DistinctQuestion
|
||||
if (name === undefined) throw new Error('Question must have a name')
|
||||
const { [name]: answer } = await inquirer.prompt(questions)
|
||||
return answer
|
||||
}
|
||||
|
||||
askAddressesFile = this.askFile('addresses')
|
||||
|
||||
askProofFile = this.askFile('proof')
|
||||
|
||||
askPublicSignalsFile = this.askFile('public signals')
|
||||
|
||||
askVerificationKeyFile = this.askFile('verification key')
|
||||
askProveOrVerify = this.prompt<ProofAction>({
|
||||
choices: [
|
||||
{
|
||||
name: 'Prove',
|
||||
value: ProofAction.PROVE,
|
||||
},
|
||||
{
|
||||
name: 'Verify',
|
||||
value: ProofAction.VERIFY,
|
||||
},
|
||||
],
|
||||
|
||||
message: 'Hey anon, what would you like to do?',
|
||||
name: 'action',
|
||||
type: 'list',
|
||||
})
|
||||
|
||||
askAnonSetLocation = this.prompt<AnonSetLocation>({
|
||||
choices: [
|
||||
{
|
||||
name: 'On chain: query the ethereum blockchain as of latest block for me',
|
||||
value: AnonSetLocation.ONCHAIN,
|
||||
},
|
||||
{
|
||||
name: 'Locally: I have a json file with the list of addresses',
|
||||
value: AnonSetLocation.FILE,
|
||||
},
|
||||
],
|
||||
|
||||
message: 'Where does your anon set (list of addresses) lives?',
|
||||
name: 'anonSetLocation',
|
||||
type: 'list',
|
||||
})
|
||||
|
||||
askAnonSetType = this.prompt<AnonSetType>({
|
||||
choices: [
|
||||
{
|
||||
name: 'Holders of a min ERC20 Balance',
|
||||
value: AnonSetType.ERC20_BALANCE,
|
||||
},
|
||||
{
|
||||
name: 'Holders of a min ETH Balance',
|
||||
value: AnonSetType.ETH_BALANCE,
|
||||
},
|
||||
{
|
||||
name: 'CryptoPunk owners',
|
||||
value: AnonSetType.CRYPTO_PUNK,
|
||||
},
|
||||
{
|
||||
name: 'Addresses that participated in a ENS Governance voting round',
|
||||
value: AnonSetType.ENS,
|
||||
},
|
||||
],
|
||||
|
||||
message: 'What type of anon set do you want to prove?',
|
||||
name: 'anonSetType',
|
||||
type: 'list',
|
||||
})
|
||||
|
||||
askErc20AnonSetInputs = this.prompt<Erc20BalanceAnonSetRequest>([
|
||||
{
|
||||
default: '0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72',
|
||||
message: 'ERC20 address you want to query',
|
||||
name: 'tokenAddress',
|
||||
type: 'string',
|
||||
validate: (answer: string) =>
|
||||
ethers.utils.isAddress(answer) || 'Invalid address',
|
||||
},
|
||||
{
|
||||
default: '1000',
|
||||
message:
|
||||
'Minimum balance of ERC20 one must own to be part of the anonymity set',
|
||||
name: 'min',
|
||||
type: 'string',
|
||||
validate: (answer: string) =>
|
||||
answer.match(/^[0-9]+$/)?.length !== undefined || 'Invalid number',
|
||||
},
|
||||
])
|
||||
|
||||
askEthAnonSetInput = this.prompt<Request<EthBalanceAnonSetRequest>>({
|
||||
message:
|
||||
'What is the integer minimum balance (in ETH) addresses should hold?',
|
||||
name: 'minBalance',
|
||||
type: 'input',
|
||||
validate: (answer: string) =>
|
||||
answer.match(/^[0-9]+$/) !== null || 'Invalid integer',
|
||||
})
|
||||
|
||||
askEnsAnonSetInputs = this.prompt<Request<EnsProposalVotersRequest>>([
|
||||
{
|
||||
default: '0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72',
|
||||
message: 'ERC20 address you want to query',
|
||||
name: 'tokenAddress',
|
||||
type: 'string',
|
||||
validate: (answer: string) =>
|
||||
ethers.utils.isAddress(answer) || 'Invalid address',
|
||||
},
|
||||
{
|
||||
default: '1000',
|
||||
message:
|
||||
'Minimum balance of ERC20 one must own to be part of the anonymity set',
|
||||
name: 'min',
|
||||
type: 'string',
|
||||
validate: (answer: string) =>
|
||||
answer.match(/^[0-9]+$/)?.length !== undefined || 'Invalid number',
|
||||
},
|
||||
])
|
||||
|
||||
askMessage = this.prompt<string>({
|
||||
default: 'Hello PSE',
|
||||
message: 'Message you will sign',
|
||||
name: 'message',
|
||||
validate: (answer: string) => answer.length !== 0 || 'Invalid message',
|
||||
})
|
||||
|
||||
askRawSignature = async (message: string) =>
|
||||
this.prompt<string>({
|
||||
message: `Raw signature (0x-prefixed) of the message "${message}" signed by the private key of the address you want to prove is part of the list of addresses`,
|
||||
name: 'rawSignature',
|
||||
validate: (answer: string) =>
|
||||
answer.match(/^0x[0-9a-fA-F]{130}$/)?.length !== undefined ||
|
||||
'Invalid raw signature format',
|
||||
})()
|
||||
}
|
||||
27
@anonklub/cli/src/Prompt/interface.ts
Normal file
27
@anonklub/cli/src/Prompt/interface.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { DistinctQuestion, QuestionCollection } from 'inquirer'
|
||||
import {
|
||||
EnsProposalVotersRequest,
|
||||
Erc20BalanceAnonSetRequest,
|
||||
EthBalanceAnonSetRequest,
|
||||
Request,
|
||||
} from '@anonklub/query'
|
||||
import { AnonSetLocation, AnonSetType, ProofAction } from '../types'
|
||||
|
||||
export interface PromptI {
|
||||
askAnonSetLocation: () => Promise<AnonSetLocation>
|
||||
askAnonSetType: () => Promise<AnonSetType>
|
||||
askErc20AnonSetInputs: (
|
||||
anonSetType: AnonSetType,
|
||||
) => Promise<Request<Erc20BalanceAnonSetRequest>>
|
||||
askEthAnonSetInput: () => Promise<Request<EthBalanceAnonSetRequest>>
|
||||
askEnsAnonSetInputs: () => Promise<Request<EnsProposalVotersRequest>>
|
||||
askFile: (fileName: string) => () => Promise<string>
|
||||
askAddressesFile: () => Promise<string>
|
||||
askProofFile: () => Promise<string>
|
||||
askProveOrVerify: () => Promise<ProofAction>
|
||||
askPublicSignalsFile: () => Promise<string>
|
||||
askVerificationKeyFile: () => Promise<string>
|
||||
prompt: <T>(
|
||||
questions: QuestionCollection | DistinctQuestion,
|
||||
) => () => Promise<T>
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
import { execSync } from 'child_process'
|
||||
import { ProofRequest } from '@anonklub/proof'
|
||||
import { API_URLS } from '../constants'
|
||||
import {
|
||||
fetchEnsVotersAnonSet,
|
||||
fetchErc20AnonSet,
|
||||
fetchEthAnonSet,
|
||||
fetchPunksAnonSet,
|
||||
} from '../fetch-anonset'
|
||||
import { logResult } from './log-result'
|
||||
import {
|
||||
AnonSetLocation,
|
||||
AnonSetType,
|
||||
askAddressesFile,
|
||||
askAnonSetLocation,
|
||||
askAnonSetType,
|
||||
askEnsAnonsetInputs,
|
||||
askErc20AnonsetInputs,
|
||||
askEthAnonsetInputs,
|
||||
askMessage,
|
||||
askProofFile,
|
||||
askProveOrVerify,
|
||||
askPublicSignalsFile,
|
||||
askRawSignature,
|
||||
askVerificationKeyFile,
|
||||
ProofAction,
|
||||
} from './prompts'
|
||||
|
||||
export const cli = async () => {
|
||||
const proveOrVerify = await askProveOrVerify()
|
||||
|
||||
switch (proveOrVerify) {
|
||||
case ProofAction.PROVE: {
|
||||
let addresses: string[] = []
|
||||
const location = await askAnonSetLocation()
|
||||
|
||||
switch (location) {
|
||||
case AnonSetLocation.ONCHAIN: {
|
||||
const anonSetType = await askAnonSetType()
|
||||
switch (anonSetType) {
|
||||
case AnonSetType.ERC20_BALANCE: {
|
||||
const { min, tokenAddress } = await askErc20AnonsetInputs()
|
||||
addresses = await fetchErc20AnonSet({ min, tokenAddress })
|
||||
break
|
||||
}
|
||||
case AnonSetType.ETH_BALANCE: {
|
||||
const { minBalance } = await askEthAnonsetInputs()
|
||||
addresses = await fetchEthAnonSet({ minBalance })
|
||||
break
|
||||
}
|
||||
case AnonSetType.CRYPTO_PUNK: {
|
||||
addresses = await fetchPunksAnonSet()
|
||||
break
|
||||
}
|
||||
case AnonSetType.ENS: {
|
||||
const { choice, id } = await askEnsAnonsetInputs()
|
||||
addresses = await fetchEnsVotersAnonSet({ choice, id })
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case AnonSetLocation.FILE: {
|
||||
const path = await askAddressesFile()
|
||||
const { default: _addresses } = await import(path)
|
||||
addresses = _addresses
|
||||
}
|
||||
}
|
||||
|
||||
if (addresses.length === 0) throw new Error('No addresses list found')
|
||||
|
||||
const message = await askMessage()
|
||||
const rawSignature = await askRawSignature(message)
|
||||
|
||||
const proofRequest = new ProofRequest({
|
||||
addresses,
|
||||
message,
|
||||
rawSignature,
|
||||
url: API_URLS.PROVE,
|
||||
})
|
||||
|
||||
const { jobId } = await proofRequest.submit()
|
||||
logResult(jobId)
|
||||
|
||||
break
|
||||
}
|
||||
case ProofAction.VERIFY: {
|
||||
const proofPath = await askProofFile()
|
||||
const publicSignalsPath = await askPublicSignalsFile()
|
||||
const verificationKeyPath = await askVerificationKeyFile()
|
||||
|
||||
// TODO: use groth16 module from snarkjs directly instead
|
||||
const result = execSync(
|
||||
`node_modules/.bin/snarkjs groth16 verify ${verificationKeyPath} ${publicSignalsPath} ${proofPath}`,
|
||||
)
|
||||
console.log(result.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import terminalLink from 'terminal-link'
|
||||
import { API_URLS } from '../constants'
|
||||
|
||||
export const logResult = (jobId: string) => {
|
||||
console.log('Proof Request submitted successfully and being now processed.')
|
||||
console.log(
|
||||
`Your job id is ${jobId}. Do not share this id. You'll need it to access your proof file.`,
|
||||
)
|
||||
console.log(
|
||||
`Your proof input is already available at ${terminalLink(
|
||||
'input.json',
|
||||
`${API_URLS.PROVE}/proofs/${jobId}/input.json`,
|
||||
)}`,
|
||||
)
|
||||
console.log('Wait 5-10 min and your results will be available at:')
|
||||
console.log(
|
||||
terminalLink('proof.json', `${API_URLS.PROVE}/proofs/${jobId}/proof.json`),
|
||||
)
|
||||
console.log(
|
||||
terminalLink(
|
||||
'publicSignals.json',
|
||||
`${API_URLS.PROVE}/proofs/${jobId}/publicSignals.json`,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { prompt } from './prompt'
|
||||
|
||||
export enum AnonSetLocation {
|
||||
ONCHAIN = 'onchain',
|
||||
FILE = 'file',
|
||||
}
|
||||
|
||||
export const askAnonSetLocation = prompt<AnonSetLocation>({
|
||||
choices: [
|
||||
{
|
||||
name: 'On chain: query the ethereum blockchain as of latest block for me',
|
||||
value: AnonSetLocation.ONCHAIN,
|
||||
},
|
||||
{
|
||||
name: 'Locally: I have a json file with the list of addresses',
|
||||
value: AnonSetLocation.FILE,
|
||||
},
|
||||
],
|
||||
|
||||
message: 'Where does your anon set (list of addresses) lives?',
|
||||
name: 'anonSetLocation',
|
||||
type: 'list',
|
||||
})
|
||||
@@ -1,33 +0,0 @@
|
||||
import { prompt } from './prompt'
|
||||
|
||||
export enum AnonSetType {
|
||||
ERC20_BALANCE = 'erc20_balance',
|
||||
ETH_BALANCE = 'eth_balance',
|
||||
CRYPTO_PUNK = 'crypto_punk',
|
||||
ENS = 'ens',
|
||||
}
|
||||
|
||||
export const askAnonSetType = prompt<AnonSetType>({
|
||||
choices: [
|
||||
{
|
||||
name: 'Holders of a min ERC20 Balance',
|
||||
value: AnonSetType.ERC20_BALANCE,
|
||||
},
|
||||
{
|
||||
name: 'Holders of a min ETH Balance',
|
||||
value: AnonSetType.ETH_BALANCE,
|
||||
},
|
||||
{
|
||||
name: 'CryptoPunk owners',
|
||||
value: AnonSetType.CRYPTO_PUNK,
|
||||
},
|
||||
{
|
||||
name: 'Addresses that participated in a ENS Governance voting round',
|
||||
value: AnonSetType.ENS,
|
||||
},
|
||||
],
|
||||
|
||||
message: 'What type of anon set do you want to prove?',
|
||||
name: 'anonSetType',
|
||||
type: 'list',
|
||||
})
|
||||
@@ -1,22 +0,0 @@
|
||||
import { prompt } from './prompt'
|
||||
|
||||
export enum Choice {
|
||||
For = 'FOR',
|
||||
Against = 'AGAINST',
|
||||
Abstain = 'ABSTAIN',
|
||||
}
|
||||
|
||||
export const askEnsAnonsetInputs = prompt<{ choice: Choice; id: string }>([
|
||||
{
|
||||
message:
|
||||
'What is the ENS Proposal ID (can be found e.g on https://tally.xyz)?',
|
||||
name: 'id',
|
||||
type: 'input',
|
||||
},
|
||||
{
|
||||
choices: [Choice.For, Choice.Against, Choice.Abstain],
|
||||
message: 'Vote?',
|
||||
name: 'choice',
|
||||
type: 'list',
|
||||
},
|
||||
])
|
||||
@@ -1,25 +0,0 @@
|
||||
import { ethers } from 'ethers'
|
||||
import { prompt } from './prompt'
|
||||
|
||||
export const askErc20AnonsetInputs = prompt<{
|
||||
tokenAddress: string
|
||||
min: string
|
||||
}>([
|
||||
{
|
||||
default: '0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72',
|
||||
message: 'ERC20 address you want to query',
|
||||
name: 'tokenAddress',
|
||||
type: 'string',
|
||||
validate: (answer: string) =>
|
||||
ethers.utils.isAddress(answer) || 'Invalid address',
|
||||
},
|
||||
{
|
||||
default: '1000',
|
||||
message:
|
||||
'Minimum balance of ERC20 one must own to be part of the anonymity set',
|
||||
name: 'min',
|
||||
type: 'string',
|
||||
validate: (answer: string) =>
|
||||
answer.match(/^[0-9]+$/)?.length !== undefined || 'Invalid number',
|
||||
},
|
||||
])
|
||||
@@ -1,10 +0,0 @@
|
||||
import { prompt } from './prompt'
|
||||
|
||||
export const askEthAnonsetInputs = prompt<{ minBalance: string }>({
|
||||
message:
|
||||
'What is the integer minimum balance (in ETH) addresses should hold?',
|
||||
name: 'minBalance',
|
||||
type: 'input',
|
||||
validate: (answer: string) =>
|
||||
answer.match(/^[0-9]+$/) !== null || 'Invalid integer',
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
import { join } from 'path'
|
||||
import { prompt } from './prompt'
|
||||
|
||||
const excludeRegex =
|
||||
/(coverage|dist|Library|node_modules|turbo|package|tsconfig|\/\.\w+)/
|
||||
|
||||
const askFile = (fileName: string) =>
|
||||
prompt<string>({
|
||||
depthLimit: 6,
|
||||
excludeFilter: (path: string) => !path.endsWith('.json'),
|
||||
excludePath: (path: string) => excludeRegex.test(path),
|
||||
itemType: 'file',
|
||||
message: `What is the path to your ${fileName} .json file?`,
|
||||
name: 'file',
|
||||
rootPath: join(__dirname, '..', '..', '..', '..'),
|
||||
// @ts-expect-error
|
||||
type: 'fuzzypath',
|
||||
})
|
||||
|
||||
export const askAddressesFile = askFile('addresses.')
|
||||
export const askProofFile = askFile('proof')
|
||||
export const askPublicSignalsFile = askFile('public signals')
|
||||
export const askVerificationKeyFile = askFile('verification key')
|
||||
@@ -1,8 +0,0 @@
|
||||
import { prompt } from './prompt'
|
||||
|
||||
export const askMessage = prompt<string>({
|
||||
default: 'Hello PSE',
|
||||
message: 'Message you will sign',
|
||||
name: 'message',
|
||||
validate: (answer: string) => answer.length !== 0 || 'Invalid message',
|
||||
})
|
||||
@@ -1,22 +0,0 @@
|
||||
import { prompt } from './prompt'
|
||||
|
||||
export enum ProofAction {
|
||||
PROVE = 'prove',
|
||||
VERIFY = 'verify',
|
||||
}
|
||||
|
||||
export const askProveOrVerify = prompt<ProofAction>({
|
||||
choices: [
|
||||
{
|
||||
name: 'Prove',
|
||||
value: ProofAction.PROVE,
|
||||
},
|
||||
{
|
||||
name: 'Verify',
|
||||
value: ProofAction.VERIFY,
|
||||
},
|
||||
],
|
||||
message: 'Hey anon, what would you like to do?',
|
||||
name: 'action',
|
||||
type: 'list',
|
||||
})
|
||||
@@ -1,10 +0,0 @@
|
||||
import { prompt } from './prompt'
|
||||
|
||||
export const askRawSignature = async (message: string) =>
|
||||
prompt<string>({
|
||||
message: `Raw signature (0x-prefixed) of the message "${message}" signed by the private key of the address you want to prove is part of the list of addresses`,
|
||||
name: 'rawSignature',
|
||||
validate: (answer: string) =>
|
||||
answer.match(/^0x[0-9a-fA-F]{130}$/)?.length !== undefined ||
|
||||
'Invalid raw signature format',
|
||||
})()
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* @file Automatically generated by barrelsby.
|
||||
*/
|
||||
|
||||
export * from './ask-anonset-location'
|
||||
export * from './ask-anonset-type'
|
||||
export * from './ask-ens-anonset-inputs'
|
||||
export * from './ask-erc20-anonset-inputs'
|
||||
export * from './ask-eth-anonset-inputs'
|
||||
export * from './ask-file'
|
||||
export * from './ask-message'
|
||||
export * from './ask-prove-or-verify'
|
||||
export * from './ask-raw-signature'
|
||||
export * from './prompt'
|
||||
@@ -1,16 +0,0 @@
|
||||
import inquirer, { DistinctQuestion, QuestionCollection } from 'inquirer'
|
||||
import inquirerFuzzyPath from 'inquirer-fuzzy-path'
|
||||
|
||||
inquirer.registerPrompt('fuzzypath', inquirerFuzzyPath)
|
||||
|
||||
export const prompt =
|
||||
<T>(questions: QuestionCollection | DistinctQuestion) =>
|
||||
async (): Promise<T> => {
|
||||
if (questions instanceof Array)
|
||||
return (await inquirer.prompt(questions as QuestionCollection)) as T
|
||||
|
||||
const { name } = questions as DistinctQuestion
|
||||
if (name === undefined) throw new Error('Question must have a name')
|
||||
const { [name]: answer } = await inquirer.prompt(questions)
|
||||
return answer
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export const API_URLS = {
|
||||
PROVE: 'http://localhost:3000',
|
||||
QUERY: 'https://anonset.fly.dev',
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import ora from 'ora'
|
||||
import { Choice } from './cli/prompts'
|
||||
import { API_URLS } from './constants'
|
||||
|
||||
const fetchAnonset = async (url): Promise<string[]> => {
|
||||
const spinner = ora(
|
||||
'Fetching Anonymity Set from 3rd party ethereum indexing services...',
|
||||
)
|
||||
|
||||
spinner.start()
|
||||
const res = await fetch(url)
|
||||
const anonSet = await res.json()
|
||||
spinner.stop()
|
||||
|
||||
return anonSet
|
||||
}
|
||||
|
||||
export const fetchErc20AnonSet = async ({
|
||||
min,
|
||||
tokenAddress,
|
||||
}: {
|
||||
tokenAddress: string
|
||||
min: string
|
||||
}) =>
|
||||
fetchAnonset(
|
||||
`${API_URLS.QUERY}/balance/ERC20?${new URLSearchParams({
|
||||
min,
|
||||
tokenAddress,
|
||||
}).toString()}`,
|
||||
)
|
||||
|
||||
export const fetchEthAnonSet = async ({ minBalance }: { minBalance: string }) =>
|
||||
fetchAnonset(`${API_URLS.QUERY}/balance/ETH?min=${minBalance}`)
|
||||
|
||||
export const fetchEnsVotersAnonSet = async ({
|
||||
choice,
|
||||
id,
|
||||
}: {
|
||||
choice: Choice
|
||||
id: string
|
||||
}) =>
|
||||
fetchAnonset(
|
||||
`${API_URLS.QUERY}/balance/ERC20?${new URLSearchParams({
|
||||
choice,
|
||||
id,
|
||||
}).toString()}`,
|
||||
)
|
||||
|
||||
export const fetchPunksAnonSet = async () =>
|
||||
fetchAnonset(`${API_URLS.QUERY}/punks`)
|
||||
@@ -1,6 +1,8 @@
|
||||
import { cli } from './cli'
|
||||
#!/usr/bin/env node
|
||||
|
||||
cli().catch((err) => {
|
||||
import { cli } from './Cli'
|
||||
|
||||
cli.run().catch((err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
16
@anonklub/cli/src/types.ts
Normal file
16
@anonklub/cli/src/types.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export enum ProofAction {
|
||||
PROVE = 'prove',
|
||||
VERIFY = 'verify',
|
||||
}
|
||||
|
||||
export enum AnonSetLocation {
|
||||
ONCHAIN = 'onchain',
|
||||
FILE = 'file',
|
||||
}
|
||||
|
||||
export enum AnonSetType {
|
||||
ERC20_BALANCE = 'erc20_balance',
|
||||
ETH_BALANCE = 'eth_balance',
|
||||
CRYPTO_PUNK = 'crypto_punk',
|
||||
ENS = 'ens',
|
||||
}
|
||||
16
@anonklub/cli/tsconfig.compile.json
Normal file
16
@anonklub/cli/tsconfig.compile.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["test", "test/jest.config.ts"],
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"declarationDir": "dist/types",
|
||||
"declarationMap": true,
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"noEmit": false,
|
||||
"outDir": "dist",
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true,
|
||||
"target": "ES2022"
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "node",
|
||||
"target": "es2020"
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "Node",
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"strictPropertyInitialization": false
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../proof"
|
||||
}
|
||||
]
|
||||
"include": ["src", "test"]
|
||||
}
|
||||
|
||||
@@ -1,22 +1,37 @@
|
||||
{
|
||||
"name": "@anonklub/proof",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared utilities for @anonklub",
|
||||
"main": "dist/index.js",
|
||||
"description": "Build Anonymous Proofs (Requests) of Ethereum Address Ownership",
|
||||
"author": "BlakeMScurr <blakemscurr@gmail.com>",
|
||||
"contributors": ["sripwoud <me@sripwoud.xyz>"],
|
||||
"repository": "https://github.com/anonklub/anonklub/tree/main/%40anonklub/proof",
|
||||
"homepage": "https://github.com/anonklub/anonklub",
|
||||
"bugs": "https://github.com/anonklub/anonklub/issues",
|
||||
"keywords": [
|
||||
"zk",
|
||||
"ethereum",
|
||||
"proof",
|
||||
"zk-snark",
|
||||
"address",
|
||||
"ownership",
|
||||
"snarkjs",
|
||||
"circom",
|
||||
"privacy"
|
||||
],
|
||||
"license": "MIT",
|
||||
"files": ["dist"],
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/types/index.d.ts",
|
||||
"dependencies": {
|
||||
"@noble/secp256k1": "^1.7.0",
|
||||
"circomlibjs": "^0.1.7",
|
||||
"ethers": "^5.7.2",
|
||||
"undici": "^5.21.2"
|
||||
},
|
||||
"files": ["dist"],
|
||||
"types": "dist/types/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc --project tsconfig.compile.json",
|
||||
"test": "jest -c test/jest.config.ts",
|
||||
"build": "pnpm run clean && tsc --project tsconfig.compile.json",
|
||||
"clean": "rm -rf dist",
|
||||
"prepack": "npm run build",
|
||||
"typecheck": "tsc"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ProofRequestJson } from '../ProofRequest/interface'
|
||||
import { ProofRequestJson } from '../ProofRequest'
|
||||
|
||||
export interface CircuitInputArgs {
|
||||
proofRequest: ProofRequestJson
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
ProofResult,
|
||||
} from './interface'
|
||||
|
||||
export { ProofRequestJson }
|
||||
|
||||
export class ProofRequest implements ProofRequestInterface {
|
||||
public readonly addresses: string[]
|
||||
public readonly message: string
|
||||
|
||||
@@ -10,4 +10,3 @@ export {
|
||||
export { MerkleTree } from './MerkleTree'
|
||||
export * from './poseidon'
|
||||
export * from './ProofRequest'
|
||||
export { ProofRequestJson } from './ProofRequest/interface'
|
||||
|
||||
@@ -2,14 +2,6 @@ import type { JestConfigWithTsJest } from 'ts-jest'
|
||||
|
||||
const jestConfig: JestConfigWithTsJest = {
|
||||
coveragePathIgnorePatterns: ['!src/poseidon.ts'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 37,
|
||||
functions: 55,
|
||||
lines: 75,
|
||||
statements: 75,
|
||||
},
|
||||
},
|
||||
displayName: '@anonklub/proof',
|
||||
preset: '@anonklub/test',
|
||||
rootDir: '..',
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["test", "test/jest.config.ts"],
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"declarationDir": "dist/types",
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"inlineSourceMap": true,
|
||||
"noEmit": false,
|
||||
"outDir": "dist",
|
||||
"moduleResolution": "Node",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"declarationDir": "./dist/types",
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true
|
||||
"target": "ES2022"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src", "test"],
|
||||
"compilerOptions": {
|
||||
"noUncheckedIndexedAccess": false
|
||||
}
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "Node",
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"strictPropertyInitialization": false
|
||||
},
|
||||
"include": ["src", "test"]
|
||||
}
|
||||
|
||||
@@ -2,17 +2,20 @@
|
||||
"name": "@anonklub/query",
|
||||
"version": "1.0.0",
|
||||
"description": "Query Anonymity sets of Ethereum addresses",
|
||||
"main": "dist/index.js",
|
||||
"repository": "https://github.com/anonklub/anonklub/tree/main/%40anonklub/query",
|
||||
"homepage": "https://github.com/anonklub/anonklub",
|
||||
"bugs": "https://github.com/anonklub/anonklub/issues",
|
||||
"files": ["dist"],
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/types/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc --project tsconfig.compile.json",
|
||||
"test": "jest -c test/jest.config.ts",
|
||||
"build": "pnpm run clean && tsc --project tsconfig.compile.json",
|
||||
"clean": "rm -rf dist",
|
||||
"prepack": "npm run build",
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"keywords": ["ethereum", "query", "sets", "anonymity", "addresses"],
|
||||
"keywords": ["ethereum", "query", "sets", "anonymity", "address"],
|
||||
"author": "sripwoud <me@sripwoud.xyz>",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"class-validator": "^0.14.0"
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { URLS } from 'CONSTANTS'
|
||||
import { fetchJson } from 'fetch-json'
|
||||
import { URLS } from '../CONSTANTS'
|
||||
import { fetchJson } from '../fetch-json'
|
||||
import {
|
||||
EnsProposalVotersRequest,
|
||||
Erc20BalanceAnonSetRequest,
|
||||
EthBalanceAnonSetRequest,
|
||||
} from 'requests'
|
||||
import { Endpoint, Environment, Request, RequestClass } from 'types'
|
||||
} from '../requests'
|
||||
import { Endpoint, Environment, Request, RequestClass } from '../types'
|
||||
import { AnonymitySetI } from './interface'
|
||||
|
||||
export class AnonymitySet implements AnonymitySetI {
|
||||
|
||||
@@ -2,8 +2,8 @@ import {
|
||||
EnsProposalVotersRequest,
|
||||
Erc20BalanceAnonSetRequest,
|
||||
EthBalanceAnonSetRequest,
|
||||
} from 'requests'
|
||||
import { AnonSetResponse } from 'types'
|
||||
} from '../requests'
|
||||
import { AnonSetResponse } from '../types'
|
||||
|
||||
export interface AnonymitySetI {
|
||||
fromEthBalance: ({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Environment } from 'types'
|
||||
import { Environment } from './types'
|
||||
|
||||
export const URLS = {
|
||||
[Environment.PRODUCTION]: 'https://anonset.fly.dev',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AnonSetResponse } from 'types'
|
||||
import { AnonSetResponse } from './types'
|
||||
|
||||
export const fetchJson = async (
|
||||
url: string,
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
export * from 'AnonymitySet'
|
||||
export * from 'CONSTANTS'
|
||||
export * from 'requests'
|
||||
export { Choice, Environment } from 'types'
|
||||
export * from './AnonymitySet'
|
||||
export * from './CONSTANTS'
|
||||
export * from './requests'
|
||||
export {
|
||||
AnonSetResponse,
|
||||
Choice,
|
||||
Endpoint,
|
||||
Environment,
|
||||
Request,
|
||||
} from './types'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IsDefined, IsIn, IsNumberString, Length } from 'class-validator'
|
||||
import { Choice } from 'types'
|
||||
import { Choice } from '../types'
|
||||
|
||||
export class EnsProposalVotersRequest {
|
||||
@IsDefined()
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
EnsProposalVotersRequest,
|
||||
Erc20BalanceAnonSetRequest,
|
||||
EthBalanceAnonSetRequest,
|
||||
} from 'requests'
|
||||
} from './requests'
|
||||
|
||||
export enum Environment {
|
||||
PRODUCTION = 'production',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { createEthAddresses } from '@anonklub/test'
|
||||
import { faker } from '@faker-js/faker'
|
||||
import { Endpoint } from 'types'
|
||||
import { AnonymitySet, Choice, Environment, URLS } from '../src'
|
||||
import { AnonymitySet, Choice, Endpoint, Environment, URLS } from '../src'
|
||||
|
||||
describe('AnonymitySet', () => {
|
||||
let anonymitySet: AnonymitySet
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["test", "test/jest.config.ts"],
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"declarationDir": "dist/types",
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"inlineSourceMap": true,
|
||||
"noEmit": false,
|
||||
"outDir": "dist",
|
||||
"moduleResolution": "Node",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"declarationDir": "./dist/types",
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true
|
||||
"target": "ES6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "Node",
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"strictPropertyInitialization": false
|
||||
},
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
{
|
||||
"name": "@anonklub/prove-api",
|
||||
"version": "1.0.0",
|
||||
"description": "ZK Proofs generation API",
|
||||
"main": "index.js",
|
||||
"author": "BlakeMScurr <blakemscurr@gmail.com>",
|
||||
"contributors": ["sripwoud <me@sripwoud.xyz>"],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anonklub/proof": "workspace:^",
|
||||
"@anonklub/proof": "1.0.0",
|
||||
"@bull-board/api": "^5.0.0",
|
||||
"@bull-board/express": "^5.0.0",
|
||||
"@noble/hashes": "^1.3.0",
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
{
|
||||
"name": "@anonklub/query-api",
|
||||
"version": "1.0.0",
|
||||
"description": "Express Server Template",
|
||||
"main": "index.js",
|
||||
"author": "sripwoud <me@sripwoud.xyz>",
|
||||
"license": "MIT",
|
||||
"_moduleAliases": {
|
||||
"@controllers": "dist/api/controllers",
|
||||
"@decorators": "dist/decorators",
|
||||
@@ -61,7 +56,6 @@
|
||||
"start.dev": "NODE_ENV=development tsnd --rs --exit-child --clear --quiet --files -r tsconfig-paths/register ./src",
|
||||
"start.prod": "node -r module-alias/register ./dist",
|
||||
"start.watch": "tsnd --respawn --rs --exit-child --clear --quiet --files -r tsconfig-paths/register ./src",
|
||||
"test": "jest -c test/jest.config.ts",
|
||||
"typecheck": "tsc"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ import { getMesh, ExecuteMeshFn, SubscribeMeshFn, MeshContext as BaseMeshContext
|
||||
import { MeshStore, FsStoreStorageAdapter } from '@graphql-mesh/store';
|
||||
import { path as pathModule } from '@graphql-mesh/cross-helpers';
|
||||
import { ImportFn } from '@graphql-mesh/types';
|
||||
import type { CryptopunksTypes } from './sources/cryptopunks/types';
|
||||
import type { EnsGovernanceTypes } from './sources/ens-governance/types';
|
||||
import type { CryptopunksTypes } from './sources/cryptopunks/types';
|
||||
import * as importedModule$0 from "./sources/cryptopunks/introspectionSchema";
|
||||
import * as importedModule$1 from "./sources/ens-governance/introspectionSchema";
|
||||
export type Maybe<T> = T | null;
|
||||
|
||||
@@ -4,14 +4,6 @@ import { compilerOptions } from '../tsconfig.json'
|
||||
|
||||
const jestConfig: JestConfigWithTsJest = {
|
||||
coveragePathIgnorePatterns: ['.graphclient', 'lib'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 50,
|
||||
functions: 65,
|
||||
lines: 75,
|
||||
statements: 75,
|
||||
},
|
||||
},
|
||||
displayName: 'query-api',
|
||||
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
|
||||
prefix: '<rootDir>/',
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
{
|
||||
"name": "@anonklub/circom",
|
||||
"version": "1.0.0",
|
||||
"description": "Prove membership in a poseidon merkle tree by proving an ECDSA signature",
|
||||
"author": "BlakeMScurr <blakemscurr@gmail.com>",
|
||||
"contributors": ["sripwoud <me@sripwoud.xyz>"],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@anonklub/proof": "workspace:^",
|
||||
"@noble/secp256k1": "^1.7.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "contracts",
|
||||
"name": "@anonklub/contracts",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"build": "forge build",
|
||||
@@ -9,7 +9,7 @@
|
||||
"_format.fix": "forge fmt --root ..",
|
||||
"_lint": "solhint '**/*.sol'",
|
||||
"_lint.fix": "solhint --fix '**/*.sol'",
|
||||
"test": "forge test"
|
||||
"_test": "forge test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"solhint": "^3.4.1"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "anonklub-aws",
|
||||
"name": "@anonklub/infra",
|
||||
"main": "index.ts",
|
||||
"devDependencies": {
|
||||
"@types/node": "^16"
|
||||
|
||||
7
jest.config.ts
Normal file
7
jest.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { JestConfigWithTsJest } from 'ts-jest'
|
||||
|
||||
const jestConfig: JestConfigWithTsJest = {
|
||||
preset: '@anonklub/test',
|
||||
}
|
||||
|
||||
export default jestConfig
|
||||
@@ -33,7 +33,7 @@
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "turbo run barrels && turbo run build",
|
||||
"build": "turbo run build",
|
||||
"build.docker.discord": "docker build -f discord-bot/Dockerfile . -t discord-bot",
|
||||
"build.docker.prove": "docker build -f apis/prove/Dockerfile . -t prove-api",
|
||||
"build.docker.query": "docker build -f apis/query/Dockerfile . -t query-api",
|
||||
@@ -54,8 +54,8 @@
|
||||
"_lint.fix": "eslint . --ext .js,.ts,.tsx --fix",
|
||||
"lint.fix": "turbo run _lint.fix",
|
||||
"test.watch": "jest --watch",
|
||||
"pre.test": "turbo run build --filter @anonklub/proof",
|
||||
"test": "pnpm run pre.test && turbo run test --filter !@anonklub/circom",
|
||||
"_test": "jest",
|
||||
"test": "turbo run _test",
|
||||
"typecheck": "turbo run typecheck",
|
||||
"validate": "turbo run _format _lint build --cache-dir=.turbo"
|
||||
},
|
||||
|
||||
796
pnpm-lock.yaml
generated
796
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,10 @@
|
||||
const tsJestPreset = require('ts-jest/jest-preset')
|
||||
const { resolve } = require('path')
|
||||
const { join } = require('path')
|
||||
|
||||
const projects = [
|
||||
...['proof', 'query'].map((name) => `@anonklub/${name}`),
|
||||
'apis/query',
|
||||
].map((name) => join(__dirname, '..', name, 'test'))
|
||||
|
||||
/**
|
||||
* @type {import('ts-jest').JestConfigWithTsJest}
|
||||
@@ -9,13 +14,13 @@ module.exports = {
|
||||
clearMocks: true,
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: ['src/**/*.ts', '!**/*.d.ts'],
|
||||
coverageDirectory: resolve('../coverage'),
|
||||
coverageDirectory: join(__dirname, '..', 'coverage'),
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 87,
|
||||
functions: 90,
|
||||
lines: 90,
|
||||
statements: 90,
|
||||
branches: 50,
|
||||
functions: 70,
|
||||
lines: 80,
|
||||
statements: 80,
|
||||
},
|
||||
},
|
||||
moduleDirectories: [
|
||||
@@ -25,6 +30,7 @@ module.exports = {
|
||||
'<rootDir>/src',
|
||||
],
|
||||
moduleFileExtensions: ['js', 'json', 'ts'],
|
||||
projects,
|
||||
rootDir: '..',
|
||||
setupFilesAfterEnv: [require.resolve('../test/setup.ts')],
|
||||
testPathIgnorePatterns: [
|
||||
|
||||
@@ -26,5 +26,6 @@
|
||||
"strict": true,
|
||||
"target": "ES2022"
|
||||
},
|
||||
"files": ["global.d.ts"]
|
||||
"files": ["global.d.ts"],
|
||||
"include": ["jest.config.ts"]
|
||||
}
|
||||
|
||||
@@ -77,13 +77,16 @@
|
||||
"build.graph": {
|
||||
"outputs": ["src/lib/graph/.graphclient/**"]
|
||||
},
|
||||
"test": {
|
||||
"//#_test": {
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": [
|
||||
"{apis/{prove,query},@anonklub,circuits/circom}/{src,test}/**/*.{js,jsx,ts,tsx}",
|
||||
"contracts/src/**/*.sol"
|
||||
"{apis/{prove,query},@anonklub,circuits/circom}/{src,test}/**/*.{js,jsx,ts,tsx}"
|
||||
]
|
||||
},
|
||||
"_test": {
|
||||
"cache": false,
|
||||
"inputs": ["contracts/src/**/*.sol"]
|
||||
},
|
||||
"deploy": {
|
||||
"dependsOn": ["test"]
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
{
|
||||
"name": "@anonklub/ui",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"author": "sripwoud <me@sripwoud.xyz>",
|
||||
"browser": {
|
||||
"fs": false,
|
||||
"tls": false,
|
||||
@@ -40,7 +37,6 @@
|
||||
"pinojs": "^1.0.0",
|
||||
"prettier": "2.8.8"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
"dev": "next dev",
|
||||
|
||||
Reference in New Issue
Block a user