diff --git a/README.md b/README.md index 0a64a19..6b2c696 100644 --- a/README.md +++ b/README.md @@ -58,4 +58,4 @@ pnpm watch To see the apps in action, run either `client-web` or `client-nodejs`. Then run `test-app` and open `test-app` at [http://localhost:3200](http://localhost:3200). -By default it will connect to `client-web` on `http://localhost:5173`. By clicking the lightning icon in the top-right of the screen, you can configure it to connect to another client. `client-nodejs` runs at `http://localhost:3050` - remember to select "websocket" rather than "iframe" when using `client-nodejs`. +By default it will connect to `client-web` on `http://localhost:5173`. By clicking the lightning icon in the top-right of the screen, you can configure it to connect to another client. `client-nodejs` runs at `http://localhost:3050` - remember to select "websocket" rather than "iframe" when using `client-nodejs`. \ No newline at end of file diff --git a/examples/test-app/package.json b/examples/test-app/package.json index 3bd73e0..8b05669 100644 --- a/examples/test-app/package.json +++ b/examples/test-app/package.json @@ -12,7 +12,7 @@ "dependencies": { "@parcnet-js/app-connector": "workspace:*", "@parcnet-js/podspec": "workspace:*", - "@pcd/pod": "0.1.6", + "@pcd/pod": "0.1.7", "json-bigint": "^1.0.0", "lodash": "^4.17.21", "react": "^18.2.0", diff --git a/examples/test-app/src/apis/GPC.tsx b/examples/test-app/src/apis/GPC.tsx index a390376..cb89b13 100644 --- a/examples/test-app/src/apis/GPC.tsx +++ b/examples/test-app/src/apis/GPC.tsx @@ -9,32 +9,36 @@ import { useParcnetClient } from "../hooks/useParcnetClient"; const request: PodspecProofRequest = { pods: { pod1: { - entries: { - wis: { - type: "int", - inRange: { min: BigInt(5), max: BigInt(1000) }, - isRevealed: true + pod: { + entries: { + wis: { + type: "int", + inRange: { min: BigInt(5), max: BigInt(1000) }, + isRevealed: true + }, + str: { type: "int", inRange: { min: BigInt(5), max: BigInt(1000) } } }, - str: { type: "int", inRange: { min: BigInt(5), max: BigInt(1000) } } - }, - tuples: [ - { - entries: ["wis", "str"], - isNotMemberOf: [ - [ - { type: "int", value: BigInt(100) }, - { type: "int", value: BigInt(500) } + tuples: [ + { + entries: ["wis", "str"], + isNotMemberOf: [ + [ + { type: "int", value: BigInt(100) }, + { type: "int", value: BigInt(500) } + ] ] - ] - } - ] + } + ] + } }, pod2: { - entries: { - test: { - type: "string", - isMemberOf: [{ type: "string", value: "secret" }], - isRevealed: true + pod: { + entries: { + test: { + type: "string", + isMemberOf: [{ type: "string", value: "secret" }], + isRevealed: true + } } } } diff --git a/examples/test-app/src/apis/PODSection.tsx b/examples/test-app/src/apis/PODSection.tsx index b5e9017..30acd88 100644 --- a/examples/test-app/src/apis/PODSection.tsx +++ b/examples/test-app/src/apis/PODSection.tsx @@ -464,7 +464,10 @@ function DeletePOD({ z }: { z: ParcnetAPI }): ReactNode { function SubscribeToPODs({ z }: { z: ParcnetAPI }): ReactNode { const [pods, setPODs] = useState([]); - const [subscription, setSubscription] = useState(null); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const [subscription, setSubscription] = useState | null>( + null + ); return (
diff --git a/packages/app-connector/README.md b/packages/app-connector/README.md new file mode 100644 index 0000000..3699550 --- /dev/null +++ b/packages/app-connector/README.md @@ -0,0 +1,108 @@ +# PARCNET App Connector + +The app connector is for connecting your app to PARCNET. + +## Getting started + +Install the package: + +``` +npm install @parcnet-js/app-connector +``` + +Then connect to a PARCNET client: + +```ts +import { connect } from "@parcnet-js/app-connector"; + +// Ensure this HTML element exists: +const element = document.getElementById("app-connector"); +// The URL to the PARCNET client, e.g. Zupass +const clientUrl = "http://localhost:5173"; + +// Returns an API object that you can use to invoke actions +// See api_wrapper.ts for details +const api = await connect({ name: "My App Name", permissions: []}, element, clientUrl); +``` + +This will give you an API object, which can be used to invoke various APIs. + +## APIs + +### Identity + +#### identity.getSemaphoreV3Commitment(); +```ts +await api.identity.getSemaphoreV3Commitment(); +``` + +This returns a `bigint` representing the Semaphore v3 commitment. + +#### identity.getSemaphoreV4Commitment(); +```ts +await api.identity.getSemaphoreV4Commitment(); +``` + +This returns a `bigint` representing the Semaphore v4 commitment. + +#### identity.getSemaphoreV4PublicKey(); +```ts +await api.identity.getSemaphoreV4PublicKey(); +``` + +This returns a `string` representing the Semaphore v4 public key. + +### POD + +#### pod.sign +```ts +const pod: POD = await api.pod.sign({ type: "string", value: "entry value" }); +``` + +This will request the signature of a POD with the entries given. An exception may be thrown if the POD is invalid, or if permission is refused for the signing of the POD. Otherwise, the signed POD is returned. + +#### pod.insert +```ts +await api.pod.insert(pod); +``` + +This inserts a POD into the user's POD collection for permanent storage. + +#### pod.delete +```ts +await api.pod.delete(pod_signature); +``` + +This deletes a POD from the user's POD collection, as identified by the POD's signature, which can be retrieved from the `signature` property on a POD. + +#### pod.query +```ts +await api.pod.query({ + entries: { + str: { type: "int", inRange: { min: 10n, max: 100n }}, + wis: { type: "int", inRange: { min: 5n, max: 100n }} + } +}); +``` + +This queries the user's POD collection for matching PODs. For details of the possible query types, see the `@parcnet-js/podspec` package. + +### GPC + +#### gpc.prove +```ts +await api.gpc.prove({ + pods: { + podName: { + pod: { + entries: { + str: { type: "int", inRange: { min: 10n, max: 100n }}, + wis: { type: "int", inRange: { min: 5n, max: 100n }} + } + } + } + } +}) +``` + +This requests that the user make a GPC proof about a POD in their collection which matches the criteria specified above. For more examples of the available criteria, see the `@parcnet-js/podspec` library. \ No newline at end of file diff --git a/packages/app-connector/src/api_wrapper.ts b/packages/app-connector/src/api_wrapper.ts index 1bae57c..2a6422e 100644 --- a/packages/app-connector/src/api_wrapper.ts +++ b/packages/app-connector/src/api_wrapper.ts @@ -19,12 +19,16 @@ import type { ParcnetRPCConnector } from "./rpc_client.js"; * It also allows the caller to run the query immediately, which is useful on * first creating the subscription, before any updates are available. */ -export class Subscription { +export class Subscription { #emitter: EventEmitter; - #query: p.PodSpec; + #query: p.PodSpec; #api: ParcnetPODWrapper; - constructor(query: p.PodSpec, emitter: EventEmitter, api: ParcnetPODWrapper) { + constructor( + query: p.PodSpec, + emitter: EventEmitter, + api: ParcnetPODWrapper + ) { this.#emitter = emitter; this.#query = query; this.#api = api; @@ -61,12 +65,14 @@ class ParcnetPODWrapper { }); } - async query(query: p.PodSpec): Promise { + async query(query: p.PodSpec): Promise { const pods = await this.#api.pod.query(query.schema); return pods.map((pod) => POD.deserialize(pod)); } - async subscribe(query: p.PodSpec): Promise { + async subscribe( + query: p.PodSpec + ): Promise> { const subscriptionId = await this.#api.pod.subscribe(query.schema); const emitter = new EventEmitter(); const subscription = new Subscription(query, emitter, this); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5d36140..717ed69 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,8 +118,8 @@ importers: specifier: workspace:* version: link:../../packages/podspec '@pcd/pod': - specifier: 0.1.6 - version: 0.1.6 + specifier: 0.1.7 + version: 0.1.7 json-bigint: specifier: ^1.0.0 version: 1.0.0 @@ -1001,9 +1001,6 @@ packages: '@pcd/gpcircuits@0.1.7': resolution: {integrity: sha512-7D7KfXHD6BlTyXzg1fJ8HQOW7Kz+AzHYGiAENQxQySelgo7idgxoC/nh5o5/ZLTvU3tLyWV4Wnixyr0WZI4Zpg==} - '@pcd/pod@0.1.6': - resolution: {integrity: sha512-OBj2QvXn8ew9n+Zg93RufoJNll1VFdhqh+JM19FOwUuJ+yQeMYLDzBIsX4uB1bGQ3B01obvfoN9ZSeBLWuj/rg==} - '@pcd/pod@0.1.7': resolution: {integrity: sha512-y8wPXLw36VPsTBYR05TkURyb5v1S40kQmDwL26sEDxX90ZwAv6vTIjRpC4pmxk5gIFGtxTgxMembfrH2vZDOkA==} @@ -1013,9 +1010,6 @@ packages: '@pcd/proto-pod-gpc-artifacts@0.9.0': resolution: {integrity: sha512-DipHMqcwpwCGy0/4hpENYjhKPcxVDEM4Koe13L1QpDYlOVVlUfsV9Y5ObpjtZELH4Q8jLWEW2tnMW9vR+c388w==} - '@pcd/util@0.5.3': - resolution: {integrity: sha512-mAfbpQrl2CYJJwNDHJKwkUD0JYuEAtD3Tli87hb7jZ3vvHmtT/mXlivbpnNZ5uyoowiArnhPMwng/2JmyuU3Cw==} - '@pcd/util@0.5.4': resolution: {integrity: sha512-BkG3PxujAOE3K9odBJqMUzhvK97tzJwVrMTbqSqhGqHnKLVQNuzhURkbdjoNfVRSp8BwaxP516ocjyY1KrxpuA==} @@ -4401,17 +4395,6 @@ snapshots: snarkjs: 0.7.4 url-join: 4.0.1 - '@pcd/pod@0.1.6': - dependencies: - '@pcd/util': 0.5.3 - '@zk-kit/baby-jubjub': 1.0.1 - '@zk-kit/eddsa-poseidon': 1.0.3 - '@zk-kit/lean-imt': 2.0.1 - '@zk-kit/utils': 1.2.0 - js-sha256: 0.10.1 - json-bigint: 1.0.0 - poseidon-lite: 0.2.1 - '@pcd/pod@0.1.7': dependencies: '@pcd/util': 0.5.4 @@ -4427,14 +4410,6 @@ snapshots: '@pcd/proto-pod-gpc-artifacts@0.9.0': {} - '@pcd/util@0.5.3': - dependencies: - buffer: 6.0.3 - email-validator: 2.0.4 - js-sha256: 0.10.1 - secure-random: 1.1.2 - uuid: 9.0.1 - '@pcd/util@0.5.4': dependencies: buffer: 6.0.3