Skip to content

Commit fbc7ec3

Browse files
authored
feat!: slimmer wallet interface (#17136)
Keep removing methods that do not belong in the wallet interface. This time: - `createPublicAuthwit`: This is just a contract interaction. A few convenience methods have been added, and the `TestWallet` keeps the interface to avoid pain the e2e tests, but from now on public authwits are just that: glorified ContractInteractions - `getPublicEvents`: Nuked also from PXE. There's 0 private information required to decode them, moved to an utility instead `createAuthwit` in the Wallet loses the ability to compute them from `ContractFunctionInteraction`s since they're not serializable (still kept it in `TestWallet` for the same reasons as above) Added a new `getChainInfo` method that returns chainId and version and can be used to generate authwits externally and generally to gather information about the chain the wallet is connected to. Also added a proper validated interface to the `Wallet` that will be used soon ™️
2 parents 04892c1 + 59ef4f1 commit fbc7ec3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+832
-475
lines changed

playground/src/components/contract/components/CreateAuthwitDialog.tsx

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import DialogTitle from '@mui/material/DialogTitle';
22
import Dialog from '@mui/material/Dialog';
3-
import { AztecAddress, Contract, ContractFunctionInteraction, type SendMethodOptions } from '@aztec/aztec.js';
3+
import {
4+
AztecAddress,
5+
computeAuthWitMessageHash,
6+
Contract,
7+
ContractFunctionInteraction,
8+
Fr,
9+
SetPublicAuthwitContractInteraction,
10+
type SendMethodOptions,
11+
} from '@aztec/aztec.js';
412
import Button from '@mui/material/Button';
513
import CircularProgress from '@mui/material/CircularProgress';
614
import FormControl from '@mui/material/FormControl';
@@ -49,30 +57,27 @@ export function CreateAuthwitDialog({ open, contract, fnName, args, isPrivate, o
4957

5058
const [feePaymentMethod, setFeePaymentMethod] = useState(null);
5159

52-
const { wallet, playgroundDB, from } = useContext(AztecContext);
60+
const { wallet, node, playgroundDB, from } = useContext(AztecContext);
5361

5462
const handleClose = () => {
5563
onClose();
5664
};
5765

5866
const createAuthwit = async () => {
5967
setCreating(true);
68+
const call = await contract.methods[fnName](...args).getFunctionCall();
69+
const intent = {
70+
caller: AztecAddress.fromString(caller),
71+
call,
72+
};
6073
try {
61-
const action = contract.methods[fnName](...args);
6274
let witness;
6375
if (isPrivate) {
64-
witness = await wallet.createAuthWit(from, {
65-
caller: AztecAddress.fromString(caller),
66-
action,
67-
});
76+
witness = await wallet.createAuthWit(from, intent);
6877
await playgroundDB.storeAuthwitness(witness, undefined, alias);
6978
onClose();
7079
} else {
71-
const validateActionInteraction = await wallet.setPublicAuthWit(
72-
from,
73-
{ caller: AztecAddress.fromString(caller), action },
74-
true,
75-
);
80+
const validateActionInteraction = await SetPublicAuthwitContractInteraction.create(wallet, from, intent, true);
7681
const opts: SendMethodOptions = { from, fee: { paymentMethod: feePaymentMethod } };
7782
onClose(true, validateActionInteraction, opts);
7883
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { type Wallet, WalletSchema } from '@aztec/aztec.js/wallet';
2+
import { promiseWithResolvers, type PromiseWithResolvers } from '@aztec/foundation/promise';
3+
import { schemaHasMethod } from '@aztec/foundation/schemas';
4+
import { jsonStringify } from '@aztec/foundation/json-rpc';
5+
6+
type FunctionsOf<T> = { [K in keyof T as T[K] extends Function ? K : never]: T[K] };
7+
8+
export class ExtensionWallet {
9+
private inFlight = new Map<string, PromiseWithResolvers<any>>();
10+
11+
private constructor() {}
12+
13+
static create() {
14+
const wallet = new ExtensionWallet();
15+
window.addEventListener('message', async event => {
16+
if (event.source !== window) return;
17+
18+
const { messageId, response } = event.data;
19+
if (!response) {
20+
return;
21+
}
22+
const { resolve, reject } = wallet.inFlight.get(messageId);
23+
if (!resolve || !reject) {
24+
console.error('No in-flight message for id', messageId);
25+
return;
26+
}
27+
const { value, error } = response;
28+
if (error) {
29+
reject(new Error(error));
30+
} else {
31+
resolve(value);
32+
}
33+
wallet.inFlight.delete(messageId);
34+
});
35+
return new Proxy(wallet, {
36+
get: (target, prop) => {
37+
if (schemaHasMethod(WalletSchema, prop.toString())) {
38+
return async (...args: any[]) => {
39+
return await target.postMessage({
40+
type: prop.toString() as keyof FunctionsOf<Wallet>,
41+
args,
42+
});
43+
};
44+
} else {
45+
return target[prop];
46+
}
47+
},
48+
}) as unknown as Wallet;
49+
}
50+
51+
private async postMessage({ type, args }: { type: keyof FunctionsOf<Wallet>; args: any[] }) {
52+
const messageId = globalThis.crypto.randomUUID();
53+
window.postMessage(jsonStringify({ type, args, messageId }), '*');
54+
const { promise, resolve, reject } = promiseWithResolvers<any>();
55+
this.inFlight.set(messageId, { promise, resolve, reject });
56+
return promise;
57+
}
58+
}

yarn-project/aztec.js/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@
9090
"@aztec/stdlib": "workspace:^",
9191
"axios": "^1.8.2",
9292
"tslib": "^2.4.0",
93-
"viem": "2.23.7"
93+
"viem": "2.23.7",
94+
"zod": "^3.23.8"
9495
},
9596
"devDependencies": {
9697
"@jest/globals": "^30.0.0",
Lines changed: 4 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,10 @@
11
import type { FeeOptions, TxExecutionOptions } from '@aztec/entrypoints/interfaces';
22
import type { ExecutionPayload } from '@aztec/entrypoints/payload';
33
import { Fr } from '@aztec/foundation/fields';
4-
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
5-
import { ABIParameterVisibility, type FunctionAbi, FunctionType } from '@aztec/stdlib/abi';
64
import { AuthWitness } from '@aztec/stdlib/auth-witness';
7-
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
85
import type { TxExecutionRequest } from '@aztec/stdlib/tx';
96

10-
import { ContractFunctionInteraction } from '../contract/contract_function_interaction.js';
11-
import {
12-
type IntentAction,
13-
type IntentInnerHash,
14-
computeAuthWitMessageHash,
15-
computeInnerAuthWitHashFromAction,
16-
} from '../utils/authwit.js';
17-
import type { Wallet } from '../wallet/wallet.js';
7+
import { type CallIntent, type IntentInnerHash, computeAuthWitMessageHash } from '../utils/authwit.js';
188
import type { AccountInterface } from './interface.js';
199

2010
/**
@@ -27,18 +17,7 @@ interface AuthwitnessIntentProvider {
2717
* during function execution
2818
* @param intent - The action (or inner hash) to authorize
2919
*/
30-
createAuthWit(intent: IntentInnerHash | IntentAction | Buffer | Fr): Promise<AuthWitness>;
31-
/**
32-
* Sets a public authwit for an intent or inner hash
33-
* @param wallet - The wallet to send the transaction authorizing the action from
34-
* @param messageHashOrIntent - The action (or inner hash) to authorize/deny
35-
* @param authorized - Whether to authorize or deny the action
36-
*/
37-
setPublicAuthWit(
38-
wallet: Wallet,
39-
messageHashOrIntent: Fr | Buffer | IntentInnerHash | IntentAction,
40-
authorized: boolean,
41-
): Promise<ContractFunctionInteraction>;
20+
createAuthWit(intent: IntentInnerHash | CallIntent | Buffer | Fr): Promise<AuthWitness>;
4221
}
4322

4423
/**
@@ -90,7 +69,7 @@ export class BaseAccount implements Account {
9069
* @param messageHashOrIntent - The message hash of the intent to approve
9170
* @returns The authentication witness
9271
*/
93-
async createAuthWit(messageHashOrIntent: Fr | Buffer | IntentAction | IntentInnerHash): Promise<AuthWitness> {
72+
async createAuthWit(messageHashOrIntent: Fr | Buffer | CallIntent | IntentInnerHash): Promise<AuthWitness> {
9473
let messageHash: Fr;
9574
if (Buffer.isBuffer(messageHashOrIntent)) {
9675
messageHash = Fr.fromBuffer(messageHashOrIntent);
@@ -103,175 +82,15 @@ export class BaseAccount implements Account {
10382
return this.account.createAuthWit(messageHash);
10483
}
10584

106-
/**
107-
* Returns a function interaction to set a message hash as authorized or revoked in this account.
108-
*
109-
* Public calls can then consume this authorization.
110-
*
111-
* @param wallet - The wallet to send the transaction authorizing the action from
112-
* @param messageHashOrIntent - The message hash or intent to authorize/revoke
113-
* @param authorized - True to authorize, false to revoke authorization.
114-
* @returns - A function interaction.
115-
*/
116-
public async setPublicAuthWit(
117-
wallet: Wallet,
118-
messageHashOrIntent: Fr | Buffer | IntentInnerHash | IntentAction,
119-
authorized: boolean,
120-
): Promise<ContractFunctionInteraction> {
121-
let messageHash: Fr;
122-
if (Buffer.isBuffer(messageHashOrIntent)) {
123-
messageHash = Fr.fromBuffer(messageHashOrIntent);
124-
} else if (messageHashOrIntent instanceof Fr) {
125-
messageHash = messageHashOrIntent;
126-
} else {
127-
messageHash = await this.getMessageHash(messageHashOrIntent);
128-
}
129-
130-
return new ContractFunctionInteraction(wallet, ProtocolContractAddress.AuthRegistry, this.getSetAuthorizedAbi(), [
131-
messageHash,
132-
authorized,
133-
]);
134-
}
135-
136-
private async getInnerHashAndConsumer(intent: IntentInnerHash | IntentAction): Promise<{
137-
/** The inner hash */
138-
innerHash: Fr;
139-
/** The consumer of the authwit */
140-
consumer: AztecAddress;
141-
}> {
142-
if ('caller' in intent && 'action' in intent) {
143-
const action =
144-
intent.action instanceof ContractFunctionInteraction ? (await intent.action.request()).calls[0] : intent.action;
145-
return {
146-
innerHash: await computeInnerAuthWitHashFromAction(intent.caller, action),
147-
consumer: action.to,
148-
};
149-
} else if (Buffer.isBuffer(intent.innerHash)) {
150-
return { innerHash: Fr.fromBuffer(intent.innerHash), consumer: intent.consumer };
151-
}
152-
return { innerHash: intent.innerHash, consumer: intent.consumer };
153-
}
154-
15585
/**
15686
* Returns the message hash for the given intent
15787
*
15888
* @param intent - A tuple of (consumer and inner hash) or (caller and action)
15989
* @returns The message hash
16090
*/
161-
private getMessageHash(intent: IntentInnerHash | IntentAction): Promise<Fr> {
91+
private getMessageHash(intent: IntentInnerHash | CallIntent): Promise<Fr> {
16292
const chainId = this.getChainId();
16393
const version = this.getVersion();
16494
return computeAuthWitMessageHash(intent, { chainId, version });
16595
}
166-
167-
/**
168-
* Lookup the validity of an authwit in private and public contexts.
169-
*
170-
* Uses the chain id and version of the wallet.
171-
*
172-
* @param wallet - The wallet use to simulate and read the public data
173-
* @param onBehalfOf - The address of the "approver"
174-
* @param intent - The consumer and inner hash or the caller and action to lookup
175-
* @param witness - The computed authentication witness to check
176-
* @returns - A struct containing the validity of the authwit in private and public contexts.
177-
*/
178-
async lookupValidity(
179-
wallet: Wallet,
180-
onBehalfOf: AztecAddress,
181-
intent: IntentInnerHash | IntentAction,
182-
witness: AuthWitness,
183-
): Promise<{
184-
/** boolean flag indicating if the authwit is valid in private context */
185-
isValidInPrivate: boolean;
186-
/** boolean flag indicating if the authwit is valid in public context */
187-
isValidInPublic: boolean;
188-
}> {
189-
const { innerHash, consumer } = await this.getInnerHashAndConsumer(intent);
190-
191-
const messageHash = await this.getMessageHash(intent);
192-
const results = { isValidInPrivate: false, isValidInPublic: false };
193-
194-
// Check private
195-
try {
196-
results.isValidInPrivate = (await new ContractFunctionInteraction(
197-
wallet,
198-
onBehalfOf,
199-
this.getLookupValidityAbi(),
200-
[consumer, innerHash],
201-
).simulate({ from: this.getAddress(), authWitnesses: [witness] })) as boolean;
202-
// TODO: Narrow down the error to make sure simulation failed due to an invalid authwit
203-
// eslint-disable-next-line no-empty
204-
} catch {}
205-
206-
// check public
207-
results.isValidInPublic = (await new ContractFunctionInteraction(
208-
wallet,
209-
ProtocolContractAddress.AuthRegistry,
210-
this.getIsConsumableAbi(),
211-
[onBehalfOf, messageHash],
212-
).simulate({ from: this.getAddress() })) as boolean;
213-
214-
return results;
215-
}
216-
217-
private getSetAuthorizedAbi(): FunctionAbi {
218-
return {
219-
name: 'set_authorized',
220-
isInitializer: false,
221-
functionType: FunctionType.PUBLIC,
222-
isInternal: true,
223-
isStatic: false,
224-
parameters: [
225-
{
226-
name: 'message_hash',
227-
type: { kind: 'field' },
228-
visibility: 'private' as ABIParameterVisibility,
229-
},
230-
{
231-
name: 'authorize',
232-
type: { kind: 'boolean' },
233-
visibility: 'private' as ABIParameterVisibility,
234-
},
235-
],
236-
returnTypes: [],
237-
errorTypes: {},
238-
};
239-
}
240-
241-
private getLookupValidityAbi(): FunctionAbi {
242-
return {
243-
name: 'lookup_validity',
244-
isInitializer: false,
245-
functionType: FunctionType.UTILITY,
246-
isInternal: false,
247-
isStatic: false,
248-
parameters: [{ name: 'message_hash', type: { kind: 'field' }, visibility: 'private' as ABIParameterVisibility }],
249-
returnTypes: [{ kind: 'boolean' }],
250-
errorTypes: {},
251-
};
252-
}
253-
254-
private getIsConsumableAbi(): FunctionAbi {
255-
return {
256-
name: 'utility_is_consumable',
257-
isInitializer: false,
258-
functionType: FunctionType.UTILITY,
259-
isInternal: false,
260-
isStatic: false,
261-
parameters: [
262-
{
263-
name: 'address',
264-
type: {
265-
fields: [{ name: 'inner', type: { kind: 'field' } }],
266-
kind: 'struct',
267-
path: 'authwit::aztec::protocol_types::address::aztec_address::AztecAddress',
268-
},
269-
visibility: 'private' as ABIParameterVisibility,
270-
},
271-
{ name: 'message_hash', type: { kind: 'field' }, visibility: 'private' as ABIParameterVisibility },
272-
],
273-
returnTypes: [{ kind: 'boolean' }],
274-
errorTypes: {},
275-
};
276-
}
27796
}

yarn-project/aztec.js/src/account/signerless_account.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { CompleteAddress } from '@aztec/stdlib/contract';
77
import type { TxExecutionRequest } from '@aztec/stdlib/tx';
88

99
import type { ContractFunctionInteraction } from '../contract/contract_function_interaction.js';
10-
import type { IntentAction, IntentInnerHash } from '../utils/authwit.js';
10+
import type { CallIntent, IntentInnerHash } from '../utils/authwit.js';
1111
import type { Wallet } from '../wallet/wallet.js';
1212
import type { Account } from './account.js';
1313

@@ -45,13 +45,13 @@ export class SignerlessAccount implements Account {
4545
throw new Error('SignerlessAccount: Method getAddress not implemented.');
4646
}
4747

48-
createAuthWit(_intent: Fr | Buffer | IntentInnerHash | IntentAction): Promise<AuthWitness> {
48+
createAuthWit(_intent: Fr | Buffer | IntentInnerHash | CallIntent): Promise<AuthWitness> {
4949
throw new Error('SignerlessAccount: Method createAuthWit not implemented.');
5050
}
5151

5252
setPublicAuthWit(
5353
_wallet: Wallet,
54-
_messageHashOrIntent: Fr | Buffer | IntentInnerHash | IntentAction,
54+
_messageHashOrIntent: Fr | Buffer | IntentInnerHash | CallIntent,
5555
_authorized: boolean,
5656
): Promise<ContractFunctionInteraction> {
5757
throw new Error('SignerlessAccount: Method setPublicAuthWit not implemented.');
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
11
export { AuthWitness } from '@aztec/stdlib/auth-witness';
2+
export {
3+
SetPublicAuthwitContractInteraction,
4+
type ContractFunctionInteractionCallIntent,
5+
getMessageHashFromIntent,
6+
computeAuthWitMessageHash,
7+
computeInnerAuthWitHashFromAction,
8+
lookupValidity,
9+
type CallIntent,
10+
type IntentInnerHash,
11+
} from '../utils/authwit.js';
12+
export { computeInnerAuthWitHash } from '@aztec/stdlib/auth-witness';
213

314
export { CallAuthorizationRequest } from '../authorization/call_authorization_request.js';

0 commit comments

Comments
 (0)