You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add type: "derived" InputRequest for wallet-computed cryptographic values
Adds a fourth `InputRequest` variant `{ type: "derived", algorithm, args }`
that asks the wallet to run a named cryptographic algorithm over its own
state (view key, wallet-maintained counters, etc.) plus dapp-supplied args,
and substitutes the result into a transaction input slot. The dapp never
observes the wallet-side inputs — only the output.
Strict opt-in via a new `algorithmsAllowed?: AlgorithmGrant[]` field on
`ConnectOptions`: each grant authorizes exactly one
`(algorithm, program, function, inputPosition)` call site, all four fields
required and exact-match. The wallet refuses every derived request whose
tuple is not present. No broad default.
A new `algorithmsSupported(): Promise<string[]>` adapter method lets dapps
discover what a wallet implements before populating the allowlist.
Wallets without derived-input support return `[]` (default) and connections
with non-empty `algorithmsAllowed` throw `WalletConnectOptionsNotSupportedError`
via the existing `hasUnsupportedConnectOptions` path.
Inaugural algorithm: `program-scoped-address-blind`. Inputs: `{ "domain-separator": field }`.
Output: `address`. Valid slot positions: `address`, `group`, `scalar`, `field`.
Updates the PrivateInputs example with a fourth `Derived` mode on primitive
slots whose baseType is in an algorithm's `validSlotTypes`, plus a
connect-time `AlgorithmGrant[]` editor with an "Auto-grant this function's
eligible slots" convenience button. The grant JSON preview now includes
`algorithmsAllowed`.
See docs/adapter-privacy-extension.md §"Derived inputs" for the spec and
docs/dapp-privacy-quickstart.md for an implementor's guide.
Add `type: "derived"` InputRequest for wallet-evaluated cryptographic algorithms
14
+
15
+
A new `InputRequest` variant lets a dapp ask the wallet to compute a value by running a named cryptographic algorithm over the wallet's own state (view key, wallet-maintained counters, etc.) plus dapp-supplied `args`, and substitute the result into a transaction input slot. The dapp never observes the wallet-side inputs — only the output.
16
+
17
+
Strictly opt-in: a new `algorithmsAllowed?: AlgorithmGrant[]` field on `ConnectOptions` authorizes derived inputs at exact `(algorithm, program, function, inputPosition)` call sites. All four fields are required and exact-match; there is no broad default. The wallet refuses every derived request whose tuple is not present.
18
+
19
+
A new adapter method `algorithmsSupported(): Promise<string[]>` lets a dapp discover which algorithms a wallet implements before populating `algorithmsAllowed`. Wallets without derived-input support return `[]` (the base implementation's default).
20
+
21
+
Inaugural algorithm: `program-scoped-address-blind`. Inputs (dapp-provided): `{ "domain-separator": field }`. Output type: `address`. Valid input slot positions: `address`, `group`, `scalar`, `field`. The output is a per-program blinded address whose link to the active address is hidden by a BHP256 commitment.
22
+
23
+
The `<AleoWalletProvider>` React component accepts a new optional `algorithmsAllowed` prop and forwards it on connect; the `useWallet()` context exposes `algorithmsSupported`. Existing usages without these are unaffected.
24
+
25
+
See `docs/adapter-privacy-extension.md` § "Derived inputs" for the full spec, and `docs/dapp-privacy-quickstart.md` for an implementor's guide.
Copy file name to clipboardExpand all lines: docs/adapter-privacy-extension.md
+90-2Lines changed: 90 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
## Goal
4
4
5
-
Let dapps emit `TransactionOptions` whose `inputs` slots are not always literal Aleo values. Each non-literal slot is a **request** to the wallet — to prompt the user or to auto-select an owned record matching dapp-supplied criteria. The wallet fulfills the request before passing the transaction to the SDK.
5
+
Let dapps emit `TransactionOptions` whose `inputs` slots are not always literal Aleo values. Each non-literal slot is a **request** to the wallet — to prompt the user, to auto-select an owned record matching dapp-supplied criteria, or to compute a value by running a named cryptographic algorithm over the user's wallet state. The wallet fulfills the request before passing the transaction to the SDK.
6
6
7
7
## Wire-level types
8
8
@@ -11,14 +11,32 @@ type Input = string | InputRequest;
11
11
12
12
typeInputRequest=
13
13
| { type:"address"; label?:string } // Specification to fill the input field with the active address. Allowed in an input position with an aleo type of: `address, group, scalar, or field`.
14
-
| { type:"record"; program:string; filters?:RecordFilters; uid?:string }; // Specification to use a record from a specific program. When `uid` is present, it pins the exact record previously returned by `requestRecords` and `filters` is ignored. When absent, the wallet picks any unspent record matching `filters`. Allowed in an input position with an aleo type of: `record, dynamic_record, or external_record`.
14
+
| { type:"record"; program:string; filters?:RecordFilters; uid?:string } // Specification to use a record from a specific program. When `uid` is present, it pins the exact record previously returned by `requestRecords` and `filters` is ignored. When absent, the wallet picks any unspent record matching `filters`. Allowed in an input position with an aleo type of: `record, dynamic_record, or external_record`.
15
+
| { type:"derived"; algorithm:AlgorithmName; args:Record<string, AlgorithmArg>; label?:string }; // Specification to fill the input field with the output of a wallet-evaluated cryptographic algorithm. Each algorithm declares its expected `args` schema and its output Aleo type; the output type determines which input positions are valid. Strictly opt-in — the wallet refuses every derived request whose (algorithm, program, function, inputPosition) tuple is not in the connection's `algorithmsAllowed`. See "Derived inputs" below.
15
16
16
17
typeRecordFilters=Record<string, RecordFieldFilter>; // keys are top-level record field names or dotted paths into struct fields, e.g. "amount" or "data.amount".
fields:Record<string, string>; // parsed structured form of the record's plaintext. Only fields the dapp has read access to are present; redacted fields are omitted (not present-with-undefined).
21
22
}
23
+
24
+
// Re-exports the existing LiteralType enum from `@provablehq/aleo-types` (`packages/aleo-types/src/data.ts`).
25
+
typeLiteralType=
26
+
|"address"|"bool"|"group"
27
+
|"u8"|"u16"|"u32"|"u64"|"u128"
28
+
|"i8"|"i16"|"i32"|"i64"|"i128"
29
+
|"field"|"scalar"|"signature";
30
+
31
+
// New algorithms are added to this literal union as they're standardized. The `(string & {})` permits
32
+
// unknown values for forward-compat — the wallet validates against its own `algorithmsSupported()` list at runtime.
type:LiteralType; // parsing directive — the wallet decodes `value` according to this Aleo primitive type
38
+
value:string; // an Aleo literal in canonical string form (e.g. "12345field", "100u64", "true")
39
+
}
22
40
```
23
41
24
42
`requestRecords` continues to return its existing wallet-defined record shape (e.g. Shield's `OwnedRecord` at `shield-extension/src/background/types/RecordScanner.ts:83`). Two optional fields are added additively to each returned record:
@@ -33,6 +51,7 @@ The remaining (legacy) fields — including `recordPlaintext`, `commitment`, `ta
33
51
The `InputRequest` sends a request to the wallet (which is then authorized by the user) to do the following:
34
52
1. Input the user's address into a position where there's an address, group, scalar, or field input.
35
53
2. Use a record whose fields match the `filters` on specific record's members and filter for records that match them if applicable, returning an error if the condition cannot be applied or a record matching it cannot be found.
54
+
3. Run a named cryptographic algorithm over the wallet's state (view key, program-scoped counters, etc.) plus dapp-supplied arguments, and use the output as input. Authorized only at exact `(algorithm, program, function, inputPosition)` call sites.
36
55
37
56
The wallet has the program's source, so it reads a function's parameter signature for input position `i` and renders the form control accordingly. `label` is UX-only.
38
57
@@ -55,6 +74,7 @@ interface ConnectHistory {
55
74
programs?:string[]; // unchanged — program-level gate for both transaction execution and record operations
56
75
readAddress?:boolean; // new — opt-in address withholding; default true
57
76
recordAccess?:RecordAccessGrant; // new — opt-in record/field narrowing
77
+
algorithmsAllowed?:AlgorithmGrant[]; // new — strict opt-in derived-input allowlist; default undefined → every `type: "derived"` request is refused
58
78
}
59
79
60
80
typeRecordAccessGrant=
@@ -75,6 +95,16 @@ interface FieldGrant {
75
95
name:string; // a record-body field name (e.g. "amount", "data.amount"), or a `$`-prefixed envelope-metadata name from the reserved set: `$commitment`, `$tag`, `$transitionId`, `$transactionId`, `$outputIndex`, `$transactionIndex`, `$transitionIndex`, `$owner`, `$sender`. The `$` prefix prevents collision with any body field named identically.
76
96
readAccess?:boolean; // undefined → true; false → field is usable as a filter key but plaintext is withheld on decrypt
77
97
}
98
+
99
+
// Each grant authorizes one specific call site. All four fields are required and exact-match;
100
+
// there is no wildcard. A dapp that wants to use the same algorithm at multiple call sites lists
101
+
// each one as its own entry.
102
+
interfaceAlgorithmGrant {
103
+
algorithm:AlgorithmName; // must appear in the wallet's `algorithmsSupported()` list
104
+
program:string; // must also appear in `programs`
105
+
function:string; // exact transition name within `program`
106
+
inputPosition:number; // 0-based index into the function's input slots
107
+
}
78
108
```
79
109
80
110
| Configuration | Meaning |
@@ -121,6 +151,55 @@ Rationale: when the dapp asks for narrow field access, it has explicitly given u
121
151
122
152
Under `readAddress: false`, the strip rules tighten further independently of the grant: `owner`, `sender`, `commitment`, `tag`, `transitionId`, `transactionId`, and `recordPlaintext` are always omitted, and any `recordView.fields` whose Aleo type is `address` and whose plaintext equals the active address is omitted. `uid` and `recordView` (with non-address fields) remain the dapp's only handles to the record.
123
153
154
+
### Derived inputs
155
+
156
+
A `type: "derived"` request asks the wallet to compute a value by running a named cryptographic algorithm over the wallet's own state (the view key, a per-(origin, program) counter, etc.) combined with the dapp's `args`, then substitute the result into the input slot. The dapp never observes the wallet-side inputs — only the final output.
157
+
158
+
Derived inputs are **strictly opt-in**: the wallet refuses every derived request whose `(algorithm, program, function, inputPosition)` tuple is not present in `algorithmsAllowed`. There is no broad default. The four fields are required and exact-match — a grant for `(algorithm: X, program: "p.aleo", function: "f", inputPosition: 0)` does not authorize the same algorithm at `inputPosition: 1`, at a different function, or at a different program. A dapp that wants the same algorithm at multiple call sites lists each one as its own entry.
159
+
160
+
#### Discovery
161
+
162
+
Adapters expose `algorithmsSupported(): Promise<AlgorithmName[]>`. A dapp calls this before connect to learn which algorithms a wallet implements, then requests a matching subset in `algorithmsAllowed`. Wallets that don't implement derived inputs at all return an empty array or throw `MethodNotImplementedError`.
163
+
164
+
Every `algorithmsAllowed[].algorithm` is validated at connect time against the wallet's `algorithmsSupported()`. Unknown names are rejected.
165
+
166
+
#### Output type and slot compatibility
167
+
168
+
Each `KnownAlgorithm` has a fixed Aleo output type, declared in the catalog below. The wallet additionally validates at execute time that the function's signature at `inputs[i]` is a valid position for that output type — same rules as `type: "address"` (e.g. an `address`-typed output is valid in `address` / `group` / `scalar` / `field` slots).
169
+
170
+
#### Algorithm catalog
171
+
172
+
##### `program-scoped-address-blind`
173
+
174
+
Produces a blinded address scoped to a specific program. Two dapps using the same wallet against different programs derive different blinded addresses. Whether the same dapp derives the same blinded address across executions is governed by wallet-internal state; the dapp can neither control nor observe that state.
The dapp never observes the view key, the counter, or the intermediate scalar `r`. The output is an address whose link to the active address is hidden by the BHP256 commitment — recovering the active address from the output is computationally infeasible without `r`.
192
+
193
+
Future algorithms are added to `KnownAlgorithm` and documented under their own catalog subsection in this spec.
194
+
195
+
#### Interaction with `readAddress: false`
196
+
197
+
Derived inputs are allowed under `readAddress: false`. The dapp does not learn the active address through the derived flow — it only sees the algorithm's output. For `program-scoped-address-blind` the output is itself a fresh blinded address, so it does not leak the active address even when the dapp inspects the resulting transaction. Algorithms whose output is the active address itself (or trivially reversible to it) must not be admitted to `KnownAlgorithm` for this reason.
198
+
199
+
#### Interaction with `programs`
200
+
201
+
`algorithmsAllowed[].program` must appear in `programs`. The wallet rejects mismatches at connect time. This mirrors the subset constraint already enforced for `recordAccess.programs[].program`.
202
+
124
203
### Address exposure
125
204
126
205
`readAddress?: boolean` controls whether the dapp learns the user's address. Defaults to `true` (undefined treated as `true`); `false` is opt-in for privacy-preserving dapps.
@@ -165,8 +244,10 @@ flowchart TD
165
244
D --> R["fulfillInputRequests.ts"]
166
245
R -- "type: record" --> R1["filter unspent records<br/>by where clause"]
167
246
R -- "type: user" --> R3["render typed form<br/>from program signature"]
247
+
R -- "type: derived" --> R4["run algorithm via SDK<br/>using wallet-derived secrets"]
168
248
R1 --> F["confirm screen<br/>shows every fulfilled value"]
169
249
R3 --> F
250
+
R4 --> F
170
251
F -- user confirms --> G["initializeGenericTransaction<br/>(fulfilled string[], lockedRecords)"]
171
252
G ===> H["worker.ts → SDK<br/>UNCHANGED"]
172
253
@@ -195,3 +276,10 @@ The worker boundary still receives `string[]`. All fulfillment is wallet-side; t
195
276
|`connect()`|`recordAccess.programs[].program` not present in `programs`| connect-time validation error |
196
277
|`requestRecords`| called against a program in `programs` but absent from `recordAccess.programs[]` (when `recordAccess` is set) | permission error at gate |
197
278
|`requestRecords`|`includePlaintext: true` while `decryptPermission: NoDecrypt`| permission error at gate (today's behavior, restated) |
279
+
|`type: "derived"`|`(algorithm, program, function, inputPosition)` tuple not in `algorithmsAllowed`| permission error at gate |
280
+
|`type: "derived"`|`algorithm` not in the wallet's `algorithmsSupported()` list | permission error at gate (also caught at connect time if the dapp listed it in `algorithmsAllowed`) |
281
+
|`type: "derived"`|`args` missing a key required by the algorithm's schema, has an extra key, or an `AlgorithmArg.type` mismatches the algorithm's expected type for that key | validation error before gate |
282
+
|`type: "derived"`|`AlgorithmArg.value` does not parse as a literal of `AlgorithmArg.type`| validation error before gate |
283
+
|`type: "derived"`| algorithm output type incompatible with the function signature at `inputs[i]` (e.g. an `address`-producing algorithm used in a `u64` slot) | validation error before gate |
284
+
|`connect()`|`algorithmsAllowed[].program` not present in `programs`| connect-time validation error |
285
+
|`connect()`|`algorithmsAllowed[].algorithm` not in the wallet's `algorithmsSupported()`| connect-time validation error |
Copy file name to clipboardExpand all lines: docs/dapp-privacy-quickstart.md
+47-1Lines changed: 47 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,18 +4,20 @@ How to use the wallet-adapter's new privacy features from a dapp. For the full s
4
4
5
5
## What's new
6
6
7
-
Two connect-time grants and two transaction-input request types:
7
+
Three connect-time grants and three transaction-input request types:
8
8
9
9
| Grant | Type | Effect |
10
10
|---|---|---|
11
11
|`readAddress`|`boolean` (default `true`) | When `false`, the dapp transacts without learning the active address. Requires `decryptPermission: NoDecrypt`. |
12
12
|`recordAccess`|`RecordAccessGrant` (default `undefined` = broad) | Per-program / per-record / per-field narrowing of record reads. |
13
+
|`algorithmsAllowed`|`AlgorithmGrant[]` (default `undefined` = none) | Strict opt-in allowlist for `type: "derived"` requests. Each entry authorizes one exact `(algorithm, program, function, inputPosition)` call site. |
|`{ type: "record", program, uid }`| pin specific record by handle |`record`, `dynamic_record`, `external_record`|
18
19
|`{ type: "record", program, filters }`| wallet auto-selects matching record | same |
20
+
|`{ type: "derived", algorithm, args }`| wallet runs a named crypto algorithm | depends on algorithm — see catalog |
19
21
20
22
## Wiring connect-time options
21
23
@@ -133,6 +135,49 @@ await executeTransaction({
133
135
});
134
136
```
135
137
138
+
## Derived inputs (`type: "derived"`)
139
+
140
+
A `type: "derived"` slot tells the wallet to compute a value by running a named cryptographic algorithm over its own state (view key, wallet-maintained counters, etc.) plus your `args`, and substitute the result. **You never see the wallet-side inputs — only the output.**
141
+
142
+
Strictly opt-in via `algorithmsAllowed` at connect time. Each grant authorizes exactly one `(algorithm, program, function, inputPosition)` call site:
`ALGORITHM_SCHEMAS`from`@provablehq/aleo-types`shipstheargsschema, output type, and valid slot positions for every known algorithm — use it to render correct forms or pre-validate shapes. Full algorithm catalog: see [`adapter-privacy-extension.md`](./adapter-privacy-extension.md) § "Algorithm catalog".
-**Wantstousederivedinputs** — populate`algorithmsAllowed`withonegrantpercallsite, then place `{ type: "derived", algorithm, args }` in the corresponding `inputs[i]`. There is no broad default — empty `algorithmsAllowed` refuses every derived request.
0 commit comments