feat: compute shares address inside vault constructor#300
feat: compute shares address inside vault constructor#300
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughVault and shares-token deployment moved to deterministic, constructor-driven initialization: vault constructors now accept shares-token constructor inputs and compute the shares address during construction; admin setter entrypoints were removed; TypeScript test deploy utilities were updated to precompute addresses, use a shared salt, deploy shares with the vault as minter, and deploy vaults via constructors (including initial-deposit flows). Changes
Sequence DiagramssequenceDiagram
participant Deployer
participant Artifact as TokenContractArtifact
participant Deriver as AddressDeriver
participant Token as SharesToken
participant Vault as VaultContract
participant Asset as AssetToken
Note over Deployer,Vault: Deterministic deployment flow (standard / initial-deposit)
Deployer->>Artifact: fetch shares_class_id
Artifact-->>Deployer: shares_class_id
Deployer->>Deriver: derive_vault_address(constructor args, shares_class_id, salt)
Deriver-->>Deployer: precomputed_vault_address
Deployer->>Deriver: derive_token_address(token constructor args, salt, deployer)
Deriver-->>Deployer: precomputed_token_address
Deployer->>Token: deploy(token args with minter=precomputed_vault_address, contractAddressSalt=salt)
Token-->>Deployer: token deployed at precomputed_token_address
Deployer->>Asset: setPublicAuthWit(precomputed_vault_address) %% only for initial-deposit path
Asset-->>Deployer: authorization set
Deployer->>Vault: deploy(constructor | constructor_with_initial_deposit(..., depositor, amount, nonce))
Vault->>Asset: transferFrom(depositor, amount)
Asset-->>Vault: assets transferred
Vault->>Token: mint(depositor or vault, shares)
Token-->>Vault: shares minted
Vault-->>Deployer: vault initialized deterministically
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
2 issues found across 3 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/vault_contract/src/test/utils.nr">
<violation number="1" location="src/vault_contract/src/test/utils.nr:62">
P2: Extra `owner` argument passed to `constructor()` — the contract only takes 7 parameters (no `deployer`). This will be a compile error when the test module is re-enabled. Remove the trailing `owner` argument.</violation>
<violation number="2" location="src/vault_contract/src/test/utils.nr:118">
P1: Wrong argument order and extra argument in `constructor_with_initial_deposit()` call. `owner` is incorrectly inserted as 8th arg (where `initial_deposit: u128` is expected), shifting all remaining arguments and adding an extra 11th parameter. The contract only takes 10 params — remove `owner` (the deployer is resolved internally by the constructor).</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| assert(self.msg_sender().eq(admin), "caller is not admin"); | ||
| assert(vault_offset > 0, "vault_offset must be greater than 0"); | ||
|
|
||
| let deployer = get_contract_instance_deployer_avm(self.address).unwrap(); |
There was a problem hiding this comment.
who is self.maybe_msg_sender() in this context?
There was a problem hiding this comment.
Either the deployer or anyone in the case of using universal deployment. And it is being required that the vault and shares contracts have the same deployer. Do you see an issue here?
| self.storage.shares.initialize(shares); | ||
| self.storage.vault_offset.initialize(vault_offset); | ||
|
|
||
| // Caller deposits assets, shares are minted to this contract and are permanently locked. |
There was a problem hiding this comment.
beware idk why but ai seems to modify a lot of comments (unrequested)
src/vault_contract/src/main.nr
Outdated
| self.call(Token::at(asset_token).transfer_public_to_public(from, to, initial_deposit, nonce)); | ||
|
|
||
| // Mint shares to this contract address (permanently locked) | ||
| self.call(Token::at(asset).transfer_public_to_public(from, to, initial_deposit, nonce)); |
There was a problem hiding this comment.
id prefer using self.address instead of to (confusing)
| symbol: str<31>, | ||
| decimals: u8, | ||
| minter: AztecAddress, | ||
| class_id: ContractClassId, |
There was a problem hiding this comment.
we shouldn't require to define the class id, no? we can just read it (as we do in constructor), this parameter seems to make it more flexible, while the constructor hardcodes that the token is indeed the imported one
There was a problem hiding this comment.
I'm not sure I understand.
You mean to enforce that the shares contract class ID matches the one used for compilation? Is that possible or would we need to define it as a constant?
Anyways, why would we want this?
| ); | ||
|
|
||
| self.storage.asset.initialize(asset); | ||
| self.storage.shares.initialize(shares); |
There was a problem hiding this comment.
what's the failure mode? we're correctly binding this contract with an instance of "shares" that should be later initialized, what if the deployer doesn't initialize it? what fails? should we enforce universal deployment so that anyone can initialize it later?
There was a problem hiding this comment.
For constructor_with_initial_deposit the shares contract must be deployed first. Otherwise the minting of the initial deposit would fail.
We could enforce constructor to check that the shares contract has been initialized by checking the existence of the initialization nullifier. However, if the shares contract has not been initialized yet, the vault contract cannot be used at all, since its functions need to interact with the shares contract.
There was a problem hiding this comment.
I think enforcing universal deployment is not needed. Enforcing shares deployment before vault deployment also not. But not sure if I'm missing something
There was a problem hiding this comment.
Warning
CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.
Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/vault_contract/src/main.nr (1)
107-151:⚠️ Potential issue | 🔴 CriticalAdd vault-level auth for
depositor.
constructor_with_initial_depositpulls funds fromdepositor, but unlike the other publicfromflows it never calls_validate_from_public(...)at the start. Right now the initializer relies only on the downstream token auth check.As per coding guidelines, "Every function operating on behalf of another address MUST call `_validate_from_private` or `_validate_from_public` at the start."🔒 Proposed fix
fn constructor_with_initial_deposit( asset: AztecAddress, vault_offset: u128, shares_name: str<31>, shares_symbol: str<31>, shares_decimals: u8, shares_class_id: ContractClassId, shares_salt: Field, initial_deposit: u128, depositor: AztecAddress, nonce: Field, ) { + self.internal._validate_from_public(depositor); assert(vault_offset > 0, "vault_offset must be greater than 0");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/vault_contract/src/main.nr` around lines 107 - 151, constructor_with_initial_deposit currently pulls tokens from depositor without performing vault-level auth; add a call to the appropriate validation helper at the start (e.g., call _validate_from_public(depositor) — or _validate_from_private if intended private flow) before any token transfer logic so the function follows the rule that every function operating on behalf of another address must call _validate_from_public/_validate_from_private; place this validation near the top of constructor_with_initial_deposit (before computing shares or calling Token::at(...).transfer_public_to_public) and ensure the chosen helper matches the rest of the contract's public/private semantics.src/vault_contract/src/test/utils.nr (1)
119-146:⚠️ Potential issue | 🟠 MajorThis helper still cannot exercise the initial-deposit path once TXE support lands.
constructor_with_initial_depositmints on the shares token during vault deployment, but this helper deploys the shares contract only afterward. It also never preauthorizestransfer_public_to_public(owner, vault, initial_deposit, 0)for the vault caller. Even with working custom salts/oracles, this setup will still revert. Mirror the TS flow here: precompute the vault address, deploy shares first with that minter, add the asset authwit, then deploy the vault.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/vault_contract/src/test/utils.nr` around lines 119 - 146, The helper currently deploys the Vault with constructor_with_initial_deposit but only deploys the shares Token afterward, and it never preauthorizes the asset transfer; to fix, precompute the vault address (as the TS flow does), deploy the shares Token first using Token::interface().constructor_with_minter(...) with that precomputed vault_address as minter, preauthorize the asset transfer via transfer_public_to_public(owner, vault_address, initial_deposit, 0) (or add the asset authwit for the vault caller), and only then deploy the Vault using Vault::interface().constructor_with_initial_deposit(...) so the mint to the shares token during vault deployment will succeed; refer to constructor_with_initial_deposit, constructor_with_minter, transfer_public_to_public and setup_with_vault to locate the relevant code.src/ts/test/utils.ts (1)
290-330: 🛠️ Refactor suggestion | 🟠 MajorNarrow
deployVaultWithInitialDepositparameter fromWallettoEmbeddedWallet.The function casts
wallettoEmbeddedWalletat line 330 to callsetPublicAuthWitwheninitialDeposit > 0. All current callers invault.test.tsalready passEmbeddedWallet, so tightening the signature fromWallettoEmbeddedWalletremoves the dishonest cast and clarifies the actual type requirement.export async function deployVaultWithInitialDeposit( - wallet: Wallet, + wallet: EmbeddedWallet, deployer: AztecAddress, assetContract: TokenContract, initialDeposit: bigint, depositor: AztecAddress, options?: DeployOptions, ): Promise<[VaultContract, TokenContract]> {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ts/test/utils.ts` around lines 290 - 330, Change the deployVaultWithInitialDeposit function signature to accept EmbeddedWallet instead of Wallet (update the parameter type for "wallet"), remove the unsafe cast to EmbeddedWallet where setPublicAuthWit is called, and ensure all references inside deployVaultWithInitialDeposit (e.g., TokenContract.deployWithOpts call that passes { method: 'constructor_with_minter', wallet } and the setPublicAuthWit(vaultAddress, transfer, depositor, wallet as EmbeddedWallet) call) now use the correctly typed EmbeddedWallet parameter; keep the rest of the logic and parameter names unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/ts/test/utils.ts`:
- Around line 247-280: Add a fast-fail invariant after deterministic deploys:
after computing vaultAddress with deriveContractAddressWithConstructor and
deploying sharesContract and vaultContract via TokenContract.deployWithOpts and
VaultContract.deployWithOpts, immediately assert that
vaultContract.options.address (or vaultContract.address) equals the precomputed
vaultAddress and that await vaultContract.call('shares') (or the vault's
shares() view) equals sharesContract.address; if either check fails, throw a
clear error immediately so setup fails at source instead of producing later
app_logic_reverted errors.
In `@src/vault_contract/src/main.nr`:
- Around line 1-8: The test module was entirely commented out, removing Noir
unit coverage that checks vault-specific auth and exact revert paths; re-enable
the test module (restore `pub mod test;`) but modify the tests so TXE-dependent
cases are skipped/ignored while keeping pure unit tests enabled—specifically
keep tests exercising `_compute_token_address`, the vault
constructor/initializer invariants, and any auth/revert-path checks as pure Noir
tests; for TXE-only flows, gate those cases behind a compile-time or runtime
skip annotation so they don't run in the TXE environment but the rest of the
`test` module still compiles and runs.
---
Outside diff comments:
In `@src/ts/test/utils.ts`:
- Around line 290-330: Change the deployVaultWithInitialDeposit function
signature to accept EmbeddedWallet instead of Wallet (update the parameter type
for "wallet"), remove the unsafe cast to EmbeddedWallet where setPublicAuthWit
is called, and ensure all references inside deployVaultWithInitialDeposit (e.g.,
TokenContract.deployWithOpts call that passes { method:
'constructor_with_minter', wallet } and the setPublicAuthWit(vaultAddress,
transfer, depositor, wallet as EmbeddedWallet) call) now use the correctly typed
EmbeddedWallet parameter; keep the rest of the logic and parameter names
unchanged.
In `@src/vault_contract/src/main.nr`:
- Around line 107-151: constructor_with_initial_deposit currently pulls tokens
from depositor without performing vault-level auth; add a call to the
appropriate validation helper at the start (e.g., call
_validate_from_public(depositor) — or _validate_from_private if intended private
flow) before any token transfer logic so the function follows the rule that
every function operating on behalf of another address must call
_validate_from_public/_validate_from_private; place this validation near the top
of constructor_with_initial_deposit (before computing shares or calling
Token::at(...).transfer_public_to_public) and ensure the chosen helper matches
the rest of the contract's public/private semantics.
In `@src/vault_contract/src/test/utils.nr`:
- Around line 119-146: The helper currently deploys the Vault with
constructor_with_initial_deposit but only deploys the shares Token afterward,
and it never preauthorizes the asset transfer; to fix, precompute the vault
address (as the TS flow does), deploy the shares Token first using
Token::interface().constructor_with_minter(...) with that precomputed
vault_address as minter, preauthorize the asset transfer via
transfer_public_to_public(owner, vault_address, initial_deposit, 0) (or add the
asset authwit for the vault caller), and only then deploy the Vault using
Vault::interface().constructor_with_initial_deposit(...) so the mint to the
shares token during vault deployment will succeed; refer to
constructor_with_initial_deposit, constructor_with_minter,
transfer_public_to_public and setup_with_vault to locate the relevant code.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Central YAML (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 9be75638-a803-4e5d-8be1-b64af9f5494f
📒 Files selected for processing (3)
src/ts/test/utils.tssrc/vault_contract/src/main.nrsrc/vault_contract/src/test/utils.nr
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/vault_contract/src/test/utils.nr">
<violation number="1" location="src/vault_contract/src/test/utils.nr:74">
P1: Calling the vault initializer via `env.call_public` breaks these TXE setup helpers because the initializer uses `get_contract_instance_deployer_avm`, which is unavailable in TXE public context.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Warning
CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.
Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/vault_contract/src/main.nr`:
- Around line 1455-1502: _compute_token_address duplicates the Token constructor
ABI packing and selector logic, risking mismatched addresses if
Token::constructor_with_minter changes; extract the initializer hashing/packing
into a single shared utility (e.g., compute_token_initializer_hash or
Token::initializer_hash) that encapsulates the constructor selector, str<31>
packing, decimals/minter field layout, calls hash_args and
poseidon2_hash_with_separator with DOM_SEP__INITIALIZER, and returns the
initialization_hash; then replace the hardcoded logic in _compute_token_address
to call that utility and build the ContractInstance (salt, deployer, class_id,
public_keys) using the returned initialization_hash so both vault and token code
share the same authoritative implementation.
- Around line 119-121: The constructor_with_initial_deposit initializer
currently spends depositor's public balance without performing authwit
validation; add a call to _validate_from_public(depositor) (or
_validate_from_private if this should be a private-sender flow) at the start of
constructor_with_initial_deposit before any balance operations to ensure the
vault is authorized to act on behalf of depositor; update any early-return or
unwrap paths accordingly so validation happens unconditionally for that
entrypoint.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Central YAML (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 071b294b-ec8b-4213-8f0e-93ed5fc1bf0e
📒 Files selected for processing (3)
src/vault_contract/src/main.nrsrc/vault_contract/src/test/constructor_with_initial_deposit.nrsrc/vault_contract/src/test/utils.nr
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
♻️ Duplicate comments (2)
src/vault_contract/src/main.nr (2)
107-159:⚠️ Potential issue | 🔴 CriticalAuthorize
depositorbefore spending its public balance.
constructor_with_initial_depositpullsinitial_depositfromdepositor, but unlike the other public entrypoints it never calls_validate_from_public(depositor). That lets this initializer act on behalf of another address without the vault-layer auth check and then permanently lock the minted shares inself.address.🔒 Minimal fix
) { assert(vault_offset > 0, "vault_offset must be greater than 0"); + self.internal._validate_from_public(depositor); let deployer = get_contract_instance_deployer_avm(self.address).unwrap(); let shares = _compute_token_address(As per coding guidelines, "Every function operating on behalf of another address MUST call
_validate_from_privateor_validate_from_publicat the start. Missing authwit checks are critical security vulnerabilities."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/vault_contract/src/main.nr` around lines 107 - 159, The constructor_with_initial_deposit function is missing an authorization check for the depositor; add a call to _validate_from_public(depositor) (or _validate_from_private if appropriate) before performing any actions on behalf of depositor (e.g., before the Token::at(asset).transfer_public_to_public call and before using `from`), so the vault enforces the same vault-layer auth checks as other entrypoints and prevents acting on another address's behalf.
1-8:⚠️ Potential issue | 🟠 MajorDon't disable the whole Noir test module.
Skipping all of
pub mod test;removes the only layer that can still cover vault-specific auth and exact constructor/address-derivation failures. Keep the TXE/oracle-dependent cases skipped, but leave pure/unit tests for_compute_token_addressand initializer invariants enabled. Based on learnings, "TypeScript integration tests (src/ts/test/**/*.ts) can only assert the genericapp_logic_revertederror. Specific revert message validation must be done in Noir unit tests (unconstrained fn tests in *.nr files)."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/vault_contract/src/main.nr` around lines 1 - 8, Re-enable the Noir test module by restoring "pub mod test;" instead of commenting it out, but keep TXE/oracle-dependent tests skipped or gated; specifically ensure pure unit tests for _compute_token_address and the vault initializer invariants remain active (move or mark only the tests that rely on get_contract_instance/get_contract_instance_deployer_avm or custom deployment salts as ignored/conditioned). Locate the test module declaration (pub mod test) and update the contained test cases so TXE-dependent tests are behind a feature flag or #[ignore], while unconstrained fn tests asserting _compute_token_address and constructor/initializer revert messages remain enabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/vault_contract/src/main.nr`:
- Around line 107-159: The constructor_with_initial_deposit function is missing
an authorization check for the depositor; add a call to
_validate_from_public(depositor) (or _validate_from_private if appropriate)
before performing any actions on behalf of depositor (e.g., before the
Token::at(asset).transfer_public_to_public call and before using `from`), so the
vault enforces the same vault-layer auth checks as other entrypoints and
prevents acting on another address's behalf.
- Around line 1-8: Re-enable the Noir test module by restoring "pub mod test;"
instead of commenting it out, but keep TXE/oracle-dependent tests skipped or
gated; specifically ensure pure unit tests for _compute_token_address and the
vault initializer invariants remain active (move or mark only the tests that
rely on get_contract_instance/get_contract_instance_deployer_avm or custom
deployment salts as ignored/conditioned). Locate the test module declaration
(pub mod test) and update the contained test cases so TXE-dependent tests are
behind a feature flag or #[ignore], while unconstrained fn tests asserting
_compute_token_address and constructor/initializer revert messages remain
enabled.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Central YAML (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 54f9042e-781c-4ae5-85ce-e365844e6550
📒 Files selected for processing (1)
src/vault_contract/src/main.nr
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Code Review by Qodo
1.
|
| // TODO: TXE does not support custom salts, so the vault's computed shares address won't match | ||
| // the deployed shares token. Tests will fail until custom salt support is added to TXE. | ||
| // See: https://github.com/AztecProtocol/aztec-packages/issues/16656 | ||
|
|
||
| #[test] | ||
| unconstrained fn vault_with_initial_deposit_success() { |
There was a problem hiding this comment.
1. Known failing tests enabled 🐞 Bug ☼ Reliability
The vault initial-deposit Noir tests are still marked #[test] while the file explicitly states TXE cannot set custom salts, which means the vault-computed shares address can’t match the deployed shares token and initialization will revert when calling Token::at(shares). This is likely to break CI in TXE-based test runs until salt support is added or the tests are gated/adjusted.
Agent Prompt
### Issue description
Noir TXE tests for `constructor_with_initial_deposit` are enabled while the test file states TXE cannot set custom salts, making shares-address matching impossible and causing initializer reverts.
### Issue Context
The initializer computes `shares` deterministically and then calls `Token::at(shares)` during initialization. If TXE ignores/doesn't support the requested salt, the shares token will be deployed at a different address than the vault computes.
### Fix Focus Areas
- src/vault_contract/src/test/constructor_with_initial_deposit.nr[11-37]
- src/vault_contract/src/test/utils.nr[134-193]
- src/vault_contract/src/main.nr[98-154]
### Suggested changes
- Until TXE supports salts, either:
- Mark these tests as ignored/skipped in TXE (e.g., feature-gate), **or**
- Rework the TXE test setup to avoid relying on custom salts/address matching (e.g., keep the legacy 2-step wiring for TXE-only tests).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment |
|
Persistent review updated to latest commit a1c2e20 |
|
Persistent review updated to latest commit a68d1ea |
Benchmark Comparison
Contract: escrow
Contract: logic
Contract: nft
Contract: token
Contract: vault
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@cubic-dev-ai review |
@ilpepepig I have started the AI code review. It will take a few minutes to complete. |
🤖 Linear
Closes AZT-XXX
Description