diff --git a/docs/Nargo.toml b/docs/Nargo.toml index e9f188b498da..d2c2493fc83e 100644 --- a/docs/Nargo.toml +++ b/docs/Nargo.toml @@ -2,6 +2,6 @@ members = [ "examples/contracts/counter_contract", "examples/contracts/bob_token_contract", - "examples/tutorials/token_bridge_contract/contracts/aztec/nft", - "examples/tutorials/token_bridge_contract/contracts/aztec/nft_bridge" + "examples/contracts/nft", + "examples/contracts/nft_bridge" ] diff --git a/docs/docs-developers/docs/aztec-nr/framework-description/how_to_implement_custom_notes.md b/docs/docs-developers/docs/aztec-nr/framework-description/how_to_implement_custom_notes.md index 4b7113842e99..91a94cb714d2 100644 --- a/docs/docs-developers/docs/aztec-nr/framework-description/how_to_implement_custom_notes.md +++ b/docs/docs-developers/docs/aztec-nr/framework-description/how_to_implement_custom_notes.md @@ -51,7 +51,7 @@ address_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#i Define your custom note with the `#[note]` macro: -#include_code nft_note_struct /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/nft.nr rust +#include_code nft_note_struct /docs/examples/contracts/nft/src/nft.nr rust The `#[note]` macro generates the following for your struct: @@ -98,13 +98,13 @@ struct Storage { ### Inserting notes -#include_code mint /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/main.nr rust +#include_code mint /docs/examples/contracts/nft/src/main.nr rust ### Reading and removing notes Use `pop_notes` to read and nullify notes atomically. This is the recommended pattern for most use cases: -#include_code burn /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/main.nr rust +#include_code burn /docs/examples/contracts/nft/src/main.nr rust :::warning There's also a `get_notes` function that reads without nullifying, but use it with caution - the returned notes may have already been spent in another transaction. diff --git a/docs/docs-developers/docs/tutorials/js_tutorials/token_bridge.md b/docs/docs-developers/docs/tutorials/js_tutorials/token_bridge.md index fa732cf3ab66..93d1bc8f417a 100644 --- a/docs/docs-developers/docs/tutorials/js_tutorials/token_bridge.md +++ b/docs/docs-developers/docs/tutorials/js_tutorials/token_bridge.md @@ -108,7 +108,7 @@ touch src/nft.nr In this file, you're going to create a **private note** that represents NFT ownership. This is a struct with macros that indicate it is a note that can be compared and packed: -#include_code nft_note_struct /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/nft.nr rust +#include_code nft_note_struct /docs/examples/contracts/nft/src/nft.nr rust You now have a note that represents the owner of a particular NFT. Next, move on to the contract itself. @@ -134,7 +134,7 @@ Write the storage struct and a simple [initializer](../../foundational-topics/co ```rust -#include_code contract_setup /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/main.nr raw +#include_code contract_setup /docs/examples/contracts/nft/src/main.nr raw } ``` @@ -142,28 +142,28 @@ Write the storage struct and a simple [initializer](../../foundational-topics/co Add an internal function to handle the `DelayedPublicMutable` value change. Mark the function as public and `#[only_self]` so only the contract can call it: -#include_code mark_nft_exists /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/main.nr rust +#include_code mark_nft_exists /docs/examples/contracts/nft/src/main.nr rust This function is marked with `#[only_self]`, meaning only the contract itself can call it. It uses `schedule_value_change` to update the `nfts` storage, preventing the same NFT from being minted twice or burned when it doesn't exist. You'll call this public function from a private function later using `enqueue_self`. Another useful function checks how many notes a caller has. You can use this later to verify the claim and exit from L2: -#include_code notes_of /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/main.nr rust +#include_code notes_of /docs/examples/contracts/nft/src/main.nr rust ### Add Minting and Burning Before anything else, you need to set the minter. This will be the bridge contract, so only the bridge contract can mint NFTs. This value doesn't need to change after initialization. Here's how to initialize the `PublicImmutable`: -#include_code set_minter /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/main.nr rust +#include_code set_minter /docs/examples/contracts/nft/src/main.nr rust Now for the magic - minting NFTs **privately**. The bridge will call this to mint to a user, deliver the note using [constrained message delivery](../../aztec-nr/framework-description/how_to_emit_event.md) (best practice when "sending someone a note") and then [enqueue a public call](../../aztec-nr/framework-description/how_to_call_contracts.md) to the `_mark_nft_exists` function: -#include_code mint /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/main.nr rust +#include_code mint /docs/examples/contracts/nft/src/main.nr rust The bridge will also need to burn NFTs when users withdraw back to L1: -#include_code burn /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/main.nr rust +#include_code burn /docs/examples/contracts/nft/src/main.nr rust ### Compiling! @@ -241,7 +241,7 @@ Clean up `main.nr` which is just a placeholder, and let's write the storage stru ```rust -#include_code bridge_setup /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft_bridge/src/main.nr raw +#include_code bridge_setup /docs/examples/contracts/nft_bridge/src/main.nr raw } ``` @@ -255,7 +255,7 @@ You need to define how to encode messages. Here's a simple approach: when an NFT Build the `claim` function, which consumes the message and mints the NFT on the L2 side: -#include_code claim /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft_bridge/src/main.nr rust +#include_code claim /docs/examples/contracts/nft_bridge/src/main.nr rust :::tip Secret @@ -265,7 +265,7 @@ The secret prevents front-running. Certainly you don't want anyone to claim your Similarly, exiting to L1 means burning the NFT on the L2 side and pushing a message through the protocol. To ensure only the L1 recipient can claim it, hash the `token_id` together with the `recipient`: -#include_code exit /docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft_bridge/src/main.nr rust +#include_code exit /docs/examples/contracts/nft_bridge/src/main.nr rust Cross-chain messaging on Aztec is powerful because it doesn't conform to any specific format—you can structure messages however you want. @@ -319,7 +319,7 @@ touch contracts/SimpleNFT.sol Create a minimal NFT contract sufficient for demonstrating bridging: -#include_code simple_nft /docs/examples/tutorials/token_bridge_contract/contracts/SimpleNFT.sol solidity +#include_code simple_nft /docs/examples/solidity/nft_bridge/SimpleNFT.sol solidity ### Create the NFT Portal @@ -332,7 +332,7 @@ touch contracts/NFTPortal.sol Initialize it with Aztec's registry, which holds the canonical contracts for Aztec-related contracts, including the Inbox and Outbox. These are the message-passing contracts—Aztec sequencers read any messages on these contracts. ```solidity -#include_code portal_setup /docs/examples/tutorials/token_bridge_contract/contracts/NFTPortal.sol raw +#include_code portal_setup /docs/examples/solidity/nft_bridge/NFTPortal.sol raw } ``` @@ -340,7 +340,7 @@ The core logic is similar to the L2 logic. `depositToAztec` calls the `Inbox` ca Add these two functions with explanatory comments: -#include_code portal_deposit_and_withdraw /docs/examples/tutorials/token_bridge_contract/contracts/NFTPortal.sol solidity +#include_code portal_deposit_and_withdraw /docs/examples/solidity/nft_bridge/NFTPortal.sol solidity The portal handles two flows: @@ -383,19 +383,19 @@ This section assumes you're working locally using the local network. For the tes First, initialize the clients: `aztec.js` for Aztec and `viem` for Ethereum: -#include_code setup /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code setup /docs/examples/ts/token_bridge/index.ts typescript You now have wallets for both chains, correctly connected to their respective chains. Next, deploy the L1 contracts: -#include_code deploy_l1_contracts /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code deploy_l1_contracts /docs/examples/ts/token_bridge/index.ts typescript Now deploy the L2 contracts. Thanks to the TypeScript bindings generated with `aztec codegen`, deployment is straightforward: -#include_code deploy_l2_contracts /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code deploy_l2_contracts /docs/examples/ts/token_bridge/index.ts typescript Now that you have the L2 bridge's contract address, initialize the L1 bridge: -#include_code initialize_portal /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code initialize_portal /docs/examples/ts/token_bridge/index.ts typescript The L2 contracts were already initialized when you deployed them, but you still need to: @@ -404,7 +404,7 @@ The L2 contracts were already initialized when you deployed them, but you still Complete these initialization steps: -#include_code initialize_l2_bridge /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code initialize_l2_bridge /docs/examples/ts/token_bridge/index.ts typescript This completes the setup. It's a lot of configuration, but you're dealing with four contracts across two chains. @@ -412,41 +412,41 @@ This completes the setup. It's a lot of configuration, but you're dealing with f Now for the main flow. Mint a CryptoPunk on L1, deposit it to Aztec, and claim it on Aztec. Put everything in the same script. To mint, call the L1 contract with `mint`, which will mint `tokenId = 0`: -#include_code mint_nft_l1 /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code mint_nft_l1 /docs/examples/ts/token_bridge/index.ts typescript To bridge, first approve the portal address to transfer the NFT, then transfer it by calling `depositToAztec`: -#include_code deposit_to_aztec /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code deposit_to_aztec /docs/examples/ts/token_bridge/index.ts typescript The `Inbox` contract will emit an important log: `MessageSent(inProgress, index, leaf, updatedRollingHash);`. This log provides the **leaf index** of the message in the [L1-L2 Message Tree](../../aztec-nr/framework-description/ethereum-aztec-messaging/index.md)—the location of the message in the tree that will appear on L2. You need this index, plus the secret, to correctly claim and decrypt the message. Use viem to extract this information: -#include_code get_message_leaf_index /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code get_message_leaf_index /docs/examples/ts/token_bridge/index.ts typescript This extracts the logs from the deposit and retrieves the leaf index. You can now claim it on L2. However, for security reasons, at least 2 blocks must pass before a message can be claimed on L2. If you called `claim` on the L2 contract immediately, it would return "no message available". Add a utility function to mine two blocks (it deploys a contract with a random salt): -#include_code mine_blocks /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code mine_blocks /docs/examples/ts/token_bridge/index.ts typescript Now claim the message on L2: -#include_code claim_on_l2 /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code claim_on_l2 /docs/examples/ts/token_bridge/index.ts typescript ### L2 → L1 Flow Great! You can expand the L2 contract to add features like NFT transfers. For now, exit the NFT on L2 and redeem it on L1. Mine two blocks because of `DelayedMutable`: -#include_code exit_from_l2 /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code exit_from_l2 /docs/examples/ts/token_bridge/index.ts typescript Just like in the L1 → L2 flow, you need to know what to claim on L1. Where in the message tree is the message you want to claim? Use the utility `computeL2ToL1MembershipWitness`, which provides the leaf and the sibling path of the message: -#include_code get_withdrawal_witness /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code get_withdrawal_witness /docs/examples/ts/token_bridge/index.ts typescript With this information, call the L1 contract and use the index and the sibling path to claim the L1 NFT: -#include_code withdraw_on_l1 /docs/examples/tutorials/token_bridge_contract/scripts/index.ts typescript +#include_code withdraw_on_l1 /docs/examples/ts/token_bridge/index.ts typescript You can now try the whole flow with: diff --git a/docs/examples/bootstrap.sh b/docs/examples/bootstrap.sh index 1e4bb170cc1d..eff21032626a 100755 --- a/docs/examples/bootstrap.sh +++ b/docs/examples/bootstrap.sh @@ -17,6 +17,39 @@ function compile { $REPO_ROOT/noir-projects/noir-contracts/bootstrap.sh compile "$@" } +function compile-solidity { + echo_header "Compiling Solidity examples" + local SOLIDITY_DIR="$REPO_ROOT/docs/examples/solidity" + local OUTPUT_DIR="$REPO_ROOT/docs/target/solidity" + + # Find all .sol files recursively + local sol_files + sol_files=$(find "$SOLIDITY_DIR" -name "*.sol" 2>/dev/null) + if [ -z "$sol_files" ]; then + echo_stderr "No Solidity files found in $SOLIDITY_DIR" + return 0 + fi + + mkdir -p "$OUTPUT_DIR" + + # Compile using the local foundry.toml with proper remappings + ( + cd "$SOLIDITY_DIR" + for subdir in */; do + if [ -d "$subdir" ] && ls "$subdir"/*.sol >/dev/null 2>&1; then + local subdir_name=$(basename "$subdir") + echo_stderr "Compiling $subdir_name..." + forge build \ + --contracts "$subdir" \ + --out "$OUTPUT_DIR/$subdir_name" \ + --no-cache + fi + done + ) + + echo_stderr "Solidity artifacts written to $OUTPUT_DIR" +} + function validate-ts { echo_header "Validating TypeScript examples" (cd ts && ./bootstrap.sh "$@") @@ -25,8 +58,12 @@ function validate-ts { case "$cmd" in "") compile + compile-solidity validate-ts ;; + compile-solidity) + compile-solidity + ;; *) default_cmd_handler "$@" ;; diff --git a/docs/examples/contracts/nft/Nargo.toml b/docs/examples/contracts/nft/Nargo.toml new file mode 100644 index 000000000000..781ca6ca58cd --- /dev/null +++ b/docs/examples/contracts/nft/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "nft" +type = "contract" + +[dependencies] +aztec = { path = "../../../../noir-projects/aztec-nr/aztec" } diff --git a/docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/main.nr b/docs/examples/contracts/nft/src/main.nr similarity index 100% rename from docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/main.nr rename to docs/examples/contracts/nft/src/main.nr diff --git a/docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/nft.nr b/docs/examples/contracts/nft/src/nft.nr similarity index 100% rename from docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/src/nft.nr rename to docs/examples/contracts/nft/src/nft.nr diff --git a/docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft_bridge/Nargo.toml b/docs/examples/contracts/nft_bridge/Nargo.toml similarity index 56% rename from docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft_bridge/Nargo.toml rename to docs/examples/contracts/nft_bridge/Nargo.toml index 7fea03720fc9..c94e33994b92 100644 --- a/docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft_bridge/Nargo.toml +++ b/docs/examples/contracts/nft_bridge/Nargo.toml @@ -3,5 +3,5 @@ name = "nft_bridge" type = "contract" [dependencies] -aztec = { path = "../../../../../../../noir-projects/aztec-nr/aztec" } +aztec = { path = "../../../../noir-projects/aztec-nr/aztec" } NFTPunk = { path = "../nft" } diff --git a/docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft_bridge/src/main.nr b/docs/examples/contracts/nft_bridge/src/main.nr similarity index 100% rename from docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft_bridge/src/main.nr rename to docs/examples/contracts/nft_bridge/src/main.nr diff --git a/docs/examples/solidity/foundry.toml b/docs/examples/solidity/foundry.toml new file mode 100644 index 000000000000..3d7408f7d88b --- /dev/null +++ b/docs/examples/solidity/foundry.toml @@ -0,0 +1,15 @@ +[profile.default] +src = '.' +out = 'out' +libs = ['../../../l1-contracts/lib', '../../../l1-contracts/src'] +solc_version = '0.8.27' +evm_version = 'prague' +optimizer = true + +# Match l1-contracts remappings exactly +remappings = [ + "@oz/=../../../l1-contracts/lib/openzeppelin-contracts/contracts/", + "@aztec/=../../../l1-contracts/src/", + "@openzeppelin/contracts/=../../../l1-contracts/lib/openzeppelin-contracts/contracts/", + "@aztec-blob-lib/=../../../l1-contracts/src/mock/libraries/" +] diff --git a/docs/examples/tutorials/token_bridge_contract/contracts/NFTPortal.sol b/docs/examples/solidity/nft_bridge/NFTPortal.sol similarity index 78% rename from docs/examples/tutorials/token_bridge_contract/contracts/NFTPortal.sol rename to docs/examples/solidity/nft_bridge/NFTPortal.sol index 6518b1be238a..35b00f1f7636 100644 --- a/docs/examples/tutorials/token_bridge_contract/contracts/NFTPortal.sol +++ b/docs/examples/solidity/nft_bridge/NFTPortal.sol @@ -2,13 +2,13 @@ pragma solidity >=0.8.27; // docs:start:portal_setup -import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {IRegistry} from "@aztec/l1-contracts/src/governance/interfaces/IRegistry.sol"; -import {IInbox} from "@aztec/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol"; -import {IOutbox} from "@aztec/l1-contracts/src/core/interfaces/messagebridge/IOutbox.sol"; -import {IRollup} from "@aztec/l1-contracts/src/core/interfaces/IRollup.sol"; -import {DataStructures} from "@aztec/l1-contracts/src/core/libraries/DataStructures.sol"; -import {Hash} from "@aztec/l1-contracts/src/core/libraries/crypto/Hash.sol"; +import {IERC721} from "@oz/token/ERC721/IERC721.sol"; +import {IRegistry} from "@aztec/governance/interfaces/IRegistry.sol"; +import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; +import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol"; +import {IRollup} from "@aztec/core/interfaces/IRollup.sol"; +import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; +import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; contract NFTPortal { IRegistry public registry; diff --git a/docs/examples/tutorials/token_bridge_contract/contracts/SimpleNFT.sol b/docs/examples/solidity/nft_bridge/SimpleNFT.sol similarity index 84% rename from docs/examples/tutorials/token_bridge_contract/contracts/SimpleNFT.sol rename to docs/examples/solidity/nft_bridge/SimpleNFT.sol index 2193a2112088..fd479fb599a2 100644 --- a/docs/examples/tutorials/token_bridge_contract/contracts/SimpleNFT.sol +++ b/docs/examples/solidity/nft_bridge/SimpleNFT.sol @@ -2,7 +2,7 @@ // docs:start:simple_nft pragma solidity >=0.8.27; -import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import {ERC721} from "@oz/token/ERC721/ERC721.sol"; contract SimpleNFT is ERC721 { uint256 private _currentTokenId; diff --git a/docs/examples/ts/bootstrap.sh b/docs/examples/ts/bootstrap.sh index 30ba2821ec54..dba87ef01035 100755 --- a/docs/examples/ts/bootstrap.sh +++ b/docs/examples/ts/bootstrap.sh @@ -171,12 +171,14 @@ validate_project() { if [ "$has_deps" = true ]; then # Install linked @aztec dependencies if [ ${#aztec_deps[@]} -gt 0 ]; then - yarn add "${aztec_deps[@]}" >/dev/null 2>&1 + echo_stderr "Adding aztec deps: ${aztec_deps[*]}" + yarn add "${aztec_deps[@]}" fi # Install external npm dependencies if [ ${#npm_deps[@]} -gt 0 ]; then - yarn add "${npm_deps[@]}" >/dev/null 2>&1 + echo_stderr "Adding npm deps: ${npm_deps[*]}" + yarn add "${npm_deps[@]}" fi else # Fallback to default dependencies if none specified @@ -185,10 +187,39 @@ validate_project() { @aztec/aztec.js@link:$REPO_ROOT/yarn-project/aztec.js \ @aztec/accounts@link:$REPO_ROOT/yarn-project/accounts \ @aztec/test-wallet@link:$REPO_ROOT/yarn-project/test-wallet \ - @aztec/kv-store@link:$REPO_ROOT/yarn-project/kv-store \ - >/dev/null 2>&1 + @aztec/kv-store@link:$REPO_ROOT/yarn-project/kv-store fi + # Verify linked packages exist and have built artifacts + echo_stderr "Verifying linked packages..." + for dep in "${aztec_deps[@]}"; do + # Extract package name from @aztec/foo@link:... format + local pkg_full=$(echo "$dep" | cut -d'@' -f2) # aztec/foo + local pkg_name=${pkg_full#aztec/} # foo + local link_target="$REPO_ROOT/yarn-project/$pkg_name" + + if [ ! -d "$link_target" ]; then + echo_stderr "ERROR: Link target does not exist: $link_target" + return 1 + fi + + if [ ! -d "$link_target/dest" ]; then + echo_stderr "ERROR: Package not built (no dest/): $link_target" + ls -la "$link_target" || true + return 1 + fi + + # Check for .d.ts files (type declarations) + local dts_count=$(find "$link_target/dest" -name "*.d.ts" 2>/dev/null | wc -l) + if [ "$dts_count" -eq 0 ]; then + echo_stderr "ERROR: No .d.ts files found in $link_target/dest" + ls -la "$link_target/dest" | head -20 || true + return 1 + fi + + echo_stderr " ✓ @aztec/$pkg_name: $dts_count .d.ts files" + done + yarn add -D typescript >/dev/null 2>&1 # Create tsconfig.json from template diff --git a/docs/examples/ts/token_bridge/config.yaml b/docs/examples/ts/token_bridge/config.yaml new file mode 100644 index 000000000000..7a1439d534be --- /dev/null +++ b/docs/examples/ts/token_bridge/config.yaml @@ -0,0 +1,19 @@ +# Configuration for token_bridge example + +# Contract artifacts to import for codegen (names without .json extension) +# These must already be compiled to docs/target/ directory +contracts: + - nft-NFTPunk + - nft_bridge-NFTBridge + +# Dependencies: +# - @aztec/* packages are auto-linked from yarn-project/ +# - Use npm:package-name for external npm packages +dependencies: + - "@aztec/aztec.js" + - "@aztec/accounts" + - "@aztec/test-wallet" + - "@aztec/stdlib" + - "@aztec/foundation" + - "@aztec/ethereum" + - "npm:viem@npm:@aztec/viem@2.38.2" diff --git a/docs/examples/tutorials/token_bridge_contract/scripts/index.ts b/docs/examples/ts/token_bridge/index.ts similarity index 68% rename from docs/examples/tutorials/token_bridge_contract/scripts/index.ts rename to docs/examples/ts/token_bridge/index.ts index a8c3338c7ff7..e8fdd3804ea9 100644 --- a/docs/examples/tutorials/token_bridge_contract/scripts/index.ts +++ b/docs/examples/ts/token_bridge/index.ts @@ -1,17 +1,8 @@ -// @ts-nocheck - // docs:start:setup import { privateKeyToAccount } from "viem/accounts"; -import { - createPublicClient, - createWalletClient, - http, - pad, - getAbiItem, - toEventHash, -} from "viem"; +import { decodeEventLog, getContract, pad } from "viem"; import { foundry } from "viem/chains"; -import { EthAddress } from "@aztec/aztec.js/addresses"; +import { AztecAddress, EthAddress } from "@aztec/aztec.js/addresses"; import { Fr } from "@aztec/aztec.js/fields"; import { createAztecNodeClient } from "@aztec/aztec.js/node"; import { computeSecretHash } from "@aztec/stdlib/hash"; @@ -20,24 +11,22 @@ import { sha256ToField } from "@aztec/foundation/crypto/sha256"; import { computeL2ToL1MessageHash } from "@aztec/stdlib/hash"; import { TestWallet } from "@aztec/test-wallet/server"; import { getInitialTestAccountsData } from "@aztec/accounts/testing"; -import SimpleNFT from "../artifacts/contracts/SimpleNFT.sol/SimpleNFT.json"; -import NFTPortal from "../artifacts/contracts/NFTPortal.sol/NFTPortal.json"; -import { NFTPunkContract } from "../contracts/aztec/artifacts/NFTPunk.js"; -import { NFTBridgeContract } from "../contracts/aztec/artifacts/NFTBridge.js"; - -// Setup L1 clients using anvil's 1st account which should have a ton of ETH already +import { createExtendedL1Client } from "@aztec/ethereum/client"; +import { deployL1Contract } from "@aztec/ethereum/deploy-l1-contract"; +import SimpleNFT from "../../../target/solidity/nft_bridge/SimpleNFT.sol/SimpleNFT.json" with { type: "json" }; +import NFTPortal from "../../../target/solidity/nft_bridge/NFTPortal.sol/NFTPortal.json" with { type: "json" }; +import { NFTPunkContract } from "./artifacts/NFTPunk.js"; +import { NFTBridgeContract } from "./artifacts/NFTBridge.js"; + +// Setup L1 client using anvil's 1st account which should have a ton of ETH already const l1Account = privateKeyToAccount( "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" ); -const publicClient = createPublicClient({ - chain: foundry, - transport: http("http://localhost:8545"), -}); -const ethWallet = createWalletClient({ - account: l1Account, - chain: foundry, - transport: http("http://localhost:8545"), -}); +const l1Client = createExtendedL1Client( + ["http://localhost:8545"], + l1Account, + foundry +); // Setup L2 using Aztec's local network and one of its initial accounts console.log("🔮 Setting up L2...\n"); @@ -59,23 +48,17 @@ const inboxAddress = nodeInfo.l1ContractAddresses.inboxAddress.toString(); // docs:start:deploy_l1_contracts console.log("📦 Deploying L1 contracts...\n"); -const nftDeploymentHash = await ethWallet.deployContract({ - abi: SimpleNFT.abi, - bytecode: SimpleNFT.bytecode as `0x${string}`, -}); -const nftReceipt = await publicClient.waitForTransactionReceipt({ - hash: nftDeploymentHash, -}); -const nftAddress = nftReceipt.contractAddress!; +const { address: nftAddress } = await deployL1Contract( + l1Client, + SimpleNFT.abi, + SimpleNFT.bytecode.object as `0x${string}` +); -const portalDeploymentHash = await ethWallet.deployContract({ - abi: NFTPortal.abi, - bytecode: NFTPortal.bytecode as `0x${string}`, -}); -const portalReceipt = await publicClient.waitForTransactionReceipt({ - hash: portalDeploymentHash, -}); -const portalAddress = portalReceipt.contractAddress!; +const { address: portalAddress } = await deployL1Contract( + l1Client, + NFTPortal.abi, + NFTPortal.bytecode.object as `0x${string}` +); console.log(`✅ SimpleNFT: ${nftAddress}`); console.log(`✅ NFTPortal: ${portalAddress}\n`); @@ -99,17 +82,23 @@ console.log(`✅ L2 Bridge: ${l2Bridge.address.toString()}\n`); // docs:start:initialize_portal console.log("🔧 Initializing portal..."); -const hash = await ethWallet.writeContract({ - address: portalAddress as `0x${string}`, +const nftContract = getContract({ + address: nftAddress.toString() as `0x${string}`, + abi: SimpleNFT.abi, + client: l1Client, +}); +const portalContract = getContract({ + address: portalAddress.toString() as `0x${string}`, abi: NFTPortal.abi, - functionName: "initialize", - args: [ - registryAddress as `0x${string}`, - nftAddress as `0x${string}`, - l2Bridge.address.toString() as `0x${string}`, - ], + client: l1Client, }); -await publicClient.waitForTransactionReceipt({ hash }); + +const hash = await portalContract.write.initialize([ + registryAddress as `0x${string}`, + nftAddress.toString() as `0x${string}`, + l2Bridge.address.toString() as `0x${string}`, +]); +await l1Client.waitForTransactionReceipt({ hash }); console.log("✅ Portal initialized\n"); // docs:end:initialize_portal @@ -118,7 +107,7 @@ console.log("✅ Portal initialized\n"); console.log("🔧 Setting up L2 bridge..."); await l2Bridge.methods - .set_portal(EthAddress.fromString(portalAddress)) + .set_portal(EthAddress.fromString(portalAddress.toString())) .send({ from: account.address }) .wait(); @@ -133,13 +122,8 @@ console.log("✅ Bridge configured\n"); // docs:start:mint_nft_l1 console.log("🎨 Minting NFT on L1..."); -const mintHash = await ethWallet.writeContract({ - address: nftAddress as `0x${string}`, - abi: SimpleNFT.abi, - functionName: "mint", - args: [l1Account.address], -}); -await publicClient.waitForTransactionReceipt({ hash: mintHash }); +const mintHash = await nftContract.write.mint([l1Account.address]); +await l1Client.waitForTransactionReceipt({ hash: mintHash }); // no need to parse logs, this will be tokenId 0 since it's a fresh contract const tokenId = 0n; @@ -153,24 +137,17 @@ console.log("🌉 Depositing NFT to Aztec..."); const secret = Fr.random(); const secretHash = await computeSecretHash(secret); -const approveHash = await ethWallet.writeContract({ - address: nftAddress as `0x${string}`, - abi: SimpleNFT.abi, - functionName: "approve", - args: [portalAddress as `0x${string}`, tokenId], -}); -await publicClient.waitForTransactionReceipt({ hash: approveHash }); +const approveHash = await nftContract.write.approve([ + portalAddress.toString() as `0x${string}`, + tokenId, +]); +await l1Client.waitForTransactionReceipt({ hash: approveHash }); -const depositHash = await ethWallet.writeContract({ - address: portalAddress as `0x${string}`, - abi: NFTPortal.abi, - functionName: "depositToAztec", - args: [ - tokenId, - pad(secretHash.toString() as `0x${string}`, { dir: "left", size: 32 }), - ], -}); -const depositReceipt = await publicClient.waitForTransactionReceipt({ +const depositHash = await portalContract.write.depositToAztec([ + tokenId, + pad(secretHash.toString() as `0x${string}`, { dir: "left", size: 32 }), +]); +const depositReceipt = await l1Client.waitForTransactionReceipt({ hash: depositHash, }); // docs:end:deposit_to_aztec @@ -188,19 +165,28 @@ const INBOX_ABI = [ ], }, ] as const; -const messageSentTopic = toEventHash(INBOX_ABI[0]); -const messageSentLog = depositReceipt.logs!.find( - (log: any) => - log.address.toLowerCase() === inboxAddress.toLowerCase() && - log.topics[0] === messageSentTopic -); -const indexHex = messageSentLog!.data!.slice(0, 66); -const messageLeafIndex = new Fr(BigInt(indexHex)); +// Find and decode the MessageSent event from the Inbox contract +const messageSentLog = depositReceipt.logs + .filter((log) => log.address.toLowerCase() === inboxAddress.toLowerCase()) + .map((log) => { + try { + return decodeEventLog({ + abi: INBOX_ABI, + data: log.data, + topics: log.topics, + }); + } catch { + return null; + } + }) + .find((decoded) => decoded?.eventName === "MessageSent"); + +const messageLeafIndex = new Fr(messageSentLog!.args.index); // docs:end:get_message_leaf_index // docs:start:mine_blocks -async function mine2Blocks(aztecWallet: TestWallet, accountAddress: any) { +async function mine2Blocks(aztecWallet: TestWallet, accountAddress: AztecAddress) { await NFTPunkContract.deploy(aztecWallet, accountAddress) .send({ from: accountAddress, contractAddressSalt: Fr.random() }) .deployed(); @@ -272,16 +258,12 @@ const content = sha256ToField([ ]); // Get rollup version from the portal contract (it stores it during initialize) -const version = (await publicClient.readContract({ - address: portalAddress as `0x${string}`, - abi: NFTPortal.abi, - functionName: "rollupVersion", -})) as number; +const version = await portalContract.read.rollupVersion(); // Compute the L2→L1 message hash const msgLeaf = computeL2ToL1MessageHash({ l2Sender: l2Bridge.address, - l1Recipient: EthAddress.fromString(portalAddress), + l1Recipient: EthAddress.fromString(portalAddress.toString()), content, rollupVersion: new Fr(version), chainId: new Fr(foundry.id), @@ -318,17 +300,12 @@ const siblingPathHex = witness!.siblingPath // docs:start:withdraw_on_l1 console.log("💰 Withdrawing NFT on L1..."); -const withdrawHash = await ethWallet.writeContract({ - address: portalAddress as `0x${string}`, - abi: NFTPortal.abi, - functionName: "withdraw", - args: [ - tokenId, - BigInt(exitReceipt.blockNumber!), - BigInt(witness!.leafIndex), - siblingPathHex, - ], -}); -await publicClient.waitForTransactionReceipt({ hash: withdrawHash }); +const withdrawHash = await portalContract.write.withdraw([ + tokenId, + BigInt(exitReceipt.blockNumber!), + BigInt(witness!.leafIndex), + siblingPathHex, +]); +await l1Client.waitForTransactionReceipt({ hash: withdrawHash }); console.log("✅ NFT withdrawn to L1\n"); // docs:end:withdraw_on_l1 diff --git a/docs/examples/ts/token_bridge/yarn.lock b/docs/examples/ts/token_bridge/yarn.lock new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/Nargo.toml b/docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/Nargo.toml deleted file mode 100644 index 59089e812edd..000000000000 --- a/docs/examples/tutorials/token_bridge_contract/contracts/aztec/nft/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "nft" -type = "contract" - -[dependencies] -aztec = { path = "../../../../../../../noir-projects/aztec-nr/aztec" }