fix: better DeployMethod#22985
Conversation
nchamo
left a comment
There was a problem hiding this comment.
It's a really good change, but I feel what it would be a good opportunity to use obj params for constructor args. Feels off to have different versions do different things
Would also like @mverzilli 's opinion on it
| - await cd.deploy(...ctorArgs).send({ from: alice, contractAddressSalt: salt }); | ||
| + const cd = new ContractDeployer(artifact, wallet); | ||
| + await cd.deploy({ salt }, ...ctorArgs).send({ from: alice }); | ||
| ``` |
There was a problem hiding this comment.
It feels weird that in other cases we are doing params at the end:
const deploy = MyContract.deploy(wallet, ...args, { publicKeys });
But here, we are doing params at the start
await cd.deploy({ salt }, ...ctorArgs).send({ from: alice });
I know they are different things, but it felt weird when I saw this:
const { receipt: txReceipt } = await deployer
.deploy({ salt: new Fr(BigInt(1)) }, ownerAddress, 1)
.send({ from: ownerAddress });
Maybe could add the constructor args as part of the object?
const { receipt: txReceipt } = await deployer
.deploy({ ctorArgs: [ownerAddress, 1], salt: new Fr(BigInt(1)) })
.send({ from: ownerAddress });
Update:
Now, that I think about it, I think this could also look clearer if we used object args:
const deployMethod = StatefulTestContract.deploy(wallet, defaultAccountAddress, 0, {
salt: Fr.random(),
deployer: defaultAccountAddress,
});
vs
const deployMethod = StatefulTestContract.deploy({
wallet,
ctorArgs: [defaultAccountAddress, 0],
salt: Fr.random(),
deployer: defaultAccountAddress,
});
I do love object params 😅 But I really feel like it helps understand what each param actually is in this case
There was a problem hiding this comment.
TBH I didn't pay much attention to the ContractDeployer since it's on its way out, unified so now looks like:
const deployMethod = StatefulTestContract.deploy(wallet, defaultAccountAddress, 0, {
salt: Fr.random(),
deployer: defaultAccountAddress,
});
const { receipt: txReceipt } = await deployer
.deploy([ownerAddress, 1], { salt: new Fr(BigInt(1)) })
.send({ from: ownerAddress });I quite like keeping the args separate because the options object is often omitted. But it's true the order should be preserved, done!
There was a problem hiding this comment.
I agree, and was about to make the same observation, but I guess here the intention is to reduce api breakage (you can always add params and things more or less keep working).
…packages into gj/deploy_method_improvements
| Universal deploys can be sent by any account, since the universal address does not depend on `from`: | ||
|
|
||
| ```typescript | ||
| const deploy = MyContract.deploy(wallet, ...args, { universalDeploy: true }); |
There was a problem hiding this comment.
Unpacking deploy from send makes me realize having deploy be a verb is a bit misleading, maybe deployment would be more apt (a bit offtopic maybe)
There was a problem hiding this comment.
That's actually a great idea, but I'd do it on a different PR (or even version, this change is bigger, but the other can just be a massive search and replace)
|
|
||
| ```typescript | ||
| const deploy = MyContract.deploy(wallet, ...args, { deployer: alice }); | ||
| await deploy.send({ from: bob }); // throws: deployer is locked to alice |
There was a problem hiding this comment.
maybe not practical, feel free to dismiss: if we set a deployer we can make deploy's type different, such that this line becomes a compile time error.
There was a problem hiding this comment.
mmm, but how would we detect deployer: alice and from: alice? Is that even possible using types? It's the one case we want to allow (to generate deterministic addresses, but send the tx at a later time)
| constructor( | ||
| protected publicKeys: PublicKeys, | ||
| wallet: Wallet, | ||
| protected artifact: ContractArtifact, | ||
| protected postDeployCtor: (instance: ContractInstanceWithAddress, wallet: Wallet) => TContract, | ||
| protected args: any[] = [], | ||
| constructorNameOrArtifact?: string | FunctionArtifact, | ||
| instantiation: DeployInstantiationOptions = {}, | ||
| authWitnesses: AuthWitness[] = [], | ||
| capsules: Capsule[] = [], | ||
| protected extraHashedArgs: HashedValues[] = [], | ||
| ) { | ||
| super(wallet, authWitnesses, capsules); | ||
| this.constructorArtifact = getInitializer(artifact, constructorNameOrArtifact); | ||
| this.salt = instantiation.salt ?? Fr.random(); | ||
| this.publicKeys = instantiation.publicKeys ?? PublicKeys.default(); | ||
| if (instantiation.deployer !== undefined && instantiation.universalDeploy) { | ||
| throw new Error('DeployInstantiationOptions: `deployer` and `universalDeploy` are mutually exclusive.'); | ||
| } | ||
| if (instantiation.universalDeploy) { | ||
| this.deployer = AztecAddress.ZERO; | ||
| } else if (instantiation.deployer !== undefined) { | ||
| this.deployer = instantiation.deployer; | ||
| } | ||
| } |
There was a problem hiding this comment.
the constructor is getting logically dense in particular as regards deployers. may be a future enhancement idea could be to provide some factory/builder methods?
| * @returns The execution payload for this operation | ||
| */ | ||
| public async request(options: RequestDeployOptions = {}): Promise<ExecutionPayload> { | ||
| if (this.deployer === undefined) { |
There was a problem hiding this comment.
this is starting to look a lot like it wants to be a DeployerLockedDeployMethod subclass
| protected cloneInstantiation(): DeployInstantiationOptions { | ||
| if (this.deployer === undefined) { | ||
| return { salt: this.salt, publicKeys: this.publicKeys }; | ||
| } | ||
| if (this.deployer.equals(AztecAddress.ZERO)) { | ||
| return { salt: this.salt, publicKeys: this.publicKeys, universalDeploy: true }; | ||
| } | ||
| return { salt: this.salt, publicKeys: this.publicKeys, deployer: this.deployer }; | ||
| } |
There was a problem hiding this comment.
another symptom that maybe we want to refactor into three different types: UnlockedDeployMethod, SpecificDeployerDeployMethod, UniversalDeployerDeployMethod (names suck ack)
There was a problem hiding this comment.
Exploring this for a next PR, I like the idea!
mverzilli
left a comment
There was a problem hiding this comment.
Looks good. I do think DeployMethod could use some refactoring. Not sure what the best way is as I don't have a feel for the code. Maybe it could be split into specific classes to handle different deployer cases, or maybe we keep a single DeployMethod type but it holds a new Deployer union type or class.
|
❌ Failed to cherry-pick to |
After rebasing onto backport-to-v4-next-staging (which now includes the backport of #22889 'feat(txe): add tx private logs to tx side effects oracle'), only three files still need cross-PR-drift handling: - docs/docs-developers/docs/resources/migration_notes.md: keep just the two new TBD entries from #22968 (TXE `call_public_incognito` no longer takes `from`; `view_public_incognito` deprecated). The DeployMethod and aztec-up bundled-binary entries that the cherry-pick brought along belong to other PRs not yet backported (e.g. #22985). - yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts: keep the v4-next-staging-specific tree-height imports (L1_TO_L2_MSG_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT) which are still in use here — #21577 removed them on next but hasn't been backported. Drop only NULL_MSG_SENDER_CONTRACT_ADDRESS (replaced by AztecAddress.NULL_MSG_SENDER). - yarn-project/stdlib/src/aztec-address/index.ts: keep both the existing eslint-disable comment and the new NULL_MSG_SENDER_CONTRACT_ADDRESS import. - noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr: with #22889 backported, the imports now match next exactly — just drop NULL_MSG_SENDER_CONTRACT_ADDRESS.
…2968) to v4-next (#23057) Backport of #22968 ("feat: allow setting additional scopes in nr tests") to `backport-to-v4-next-staging`. Rebased on top of latest staging (which now includes the backport of #22889 — `feat(txe): add tx private logs to tx side effects oracle`). All conflicts handled in a single resolution commit. ## What The PR adds `call_private_opts` / `view_private_opts` (with `CallPrivateOptions` / `ViewPrivateOptions`) to `TestEnvironment`, letting tests grant access to additional account scopes when calling private functions. It also reshapes the TXE oracle ABI for `private_call_new_flow` / `public_call_new_flow` to take an `Option<AztecAddress>` for `from`, removes `call_public_incognito`'s ignored `from` parameter, deprecates `view_public_incognito`, and introduces `AztecAddress.NULL_MSG_SENDER` on the TS side. ## Conflicts handled (single commit) After rebasing, four files still drifted vs `next`: - **`noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr`** — with #22889 now on staging, the imports here match `next` exactly aside from `NULL_MSG_SENDER_CONTRACT_ADDRESS`. Just drop that one symbol (the PR removes its only use). - **`docs/docs-developers/docs/resources/migration_notes.md`** — keep only the two new TBD entries from #22968. The DeployMethod and `aztec-up` bundled-binary entries that the cherry-pick brought along belong to other PRs not yet backported (e.g. #22985); leave them out so those backports add them in their own PRs. - **`yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts`** — drop `NULL_MSG_SENDER_CONTRACT_ADDRESS` (replaced by `AztecAddress.NULL_MSG_SENDER`). Keep `L1_TO_L2_MSG_TREE_HEIGHT`, `NOTE_HASH_TREE_HEIGHT`, `PUBLIC_DATA_TREE_HEIGHT` — they're still in use on this branch since #21577 hasn't been backported. - **`yarn-project/stdlib/src/aztec-address/index.ts`** — keep both the existing `eslint-disable @typescript-eslint/no-unsafe-declaration-merging` comment and the new `NULL_MSG_SENDER_CONTRACT_ADDRESS` import. ## Verification - `nargo check` passes for both Noir contracts the PR touches (`scope_test_contract`, `public_checks_contract`). - No conflict markers remain. Original PR: #22968
Cherry-pick conflict resolution on commits 3befdc8/2ee3b06763 accidentally reverted 7 files to an older API. Upstream PR #22985 only touched 43 files; none of these 7 are among them. Restoring to origin/backport-to-v4-next-staging. - yarn-project/foundation/src/collection/array.ts (re-add uniqueBy) - yarn-project/foundation/src/collection/array.test.ts (re-add uniqueBy tests) - yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts - yarn-project/pxe/src/events/event_service.ts (batched validateAndStoreEvents) - yarn-project/pxe/src/events/event_service.test.ts - yarn-project/pxe/src/notes/note_service.ts (chunked nullifier batching) - yarn-project/pxe/src/notes/note_service.test.ts
Records the raw cherry-pick state (including conflict markers) so reviewers can see exactly what conflicted before resolution. Original PR: #22985
Resolve conflicts in #22985 cherry-pick onto v4-next-staging: - migration_notes.md: keep only the new DeployMethod note. Drop unrelated entries (getBlock/getCheckpoint, feeAssetPriceModifier, Domain separators, aztec-up table, slasher renames, Unreleased v5 returnReceipt removal) from PRs not yet backported. - txe_oracles.nr / constants*.nr: drop bleed-through constants (NULL_MSG_SENDER_CONTRACT_ADDRESS, DOM_SEP__HANDSHAKE_SECRET_HASH, DOM_SEP__MERKLE_HASH, DOM_SEP__NON_INTERACTIVE_HANDSHAKE_LOG_TAG, DOM_SEP__NULLIFIER_MERKLE, DOM_SEP__PARTIAL_NOTE_COMMITMENT, DOM_SEP__PUBLIC_DATA_MERKLE) introduced by other PRs. - bot/factory.ts: adopt new DeployInstantiationOptions API; drop setupTokenWithOptionalEarlyRefuel / setupTokenContractWithOptionalEarlyRefuel / getTokenInstance helpers from an unbackported refuel PR; hoist instance variable so the post-if address read works on v4-next. - e2e tests + cli-wallet/deploy.ts: adopt new DeployInstantiationOptions API (salt/deployer/universalDeploy/publicKeys move to construction); keep v4-next-only 'wait: { returnReceipt: true }' callers compatible. - aztec.js/api/contract.ts: keep DeployTxReceipt / DeployWaitOptions / DeployInteractionWaitOptions exports (v4-next-only). - e2e_ha_full.test.ts: drop the keystore-reload test (introduced by an unbackported keystore-reload PR). - token_bridge/index.ts: keep node.getProvenBlockNumber() (the new getBlockNumber('proven') ships with the unbackported getBlock RPC PR).
Records the raw cherry-pick state (including conflict markers) so reviewers can see exactly what conflicted before resolution. Original PR: #22985
Resolve conflicts in #22985 cherry-pick onto v4-next-staging: - migration_notes.md: keep only the new DeployMethod note. Drop unrelated entries (getBlock/getCheckpoint, feeAssetPriceModifier, Domain separators, aztec-up table, slasher renames, Unreleased v5 returnReceipt removal) from PRs not yet backported. - txe_oracles.nr / constants*.nr: drop bleed-through constants (NULL_MSG_SENDER_CONTRACT_ADDRESS, DOM_SEP__HANDSHAKE_SECRET_HASH, DOM_SEP__MERKLE_HASH, DOM_SEP__NON_INTERACTIVE_HANDSHAKE_LOG_TAG, DOM_SEP__NULLIFIER_MERKLE, DOM_SEP__PARTIAL_NOTE_COMMITMENT, DOM_SEP__PUBLIC_DATA_MERKLE) introduced by other PRs. - bot/factory.ts: adopt new DeployInstantiationOptions API; drop setupTokenWithOptionalEarlyRefuel / setupTokenContractWithOptionalEarlyRefuel / getTokenInstance helpers from an unbackported refuel PR; hoist instance variable so the post-if address read works on v4-next. - e2e tests + cli-wallet/deploy.ts: adopt new DeployInstantiationOptions API (salt/deployer/universalDeploy/publicKeys move to construction); keep v4-next-only 'wait: { returnReceipt: true }' callers compatible. - aztec.js/api/contract.ts: keep DeployTxReceipt / DeployWaitOptions / DeployInteractionWaitOptions exports (v4-next-only). - e2e_ha_full.test.ts: drop the keystore-reload test (introduced by an unbackported keystore-reload PR). - token_bridge/index.ts: keep node.getProvenBlockNumber() (the new getBlockNumber('proven') ships with the unbackported getBlock RPC PR).
Refactor to avoid multiple footguns with
DeployMethod. The idea is that all address-affecting parameters have to be "locked in" in the constructor, so any subsequent invocation yields a deterministic address. Any other path throws and exception.The "convenient" flow of just saying
await Contract.deploy(wallet, ...args).send({ from: acc })is still supported, but generating an incompatibleDeployMethodinstance (for example, using a different deployer) is not allowed anymore. Furthermore, it is not possible to set the salt on send to avoid the same issue, and the object has to be regenerated. Read migration notes for more details