diff --git a/apps/nextra/pages/zh/build/_meta.tsx b/apps/nextra/pages/zh/build/_meta.tsx
index 69f4737a0..fcef86b69 100644
--- a/apps/nextra/pages/zh/build/_meta.tsx
+++ b/apps/nextra/pages/zh/build/_meta.tsx
@@ -1,6 +1,9 @@
export default {
"get-started": "开始",
- "smart-contracts": "Smart Contracts (Move)",
+ "smart-contracts": {
+ title: "Smart Contracts (Move)",
+ href: "/en/build/smart-contracts",
+ },
cli: "CLI",
apis: "APIs",
guides: "Guides",
diff --git a/apps/nextra/pages/zh/build/apis/_meta.tsx b/apps/nextra/pages/zh/build/apis/_meta.tsx
index 6ad57d471..d83fb7f42 100644
--- a/apps/nextra/pages/zh/build/apis/_meta.tsx
+++ b/apps/nextra/pages/zh/build/apis/_meta.tsx
@@ -6,15 +6,23 @@ export default {
layout: "full",
sidebar: false,
},
+ href: "/en/build/apis/fullnode-rest-api-reference",
},
"fullnode-rest-api": "全节点 REST API 指南",
"indexer-graphql-api": {
title: "索引器 GraphQL API",
href: "/zh/build/indexer",
},
- "data-providers": "数据提供商",
+ "data-providers": {
+ title: "数据提供商",
+ href: "/en/build/apis/data-providers",
+ },
"faucet-api": {
title: "水龙头 API",
+ href: "/en/build/apis/faucet-api",
+ },
+ "aptos-labs-developer-portal": {
+ title: "API 网关",
+ href: "/en/build/apis/aptos-labs-developer-portal",
},
- "aptos-labs-developer-portal": "API 网关",
};
diff --git a/apps/nextra/pages/zh/build/apis/data-providers.mdx b/apps/nextra/pages/zh/build/apis/data-providers.mdx
deleted file mode 100644
index 1b0f2e835..000000000
--- a/apps/nextra/pages/zh/build/apis/data-providers.mdx
+++ /dev/null
@@ -1,79 +0,0 @@
----
-title: "Data Providers"
----
-
-# Data Providers
-
-If you want to access aptos blockchain data but don't need it in real-time.
-We have a few options that will let you access this data using SQL or UIs for building dashboards.
-This type of data is often used for analytics since it allows for aggregations.
-
-## Review of data endpoints
-
-Hitting the full node directly will give the latest data (will be missing historical unless it's an archival full node) using [REST API](../apis.mdx#aptos-fullnode)
-
-Indexer layer on top of this will provide a [GRPC transaction stream](../indexer/txn-stream/aptos-hosted-txn-stream)
-
-On top of this transaction stream, we've built out some product logic tables that can be queried through [GraphQL](../indexer/)
-
-Since the logic to parse out transaction is [public](https://github.com/aptos-labs/aptos-indexer-processors), some vendors have implemented similar parsing logic to create a subset of tables and made them available to query.
-
-## SQL Tables
-
-Indexer defines several processors that create different database tables.
-
-### Core tables
-
-These are parsed directly from node API response, one option is to split it out into the following tables:
-
-- Blocks - version, block height, epoch, timestamp
-- Transactions - version, sender, entry function, gas
-- Signatures - signature types, signer, fee payer address
-- Events - type and data for events
-
-We store data as table items, resources or modules
-
-- (write set) changes - change index, change type, resource address
-- Table items - table key, table handle, key (content and type), value (content and type)
-- (move) resources - resource address, resource type, data
-- (move) modules - bytecode for deployed modules
-
-## Vendors of off-chain data
-
-Most of our data vendors only provide core datasets.
-A [subset of vendors](https://aptosfoundation.org/currents/aptos-on-chain-data-capabilities-with-dune-nansen-and-other-providers) is listed below
-
-### Google bigquery public dataset
-
-Provides data through [google public data](https://console.cloud.google.com/marketplace/product/bigquery-public-data/crypto-aptos-mainnet-us)
-
-
-
-We also have sample analytics queries [using the above resources](https://github.com/aptos-labs/explorer/tree/main/analytics)
-
-### Dune
-
-We have a dashboard here: https://dune.com/aptos/aptos-chain-metrics-overview
-
-### Flipside
-
-Another dashboard vendor, signatures in core tables were merged into `fact_transactions`
-They also have a few more convenience tables (defi, nft, price), [table list](https://flipsidecrypto.github.io/aptos-models/#!/overview)
-
-### Sentio
-
-They have a guide here: https://docs.sentio.xyz/docs/aptos
-Data is found in data source -> external project -> sentio/aptos-overview
-
-### Space and Time
-
-Data can be found here: https://app.spaceandtime.ai/data-sets?selectedChain=aptos
-They also have some non-core tables (Token, Staking, etc)
-
-## Other vendors
-
-We also have some partners who target more enterprise use cases
-
-- [Token Terminal](https://tokenterminal.com/resources/articles/aptos-data-partnership)
-- [The Tie](https://www.thetie.io/insights/news/introducing-aptos-ecosystem-dashboard-and-on-chain-data/)
-- [Elliptic](https://www.elliptic.co/media-center/elliptic-partners-with-aptos-foundation-as-a-data-integration-provider-to-offer-compliance-screening-and-risk-services-for-aptos-network)
diff --git a/apps/nextra/pages/zh/build/apis/faucet-api.mdx b/apps/nextra/pages/zh/build/apis/faucet-api.mdx
deleted file mode 100644
index 3ab59d9ce..000000000
--- a/apps/nextra/pages/zh/build/apis/faucet-api.mdx
+++ /dev/null
@@ -1,75 +0,0 @@
----
-title: "Faucet API"
----
-
-# Faucet API
-
-The faucet allows users to get `APT` on devnet. On testnet you can only mint at the [mint page](/network/faucet). It is not available on Mainnet.
-
-The endpoints for each faucet are:
-
-- Devnet: https://faucet.devnet.aptoslabs.com
-
-## Using the faucet
-
-Each SDK has integration for devnet to use the faucet. Below are a few examples, but you can
-see more information on each individual [SDK's documentation](../sdks.mdx).
-
-### Using the faucet in a wallet
-
-Most wallets, such as [Petra](https://aptosfoundation.org/ecosystem/project/petra) or [Pontem](https://aptosfoundation.org/ecosystem/project/pontem-wallet)
-will have a faucet button for devnet. See full list of [Aptos Wallets](https://aptosfoundation.org/ecosystem/projects/wallets).
-
-### Using the faucet in the Aptos CLI
-
-Once you've [set up your CLI](../cli/setup-cli.mdx), you can simply call fund-with-faucet. The amount used is in Octas (1 APT = 100,000,000 Octas).
-
-```bash filename="Terminal"
-aptos account fund-with-faucet --account 0xd0f523c9e73e6f3d68c16ae883a9febc616e484c4998a72d8899a1009e5a89d6 --amount 100000000
-```
-
-### Using the faucet in the TypeScript SDK
-
-Here is an example funding the account `0xd0f523c9e73e6f3d68c16ae883a9febc616e484c4998a72d8899a1009e5a89d6` with 1 APT in Devnet. The amount used is in Octas (1 APT = 100,000,000 Octas).
-
-```ts filename="index.ts"
-import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";
-
-const aptos = new Aptos(new AptosConfig({network: Network.Devnet}));
-aptos.fundAccount({accountAddress: "0xd0f523c9e73e6f3d68c16ae883a9febc616e484c4998a72d8899a1009e5a89d6", amount: 100000000});
-```
-
-### Using the faucet in the Go SDK
-
-Here is an example funding the account `0xd0f523c9e73e6f3d68c16ae883a9febc616e484c4998a72d8899a1009e5a89d6` with 1 APT in Devnet. The amount used is in Octas (1 APT = 100,000,000 Octas).
-
-```go filename="index.go"
-import "github.com/aptos-labs/aptos-go-sdk"
-
-func main() {
- client, err := aptos.NewClient(aptos.LocalnetConfig)
- if err != nil {
- panic(err)
- }
-
- client.Fund("0xd0f523c9e73e6f3d68c16ae883a9febc616e484c4998a72d8899a1009e5a89d6", 100000000)
-}
-```
-
-### Calling the faucet: Other languages not supported by SDKs
-
-If you are trying to call the faucet in other languages, you have two options:
-
-1. Generate a client from
-the [OpenAPI spec](https://github.com/aptos-labs/aptos-core/blob/main/crates/aptos-faucet/doc/spec.yaml).
-2. Call the faucet on your own.
-
-For the latter, you will want to build a query similar to this:
-
-```bash filename="Terminal"
-curl -X POST
-'https://faucet.devnet.aptoslabs.com/mint?amount=10000&address=0xd0f523c9e73e6f3d68c16ae883a9febc616e484c4998a72d8899a1009e5a89d6'
-```
-
-This means mint 10000 [octas](../../network/glossary.mdx#Octa) to
-address `0xd0f523c9e73e6f3d68c16ae883a9febc616e484c4998a72d8899a1009e5a89d6`.
diff --git a/apps/nextra/pages/zh/build/cli/trying-things-on-chain/_meta.tsx b/apps/nextra/pages/zh/build/cli/trying-things-on-chain/_meta.tsx
index ea6041a32..fbe82a530 100644
--- a/apps/nextra/pages/zh/build/cli/trying-things-on-chain/_meta.tsx
+++ b/apps/nextra/pages/zh/build/cli/trying-things-on-chain/_meta.tsx
@@ -7,5 +7,6 @@ export default {
},
ledger: {
title: "使用硬件钱包",
+ href: "/en/build/cli/trying-things-on-chain/ledger",
},
};
diff --git a/apps/nextra/pages/zh/build/cli/trying-things-on-chain/ledger.mdx b/apps/nextra/pages/zh/build/cli/trying-things-on-chain/ledger.mdx
deleted file mode 100644
index 538af18d0..000000000
--- a/apps/nextra/pages/zh/build/cli/trying-things-on-chain/ledger.mdx
+++ /dev/null
@@ -1,617 +0,0 @@
-import { Callout, Steps } from 'nextra/components'
-
-# Use Hardware Ledger via the Aptos CLI
-
-Using a hardware wallet like Ledger is the most secure way to sign transactions on `mainnet` as your private key never leaves your device.
-
-
-The `Ledger Nano S` has limited memory and may not be able to sign many transactions on Aptos. If you are trying to sign a transaction that is too big for your device to handle, you will get the error `Wrong raw transaction length`.
-
-
-## Initial Setup
-
-You will need to do a few steps of configuration for the Aptos CLI and your Ledger device to sign transactions.
-
-
-### Ensure you have the Aptos CLI installed.
- You can install the Aptos CLI by following [these steps](../../cli.mdx) if you have not done so already.
-
-### Ensure you have done the basic setup for your Ledger device.
- You can find those steps on [Ledger’s website](https://www.ledger.com/). For example, here are the set up instructions for the [Ledger Nano X](https://support.ledger.com/hc/en-us/articles/360018784134-Set-up-your-Ledger-Nano-X?docs=true).
-### Plug your Ledger device into your computer.
-### Install the Aptos App on your Ledger device by following [this guide](https://support.ledger.com/hc/en-us/articles/7326502672285-Aptos-APT?docs=true).
-### Unlock your Ledger device and open the Aptos app.
-
-
- Whenever you want to sign using your Ledger you will need to plug it in, unlock it, and open the Aptos app before running any CLI commands.
-
-
-### Create a new Ledger profile in the Aptos CLI
-
- ```bash filename="Terminal"
- aptos init --profile --ledger
- ```
-
- Then follow the terminal prompts like so:
-
- ```text filename="Terminal"
- Configuring for profile
- Choose network from [devnet, testnet, mainnet, local, custom | defaults to devnet]
-
- No network given, using devnet...
- Please choose an index from the following 5 ledger accounts, or choose an arbitrary index that you want to use:
- [0] Derivation path: m/44'/637'/0'/0'/0' (Address: 59836ba1dd0c845713bdab34346688d6f1dba290dbf677929f2fc20593ba0cfb)
- [1] Derivation path: m/44'/637'/1'/0'/0' (Address: 21563230cf6d69ee72a51d21920430d844ee48235e708edbafbc69708075a86e)
- [2] Derivation path: m/44'/637'/2'/0'/0' (Address: 667446181b3b980ef29f5145a7a2cc34d433fc3ee8c97fc044fd978435f2cb8d)
- [3] Derivation path: m/44'/637'/3'/0'/0' (Address: 2dcf037a9f31d93e202c074229a1b69ea8ee4d2f2d63323476001c65b0ec4f31)
- [4] Derivation path: m/44'/637'/4'/0'/0' (Address: 23c579a9bdde1a59f1c9d36d8d379aeefe7a5997b5b58bd5a5b0c12a4f170431)
-
- 0
- Account 59836ba1dd0c845713bdab34346688d6f1dba290dbf677929f2fc20593ba0cfb has been already found on-chain
-
- ---
- Aptos CLI is now set up for account 59836ba1dd0c845713bdab34346688d6f1dba290dbf677929f2fc20593ba0cfb as profile ! Run `aptos --help` for more information about commands
- {
- "Result": "Success"
- }
- ```
-
- In the example, they chose to use the first ledger account by entering `0` after the `aptos init` command. You may choose whichever account you want.
-
- **Common errors:**
-
- 1. If you see the error `Device Not Found`, make sure to unlock your Ledger then try this step again.
- 2. If you see the error `Aptos ledger app is not opened`, make sure to open the Aptos app on your Ledger, then try this step again.
-
-### Finally, you will need to enable blind signing on your Ledger device by following [these steps](https://medium.com/@DavidLehman24/how-to-enable-disable-blind-signing-on-ledger-wallet-99113a85cdad).
- 1. Blind signing allows you to confirm a smart contract interaction you cannot verify through a human-readable language.
- 2. This is needed to execute transactions without limitation as some payloads are too big to display.
-
-
-## Signing Using Ledger
-
-After doing the initial setup, you can sign transactions by following these steps:
-
-1. Plug in your ledger.
-2. Unlock it.
-3. Open the Aptos app.
-4. Run the Aptos CLI command which requires a signature.
-
-
-This process works for any command that requires a signature, whether that’s to transfer coins, publish a Move contract, interact with a contract, etc.
-
-
-For example, if you wanted to publish a Move package like the `[hello_blockchain](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/hello_blockchain)` demo contract you could follow the above steps then run:
-
-```bash filename="Terminal"
-aptos move publish --profile --named-addresses hello_blockchain=
-```
-
-You should see a response like:
-
-```bash filename="Terminal"
-Compiling, may take a little while to download git dependencies...
-INCLUDING DEPENDENCY AptosFramework
-INCLUDING DEPENDENCY AptosStdlib
-INCLUDING DEPENDENCY MoveStdlib
-BUILDING Examples
-package size 1755 bytes
-Do you want to submit a transaction for a range of [139600 - 209400] Octas at a gas unit price of 100 Octas? [yes/no] >
-
-yes
-
-{
- "Result": {
- "transaction_hash": "0xd5a12594f85284cfd5518d547d084030b178ee926fa3d8cbf699cc0596eff538",
- "gas_used": 1396,
- "gas_unit_price": 100,
- "sender": "59836ba1dd0c845713bdab34346688d6f1dba290dbf677929f2fc20593ba0cfb",
- "sequence_number": 0,
- "success": true,
- "timestamp_us": 1689887104333038,
- "version": 126445,
- "vm_status": "Executed successfully"
- }
-}
-
-```
-
-After you have approved publishing this package you will be prompted to sign the transaction on your Ledger device. Once signed, the package will be published to the network!
-
-One error you might run into is `Error: Wrong raw transaction length`. This means that the transaction or package size was too big for your device to sign. Currently the Aptos Ledger app can only support transactions that are smaller than 20kb. The `Ledger Nano S` device has less memory than that, which is why it is more likely to produce this error.
-
-## Authentication key rotation
-
-If you have an active account that is not secured using a hardware wallet, then
-you may wish to rotate the account's authentication key so that it corresponds
-to a [BIP44 account index] private key held on your Ledger.
-
-Alternatively, if you have an account linked with a Ledger hardware wallet that
-you wish to publish a large package from, you might want to temporarily rotate
-the account's authentication key to a hot key to avoid memory issues.
-
-This tutorial will walk you through both scenarios.
-
-
-Before you start this tutorial make sure you have completed the
-[key rotation guide](../../guides/key-rotation.mdx).
-
-
-
-
-### Complete the key rotation guide
-
-Confirm that you have completed the
-[key rotation guide](../../guides/key-rotation.mdx).
-
-### Verify your Ledger is ready
-
-1. Connect and unlock your Ledger.
-1. Check what version of the Aptos app you have: `Aptos > About > Version`.
-1. If you do not have version `0.6.9` or higher, update it using Ledger Live.
-1. Enable blind signing: `Aptos > Settings > Enable Blind Signing`.
-
-### Start a localnet
-
-Start a localnet:
-
-```sh filename="Terminal"
-aptos node run-localnet
-```
-
-The localnet is ready when it prints out:
-
-```sh filename="Terminal"
-Applying post startup steps...
-
-Setup is complete, you can now use the localnet!
-```
-
-
-If you are a power user on MacOS or Linux, the following command can be used
-to start a fresh localnet as a background process:
-
-```sh filename="Terminal"
-mkdir -p localnet-data
-aptos node run-localnet \
- --assume-yes \
- --test-dir localnet-data \
- --force-restart &
-export LOCALNET_PID=$!
-```
-
-You can then stop the localnet at any point with the following command:
-
-```sh filename="Terminal"
-kill $LOCALNET_PID
-```
-
-
-### Set up localnet hot wallet profile
-
-Create a private key corresponding to an authentication key, and thus initial
-account address, that starts with the vanity prefix `0xaaa`:
-
-```sh filename="Terminal"
-aptos key generate \
- --assume-yes \
- --output-file private-key-a \
- --vanity-prefix 0xaaa
-```
-
-
-Example output
-```sh filename="Terminal"
-{
- "Result": {
- "PublicKey Path": "private-key-a.pub",
- "PrivateKey Path": "private-key-a",
- "Account Address:": "0xaaac71af5f2a4af4ec2639a15799bf9b945afb061c8bee102b636531c1b00eb5"
- }
-}
-```
-
-
-Use the private key to initialize a `hot-wallet-1` profile on the localnet:
-
-```sh filename="Terminal"
-aptos init \
- --assume-yes \
- --network local \
- --private-key-file private-key-a \
- --profile hot-wallet-1
-```
-
-
-Example output
-```sh filename="Terminal"
-Configuring for profile hot-wallet-1
-Configuring for network Local
-Using command line argument for private key
-Account 0xaaac71af5f2a4af4ec2639a15799bf9b945afb061c8bee102b636531c1b00eb5 doesn\'t exist, creating it and funding it with 100000000 Octas
-Account 0xaaac71af5f2a4af4ec2639a15799bf9b945afb061c8bee102b636531c1b00eb5 funded successfully
-
----
-Aptos CLI is now set up for account 0xaaac71af5f2a4af4ec2639a15799bf9b945afb061c8bee102b636531c1b00eb5 as profile hot-wallet-1! Run `aptos --help` for more information about commands
-{
- "Result": "Success"
-}
-```
-
-
-### Rotate the hot wallet key
-
-Rotate the authentication key of the hot wallet to use [BIP44 account index]
-1000 on your Ledger:
-
-```sh filename="Terminal"
-aptos account rotate-key \
- --assume-yes \
- --new-derivation-index 1000 \
- --profile hot-wallet-1 \
- --save-to-profile ledger-wallet-1000
-```
-
-
-As a best practice, this command uses a [BIP44 account index] that starts at a
-large number (1000) to indicate that the account is secured by a rotated
-authentication key on a Ledger, to ensure it does not conflict with any other
-existing accounts.
-
-This practice aids in profile recovery, as shown below.
-
-
-Follow the instructions from the CLI prompt:
-
-```sh filename="Terminal"
-Approve rotation proof challenge signature on your Ledger device
-```
-
-
-Example output
-```sh filename="Terminal"
-{
- "Result": {
- "message": "Saved new profile ledger-wallet-1000",
- "transaction": {
- "transaction_hash": "0x1a6df99651ac170bda10cfb9898fa196321d80a928033791b9d2231f77738bb2",
- "gas_used": 448,
- "gas_unit_price": 100,
- "sender": "aaac71af5f2a4af4ec2639a15799bf9b945afb061c8bee102b636531c1b00eb5",
- "sequence_number": 0,
- "success": true,
- "timestamp_us": 1717986382369736,
- "version": 186,
- "vm_status": "Executed successfully"
- }
- }
-}
-```
-
-
-Compare the `hot-wallet-1` and `ledger-wallet-1000` profiles, noting that they
-have the same `account` address but different `public_key` values:
-
-```sh filename="Terminal"
-aptos config show-profiles --profile hot-wallet-1
-aptos config show-profiles --profile ledger-wallet-1000
-```
-
-
-Example output
-```sh filename="Terminal"
-{
- "Result": {
- "hot-wallet-1": {
- "has_private_key": true,
- "public_key": "0xffb1240fd1267207cc3ed2e1b5386e090a9ca2c844d7f9e0077b3d7dd5d5e430",
- "account": "aaa271bca468fb8518f73a732a484b29a1bc296ebcb23f15639d4865a5cebe87",
- "rest_url": "http://localhost:8080",
- "faucet_url": "http://localhost:8081"
- }
- }
-}
-{
- "Result": {
- "ledger-wallet-1000": {
- "has_private_key": false,
- "public_key": "0x20ba83f9b9fdab73b0ace8fda26ce24c98cf55060b72b69cfbd25add6a25d09b",
- "account": "aaa271bca468fb8518f73a732a484b29a1bc296ebcb23f15639d4865a5cebe87",
- "rest_url": "http://localhost:8080",
- "faucet_url": "http://localhost:8081"
- }
- }
-}
-```
-
-
-Since the account is no longer secured by the hot private key, delete the
-private and public key files.
-
-
-If you are using a UNIX-like machine:
-
-```shell filename="Terminal"
-rm private-key-a
-rm private-key-b
-rm private-key-a.pub
-rm private-key-b.pub
-```
-
-
-Now that you have successfully rotated the authentication key of the hot wallet,
-you can delete the profiles too:
-
-```sh filename="Terminal"
-aptos config delete-profile --profile hot-wallet-1
-aptos config delete-profile --profile ledger-wallet-1000
-```
-
-
-Example output
-```sh filename="Terminal"
-{
- "Result": "Deleted profile hot-wallet-1"
-}
-{
- "Result": "Deleted profile ledger-wallet-1000"
-}
-```
-
-
-### Recover profile
-
-Since you know that you rotated the authentication key of the hot wallet to the
-Ledger, and since you used the best practice of a [BIP44 account index] offset
-of 1000, you can easily recover the profile using the [BIP44 account index]
-alone:
-
-```sh filename="Terminal"
-aptos init \
- --assume-yes \
- --derivation-index 1000 \
- --network local \
- --profile ledger-wallet-1000-recovered
-```
-
-
-Example output
-```sh filename="Terminal"
-Configuring for profile ledger-wallet-1000-recovered
-Configuring for network Local
-Account 0xaaac71af5f2a4af4ec2639a15799bf9b945afb061c8bee102b636531c1b00eb5 has been already found onchain
-
----
-Aptos CLI is now set up for account 0xaaac71af5f2a4af4ec2639a15799bf9b945afb061c8bee102b636531c1b00eb5 as profile ledger-wallet-1000-recovered! Run `aptos --help` for more information about commands
-{
- "Result": "Success"
-}
-```
-
-
-Note that this profile corresponds to the specified `0xaaa...` vanity account
-address:
-
-```sh filename="Terminal"
-aptos config show-profiles --profile ledger-wallet-1000-recovered
-```
-
-
-Example output
-```sh filename="Terminal"
-{
- "Result": {
- "ledger-wallet-1000-recovered": {
- "has_private_key": false,
- "public_key": "0x20ba83f9b9fdab73b0ace8fda26ce24c98cf55060b72b69cfbd25add6a25d09b",
- "account": "aaac71af5f2a4af4ec2639a15799bf9b945afb061c8bee102b636531c1b00eb5",
- "rest_url": "http://localhost:8080",
- "faucet_url": "http://localhost:8081"
- }
- }
-}
-```
-
-
-
-The `aptos init` command first checks the [`account::OriginatingAddress`] table
-for determining the account address associated with a public key, so as long as
-you follow best practices from the
-[key rotation guide](../../guides/key-rotation.mdx) and only
-authenticate one account at a time with a private key, you'll easily be able to
-recover your profile based on the [BIP44 account index] alone.
-
-
-### Rotate to new hot private key
-
-If you have an account linked with a Ledger hardware wallet that you wish to use
-for publication of a large package, you'll be unable to sign the package
-publication transaction due to the Ledger's memory limitations. In this case,
-you'll want to temporarily rotate to a hot wallet.
-
-Start by generating a new private key:
-
-```sh filename="Terminal"
-aptos key generate \
- --assume-yes \
- --output-file private-key-b \
- --vanity-prefix 0xbbb
-```
-
-
-Example output
-```sh filename="Terminal"
-{
- "Result": {
- "PublicKey Path": "private-key-b.pub",
- "PrivateKey Path": "private-key-b",
- "Account Address:": "0xbbbede2b4f1d49eff0b156ab0756889a6f2bb68f215399d5015da9ac45921b47"
- }
-}
-```
-
-
-Rotate the authentication key of the account linked with the Ledger to the new
-private key:
-
-```sh filename="Terminal"
-aptos account rotate-key \
- --assume-yes \
- --new-private-key-file private-key-b \
- --profile ledger-wallet-1000-recovered \
- --save-to-profile temporary-hot-wallet
-```
-
-Follow the instructions from the CLI prompt:
-
-```sh filename="Terminal"
-Approve rotation proof challenge signature on your Ledger device
-```
-
-```sh filename="Terminal"
-Approve transaction on your Ledger device
-```
-
-
-Example output
-```sh filename="Terminal"
-{
- "Result": {
- "message": "Saved new profile temporary-hot-wallet",
- "transaction": {
- "transaction_hash": "0xe49782e92d8fd824fd6dce8f6ed42a11cf8ee84c201f3aa639c435e737c80eaa",
- "gas_used": 449,
- "gas_unit_price": 100,
- "sender": "aaac71af5f2a4af4ec2639a15799bf9b945afb061c8bee102b636531c1b00eb5",
- "sequence_number": 1,
- "success": true,
- "timestamp_us": 1717986617911082,
- "version": 631,
- "vm_status": "Executed successfully"
- }
- }
-```
-
-
-Since the CLI profile `ledger-wallet-1000-recovered` is now stale, rename it
-in case you get interrupted and forget that the private key has been rotated:
-
-```sh filename="Terminal"
-aptos config rename-profile \
- --profile ledger-wallet-1000-recovered \
- --new-profile-name ledger-wallet-1000-stale
-```
-
-
-Example output
-```sh filename="Terminal"
-{
- "Result": "Renamed profile ledger-wallet-1000-recovered to ledger-wallet-1000-stale"
-}
-```
-
-
-### Rotate back to Ledger
-
-Once you've signed the large package publication transaction with the hot key,
-you can then rotate the authentication key back to to the corresponding to the
-private key on the Ledger at index 1000:
-
-```sh filename="Terminal"
-aptos account rotate-key \
- --assume-yes \
- --new-derivation-index 1000 \
- --profile temporary-hot-wallet \
- --save-to-profile ledger-wallet-1000
-```
-
-Follow the instructions from the CLI prompt:
-
-```sh filename="Terminal"
-Approve rotation proof challenge signature on your Ledger device
-```
-
-
-Example output
-```sh filename="Terminal"
-{
- "Result": {
- "message": "Saved new profile ledger-wallet-1000",
- "transaction": {
- "transaction_hash": "0x9503819d4ea13bcd9eafed25984807d86d22e8a9837565a7495b54d13890d103",
- "gas_used": 449,
- "gas_unit_price": 100,
- "sender": "aaac71af5f2a4af4ec2639a15799bf9b945afb061c8bee102b636531c1b00eb5",
- "sequence_number": 2,
- "success": true,
- "timestamp_us": 1717986672963544,
- "version": 742,
- "vm_status": "Executed successfully"
- }
- }
-}
-```
-
-
-Verify that the `ledger-wallet-1000-stale` and `ledger-wallet-1000` profiles
-have the same `account` address and `public_key`:
-
-```sh filename="Terminal"
-aptos config show-profiles --profile ledger-wallet-1000-stale
-aptos config show-profiles --profile ledger-wallet-1000
-```
-
-Delete the `temporary-hot-wallet` and `ledger-wallet-1000-stale` profiles,
-which you no longer need.
-
-```sh filename="Terminal"
-aptos config delete-profile --profile temporary-hot-wallet
-aptos config delete-profile --profile ledger-wallet-1000-stale
-```
-
-
-Example output
-```sh filename="Terminal"
-{
- "Result": "Deleted profile temporary-hot-wallet"
-}
-{
- "Result": "Deleted profile ledger-wallet-1000-stale"
-}
-```
-
-
-Since you no longer need the temporary private key, delete it too.
-
-
-If you are using a UNIX-like machine:
-
-```shell filename="Terminal"
-rm private-key-*
-```
-
-
-### Clean up
-
-Delete the remaining test profile:
-
-```shell filename="Terminal"
-aptos config delete-profile --profile ledger-wallet-1000
-```
-
-Then stop the localnet.
-
-
-If you are using a UNIX-like machine:
-
-```shell filename="Terminal"
-aptos config delete-profile --profile ledger-wallet-1000
-kill $LOCALNET_PID
-rm -fr localnet-data
-```
-
-
-
-
-[`account::OriginatingAddress`]: https://github.com/aptos-labs/aptos-core/blob/acb6c891cd42a63b3af96561a1aca164b800c7ee/aptos-move/framework/aptos-framework/sources/account.move#L70
-[BIP44 account index]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
\ No newline at end of file
diff --git a/apps/nextra/pages/zh/build/cli/working-with-move-contracts/_meta.tsx b/apps/nextra/pages/zh/build/cli/working-with-move-contracts/_meta.tsx
index 6c8a8b27d..e3c6ab818 100644
--- a/apps/nextra/pages/zh/build/cli/working-with-move-contracts/_meta.tsx
+++ b/apps/nextra/pages/zh/build/cli/working-with-move-contracts/_meta.tsx
@@ -1,11 +1,13 @@
export default {
"local-simulation-benchmarking-and-gas-profiling": {
title: "本地模拟、基准测试和燃料分析",
+ href: "/en/build/cli/working-with-move-contracts/local-simulation-benchmarking-and-gas-profiling",
},
"arguments-in-json-tutorial": {
title: "JSON 参数教程",
},
"multi-signature-tutorial": {
title: "多重签名治理教程",
+ href: "/en/build/cli/working-with-move-contracts/multi-signature-tutorial",
},
};
diff --git a/apps/nextra/pages/zh/build/cli/working-with-move-contracts/local-simulation-benchmarking-and-gas-profiling.mdx b/apps/nextra/pages/zh/build/cli/working-with-move-contracts/local-simulation-benchmarking-and-gas-profiling.mdx
deleted file mode 100644
index e0591c6f2..000000000
--- a/apps/nextra/pages/zh/build/cli/working-with-move-contracts/local-simulation-benchmarking-and-gas-profiling.mdx
+++ /dev/null
@@ -1,255 +0,0 @@
-import { Callout, FileTree } from "nextra/components";
-
-# Local Simulation, Benchmarking & Gas Profiling
-
-## Overview
-The previous tutorial demonstrates how you can deploy and interact with Move contracts using various CLI commands.
-
-By default, those commands send a transaction to the remote fullnode for simulation and execution.
-You can override this behavior and simulate the transaction locally, by appending one of the following command line options of your preference:
-- `--local`: Simulate the transaction locally without conducting any further measurements or analysis.
-- `--benchmark`: Benchmark the transaction and report the running time(s).
-- `--profile-gas`: Profile the transaction for detailed gas usage.
-
-These additional options can be used in combination with the following CLI commands:
-- `aptos move run`
-- `aptos move run-script`
-- `aptos move publish`
-
-Alternatively, if you are interested in replaying a past transaction, check out [this tutorial](../replay-past-transactions.mdx).
-
-
-Local simulations do not result in any to the on-chain state.
-
-
-## Deploying the Example Contract
-For demonstration purposes, we will continue to use the [`hello_blockchain`](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/hello_blockchain) package as an example.
-
-First, publish the package to devnet or testnet (if you haven't done so already).
-
-Change into the package directory.
-```bash filename="Terminal"
-cd aptos-move/move-examples/hello_blockchain
-```
-
-Then publish the package using the following command.
-```bash filename="Terminal"
-aptos move publish --named-addresses hello_blockchain=default --assume-yes
-```
-
-
-Output
-```bash
-{
- "Result": {
- "transaction_hash": "0xe4ae0ec4ea3474b2123838885b04d7f4b046c174d14d7dc1c56916f2eb553bcf",
- "gas_used": 1118,
- "gas_unit_price": 100,
- "sender": "dbcbe741d003a7369d87ec8717afb5df425977106497052f96f4e236372f7dd5",
- "sequence_number": 5,
- "success": true,
- "timestamp_us": 1713914742422749,
- "version": 1033819503,
- "vm_status": "Executed successfully"
- }
-}
-```
-
-
-Notice that you do need to have your CLI profile set up properly and bind the named addresses correctly. Please refer to [CLI Configuration](../setup-cli.mdx) for more details.
-
-
-Note: publishing the package to devnet/testnet is just one way to set up the stage for local simulation and is not the only one possible.
-Alternatively you can use a local node, or simulate transactions that do not need to have code published first, such as scripts and even the package publishing transaction itself.
-
-
-## Local Simulation
-Next, execute the entry function message::set_message with local simulation enabled using the additional command line option `--local`. This will execute the transaction locally without conducting any further measurements or analysis.
-```bash filename="Terminal"
-aptos move run --function-id 'default::message::set_message' --args 'string:abc' --local
-```
-
-
-Output
-```bash
-Simulating transaction locally...
-{
- "Result": {
- "transaction_hash": "0x5aab20980688185eed2c9a27bab624c84b8b8117241cd4a367ba2a012069f57b",
- "gas_used": 441,
- "gas_unit_price": 100,
- "sender": "dbcbe741d003a7369d87ec8717afb5df425977106497052f96f4e236372f7dd5",
- "success": true,
- "version": 1033887414,
- "vm_status": "status EXECUTED of type Execution"
- }
-}
-```
-
-
-Local and remote simulation shall produce identical results.
-
-
-## Benchmarking
-To measure the running time(s) of your transaction, use the `--benchmark` option.
-
-```bash filename="Terminal"
-aptos move run --function-id 'default::message::set_message' --args 'string:abc' --benchmark
-```
-
-
-Output
-```bash
-Benchmarking transaction locally...
-Running time (cold code cache): 985.141µs
-Running time (warm code cache): 848.159µs
-{
- "Result": {
- "transaction_hash": "0xa2fe548d37f12ee79df13e70fdd8212e37074c1b080b89b7d92e82550684ecdb",
- "gas_used": 441,
- "gas_unit_price": 100,
- "sender": "dbcbe741d003a7369d87ec8717afb5df425977106497052f96f4e236372f7dd5",
- "success": true,
- "version": 1033936831,
- "vm_status": "status EXECUTED of type Execution"
- }
-}
-```
-
-
-It's worth noting that these running times serve only as informational references, as they are contingent upon the specifications of your local machine and may be influenced by noise or other random factors.
-
-**If you are aiming to optimize your contract, you should base your decisions on the gas profiling results.**
-
-
-To minimize measurement errors, the benchmark harness executes the same transaction multiple times. For this reason, it may take a while for the benchmark task to complete.
-
-
-## Gas Profiling
-The Aptos Gas Profiler is a powerful tool that can help you understand the gas usage of Aptos transactions. Once activated, it will simulate transactions using an instrumented VM, and generate a web-based report.
-
-The gas profiler can also double as a debugger since the report also includes a full execution trace.
-
-### Using the Gas Profiler
-The gas profiler can be invoked by appending the `--profile-gas` option.
-
-```bash filename="Terminal"
-aptos move run --function-id 'default::message::set_message' --args 'string:abc' --profile-gas
-```
-
-
-Output
-```bash
-Simulating transaction locally using the gas profiler...
-Gas report saved to gas-profiling/txn-d0bc3422-0xdbcb-message-set_message.
-{
- "Result": {
- "transaction_hash": "0xd0bc342232f14a6a7d2d45251719aee45373bdb53f68403cfc6dc6062c74fa9e",
- "gas_used": 441,
- "gas_unit_price": 100,
- "sender": "dbcbe741d003a7369d87ec8717afb5df425977106497052f96f4e236372f7dd5",
- "success": true,
- "version": 1034003962,
- "vm_status": "status EXECUTED of type Execution"
- }
-}
-```
-
-
-You can then find the generated gas report in the directory `gas-profiling`:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`index.html` is the main page of the report, which can view using your web browser.
-[Sample report](/gas-profiling/sample-report/index.html)
-
-
-### Understanding the Gas Report
-
-The gas report consists of three sections that help you to understand the gas usage through different lenses.
-
-#### Flamegraphs
-
-The first section consists of visualization of the gas usage in the form of two flamegraphs: one for execution & IO, the other for storage.
-The reason why we need two graphs is that these are measured in different units: one in gas units, and the other in APT.
-
-It is possible to interact with various elements in the graph. If you hover your cursor over an item, it will show you the precise cost and percentage.
-
-
-If you click on an item, you can zoom into it and see the child items more clearly.
-You can reset the view by clicking the "Reset Zoom" button in the top-left corner.
-
-
-There is also "Search" button in the top-right corner that allows to match certain items and highlight them.
-
-
-#### Cost Break-down
-
-The second section is a detailed break-down of all gas costs. Data presented in this section is categorized, aggregated and sorted.
-This can be especially helpful if you know what numbers to look at.
-
-For example, the following tables show the execution costs of all Move bytecode instructions/operations.
-The percentage here is relative to the total cost of the belonging category (Exec + IO in this case).
-
-
-
-#### Full Execution Trace
-
-The final section of the gas report is the full execution trace of the transaction that looks like this:
-
-```text filename="Example execution trace"
- intrinsic 2.76 85.12%
- dependencies 0.0607 1.87%
- 0xdbcb..::message 0.0607 1.87%
- 0xdbcb..::message::set_message 0.32416 10.00%
- create_ty 0.0004 0.01%
- create_ty 0.0004 0.01%
- create_ty 0.0004 0.01%
- create_ty 0.0004 0.01%
- create_ty 0.0008 0.02%
- imm_borrow_loc 0.00022 0.01%
- call 0.00441 0.14%
- 0x1::signer::address_of 0.007534 0.23%
- create_ty 0.0008 0.02%
- move_loc 0.000441 0.01%
- call 0.004043 0.12%
- 0x1::signer::borrow_address 0.000735 0.02%
- read_ref 0.001295 0.04%
- ret 0.00022 0.01%
- st_loc 0.000441 0.01%
- copy_loc 0.000854 0.03%
- load<0xdbcb..::0xdbcb..::message::MessageHolder> 0.302385 9.33%
- exists_generic 0.000919 0.03%
- not 0.000588 0.02%
- br_false 0.000441 0.01%
- imm_borrow_loc 0.00022 0.01%
- move_loc 0.000441 0.01%
- pack 0.000955 0.03%
- move_to_generic 0.001838 0.06%
- branch 0.000294 0.01%
- @28
- ret 0.00022 0.01%
- ledger writes 0.097756 3.01%
- transaction
- events
- state write ops 0.097756 3.01%
- create<0xdbcb..::0xdbcb..::message::MessageHolder> 0.097756 3.01%
-```
-
-The left column lists all Move instructions and operations being executed, with each level of indentation indicating a function call.
-
-The middle column represents the gas costs associated with the operations.
-
-There is also a special notation `@number` that represents a jump to a particular location in the byte code. (`@28` in the snippet above)
-This is purely informational and to help understand the control flow.
diff --git a/apps/nextra/pages/zh/build/cli/working-with-move-contracts/multi-signature-tutorial.mdx b/apps/nextra/pages/zh/build/cli/working-with-move-contracts/multi-signature-tutorial.mdx
deleted file mode 100644
index ff0517aac..000000000
--- a/apps/nextra/pages/zh/build/cli/working-with-move-contracts/multi-signature-tutorial.mdx
+++ /dev/null
@@ -1,767 +0,0 @@
-import { Callout } from 'nextra/components'
-
-# Multisig Governance Tutorial
-
-## Background
-
-This section builds upon the [Arguments in JSON tutorial](arguments-in-json-tutorial.mdx). If you have not done that, please complete that tutorial first.
-
-This tutorial likewise references the [`CliArgs` example package](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args).
-
-
-If you would like to follow along, start by completing the [Arguments in JSON](arguments-in-json-tutorial.mdx) tutorial steps!
-
-
-For this example, Ace and Bee will conduct governance operations from a 2-of-2 "multisig v2" account (an on-chain multisig account per [`multisig_account.move`](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/sources/multisig_account.move))
-
-## Account creation
-
-Since Ace's account was created during the [Arguments in JSON](arguments-in-json-tutorial.mdx) tutorial, start by mining a vanity address account for Bee too:
-
-```bash filename="Terminal"
-aptos key generate \
- --vanity-prefix 0xbee \
- --output-file bee.key
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": {
- "PublicKey Path": "bee.key.pub",
- "PrivateKey Path": "bee.key",
- "Account Address:": "0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc"
- }
-}
-```
-
-
-
-
-The exact account address should vary for each run, though the vanity prefix should not.
-
-
-Store Bee's address in a shell variable, so you can call it inline later on:
-
-```bash filename="Terminal"
-# Your exact address should vary
-bee_addr=0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc
-```
-
-Fund Bee's account using the faucet:
-
-```bash filename="Terminal"
-aptos account fund-with-faucet --account $bee_addr
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": "Added 100000000 Octas to account beec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc"
-}
-```
-
-
-
-Ace can now create a multisig account:
-
-```bash filename="Terminal"
-aptos multisig create \
- --additional-owners $bee_addr \
- --num-signatures-required 2 \
- --private-key-file ace.key \
- --assume-yes
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": {
- "multisig_address": "57478da34604655c68b1dcb89e4f4a9124b6c0ecc1c59a0931d58cc4e60ac5c5",
- "transaction_hash": "0x849cc756de2d3b57210f5d32ae4b5e7d1f80e5d376233885944b6f3cc2124a05",
- "gas_used": 1524,
- "gas_unit_price": 100,
- "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46",
- "sequence_number": 5,
- "success": true,
- "timestamp_us": 1685078644186194,
- "version": 528428043,
- "vm_status": "Executed successfully"
- }
-}
-```
-
-
-
-Store the multisig address in a shell variable:
-
-```bash filename="Terminal"
-# Your address should vary
-multisig_addr=0x57478da34604655c68b1dcb89e4f4a9124b6c0ecc1c59a0931d58cc4e60ac5c5
-```
-
-## Inspect the multisig
-
-Use the assorted [`multisig_account.move` view functions](https://github.com/aptos-labs/aptos-core/blob/9fa0102c3e474d99ea35a0a85c6893604be41611/aptos-move/framework/aptos-framework/sources/multisig_account.move#L237) to inspect the multisig:
-
-```bash filename="Number of signatures required"
-aptos move view \
- --function-id 0x1::multisig_account::num_signatures_required \
- --args \
- address:"$multisig_addr"
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": [
- "2"
- ]
-}
-```
-
-
-
-```bash filename="Owners"
-aptos move view \
- --function-id 0x1::multisig_account::owners \
- --args \
- address:"$multisig_addr"
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": [
- [
- "0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc",
- "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46"
- ]
- ]
-}
-```
-
-
-
-```bash filename="Last resolved sequence number"
-aptos move view \
- --function-id 0x1::multisig_account::last_resolved_sequence_number \
- --args \
- address:"$multisig_addr"
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": [
- "0"
- ]
-}
-```
-
-
-
-```bash filename="Next sequence number"
-aptos move view \
- --function-id 0x1::multisig_account::next_sequence_number \
- --args \
- address:"$multisig_addr"
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": [
- "1"
- ]
-}
-```
-
-
-
-## Enqueue a publication transaction
-
-The first multisig transaction enqueued will be a transaction for publication of the [`CliArgs` example package](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args).
-First, generate a publication payload entry function JSON file:
-
-```bash filename="Command"
-aptos move build-publish-payload \
- --named-addresses test_account=$multisig_addr \
- --json-output-file publication.json \
- --assume-yes
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": "Publication payload entry function JSON file saved to publication.json"
-}
-```
-
-
-
-Now have Ace propose publication of the package from the multisig account, storing only the payload hash on-chain:
-
-```bash filename="Command"
-aptos multisig create-transaction \
- --multisig-address $multisig_addr \
- --json-file publication.json \
- --store-hash-only \
- --private-key-file ace.key \
- --assume-yes
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": {
- "transaction_hash": "0x70c75903f8e1b1c0069f1e84ef9583ad8000f24124b33a746c88d2b031f7fe2c",
- "gas_used": 510,
- "gas_unit_price": 100,
- "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46",
- "sequence_number": 6,
- "success": true,
- "timestamp_us": 1685078836492390,
- "version": 528429447,
- "vm_status": "Executed successfully"
- }
-}
-```
-
-
-
-Note that the last resolved sequence number is still 0 because no transactions have been resolved:
-
-```bash filename="Last resolved sequence number"
-aptos move view \
- --function-id 0x1::multisig_account::last_resolved_sequence_number \
- --args \
- address:"$multisig_addr"
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": [
- "0"
- ]
-}
-```
-
-
-
-However, the next sequence number has been incremented because a transaction has been enqueued:
-
-```bash filename="Next sequence number"
-aptos move view \
- --function-id 0x1::multisig_account::next_sequence_number \
- --args \
- address:"$multisig_addr"
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": [
- "2"
- ]
-}
-```
-
-
-
-The multisig transaction enqueued on-chain can now be inspected:
-
-```bash filename="Get transaction"
-aptos move view \
- --function-id 0x1::multisig_account::get_transaction \
- --args \
- address:"$multisig_addr" \
- String:1
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": [
- {
- "creation_time_secs": "1685078836",
- "creator": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46",
- "payload": {
- "vec": []
- },
- "payload_hash": {
- "vec": [
- "0x62b91159c1428c1ef488c7290771de458464bd665691d9653d195bc28e0d2080"
- ]
- },
- "votes": {
- "data": [
- {
- "key": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46",
- "value": true
- }
- ]
- }
- }
- ]
-}
-```
-
-
-
-Note from the above result that no payload is stored on-chain, and that Ace implicitly approved the transaction (voted `true`) upon the submission of the proposal.
-
-## Enqueue a governance parameter transaction
-
-Now have Bee enqueue a governance parameter setter transaction, storing the entire transaction payload on-chain:
-
-```bash filename="Command"
-aptos multisig create-transaction \
- --multisig-address $multisig_addr \
- --function-id $multisig_addr::cli_args::set_vals \
- --type-args \
- 0x1::account::Account \
- 0x1::chain_id::ChainId \
- --args \
- u8:123 \
- "bool:[false, true, false, false]" \
- 'address:[["0xace", "0xbee"], ["0xcad"], []]' \
- --private-key-file bee.key \
- --assume-yes
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": {
- "transaction_hash": "0xd0a348072d5bfc5a2e5d444f92f0ecc10b978dad720b174303bc6d91342f27ec",
- "gas_used": 511,
- "gas_unit_price": 100,
- "sender": "beec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc",
- "sequence_number": 0,
- "success": true,
- "timestamp_us": 1685078954841650,
- "version": 528430315,
- "vm_status": "Executed successfully"
- }
-}
-```
-
-
-
-Note the next sequence number has been incremented again:
-
-```bash filename="Next sequence number"
-aptos move view \
- --function-id 0x1::multisig_account::next_sequence_number \
- --args \
- address:"$multisig_addr"
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": [
- "3"
- ]
-}
-```
-
-
-
-Now both the publication and parameter transactions are pending:
-
-```bash filename="Get pending transactions"
-aptos move view \
- --function-id 0x1::multisig_account::get_pending_transactions \
- --args \
- address:"$multisig_addr"
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": [
- [
- {
- "creation_time_secs": "1685078836",
- "creator": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46",
- "payload": {
- "vec": []
- },
- "payload_hash": {
- "vec": [
- "0x62b91159c1428c1ef488c7290771de458464bd665691d9653d195bc28e0d2080"
- ]
- },
- "votes": {
- "data": [
- {
- "key": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46",
- "value": true
- }
- ]
- }
- },
- {
- "creation_time_secs": "1685078954",
- "creator": "0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc",
- "payload": {
- "vec": [
- "0x0057478da34604655c68b1dcb89e4f4a9124b6c0ecc1c59a0931d58cc4e60ac5c508636c695f61726773087365745f76616c7302070000000000000000000000000000000000000000000000000000000000000001076163636f756e74074163636f756e740007000000000000000000000000000000000000000000000000000000000000000108636861696e5f696407436861696e49640003017b0504000100006403020000000000000000000000000000000000000000000000000000000000000ace0000000000000000000000000000000000000000000000000000000000000bee010000000000000000000000000000000000000000000000000000000000000cad00"
- ]
- },
- "payload_hash": {
- "vec": []
- },
- "votes": {
- "data": [
- {
- "key": "0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc",
- "value": true
- }
- ]
- }
- }
- ]
- ]
-}
-```
-
-
-
-## Execute the publication transaction
-
-Since only Ace has voted on the publication transaction (which he implicitly approved upon proposing) the transaction can't be executed yet:
-
-```bash filename="Can be executed"
-aptos move view \
- --function-id 0x1::multisig_account::can_be_executed \
- --args \
- address:"$multisig_addr" \
- String:1
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": [
- false
- ]
-}
-```
-
-
-
-Before Bee votes, however, she verifies that the payload hash stored on-chain matches the publication entry function JSON file:
-
-```bash filename="Verifying transaction proposal"
-aptos multisig verify-proposal \
- --multisig-address $multisig_addr \
- --json-file publication.json \
- --sequence-number 1
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": {
- "Status": "Transaction match",
- "Multisig transaction": {
- "creation_time_secs": "1685078836",
- "creator": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46",
- "payload": {
- "vec": []
- },
- "payload_hash": {
- "vec": [
- "0x62b91159c1428c1ef488c7290771de458464bd665691d9653d195bc28e0d2080"
- ]
- },
- "votes": {
- "data": [
- {
- "key": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46",
- "value": true
- }
- ]
- }
- }
- }
-}
-```
-
-
-
-Since Bee has verified that the on-chain payload hash checks out against her locally-compiled package publication JSON file, she votes yes:
-
-```bash filename="Approving transaction"
-aptos multisig approve \
- --multisig-address $multisig_addr \
- --sequence-number 1 \
- --private-key-file bee.key \
- --assume-yes
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": {
- "transaction_hash": "0xa5fb49f1077de6aa6d976e6bcc05e4c50c6cd061f1c87e8f1ea74e7a04a06bd1",
- "gas_used": 6,
- "gas_unit_price": 100,
- "sender": "beec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc",
- "sequence_number": 1,
- "success": true,
- "timestamp_us": 1685079892130861,
- "version": 528437204,
- "vm_status": "Executed successfully"
- }
-}
-```
-
-
-
-Now the transaction can be executed:
-
-```bash filename="Can be executed"
-aptos move view \
- --function-id 0x1::multisig_account::can_be_executed \
- --args \
- address:"$multisig_addr" \
- String:1
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": [
- true
- ]
-}
-```
-
-
-
-Now either Ace or Bee can invoke the publication transaction from the multisig account, passing the full transaction payload since only the hash was stored on-chain:
-
-```bash filename="Publication"
-aptos multisig execute-with-payload \
- --multisig-address $multisig_addr \
- --json-file publication.json \
- --private-key-file bee.key \
- --max-gas 10000 \
- --assume-yes
-```
-
-
-Pending the resolution of [#8304](https://github.com/aptos-labs/aptos-core/issues/8304), the transaction simulator (which is used to estimate gas costs) is broken for multisig transactions, so you will have to manually specify a max gas amount.
-
-
-
-Output
-
-Also pending the resolution of [#8304](https://github.com/aptos-labs/aptos-core/issues/8304), the CLI output for a successful multisig publication transaction execution results in an API error if only the payload hash has been stored on-chain, but the transaction can be manually verified using an explorer.
-
-
-
-## Execute the governance parameter transaction
-
-Since only Bee has voted on the governance parameter transaction (which she implicitly approved upon proposing), the transaction can't be executed yet:
-
-```bash filename="Can be executed"
-aptos move view \
- --function-id 0x1::multisig_account::can_be_executed \
- --args \
- address:"$multisig_addr" \
- String:2
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": [
- false
- ]
-}
-```
-
-
-
-Before Ace votes, however, he verifies that the payload stored on-chain matches the function arguments he expects:
-
-```bash filename="Verifying transaction proposal"
-aptos multisig verify-proposal \
- --multisig-address $multisig_addr \
- --function-id $multisig_addr::cli_args::set_vals \
- --type-args \
- 0x1::account::Account \
- 0x1::chain_id::ChainId \
- --args \
- u8:123 \
- "bool:[false, true, false, false]" \
- 'address:[["0xace", "0xbee"], ["0xcad"], []]' \
- --sequence-number 2
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": {
- "Status": "Transaction match",
- "Multisig transaction": {
- "creation_time_secs": "1685078954",
- "creator": "0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc",
- "payload": {
- "vec": [
- "0x0057478da34604655c68b1dcb89e4f4a9124b6c0ecc1c59a0931d58cc4e60ac5c508636c695f61726773087365745f76616c7302070000000000000000000000000000000000000000000000000000000000000001076163636f756e74074163636f756e740007000000000000000000000000000000000000000000000000000000000000000108636861696e5f696407436861696e49640003017b0504000100006403020000000000000000000000000000000000000000000000000000000000000ace0000000000000000000000000000000000000000000000000000000000000bee010000000000000000000000000000000000000000000000000000000000000cad00"
- ]
- },
- "payload_hash": {
- "vec": []
- },
- "votes": {
- "data": [
- {
- "key": "0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc",
- "value": true
- }
- ]
- }
- }
- }
-}
-```
-
-
-
-Note that the verification fails if he modifies even a single argument:
-
-```bash filename="Failed transaction verification with modified u8"
-aptos multisig verify-proposal \
- --multisig-address $multisig_addr \
- --function-id $multisig_addr::cli_args::set_vals \
- --type-args \
- 0x1::account::Account \
- 0x1::chain_id::ChainId \
- --args \
- u8:200 \
- "bool:[false, true, false, false]" \
- 'address:[["0xace", "0xbee"], ["0xcad"], []]' \
- --sequence-number 2
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Error": "Unexpected error: Transaction mismatch: The transaction you provided has a payload hash of 0xe494b0072d6f940317344967cf0e818c80082375833708c773b0275f3ad07e51, but the on-chain transaction proposal you specified has a payload hash of 0x070ed7c3f812f25f585461305d507b96a4e756f784e01c8c59901871267a1580. For more info, see https://aptos.dev/move/move-on-aptos/cli#multisig-governance"
-}
-```
-
-
-
-Ace approves the transaction:
-
-```bash filename="Approving transaction"
-aptos multisig approve \
- --multisig-address $multisig_addr \
- --sequence-number 2 \
- --private-key-file ace.key \
- --assume-yes
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": {
- "transaction_hash": "0x233427d95832234fa13dddad5e0b225d40168b4c2c6b84f5255eecc3e68401bf",
- "gas_used": 6,
- "gas_unit_price": 100,
- "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46",
- "sequence_number": 7,
- "success": true,
- "timestamp_us": 1685080266378400,
- "version": 528439883,
- "vm_status": "Executed successfully"
- }
-}
-```
-
-
-
-Since the payload was stored on-chain, it is not required to execute the pending transaction:
-
-```bash filename="Execution"
-aptos multisig execute \
- --multisig-address $multisig_addr \
- --private-key-file ace.key \
- --max-gas 10000 \
- --assume-yes
-```
-
-
-Output
-
-```bash filename="Terminal"
-{
- "Result": {
- "transaction_hash": "0xbc99f929708a1058b223aa880d04607a78ebe503367ec4dab23af4a3bdb541b2",
- "gas_used": 505,
- "gas_unit_price": 100,
- "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46",
- "sequence_number": 8,
- "success": true,
- "timestamp_us": 1685080344045461,
- "version": 528440423,
- "vm_status": "Executed successfully"
-
-```
-
-
\ No newline at end of file
diff --git a/apps/nextra/pages/zh/build/guides/_meta.tsx b/apps/nextra/pages/zh/build/guides/_meta.tsx
index 4638f8a19..3433630f4 100644
--- a/apps/nextra/pages/zh/build/guides/_meta.tsx
+++ b/apps/nextra/pages/zh/build/guides/_meta.tsx
@@ -5,24 +5,31 @@ export default {
},
"first-transaction": {
title: "您的第一笔交易",
+ href: "/en/build/guides/first-transaction",
},
"build-e2e-dapp": {
title: "使用 Aptos 构建端到端 Dapp",
+ href: "/en/build/guides/build-e2e-dapp",
},
"your-first-nft": {
title: "您的第一个 NFT",
+ href: "/en/build/guides/your-first-nft",
},
"first-coin": {
title: "您的第一个代币",
+ href: "/en/build/guides/first-coin",
},
"first-fungible-asset": {
title: "您的第一个同质化资产",
+ href: "/en/build/guides/first-fungible-asset",
},
"first-move-module": {
title: "您的第一个 Move 模块",
+ href: "/en/build/guides/first-move-module",
},
"first-multisig": {
title: "您的第一个多重签名",
+ href: "/en/build/guides/first-multisig",
},
"---advanced---": {
type: "separator",
@@ -30,18 +37,23 @@ export default {
},
"multisig-managed-fungible-asset": {
title: "使用多重签名管理同质化资产",
+ href: "/en/build/guides/multisig-managed-fungible-asset",
},
"aptos-keyless": {
title: "Aptos 无密钥账户",
+ href: "/en/build/guides/aptos-keyless",
},
"sponsored-transactions": {
title: "代付交易",
+ href: "/en/build/guides/sponsored-transactions",
},
"transaction-management": {
title: "交易管理",
+ href: "/en/build/guides/transaction-management",
},
"key-rotation": {
title: "账户密钥轮换",
+ href: "/en/build/guides/key-rotation",
},
"---integration---": {
type: "separator",
@@ -49,8 +61,10 @@ export default {
},
exchanges: {
title: "交易所",
+ href: "/en/build/guides/integration/exchanges",
},
"system-integrators-guide": {
title: "应用程序",
+ href: "/en/build/guides/integration/system-integrators-guide",
},
};
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless.mdx b/apps/nextra/pages/zh/build/guides/aptos-keyless.mdx
deleted file mode 100644
index ed6b82f31..000000000
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless.mdx
+++ /dev/null
@@ -1,33 +0,0 @@
-# Aptos Keyless
-
-## Integrate with Aptos Keyless accounts
-
-- [Introduction](aptos-keyless/introduction.mdx)
-- [OIDC Support and Configuration](aptos-keyless/oidc-support.mdx)
-- [Integration Guide](aptos-keyless/integration-guide.mdx)
-- [Simple Example](aptos-keyless/simple-example.mdx)
-- [How Aptos Keyless works](aptos-keyless/how-keyless-works.mdx)
-- [Terminology and FAQ](aptos-keyless/other.mdx)
-
-## Using an IAM Provider? Integrate with Aptos Federated Keyless
-- [Federated Keyless](aptos-keyless/federated-keyless.mdx)
-
-## Example
-
-Visit this page to learn more [Simple Example](aptos-keyless/simple-example.mdx)
-
-
-
-
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless/_meta.tsx b/apps/nextra/pages/zh/build/guides/aptos-keyless/_meta.tsx
index 0ffcfe2a6..32c009ec0 100644
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless/_meta.tsx
+++ b/apps/nextra/pages/zh/build/guides/aptos-keyless/_meta.tsx
@@ -1,23 +1,30 @@
export default {
introduction: {
title: "简介",
+ href: "/en/build/guides/aptos-keyless/introduction",
},
"oidc-support": {
title: "OIDC 支持",
+ href: "/en/build/guides/aptos-keyless/oidc-support",
},
"integration-guide": {
title: "集成指南",
+ href: "/en/build/guides/aptos-keyless/integration-guide",
},
"simple-example": {
title: "简单示例",
+ href: "/en/build/guides/aptos-keyless/simple-example",
},
"how-keyless-works": {
title: "Keyless 工作原理",
+ href: "/en/build/guides/aptos-keyless/how-keyless-works",
},
other: {
title: "术语和常见问题",
+ href: "/en/build/guides/aptos-keyless/other",
},
"federated-keyless": {
title: "联合 Keyless",
+ href: "/en/build/guides/aptos-keyless/federated-keyless",
},
};
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless.mdx b/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless.mdx
deleted file mode 100644
index 81b50bd64..000000000
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless.mdx
+++ /dev/null
@@ -1,16 +0,0 @@
-# Federated Keyless
-
-## Federated Keyless
-
-[AIP-96](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-96.md): Federated Keyless is an extension of Aptos Keyless to support more OpenID Connect (OIDC) providers, beyond the ones that are allow-listed in `0x1::jwks` via JWK consensus, while maintaining its decentralization. Federated keyless adds support for authenticating users via identity & access management (IAM) providers (e.g. Auth0, AWS Cognito) as long as your project uses a supported IAM provider for user authentication.
-
-To elaborate further, Federated Keyless enables:
- 1. Extension of authentication methods
- a. All authentication methods supported by the IAM are available to the dApp including email/SMS OTP and their marketplace of social login integrations like Discord, Naver, X and more. Auth0 marketplace linked [here](https://marketplace.auth0.com/) as an example.
-
- 2. Compatibility with existing account systems
- a. Since IAMs also support custom authentication, it allows an application to bring its own username/password (Cognito [docs](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html), Auth0 [docs](https://auth0.com/blog/Custom-Authentication-With-Auth0/)). An application can start using an existing account system already set up with an IAM or they can migrate their existing account system to an IAM to generate Keyless-compatible JWTs.
-
-- [Federated Keyless Key Considerations](federated-keyless/key-considerations.mdx)
-- [Federated Keyless Integration Guide](federated-keyless/integration-guide.mdx)
-- [Federated Keyless FAQs](federated-keyless/other.mdx)
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/_meta.tsx b/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/_meta.tsx
index 8b90f0281..29bd0b642 100644
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/_meta.tsx
+++ b/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/_meta.tsx
@@ -1,11 +1,14 @@
export default {
"key-considerations": {
title: "关键考虑因素",
+ href: "/en/build/guides/aptos-keyless/key-considerations",
},
"integration-guide": {
title: "集成指南",
+ href: "/en/build/guides/aptos-keyless/integration-guide",
},
other: {
title: "常见问题",
+ href: "/en/build/guides/aptos-keyless/other",
},
};
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/integration-guide.mdx b/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/integration-guide.mdx
deleted file mode 100644
index 6a897d6c6..000000000
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/integration-guide.mdx
+++ /dev/null
@@ -1,73 +0,0 @@
----
-title: "Federated Keyless Integration Guide"
----
-import { Callout, Steps } from "nextra/components";
-
-# Federated Keyless Integration Guide
-
-
-### Step 1. Setup your IAM provider
-
-Set up your project with your IAM to match the account structure you are looking for.
-
-- [Getting Started with AWS Cognito](https://aws.amazon.com/cognito/getting-started/)
-- [Getting Started with Auth0](https://auth0.com/docs/get-started)
-
-### Step 2. Register the JSON Web Key Set (JWKS) on-chain
-
-Federated Keyless accounts require the JWKS to be registered on-chain.
-
-To register the JWKS - call the `0x1::jwks::update_federated_jwk_set` entry function with an Aptos account that will store the JWKs that will be used to validate transactions signed by federated keyless accounts.
-
-
-**Losing access to the JWK owner account compromises the Federated Keyless accounts created with it**
-
-The JWK owner account is the only account that can update the JWKS. If you lose access to the JWK owner account, you will not be able to update the JWKS and the Federated Keyless accounts created with it will stop working in the case of a key rotation. Users will be unable to validate their JWT tokens as they will be signed with the new key whos public key is not registered on the Aptos blockchain.
-
-
-The JWK set can be found as follows -
-
-AWS Cognito - `https://cognito-idp..amazonaws.com//.well-known/jwks.json`
-Auth0 - `https:///.well-known/jwks.json`
-
-The typescript SDK contains functionality to simplify the process given the issuer for your IAM provider setup (the `iss` claim value on your user’s JWT tokens) and an account to use to make the update.
-
-```tsx
-import {Aptos} from '@aptos-labs/ts-sdk'; // Requires version v1.29.1 or later
-
-const aptos = new Aptos(new AptosConfig({ network: Network.DEVNET })); // Configure your network here
-const alice = // Derive your Aptos account here
-const jwkTxn = await aptos.updateFederatedKeylessJwkSetTransaction({ sender: alice, iss });
-await aptos.signAndSubmitTransaction({ signer: alice, transaction: jwkTxn });
-```
-
-You can use the interactive example provided by the SDK to easily register the JWKS for your IAM provider in devnet or testnet. This will setup the JWK owner account with a Google Keyless account.
-
-```bash
-git clone https://github.com/aptos-labs/aptos-ts-sdk
-cd aptos-ts-sdk
-pnpm install && pnpm build
-cd examples/typescript
-pnpm install
-pnpm jwk_update
-```
-
-To setup the JWK owner account in mainnet, you will need create an account and use it to register the JWKS.
-
-Save the address of the account you used to register the JWKS as you will need it for the next step.
-
-To learn more about the `0x1::jwks::update_federated_jwk_set` entry function, see the [reference documentation](https://aptos.dev/en/build/smart-contracts/move-reference?page=aptos-framework%2Fdoc%2Fjwks.md#0x1_jwks_update_federated_jwk_set).
-
-
-**Handling key rotations**
-
-Whenever there is a key rotation of the JWKS, it is important to update the JWKS registered on chain promptly to avoid any loss of access to Federated Keyless accounts. See [here](key-considerations.mdx) for more info.
-
-
-### Step 3. Follow the Aptos Keyless integration guide
-
-Now that you have registered the JWKS, you can follow the Aptos Keyless integration guide starting from step 2. Be sure to set the `jwkAddress` to the address of the account you used to register the JWKS when deriving the `KeylessAccount`.
-
-[Aptos Keyless Integration Guide - Step 2](../integration-guide.mdx#step-2-install-the-aptos-typescript-sdk)
-
-
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/key-considerations.mdx b/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/key-considerations.mdx
deleted file mode 100644
index 5cf5eaf56..000000000
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/key-considerations.mdx
+++ /dev/null
@@ -1,36 +0,0 @@
----
-title: "Federated Keyless Key Considerations"
----
-
-## Federated Keyless Key Considerations
-
-**Supported IAMs**
-
-Currently, the supported IAMs are Amazon Cognito and Auth0 across devnet, testnet, and mainnet. See a table of the full set of supported IAM providers [here](../oidc-support.mdx).
-
-**Federated Keyless flow**
-
-The flow for Federated Keyless transactions is the same as described [here](../how-keyless-works.mdx). However, the difference is that in Federated Keyless, instead of the OIDC provider (e.g. Google, Apple) acting as the issuer of the JWT, the IAM provider (e.g. Auth0, Cognito) acts as the issuer. The user authenticates with the application, the IAM receives the user’s credentials, and then the IAM issues the Keyless-compatible JWT.
-
-**Available authentication methods**
-
-All authentication methods that are supported by the IAM providers are available for use - this includes SMS OTP, email link, and the traditional username + password.
-
-**Configuration limitations**
-
-A Keyless account address varies according to the `aud` (AKA application ID or client ID), and `iss` (AKA issuer). The setup of your user data within the IAM must reflect the interoperability you seek to provide to your users. JWT tokens issued for a user in the same user pool but for different applications will result in a different address derivation if the `aud` value is different.
-
-**JSON Web Key Set management**
-
-If you or the IAM platform rotates the key pairs used to signed the JWT tokens, the JWK set must be updated on chain using the same account used to instantiate your app's Federated Keyless accounts. As such it is vital to -
-
-1. Maintain access to your JWKS owner account
-2. Update the JWK set on chain whenever a key rotation occurs
-
-When a keypair is rotated existing keyless account instantiations will continue to work so long as the old JWK has not been removed. Any new JWTs issued by the new keypair will not be accepted until the JWK set on chain is updated to contain its public key.
-
-**The trust and security model for Federated Keyless**
-
-Compared to the existing Keyless implementation, dApp developers utilizing Federated Keyless alongside certain authentication methods like email/SMS, OTP and email/password may have more access to user credentials when leveraging IAM providers than with the existing direct OIDC provider integrations.
-
-We recommend each dApp developer perform their own research and consult with their legal counsel before integrating an authentication method. Developers should also understand to what extent they may have access to user credentials and what controls they have in place.
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/other.mdx b/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/other.mdx
deleted file mode 100644
index f3c23c66b..000000000
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless/federated-keyless/other.mdx
+++ /dev/null
@@ -1,23 +0,0 @@
----
-title: "FAQ"
----
-
-## Federated Keyless FAQs
-
-**What if I stop using my IAM for my application? What if I switch IAM providers?**
-
-- An account address depends on values of several variables that are specific to an IAM service, including `aud` (client ID) and `iss` (issuer). If these values are changed, then a different address will be derived.
-- If you want to switch IAM providers, you will need to develop an account migration flow, resulting in a key rotation from the account derived from the prior IAM provider to the account derived from the new IAM provider.
-- We recommend allowing your users to add a secondary authentication method to their accounts (e.g., back-up private key) so that they can maintain access should the authentication path into their account via Federated Keyless be disrupted via a service provider change. In order to implement this, you need to do a key rotation to a multikey account. For relevant documentation see [key rotation](https://aptos.dev/en/build/guides/key-rotation) and [multikey SDK](https://aptos-labs.github.io/aptos-ts-sdk/@aptos-labs/ts-sdk-1.35.0/classes/MultiKeyAccount.html).
-
-**Does using an IAM cost money?**
-
-- Yes, IAMs usually cost money, but they can help provide useful functionality within your application such as role-based access control (authorization), user management, user authentication, security + compliance, and analytics + monitoring.
-
-**In the case the dApp or IAM provider goes offline, how do I make sure my users can continue accessing their accounts?**
-
-- We recommend allowing your users to add a secondary authentication method to their accounts (e.g., back-up private key) so that they can maintain access should the authentication path into their account via Federated Keyless is disrupted via service provider change or other outage.
-
-**I use an open source IAM like Keycloak. Can I use Federated Keyless?**
-
-- Not today. Due to the trust placed in the IAM to have sufficient uptime and security standards, we have limited the accepted IAM set to the currently supported issuers. If you believe your provider should be included for consideration, please consider raising an AIP or contact us in the Keyless developers [telegram](https://t.me/+h5CN-W35yUFiYzkx).
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless/how-keyless-works.mdx b/apps/nextra/pages/zh/build/guides/aptos-keyless/how-keyless-works.mdx
deleted file mode 100644
index 48b119582..000000000
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless/how-keyless-works.mdx
+++ /dev/null
@@ -1,90 +0,0 @@
----
-title: "How Keyless Works"
----
-import { YouTube } from "@components/index";
-
-# How Keyless Works
-
-Aptos Keyless enables a dApp to **derive** and **access** a blockchain account for a user who successfully signed in to the dApp via an OIDC provider (e.g., Google). Importantly, this blockchain account is **scoped to the dApp**. This means other dApps, who can similarly sign-in the same user, via the same OIDC provider, are not able to access this account and instead get their own account.
-
-_But how does this work?_
-
-
-
-## Overview
-
-At a very high level, a successful sign-in into the dApp via the OIDC provider will result in the dApp receiving a **JSON Web Token (JWT)** signed by the OIDC provider. The JWT will contain, among other things, three important pieces of information:
-
-1. The user’s identity (contained in the JWT’s `sub` field)
-2. The dApp’s identity (contained in the JWT’s `aud` field)
-3. Application-specific data; specifically, an **ephemeral public key (EPK)** (contained in the JWT’s `nonce` field), whose associated **ephemeral secret key (ESK)** only the user knows.
-
-Now, assume that the user’s blockchain account address is (more or less) a hash of the user’s identity in `sub` and the dApp’s identity in `aud` from above.
-
-Then, the **key observation** is that the signed JWT effectively acts as a **digital certificate**, **temporarily** binding this blockchain address to the EPK, and allowing the EPK to sign TXNs for it. In other words, it securely delegates TXN signing rights for this blockchain account to the EPK. (Note: The EPK contains an expiration date and is thus short-lived.)
-
-Importantly, if the user loses their ESK, the user can obtain a new signed JWT over a new EPK via the application by simply signing in again via the OIDC provider. (Or, in some cases, by requesting a new signed JWT using an OAuth refresh token.)
-
-With this system, the **challenge** is maintaining privacy, since revealing the JWT on-chain would leak the user’s identity. Furthermore, revealing the EPK to the OIDC provider would allow it to track the user’s TXNs on-chain.
-
-We explain below how Keyless accounts work and how they address these challenges.
-
-## Flow: Deriving a keyless account for a user in a dApp
-
-First, let us look at how a dApp can sign-in a user via (say) Google, derive that user’s keyless blockchain address and, for example, send that user an asset.
-
-
-
-**Step 1**: The user generates an ephemeral key pair: an EPK with an expiration date, and its associated ESK. The dApp keeps the EPK and safely stores the ESK on the user-side (e.g., in the browser’s local storage, or in a trusted enclave if the ESK is a WebAuthn passkey).
-
-**Step 2**: The dApp commits to the EPK as $H(\mathsf{epk}, \rho)$, where $\rho$ is a blinding factor. When the user clicks on the “Sign in with Google” button, the dApp redirects the user to Google’s sign in page and, importantly, sets the `nonce` parameter in the URL to this EPK commitment. This hides the EPK from Google, maintaining privacy of the user’s TXN activity.
-
-**Step 3**: Typically, the user has an HTTP cookie from having previously-signed-in to their Google account, so Google merely checks this cookie. If the user has multiple Google accounts, Google asks the user to select which one they want to sign-in into the dApp. (The less common path is for the user to have to type in their Google username and password.)
-
-**Step 4**: Once the user has signed in, Google sends the dApp a signed JWT, which includes the user's `sub` identifier (e.g., `uid-123`), the application’s `aud` identifier (e.g., `"dapp-xyz"`) and the `nonce` with the EPK commitment. (This assumes that the dApp has previously registered with Google and received this `"dapp-xyz"` identifier.)
-
-**Step 5**: The dApp now has almost everything it needs to derive a keyless account for the user: the user’s identifier (`sub`) and the dApp’s identifier (`aud`). But, to preserve the privacy of the user, the dApp will use a third piece of information: a blinding factor $r$ called a **pepper**. The dApp will contact a so-called **guardian** who will deterministically derive a random $r$ from the given (`sub`, `aud`). Importantly, the guardian will only reveal $r$ to the dApp upon seeing a validly-signed JWT for the queried (`sub`, `aud`).
-
-**Step 6**: The dApp derives the address of the account as $\mathsf{addr} = H(\texttt{"uid-123"}, \texttt{"dapp-xyz"}, r)$, where $H$ is a cryptographic hash function.
-
-Note that the pepper $r$ is used to hide the user and app identity inside the address since, as we described above, only an authorized user with a valid JWT will be able to obtain this pepper.
-
-Also, note that the address is independent of the EPK. This is why the ESK need not be long-lived and can be lost.
-
-Finally, the dApp can, for example, send an NFT to the user at their address $\mathsf{addr}$.
-
-But how can the dApp authorize TXN from this account at $\mathsf{addr}$? We discuss that next.
-
-## Flow: Obtaining a zero-knowledge proof before transacting
-
-In the previous flow, we showed how a dApp can sign in a Google user and derive their privacy-preserving keyless address, with the help of a guardian.
-
-Next, we show how this dApp can obtain a zero-knowledge proof (ZKP), which will allow it to authorize transactions from this address for the user. Importantly, the transaction will hide the user’s identifying information (e.g., the `sub` field).
-
-
-
-**Step 1**: The dApp sends all the necessary public information (i.e., $\mathsf{epk}$, $\mathsf{GPK}$) and private information (i.e., JWT, signature $\sigma_G$ from Google, EPK blinding factor $\rho$, and pepper $r$) to the **prover service**.
-
-**Step 2**: The prover derives the user’s address $\mathsf{addr}$ and computes a zero-knowledge proof (ZKP) $\pi$ for the keyless relation $\mathcal{R}_\mathsf{keyless}$ (described below). This proof acts as a **privacy-preserving** digital certificate, and binds the user's address $\mathsf{addr}$ to the ephemeral public key $\mathsf{epk}$. The prover then sends $\pi$ to the dApp.
-
-In order to bind the $\mathsf{epk}$ with the user's address $\mathsf{addr}$, the ZKP will be used to convince the validators that the user is in possession of (1) a JWT signed by Google, (2) which commits to the $\mathsf{epk}$ in its `nonce` field, and (3) contains the same information as in the address, without leaking anything about the JWT, its signature $\sigma_G$, $\rho$, or $r$.
-
-More formally, the ZKP $\pi$ convinces a verifier (i.e., the blockchain), who has public inputs $(\mathsf{addr}, \mathsf{epk}, \mathsf{GPK})$, that the prover knows secret inputs $(\mathsf{jwt}, \sigma_G, \rho, r)$ such that the relation $\mathcal{R}_\mathsf{keyless}$ depicted below holds:
-
-
-
-Recall from before that the signed JWT itself binds the blockchain address $\mathsf{addr}$ to $\mathsf{epk}$, so that $\mathsf{epk}$ can sign transactions for $\mathsf{addr}$. However, the JWT would leak the user’s identity, so the ZKP serves to hide the JWT (and other private information) while arguing that the proper checks hold (i.e., the checks in $\mathcal{R}_\mathsf{keyless}$).
-
-Next, we show how the dApp can now authorize TXNs from $\mathsf{addr}$.
-
-## Flow: Sending a TXN from a keyless account
-
-The previous flow explained how a dApp can obtain a ZKP from the prover service. Next, we describe how the dApp leverages this ZKP to transact for the account.
-
-
-
-**Step 1**: The dApp obtains an ephemeral signature $\sigma_\mathsf{eph}$ over the TXN from the user. This could be done behind the user’s back, by the dApp itself who might manage the ESK. Or, it could be an actual signing request sent to the user, such as when the ESK is a WebAuthn passkey, which is stored on the user’s trusted hardware.
-
-**Step 2**: The dApp sends the TXN, the ZKP $\pi$, the ephemeral public key $\mathsf{epk}$, and the ephemeral signature $\sigma_\mathsf{eph}$ to the blockchain validators.
-
-**Step 3**: To check the TXN is validly-signed, the validators perform several steps: (1) check that $\mathsf{epk}$ has not expired, (2) fetch the user’s address $\mathsf{addr}$ from the TXN, (3) verify the ZKP against $(\mathsf{addr}, \mathsf{epk}, \mathsf{GPK})$, and (4) verify the ephemeral signature $\sigma_\mathsf{eph}$ on the TXN against the $\mathsf{epk}$. If all these checks pass, they can safely execute the TXN.
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless/integration-guide.mdx b/apps/nextra/pages/zh/build/guides/aptos-keyless/integration-guide.mdx
deleted file mode 100644
index 7ee3cc89e..000000000
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless/integration-guide.mdx
+++ /dev/null
@@ -1,277 +0,0 @@
----
-title: "Keyless Integration Guide"
----
-import { Callout, Steps } from "nextra/components";
-
-# Aptos Keyless Integration Guide
-
-
- **Keyless Account Scoping**
-
- Use of the **_Aptos Keyless Integration Guide_** will allow for the integration of keyless accounts directly into your application. This means that blockchain accounts are scoped to your application's domain (logging in with your Google account on dApp A and logging in with your Google account on dApp B will create separate accounts). Stay tuned for more to come on Aptos’ plan to allow Keyless accounts to be used portably across applications.
-
- To provide feedback, get support, or be a design partner as we enhance Aptos Keyless, join us here: https://t.me/+h5CN-W35yUFiYzkx
-
-
-At a high level, there are three steps to follow in order to integrate Keyless Accounts.
-
-1. **Configure your OpenID integration with your IdP.** In this step, the dApp will register with the IdP of choice (e.g. Google) and receive a `client_id`
-2. **Install the Aptos TypeScript SDK.**
-3. **Integrate Keyless Account support in your application client**
- 1. Set up the `"Sign In with [Idp]"` flow for your user.
- 2. Instantiate the user’s `KeylessAccount`
- 3. Sign and submit transactions via the `KeylessAccount`.
-
-## Example Implementaion
-
-You can find an example app demonstrating basic Keyless integration with Google in the [aptos-keyless-example repository](https://github.com/aptos-labs/aptos-keyless-example/). Follow the directions in the README to start with the example. For more detailed instructions on keyless, please read the rest of this integration guide.
-
-
-### Step 1. Configure your OpenID integration with your IdP
-
-The first step is to setup the configuration with your IdP(s).
-
-[Follow the intructions here](oidc-support.mdx)
-
-### Step 2. Install the Aptos TypeScript SDK
-
-```bash
-# Keyless is supported in version 1.18.1 and above
-pnpm install @aptos-labs/ts-sdk
-```
-
-### Step 3. Client Integration Steps
-
-Below are the default steps for a client to integrate Keyless Accounts
-
-#### 1. Present the user with a "Sign In with [IdP]" button on the UI
-
- 1. In the background, we create an ephemeral key pair. Store this in local storage.
-
- ```ts
- import {EphemeralKeyPair} from '@aptos-labs/ts-sdk';
-
- const ephemeralKeyPair = EphemeralKeyPair.generate();
- ```
-
- 2. Save the `EphemeralKeyPair` in local storage, keyed by its `nonce`.
-
- ```ts
- // This saves the EphemeralKeyPair in local storage
- storeEphemeralKeyPair(ephemeralKeyPair);
- ```
-
-
-Example implementation for `storeEphemeralKeyPair`
-
-
-This implementation is an example of how to store the `EphemeralKeyPair` in local storage. Different implementations may be used according to your application's needs.
-
-
-```ts filename="ephemeral.ts"
-
-/**
- * Store the ephemeral key pair in localStorage.
- */
-export const storeEphemeralKeyPair = (ekp: EphemeralKeyPair): void =>
- localStorage.setItem("@aptos/ekp", encodeEphemeralKeyPair(ekp));
-
-/**
- * Retrieve the ephemeral key pair from localStorage if it exists.
- */
-export const getLocalEphemeralKeyPair = (): EphemeralKeyPair | undefined => {
- try {
- const encodedEkp = localStorage.getItem("@aptos/ekp");
- return encodedEkp ? decodeEphemeralKeyPair(encodedEkp) : undefined;
- } catch (error) {
- console.warn(
- "Failed to decode ephemeral key pair from localStorage",
- error
- );
- return undefined;
- }
-};
-
-/**
- * Stringify the ephemeral key pairs to be stored in localStorage
- */
-export const encodeEphemeralKeyPair = (ekp: EphemeralKeyPair): string =>
- JSON.stringify(ekp, (_, e) => {
- if (typeof e === "bigint") return { __type: "bigint", value: e.toString() };
- if (e instanceof Uint8Array)
- return { __type: "Uint8Array", value: Array.from(e) };
- if (e instanceof EphemeralKeyPair)
- return { __type: "EphemeralKeyPair", data: e.bcsToBytes() };
- return e;
- });
-
-/**
- * Parse the ephemeral key pairs from a string
- */
-export const decodeEphemeralKeyPair = (encodedEkp: string): EphemeralKeyPair =>
- JSON.parse(encodedEkp, (_, e) => {
- if (e && e.__type === "bigint") return BigInt(e.value);
- if (e && e.__type === "Uint8Array") return new Uint8Array(e.value);
- if (e && e.__type === "EphemeralKeyPair")
- return EphemeralKeyPair.fromBytes(e.data);
- return e;
- });
-```
-
-
-
- 3. Prepare the URL params of the login URL. Set the `redirect_uri` and `client_id` to your configured values with the IdP. Set the `nonce` to the nonce of the `EphemeralKeyPair` from step 1.1.
-
- ```ts
- const redirectUri = 'https://.../login/callback'
- const clientId = env.IDP_CLIENT_ID
- // Get the nonce associated with ephemeralKeyPair
- const nonce = ephemeralKeyPair.nonce
- ```
-
- 4. Construct the login URL for the user to authenticate with the IdP. Make sure the `openid` scope is set. Other scopes such as `email` and `profile` can be set based on your app’s needs.
-
- ```ts
- const loginUrl = `https://accounts.google.com/o/oauth2/v2/auth?response_type=id_token&scope=openid+email+profile&nonce=${nonce}&redirect_uri=${redirectUri}&client_id=${clientId}`
- ```
-
- 5. When the user clicks the login button, redirect the user to the `loginUrl` that was created in step 1.4.
-
-#### 2. Handle the callback by parsing the token and create a Keyless account for the user
-
- 1. Once the user completes the login flow, they will be redirected to the `redirect_uri` set in step 1. The JWT will be set in the URL as a search parameter in a URL fragment, keyed by `id_token`. Extract the JWT from the `window` by doing the following:
-
- ```ts
- const parseJWTFromURL = (url: string): string | null => {
- const urlObject = new URL(url);
- const fragment = urlObject.hash.substring(1);
- const params = new URLSearchParams(fragment);
- return params.get('id_token');
- };
-
- // window.location.href = https://.../login/google/callback#id_token=...
- const jwt = parseJWTFromURL(window.location.href)
- ```
-
- 2. Decode the JWT and get the extract the nonce value from the payload.
-
- ```ts
- import { jwtDecode } from 'jwt-decode';
-
- const payload = jwtDecode<{ nonce: string }>(jwt);
- const jwtNonce = payload.nonce
- ```
-
- 3. Fetch the `EphemeralKeyPair` stored in step 1.2. Make sure to validate the nonce matches the decoded nonce and that the `EphemeralKeyPair` is not expired.
-
- ```ts
- const ekp = getLocalEphemeralKeyPair();
-
- // Validate the EphemeralKeyPair
- if (!ekp || ekp.nonce !== jwtNonce || ekp.isExpired() ) {
- throw new Error("Ephemeral key pair not found or expired");
- }
- ```
-
- 4. Instantiate the user’s `KeylessAccount`
-
- Depending on the type of Keyless you are using, follow the instructions below:
-
- 1. Normal Keyless
- ```tsx
- import {Aptos, AptosConfig, Network} from '@aptos-labs/ts-sdk';
-
- const aptos = new Aptos(new AptosConfig({ network: Network.DEVNET })); // Configure your network here
- const keylessAccount = await aptos.deriveKeylessAccount({
- jwt,
- ephemeralKeyPair,
- });
- ```
-
- 2. Federated Keyless
- ```tsx
- import {Aptos, AptosConfig, Network} from '@aptos-labs/ts-sdk';
-
- const aptos = new Aptos(new AptosConfig({ network: Network.DEVNET })); // Configure your network here
- const keylessAccount = await aptos.deriveKeylessAccount({
- jwt,
- ephemeralKeyPair,
- jwkAddress: jwkOwner.accountAddress
- });
- ```
-
-
-#### 3. Store the KeylessAccount in local storage (Optional)
-
- 1. After the account has been derived, store the `KeylessAccount` in local storage. This allows the user to return to the application without having to re-authenticate.
-
- ```ts filename="keyless.ts"
- export const storeKeylessAccount = (account: KeylessAccount): void =>
- localStorage.setItem("@aptos/account", encodeKeylessAccount(account));
-
- export const encodeKeylessAccount = (account: KeylessAccount): string =>
- JSON.stringify(account, (_, e) => {
- if (typeof e === "bigint") return { __type: "bigint", value: e.toString() };
- if (e instanceof Uint8Array)
- return { __type: "Uint8Array", value: Array.from(e) };
- if (e instanceof KeylessAccount)
- return { __type: "KeylessAccount", data: e.bcsToBytes() };
- return e;
- });
- ```
-
- 2. Whenever the user returns back to the application, retrieve the `KeylessAccount` from local storage and use it to sign transactions.
-
- ```ts filename="keyless.ts"
- export const getLocalKeylessAccount = (): KeylessAccount | undefined => {
- try {
- const encodedAccount = localStorage.getItem("@aptos/account");
- return encodedAccount ? decodeKeylessAccount(encodedAccount) : undefined;
- } catch (error) {
- console.warn(
- "Failed to decode account from localStorage",
- error
- );
- return undefined;
- }
- };
-
- export const decodeKeylessAccount = (encodedAccount: string): KeylessAccount =>
- JSON.parse(encodedAccount, (_, e) => {
- if (e && e.__type === "bigint") return BigInt(e.value);
- if (e && e.__type === "Uint8Array") return new Uint8Array(e.value);
- if (e && e.__type === "KeylessAccount")
- return KeylessAccount.fromBytes(e.data);
- return e;
- });
- ```
-
-
-#### 4. Submit transactions to the Aptos blockchain
-
- 1. Create the transaction you want to submit. Below is a simple coin transfer transaction for example:
-
- ```tsx
- import {Account} from '@aptos-labs/ts-sdk';
-
- const bob = Account.generate();
- const transaction = await aptos.transferCoinTransaction({
- sender: keylessAccount.accountAddress,
- recipient: bob.accountAddress,
- amount: 100,
- });
- ```
-
- 2. Sign and submit the transaction to the chain.
-
- ```tsx
- const committedTxn = await aptos.signAndSubmitTransaction({ signer: keylessAccount, transaction });
- ```
-
- 3. Wait for the transaction to be processed on-chain
-
- ```tsx
- const committedTransactionResponse = await aptos.waitForTransaction({ transactionHash: committedTxn.hash });
- ```
-
-
\ No newline at end of file
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless/introduction.mdx b/apps/nextra/pages/zh/build/guides/aptos-keyless/introduction.mdx
deleted file mode 100644
index c2c8b0e96..000000000
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless/introduction.mdx
+++ /dev/null
@@ -1,25 +0,0 @@
----
-title: "Keyless Introduction"
----
-
-# Introduction
-
-Keyless accounts represent a pivotal advancement within the Aptos ecosystem, revolutionizing the way users onboard and interact with decentralized applications (dApps). Aptos Keyless allows users to gain ownership of a **self-custodial** Aptos blockchain account from their existing OpenID Connect (OIDC) account(s) (e.g., Sign in with Google; Sign in with Apple), rather than from a traditional secret key or mnemonic. In a nutshell, with Aptos Keyless, a user’s blockchain account is their OIDC account. Over time, Keyless will evolve to support many IdPs who support the OIDC standard, but we will begin with support for the providers listed [here](oidc-support.mdx).
-
-At the core of the keyless accounts paradigm lies a deep understanding of user experience and security challenges prevalent in traditional blockchain systems. Managing private keys, the cornerstone of user identity and asset ownership, often proves cumbersome and error-prone for users, particularly those lacking technical expertise. Keyless accounts offer an elegant solution by obviating the need for users to grapple with the intricacies of private key management. Instead, users authenticate themselves through access to common social sign in options like Google, Apple, and many more. With this new system comes some important tradeoffs to understand on behalf of your users before implementing Keyless in your application. The following pages will expand on the benefits of Keyless accounts, how to integrate, the system architecture, and FAQs. For a more verbose and technical dive into Keyless accounts, please see [AIP-61-Keyless Accounts](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-61.md).
-
-There are two ways to interact with Keyless accounts in the Aptos ecosystem. Developers are able to either 1) integrate the Aptos Keyless SDK directly into their dApp or 2) integrate a wallet, like Aptos Connect, that supports Keyless account creation. This documentation will focus on case #1 and more details on #2 can be found [here](https://aptosconnect.app/docs/). Please note that a direct integration of the Keyless SDK will result in user accounts being domain specific to your dApp whereas the use of a wallet integration will allow your users to carry their accounts to any application that supports that wallet.
-
-Note: the Aptos Keyless SDK and Aptos Connect are representative examples of the aforementioned product experience, but developers in our ecosystem are building alternatives, like a Keyless Unity SDK and alternative wallet products with Keyless integration.
-
-## Aptos Keyless Benefits
-
-Keyless accounts are revolutionary to users for the following reasons:
-
-1. **Simplified login user experience**: "1-click" account creation via familiar Web2 logins like Sign In with Google.
-2. **Enhanced dApp user experience**: Ability to transact on the Aptos blockchain without needing to navigate away from the application experience to download a wallet.
-3. **Secure key management**: Requires no manual secret key management by the user. Users sign transactions with the JSON Web Token (JWT) token issued by OIDC providers. As such, blockchain account access is synonymous with access to one’s OIDC account
-4. **Improved account recovery**: Web2-like recovery flows are available to regain access to one’s blockchain account in case the user ever loses access to their OIDC account.
-5. **Seamless cross-device experiences**: Users log in with their OIDC account no matter what device they are on - no need to download wallet software on each device, import their keys and encrypt them with a password, which must be maintained.
-
-With these benefits, come some important structural components of Keyless accounts for developers to be aware of. You can see more on this in our FAQs.
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless/oidc-support.mdx b/apps/nextra/pages/zh/build/guides/aptos-keyless/oidc-support.mdx
deleted file mode 100644
index 735da2bc2..000000000
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless/oidc-support.mdx
+++ /dev/null
@@ -1,112 +0,0 @@
----
-title: "Keyless OIDC Support"
----
-
-import { Callout, Steps } from "nextra/components";
-
-# Configure Your OIDC Provider
-
-Aptos Keyless supports the following IdPs and IAM providers on our network(s). Support for additional IdPs to come. Please reach out if you have need for coverage for a specific use case.
-
-| Identity Provider | Federated Only | Devnet | Testnet | Mainnet |
-|-------------------|----------------|-----------|----------|----------|
-| Google | No | Live | Live | Live |
-| Apple | No | Live | Live | Live |
-| Auth0 | Yes | Live | Live | Live |
-| Cognito | Yes | Live | Live | Live |
-| Firebase | Yes | In review | - | - |
-| Microsoft | No | In review | - | - |
-| Github | No | In review | - | - |
-| Facebook | No | In review | - | - |
-
-If your identity provider is marked as "Federated Only", you will need to follow the instructions for [Federated Keyless](../aptos-keyless/federated-keyless.mdx).
-
-To integrate Aptos Keyless into your dApp, you must register your dApp with at least one of the available identity providers via their OIDC registration process. Each respective registration process will assign a Client ID to your application, which will serve as an identifier for your application in the Keyless architecture.
-
-## Registering your dApp with Google
-
-
-
-### Step 1: Sign in to Google Developer Console
-
-1. Navigate to the [Google Cloud Console](https://console.cloud.google.com/).
-2. Sign in with your Google account credentials.
-
-### Step 2: Create a New Project
-
-1. If you don't have an existing project, click on the "Select a project" dropdown menu at the top of the page and choose "New Project."
-2. Enter a name for your project and click "Create." Detailed instructions can be found [here](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project).
-
-### Step 3: Configure Consent Screen
-
-1. In the left sidebar, navigate to "APIs & Services" > "OAuth consent screen."
-2. Choose "External" user type and click "Create."
-3. Enter the required details such as the application name, user support email, and developer contact information.
-4. Optionally, add additional details like the application logo and privacy policy URL.
-5. Click "Save and continue." Detailed steps are available [here](https://developers.google.com/workspace/guides/create-credentials#configure_the_oauth_consent_screen).
-
-### Step 4: Register Your Application
-
-1. In the left sidebar, navigate to "APIs & Services" > "Credentials."
- 
-
-2. Click on "Create Credentials" and select "OAuth client ID."
- 
-
-3. Choose the application type (e.g., Web application, Desktop app, or Mobile app).
-4. Enter the necessary details such as the name of your application and the authorized redirect URIs. For OIDC, the redirect URIs should follow the format https://your-app-domain.com/auth/google/callback.
-5. Click "Create."
-
-### Step 5: Obtain Client ID and Client Secret
-
-1. After creating the OAuth client ID, Google will provide you with a client ID and client secret. These credentials are essential for authenticating your application.
-2. Note down the client ID and client secret securely. Do not expose them publicly.
-
-### Step 6: Configure OIDC Integration in Your Application
-
-1. Integrate OIDC authentication into your application using a suitable OIDC library or framework (e.g., Passport.js for Node.js, Spring Security for Java, or Auth0 for various platforms).
-2. Use the client ID and client secret obtained from Google to configure OIDC authentication in your application settings.
-3. Set up the appropriate callback URL (https://your-app-domain.com/auth/google/callback) for handling authentication responses from Google.
-
-
-
-## Registering your dApp with Apple
-
-
-
-### Step 1: Sign in to Apple Developer Account
-
-1. Go to the [Apple Developer website](https://developer.apple.com/).
-2. Sign in with your Apple ID.
-3. Enroll in the Apple Developer Program if not already.
- 
-
-### Step 2: Create a New App ID
-
-1. Navigate to the "Certificates, Identifiers & Profiles" section.
-2. Click on "Identifiers" in the sidebar.
-3. Click the "+" button to create a new App ID.
-4. Fill in the details for your app, including the name and bundle ID.
-5. Enable "Sign in with Apple" under the "Capabilities" section.
-6. Click "Continue" and then "Register" to create the App ID.
-
-### Step 3: Generate a Private Key
-
-1. In the "Keys" section of the "Certificates, Identifiers & Profiles" page, click the "+" button to create a new key.
-2. Enter a name for the key, enable the "Sign in with Apple" capability, and click "Continue."
-3. Download the generated private key and securely store it. This key will be used to authenticate your app with Apple's OIDC service.
-
-### Step 4: Configure Redirect URIs
-
-1. Under the "App ID" section, locate your newly created App ID and click on it.
-2. Scroll down to the "Sign in with Apple" section and click on "Edit."
-3. Add the redirect URIs that your application will use for callback after authentication. The format should be https://your-app-domain.com/auth/apple/callback.
-4. Click "Save" to update the settings.
-
-### Step 5: Set Up Your OIDC Integration
-
-1. Use an OIDC library or framework compatible with Apple's OIDC service (e.g., Passport.js for Node.js, Spring Security for Java).
-2. Configure your application to use the client ID and private key obtained from Apple during the registration process.
-3. Set up the appropriate callback URL (https://your-app-domain.com/auth/apple/callback) for handling authentication responses from Apple.
-
-
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless/other.mdx b/apps/nextra/pages/zh/build/guides/aptos-keyless/other.mdx
deleted file mode 100644
index 3f3e39822..000000000
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless/other.mdx
+++ /dev/null
@@ -1,56 +0,0 @@
----
-title: "Keyless Terminology and FAQ"
----
-
-## Terminology
-
-- **OpenID Connect (OIDC)**: is the identity authentication protocol used to enable federated identity verification. This protocol is what is used when a user goes through the "Sign in with Google" flow for example.
-- **Identity Provider (IdP)**: is the trusted authority who authenticates your identity via OIDC. Supported example includes: Google.
-- **JSON Web Token (JWT):** is an open standard used to share security information between two parties — a client and a server. Each JWT contains encoded JSON objects, including a set of claims. JWTs are signed using a cryptographic algorithm to ensure that the claims cannot be altered after the token is issued.
- - `iss`, an identifier for the OIDC provider (e.g., https://accounts.google.com)
- - `aud`, the OAuth `client_id` of the application that the user is signing in to (e.g., [Notion.so](https://notion.so))
- - `sub`, an identifier that the OIDC provider uses to identify the user
- - This could be an identifier specific to this `client_id`
- - Or, it could be an identifier shared across different `client_id`'s (e.g., Facebook’s OIDC does this)
- - `email`, some providers might also expose the user’s email as one of the fields (e.g., Google)
- - in addition, an `email_verified` field will be exposed to indicate if the provider has verified that the user owns this email address
- - `nonce`, arbitrary data that the application wants the OIDC provider to sign over
- - `iat`, the time the JWT was issued at.
-- **Ephemeral Key Pair:** a temporary public/private key pair that is used to sign transactions for an Aptos Keyless account. The public key and its expiration date are committed in the JWT token via the `nonce` field.
-- **Keyless Account:** a blockchain account that is directly-derived from (1) a user’s OIDC account (e.g., `alice@gmail.com`) and (2) an associated application’s OAuth client_id (e.g., Notion.so). Users authenticate through the OIDC flow.
-- **JSON Web Key (JWK):** is the cryptographic public key of the OIDC provider. This public key is used to verify the signature on the JWTs that the OIDC provider issues to the client application. This way, the client application can verify the authenticity of the tokens and ensure that they have not been tampered with.
-- **client_id:** the OAuth identifier for your application that you will receive from the IdP after registering your application with them. This will be used in our keyless architecture in the address derivation for your users.
-- **redirect_uri:** the URI of the callback handler once the user successfully authenticates. Needs to be registered with your IdP.
-
-## Ceremony
-
-Aptos engaged in iterative trusted setup ceremonies to secure our Groth16 based ZK circuit. A trusted setup ceremony is a multi-party computation (MPC) that outputs the prover and verifier keys used in a zkSNARK system, common for efficient zero-knowledge proof systems. As long as a single participant in the ceremony is honest, the process is considered secure and the outputs will be valid. Our initial ceremony consisted of 140+ members of the Aptos ecosystem, which was an incredible show of the power of decentralization, security, and community - and a follow up ceremony was held following a developer feedback phase that allowed us to identify and implement an improvement to our circuit that helped us ensure Keyless is universally accessible. Our final ceremony contributions can be found in this repo [here] and verified using the process outlined [here].
-
-## Frequently Asked Questions
-
-**What is the best way to use Keyless accounts?**
-
-- The best way to use Keyless accounts depends on your use case. If seamless account interoperability across our ecosystem is important to your dApp experience (think: mint an NFT on your platform and allow users to sell their NFT on an external NFT marketplace), you might want to consider integrating a wallet that supports Keyless. If you want to create a fully embedded account experience in your dApp, allowing users to transact without ever leaving your application, you might want to do a direct integration of the Aptos Keyless SDK.
-
-**Does Keyless work with sponsored transactions or do my users always need to pay for their own gas?**
-
-- Yes, Keyless works with sponsored transactions like any regular private key based account.
-
-**If I use the Aptos Keyless SDK, can my user’s use their accounts across other dApps?**
-
-- Keyless accounts are scoped to the domain they are created with as the address derivation includes a unique identifier for the application.
-
-**What is Aptos Connect?**
-
-- Account Management Infrastructure: Central to the keyless accounts paradigm is a robust account management infrastructure that facilitates the creation, deletion, and management of user accounts, alongside the storage and retrieval of associated metadata.
-
-- While the adoption of keyless accounts heralds a paradigm shift towards enhanced usability and security, it is imperative for developers to remain cognizant of tradeoffs associated with this system vs. common alternatives like plaintext private keys.
-
-**Are there dependency on external services?**
-
-- Yes, Keyless accounts introduce a degree of dependency on external authentication services (pepper and prover), necessitating contingency plans and fallback mechanisms to mitigate service disruptions and ensure uninterrupted user access
-
-**If my dApp goes down, my users cannot access their Keyless accounts. How can I help protect them in that case?**
-
-- We encourage dApp developers to support additional backup recovery options for your users when integrating Keyless into a dApp. Specifically, we recommend that you support adding a backup private key to Keyless accounts in your dApp. Practically, this would transform the accounts into 1 of 2 multi-signature accounts where both keys are owned by the user. This would allow users to continue using OIDC login via your dApp to access their Keyless accounts but would add the ability for your users to export their backup private key to any self custodial product, where they could sign transactions from that same account with their traditional private key. Doing this will ensure that users never lose access to their digital assets, even if your dApp shuts down or the user loses access to their OIDC account.
-- You should make a determination at what point in the user journey to incorporate a back-up is appropriate for your dApp. Incorporating a backup method later in the user journey would preserve the seamless onboarding experience that Keyless offers but could result in less users receiving a recovery key. Prompting users to add a backup key during the onboarding process would likely lead to more users receiving a recovery key but could add potential friction during the onboarding process.
diff --git a/apps/nextra/pages/zh/build/guides/aptos-keyless/simple-example.mdx b/apps/nextra/pages/zh/build/guides/aptos-keyless/simple-example.mdx
deleted file mode 100644
index 0b23088b5..000000000
--- a/apps/nextra/pages/zh/build/guides/aptos-keyless/simple-example.mdx
+++ /dev/null
@@ -1,27 +0,0 @@
----
-title: "Keyless Simple Example"
----
-
-# Keyless Simple Example
-
-Explore the code in [aptos-keyless-example repository](https://github.com/aptos-labs/aptos-keyless-example/).
-
-The Keyless Simple Example is currently undergoing maintenance. Please check back later.
-
-This is a live Keyless example on CodeSandbox. Follow the instructions in the `README.md` to add your own Google `client_id`. Explore the code in [aptos-keyless-example repository](https://github.com/aptos-labs/aptos-keyless-example/).
-
-
-
-
diff --git a/apps/nextra/pages/zh/build/guides/build-e2e-dapp.mdx b/apps/nextra/pages/zh/build/guides/build-e2e-dapp.mdx
deleted file mode 100644
index 5f1717405..000000000
--- a/apps/nextra/pages/zh/build/guides/build-e2e-dapp.mdx
+++ /dev/null
@@ -1,47 +0,0 @@
-# Build an End-to-End Dapp on Aptos
-
-A common way to learn a new framework or programming language is to build a simple todo list. In this tutorial, we will learn how to build an end-to-end todo list dapp, starting from the smart contract side through the front-end side and finally use of a wallet to interact with the two.
-
-See the completed code in the [my_first_dapp](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/my_first_dapp).
-
-## Chapters
-
-After meeting the [prerequisites](#prerequisites) and [getting set up](#setup) as described below, you will follow this tutorial in this order:
-
-1. [Create a smart contract](build-e2e-dapp/1-create-smart-contract.mdx)
-2. [Set up React app](build-e2e-dapp/2-set-up-react-app.mdx)
-3. [Add Wallet support](build-e2e-dapp/3-add-wallet-support.mdx)
-4. [Fetch Data from Chain](build-e2e-dapp/4-fetch-data-from-chain.mdx)
-5. [Submit data to chain](build-e2e-dapp/5-submit-data-to-chain.mdx)
-6. [Handle Tasks](build-e2e-dapp/6-handle-tasks.mdx)
-
-## Prerequisites
-
-You must have:
-
-- [Aptos CLI](../cli.mdx)
-- [Aptos TypeScript SDK](../sdks/ts-sdk.mdx)
-- [Aptos Wallet Adapter](../sdks/wallet-adapter.mdx)
-- [Create React App](https://create-react-app.dev/)
-- [node and npm](https://nodejs.org/en/)
-
-Although we will explain some React decisions, we are not going to deep dive into how React works; so we assume you have some previous experience with React.
-
-## Setup
-
-In this section, we will create a `my-first-dapp` directory to hold our project files, both client-side code (React based)and the Move code (our smart contract).
-
-1. Open a terminal and navigate to the desired directory for the project (for example, the `Desktop` directory).
-2. Create a new directory called `my-first-dapp`, for example:
-
-```bash filename="Terminal"
-mkdir my-first-dapp
-```
-
-3. Navigate into that directory:
-
-```bash filename="Terminal"
-cd my-first-dapp
-```
-
-Now let's [create a smart contract](build-e2e-dapp/1-create-smart-contract.mdx).
diff --git a/apps/nextra/pages/zh/build/guides/build-e2e-dapp/1-create-smart-contract.mdx b/apps/nextra/pages/zh/build/guides/build-e2e-dapp/1-create-smart-contract.mdx
deleted file mode 100644
index ba90564f7..000000000
--- a/apps/nextra/pages/zh/build/guides/build-e2e-dapp/1-create-smart-contract.mdx
+++ /dev/null
@@ -1,601 +0,0 @@
-import { Callout } from 'nextra/components';
-
-# 1. Create a Smart Contract
-
-This is the first chapter of the tutorial on [building an end-to-end dapp on Aptos](../build-e2e-dapp.mdx). If you haven’t done it, review that introduction, and ensure your environment meets the [prerequisites](../build-e2e-dapp.mdx#prerequisites) listed there.
-
-Now that you are all set up and at your terminal:
-
-1. `cd` into the `my-first-dapp` root directory, and create a new `move` directory.
-2. `cd` into the new `move` directory and run: `aptos move init --name my_todo_list`
- That command creates a `sources/` directory and `Move.toml` file inside the `move` directory.
-3. Your new `move` directory should now resemble:
-
- 
-
-### What is a `Move.toml` file?
-
-A `Move.toml` file is a manifest file that contains metadata such as name, version, and dependencies for the package.
-
-Take a look at the new `Move.toml` file. You should see your package information and an `AptosFramework` dependency. Note that the `name` property is the same `--name` attribute we passed to the `aptos move init` command before. The `AptosFramework` dependency points to the `aptos-core/aptos-move/framework/aptos-framework` GitHub repo main branch.
-
-### Why `sources` directory?
-
-The `sources` directory holds a collection of `.move` modules files. And later when we want to compile the package using the CLI, the compiler will look for that `sources` directory and its `Move.toml` file.
-
-### Create a Move module
-
-An account is needed to publish a Move module. So first we need to create an account. Once we have the account's private key, we can create a module under its account address and publish the module using that account.
-
-1. In our `move` directory, run `aptos init --network devnet`. Press enter when prompted.
-
- This creates for us a `.aptos` directory with a `config.yaml` file that holds our profile information. In the `config.yaml` file, we now have our profiles list that holds a `default` profile. If you open that file, you will see content resembling:
-
- ```yaml filename="config.yaml"
- profiles:
- default:
- private_key: "0xee8f387ef0b4bb0018c4b91d1c0f71776a9b85935b4c6ec2823d6c0022fbf5cb"
- public_key: "0xc6c07218d79a806380ca67761905063ec7a78d41f79619f4562462a0f8b6be11"
- account: cbddf398841353776903dbab2fdaefc54f181d07e114ae818b1a67af28d1b018
- rest_url: "https://api.devnet.aptoslabs.com"
- faucet_url: "https://faucet.devnet.aptoslabs.com"
- ```
-
- From now on, whenever we run a CLI command in this `move` directory, it will run with that default profile.
- We use the `devnet` network flag so eventually when we publish our package it will get published to the `devnet` network.
-
-
- You just created a new account on the Aptos (dev) network! Yay! You can see it by going to the [Aptos Explorer](https://explorer.aptoslabs.com/?network=devnet) Devnet network view, pasting the `account` address value from your configuration file into the search field, and clicking on the dropdown option!
-
-
-As mentioned, our `sources` directory holds our `.move` module files; so let’s add our first Move file.
-
-2. Open the `Move.toml` file.
-3. Add the following code to that Move file, substituting your actual default profile account address from `.aptos/config.yaml`:
-
-```toml filename="Move.toml"
-[addresses]
-todolist_addr=''
-```
-
-If the default profile account address is `cbddf398841353776903dbab2fdaefc54f181d07e114ae818b1a67af28d1b018`, your `Move.toml` file should look like:
-
-```toml filename="Move.toml"
-[addresses]
-todolist_addr='cbddf398841353776903dbab2fdaefc54f181d07e114ae818b1a67af28d1b018'
-```
-
-4. Create a new `todolist.move` file within the `sources` directory and add the following to that file:
-
-```move filename="todolist.move"
-module todolist_addr::todolist {
-
-}
-```
-
-
-A Move module is stored under an address (so when it published anyone can access it using that address); the syntax for a Move module is
-
-```move filename="todolist.move"
-module :: {
-
-}
-```
-
-In our module, the `account-address` is `todolist_addr` (a variable we just declared on the `Move.toml` file in the previous step that holds an `address`), and the `module-name` is `todolist` (a random name we selected).
-
-
-### Our contract logic
-
-Before jumping into writing code, let’s first understand what we want our smart contract program to do. For ease of understanding, we will keep the logic pretty simple:
-
-1. An account creates a new list.
-2. An account creates a new task on their list.
- - Whenever someone creates a new task, emit a `task_created` event.
-3. Let an account mark their task as completed.
-
-
-Creating an event is not mandatory yet useful if dapps/users want to monitor data, such as how many people create a new task, using the [Aptos Indexer](../../indexer.mdx).
-
-
-We can start with defining a `TodoList` struct, that holds the:
-
-- tasks array
-- new task event
-- a task counter that counts the number of created tasks (we can use that to differentiate between the tasks)
-
-And also create a `Task` struct that holds:
-
-- the task ID - derived from the TodoList task counter.
-- address - the account address who created that task.
-- content - the task content.
-- completed - a boolean that marks whether that task is completed or not.
-
-On the `todolist.move` file, update the content in the module with:
-
-```move filename="todolist.move"
-...
-struct TodoList has key {
- tasks: Table,
- set_task_event: event::EventHandle,
- task_counter: u64
- }
-
-struct Task has store, drop, copy {
- task_id: u64,
- address:address,
- content: String,
- completed: bool,
- }
-...
-```
-
-**What did we just add?**
-
-**TodoList**
-
-A struct that has the `key` and `store` abilities:
-
-- `Key` ability allows struct to be used as a storage identifier. In other words, `key`
- is an ability to be stored at the top-level and act as a storage. We need it here to have `TodoList` be a resource stored in our user account.
-
-When a struct has the `key` ability, it turns this struct into a `resource`:
-
-- `Resource` is stored under the account - therefore it _exists_ only when assigned to an account and can be _accessed_ through this account only.
-
-**Task**
-
-A struct that has the `store`, `drop` and `copy`abilities.
-
-• `Store` - Task needs `Store` as it’s stored inside another struct (TodoList)
-
-• `Copy` - value can be _copied_ (or cloned by value).
-
-• `Drop` - value can be _dropped_ by the end of scope.
-
-Let’s try to compile what we have now:
-
-1. `cd` into the `move` directory.
-2. Run: `aptos move compile`
-
-**Seeing errors?!** Let’s understand them.
-
-We have some errors on `Unbound type`- this is happening because we used some types but never imported them, and the compiler doesn't know where to get them from.
-
-3. On the top of the module, import those types by adding:
-
-```move filename="todolist.move"
-...
-use aptos_framework::event;
-use std::string::String;
-use aptos_std::table::Table;
-...
-```
-
-That will tell the compiler where it can get those types from.
-
-4. Run the `aptos move compile` command again; If all goes well, we should see a response resembling (where the resulting account address is your default profile account address):
-
-```bash filename="Terminal"
-INCLUDING DEPENDENCY AptosFramework
-INCLUDING DEPENDENCY AptosStdlib
-INCLUDING DEPENDENCY MoveStdlib
-BUILDING myTodolist
-{
-"Result": [
- "cbddf398841353776903dbab2fdaefc54f181d07e114ae818b1a67af28d1b018::todolist"
- ]
-}
-```
-
-At this point, we have successfully compiled our Move module. Yay!
-
-We also have a new `move/build` directory (created by the compiler) that holds our compiled modules, build information and `sources` directory.
-
-### Create list function
-
-The first thing an account can and should do with our contract is to create a new list.
-
-Creating a list is essentially submitting a transaction, and so we need to know the `signer` who signed and submitted the transaction:
-
-1. Add a `create_list` function that accepts a `signer`
-
-```move filename="todolist.move"
-public entry fun create_list(account: &signer){
-
-}
-```
-
-**Let’s understand the components of this function**
-
-- `entry` - an _entry_ function is a function that can be called via transactions. Simply put, whenever you want to submit a transaction to the chain, you should call an entry function.
-
-- `&signer` - The **signer** argument is injected by the Move VM as the address who signed that transaction.
-
-Our code has a `TodoList` resource. Resource is stored under the account; therefore, it _exists_ only when assigned to an account and can be _accessed_ only through this account.
-
-That means to create the `TodoList` resource, we need to assign it to an account that only this account can have access to.
-
-The `create_list` function can handle that `TodoList` resource creation.
-
-2. Add the following to the `create_list` function
-
-```move filename="todolist.move"
-public entry fun create_list(account: &signer){
- let tasks_holder = TodoList {
- tasks: table::new(),
- set_task_event: account::new_event_handle(account),
- task_counter: 0
- };
- // move the TodoList resource under the signer account
- move_to(account, tasks_holder);
-}
-```
-
-This function takes in a `signer`, creates a new `TodoList` resource, and uses `move_to` to have the resource stored in the provided signer account.
-
-### Create task function
-
-As mentioned before, our contract has a create task function that lets an account create a new task. Creating a task is also essentially submitting a transaction, and so we need to know the `signer` who signed and submitted the transaction. Another element we want to accept in our function is the task `content`.
-
-1. Add a `create_task` function that accepts a `signer` and task `content` and the function logic.
-
-```move filename="todolist.move"
-public entry fun create_task(account: &signer, content: String) acquires TodoList {
- // gets the signer address
- let signer_address = signer::address_of(account);
- // gets the TodoList resource
- let todo_list = borrow_global_mut(signer_address);
- // increment task counter
- let counter = todo_list.task_counter + 1;
- // creates a new Task
- let new_task = Task {
- task_id: counter,
- address: signer_address,
- content,
- completed: false
- };
- // adds the new task into the tasks table
- table::upsert(&mut todo_list.tasks, counter, new_task);
- // sets the task counter to be the incremented counter
- todo_list.task_counter = counter;
- // fires a new task created event
- event::emit_event(
- &mut borrow_global_mut(signer_address).set_task_event,
- new_task,
- );
- }
-```
-
-2. Since we now use two new modules - signer and table (you can see it being used in `signer::` and `table::`) - we need to import these modules.
- At the top of the file, add those two use statements:
-
-```move filename="todolist.move"
-use std::signer;
-use aptos_std::table::{Self, Table}; // This one we already have, need to modify it
-```
-
-**Back to the code; what is happening here?**
-
-- First, we want to get the signer address, so we can get this account’s `TodoList` resource.
-- Then, we retrieve the `TodoList` resource with the `signer_address`; with that we have access to the `TodoList` properties.
-- We can now increment the `task_counter` property, and create a new `Task` with the `signer_address`, `counter` and the provided `content`.
-- We push it to the `todo_list.tasks` table that holds all of our tasks along with the new `counter` (which is the table key) and the newly created Task.
-- Then we assign the global `task_counter` to be the new incremented counter.
-- Finally, we emit the `task_created` event that holds the new Task data. `emit_event` is an `aptos-framework` function that accepts a reference to the event handle and a message. In our case, we are passing the function a reference (using the sign &) to the account’s `TodoListresource` `set_task_event` property as the first argument and a second message argument which is the new Task we just created. Remember, we have a `set_task_event` property in our `TodoList` struct.
-
-### Complete task function
-
-Another function we want our contract to hold is the option to mark a task as completed.
-
-1. Add a `complete_task` function that accepts a `signer` and a `task_id`:
-
-```move filename="todolist.move"
-public entry fun complete_task(account: &signer, task_id: u64) acquires TodoList {
- // gets the signer address
- let signer_address = signer::address_of(account);
- // gets the TodoList resource
- let todo_list = borrow_global_mut(signer_address);
- // gets the task matches the task_id
- let task_record = table::borrow_mut(&mut todo_list.tasks, task_id);
- // update task as completed
- task_record.completed = true;
-}
-```
-
-**Let’s understand the code.**
-
-- As before in our create list function, we retrieve the `TodoList` struct by the signer address, so we can have access to the tasks table that holds all the account tasks.
-- Then, we look for the task with the provided `task_id` on the `todo_list.tasks` table.
-- Finally, we update that task completed property to be true.
-
-Now try to compile the code:
-
-2. Run: `aptos move compile`
-3. Another `Unbound` error? To fix this, add a `use` statement to use the `account` module.
-
-```move filename="todolist.move"
-use aptos_framework::account;
-```
-
-4. run `aptos move compile` again.
-
-### Add validations
-
-As this code now compiles, we want to have some validations and checks before creating a new task or updating the task as completed, so we can be sure our functions work as expected.
-
-1. Add a check to the `create_task` function to make sure the signer account has a list:
-
-```move filename="todolist.move"
-public entry fun create_task(account: &signer, content: String) acquires TodoList {
- // gets the signer address
- let signer_address = signer::address_of(account);
-
- // assert signer has created a list
- assert!(exists(signer_address), 1);
-
- ...
-}
-```
-
-2. Add a check to the `complete_task` function to make sure the:
- - signer has created a list.
- - task exists.
- - task is not completed.
-
-With:
-
-```move filename="todolist.move"
-public entry fun complete_task(account: &signer, task_id: u64) acquires TodoList {
- // gets the signer address
- let signer_address = signer::address_of(account);
- // assert signer has created a list
- assert!(exists(signer_address), 1);
- // gets the TodoList resource
- let todo_list = borrow_global_mut(signer_address);
- // assert task exists
- assert!(table::contains(&todo_list.tasks, task_id), 2);
- // gets the task matched the task_id
- let task_record = table::borrow_mut(&mut todo_list.tasks, task_id);
- // assert task is not completed
- assert!(task_record.completed == false, 3);
- // update task as completed
- task_record.completed = true;
-}
-```
-
-We just added our first `assert` statements!
-
-If you noticed, `assert` accepts two arguments: the first is what to check for, and the second is an error code. Instead of passing in an arbitrary number, a convention is to declare `errors` on the top of the module file and use these instead.
-
-On the top of the module file (under the `use` statements), add those error declarations:
-
-```move filename="todolist.move"
-// Errors
-const E_NOT_INITIALIZED: u64 = 1;
-const ETASK_DOESNT_EXIST: u64 = 2;
-const ETASK_IS_COMPLETED: u64 = 3;
-```
-
-Now we can update our asserts with these constants:
-
-```move filename="todolist.move"
-public entry fun create_task(account: &signer, content: String) acquires TodoList {
- // gets the signer address
- let signer_address = signer::address_of(account);
-
- // assert signer has created a list
- assert!(exists(signer_address), E_NOT_INITIALIZED);
-
- ...
-}
-
-
-
-public entry fun complete_task(account: &signer, task_id: u64) acquires TodoList {
- // gets the signer address
- let signer_address = signer::address_of(account);
- assert!(exists(signer_address), E_NOT_INITIALIZED);
- // gets the TodoList resource
- let todo_list = borrow_global_mut(signer_address);
- // assert task exists
- assert!(table::contains(&todo_list.tasks, task_id), ETASK_DOESNT_EXIST);
- // gets the task matched the task_id
- let task_record = table::borrow_mut(&mut todo_list.tasks, task_id);
- // assert task is not completed
- assert!(task_record.completed == false, ETASK_IS_COMPLETED);
- // update task as completed
- task_record.completed = true;
-}
-```
-
-**WONDERFUL!!**
-
-Let’s stop for one moment and make sure our code compiles by running the `aptos move compile` command. If all goes well, we should output resembling:
-
-```bash filename="Terminal"
-INCLUDING DEPENDENCY AptosFramework
-INCLUDING DEPENDENCY AptosStdlib
-INCLUDING DEPENDENCY MoveStdlib
-BUILDING myTodolist
-{
-"Result": [
- "cbddf398841353776903dbab2fdaefc54f181d07e114ae818b1a67af28d1b018::todolist"
- ]
-}
-```
-
-If you encounter errors, make sure you followed the steps above correctly and try to determine the cause of the issues.
-
-### Write tests
-
-Now that we have our smart contract logic ready, we need to add some tests for it.
-
-Test functions use the `#[test]` annotation.
-
-1. Add the following code to the bottom of the file:
-
-```move filename="todolist.move"
-#[test]
-public entry fun test_flow() {
-
-}
-```
-
-
-we need to use `entry` here because we are testing an `entry` function.
-
-
-2. For simplicity, and because we don't have much code to test, we use one function to test the whole flow of the app.
- The test steps are:
-
-```move filename="todolist.move"
- // create a list
- // create a task
- // update task as completed
-```
-
-Update the test function to be:
-
-```move filename="todolist.move"
-#[test(admin = @0x123)]
-public entry fun test_flow(admin: signer) acquires TodoList {
- // creates an admin @todolist_addr account for test
- account::create_account_for_test(signer::address_of(&admin));
- // initialize contract with admin account
- create_list(&admin);
-
- // creates a task by the admin account
- create_task(&admin, string::utf8(b"New Task"));
- let task_count = event::counter(&borrow_global(signer::address_of(&admin)).set_task_event);
- assert!(task_count == 1, 4);
- let todo_list = borrow_global(signer::address_of(&admin));
- assert!(todo_list.task_counter == 1, 5);
- let task_record = table::borrow(&todo_list.tasks, todo_list.task_counter);
- assert!(task_record.task_id == 1, 6);
- assert!(task_record.completed == false, 7);
- assert!(task_record.content == string::utf8(b"New Task"), 8);
- assert!(task_record.address == signer::address_of(&admin), 9);
-
- // updates task as completed
- complete_task(&admin, 1);
- let todo_list = borrow_global(signer::address_of(&admin));
- let task_record = table::borrow(&todo_list.tasks, 1);
- assert!(task_record.task_id == 1, 10);
- assert!(task_record.completed == true, 11);
- assert!(task_record.content == string::utf8(b"New Task"), 12);
- assert!(task_record.address == signer::address_of(&admin), 13);
-}
-```
-
-Our `#[test]` annotation has changed and declares an account variable.
-
-Additionally, the function itself now accepts a signer argument.
-
-**Let’s understand our tests.**
-
-Since our tests run outside an account scope, we need to _create_ accounts to use in our tests. The `#[test]` annotation gives us the option to declare those accounts. We use an `admin` account and set it to a random account address (`@0x123`). The function accepts this signer (account) and creates it by using a built-in function to create an account for test.
-
-Then we simply go through the flow by:
-
-- creating a list
-- creating a task
-- updating a task as completed
-
-And assert the expected data/behavior at each step.
-
-Before running the tests again, we need to import (`use`) some new modules we are now employing in our code:
-
-3. At the top of the file, add this `use` statement:
-
-```move filename="todolist.move"
-use std::string::{Self, String}; // already have it, need to modify
-```
-
-4. Run the `aptos move test` command. If all goes right, we should see a success message like:
-
-```move filename="todolist.move"
-Running Move unit tests
-[ PASS ] 0xcbddf398841353776903dbab2fdaefc54f181d07e114ae818b1a67af28d1b018::todolist::test_flow
-Test result: OK. Total tests: 1; passed: 1; failed: 0
-{
- "Result": "Success"
-}
-```
-
-5. Let’s add one more test to make sure our `complete_task` function works as expected. Add another test function with:
-
-```move filename="todolist.move"
-#[test(admin = @0x123)]
-#[expected_failure(abort_code = E_NOT_INITIALIZED)]
-public entry fun account_can_not_update_task(admin: signer) acquires TodoList {
- // creates an admin @todolist_addr account for test
- account::create_account_for_test(signer::address_of(&admin));
- // account can not toggle task as no list was created
- complete_task(&admin, 2);
-}
-```
-
-This test confirms that an account can’t use that function if they haven’t created a list before.
-
-The test also uses a special annotation `#[expected_failure]` that, as the name suggests, expects to fail with an `E_NOT_INITIALIZED` error code.
-
-6. Run the `aptos move test` command. If all goes right, we should see a success message like:
-
-```bash filename="Terminal"
-Running Move unit tests
-[ PASS ] 0xcbddf398841353776903dbab2fdaefc54f181d07e114ae818b1a67af28d1b018::todolist::account_can_not_update_task
-[ PASS ] 0xcbddf398841353776903dbab2fdaefc54f181d07e114ae818b1a67af28d1b018::todolist::test_flow
-Test result: OK. Total tests: 2; passed: 2; failed: 0
-{
- "Result": "Success"
-}
-```
-
-Now that everything works, we can compile the Move modules and publish the Move package to chain so our React app (and everyone else) can interact with our smart contract!
-
-### Publish todolist module to chain
-
-For now, the easiest way to publish a Move package to chain is using the CLI:
-
-1. `cd` into our `move` directory, and run: `aptos move compile`
-
-We are getting some _Unused alias_ errors. This is because we added the `string` alias before since we use it in our tests. But we don't use this alias in our smart contract code.
-
-This is why we are getting this error when we want to compile the module but not are getting it when we only run tests.
-
-To fix it, we can add a `use` statement that would be used only in tests.
-
-Add the following `use` statement where we have all of our import statements.
-
-```move filename="todolist.move"
-use std::string::String; // change to this
-...
-#[test_only]
-use std::string; // add this
-```
-
-2. Run: `aptos move test` and `aptos move compile` - all should work without errors.
-3. Run: `aptos move publish`
-4. Enter `yes` in the prompt.
-5. That will compile, simulate and finally publish your module into devnet. You should see a success message:
-
-```bash filename="Terminal"
-{
- "Result": {
- "transaction_hash": "0x96b84689a53a28db7be6346627a99967f719946bc22766811a674e69da7783fa",
- "gas_used": 7368,
- "gas_unit_price": 100,
- "sender": "cbddf398841353776903dbab2fdaefc54f181d07e114ae818b1a67af28d1b018",
- "sequence_number": 2,
- "success": true,
- "timestamp_us": 1674246585276143,
- "version": 651327,
- "vm_status": "Executed successfully"
- }
-}
-```
-
-6. You can now head to the [Aptos Explorer](https://explorer.aptoslabs.com/), change the dropdown on the top right to the _Devnet_ network and look for that `transaction_hash` value - this will show you the transaction details.
-
-Now let's [set up a React app](2-set-up-react-app.mdx) in chapter 2.
diff --git a/apps/nextra/pages/zh/build/guides/build-e2e-dapp/2-set-up-react-app.mdx b/apps/nextra/pages/zh/build/guides/build-e2e-dapp/2-set-up-react-app.mdx
deleted file mode 100644
index 36975a1c8..000000000
--- a/apps/nextra/pages/zh/build/guides/build-e2e-dapp/2-set-up-react-app.mdx
+++ /dev/null
@@ -1,84 +0,0 @@
-# 2. Set up React App
-
-This is the second chapter of the tutorial on [building an end-to-end dapp on Aptos](../build-e2e-dapp.mdx) where you have already [created a smart contract](1-create-smart-contract.mdx) and are now setting up a React app.
-
-## Set up the app
-
-We will use the `react` library to build the client side with [Create React App](https://create-react-app.dev/docs/getting-started#creating-an-app).
-
-For the UI, we will use [Ant Design](https://ant.design/). This is just a personal decision; you are welcome to use any different UI library/framework you wish.
-
-1. In the root folder of the `my-first-dapp` project, run:
-
-```bash filename="Terminal"
-npx create-react-app client --template typescript
-```
-
-That will create a new `client` folder in the current path:
-
-2. Your file structure should look something like:
-
- 
-
-3. Run: `cd client`
-4. Run: `npm start`
-
- At this point you should have your app running on [http://localhost:3000](http://localhost:3000), which displays the default React layout.
-
-5. In the `client/src` directory, find all the React app files. Let’s clean it up a bit.
-6. Open the `App.tsx` file and update its content to be:
-
-```ts filename="App.tsx"
-function App() {
- return
My app goes here
;
-}
-
-export default App;
-```
-
-Once you save the changes, you should see that the app content has changed in the browser and displays `My app goes here`.
-
-7. Open the `App.tsx` file and remove the `import './App.css';` and `import logo from './logo.svg';` lines. Since we remove the default imports on this file, we can remove some files in our project. Delete the files `App.css` and `logo.svg`.
-8. Open the `index.tsx` file and remove the `import './index.css';` line at the top of the file.
- Now you can also delete the `src/index.css` file.
-
-## Our dapp UI
-
-First we will build the dapp UI layout. We have two UI states for the app:
-
-- When an account hasn't created a list yet (on the left).
-- When an account has created a list and can now add tasks to it (on the right).
- 
-
-We will use the [Ant Design](https://ant.design/) library for our UI:
-
-1. Stop the local server if running.
-2. On to the `client` directory and install our UI library package: `npm i antd@5.1.4`
-3. Update `App.tsx` with the initial state UI:
-
-```ts filename="App.tsx"
-return (
- <>
-
-
-
-
Our todolist
-
-
-
Connect Wallet
-
-
-
- >
-);
-```
-
-4. Don't forget to import the _components_ we just added:
-
-```ts filename="App.tsx"
-import { Layout, Row, Col } from "antd";
-```
-
-5. Run the local server with `npm start`, you should see the _header_ that matches our UI mockup.
-
-It is now time to [add wallet support](3-add-wallet-support.mdx) in chapter 3.
diff --git a/apps/nextra/pages/zh/build/guides/build-e2e-dapp/3-add-wallet-support.mdx b/apps/nextra/pages/zh/build/guides/build-e2e-dapp/3-add-wallet-support.mdx
deleted file mode 100644
index 31e0f5536..000000000
--- a/apps/nextra/pages/zh/build/guides/build-e2e-dapp/3-add-wallet-support.mdx
+++ /dev/null
@@ -1,96 +0,0 @@
-import { Callout } from 'nextra/components';
-
-# 3. Add Wallet Support
-
-In the third chapter of the tutorial on [building an end-to-end dapp on Aptos](../build-e2e-dapp.mdx), you will be adding _wallet_ support to your [React app](2-set-up-react-app.mdx). You now need a wallet to submit a transaction to the blockchain.
-
-Aptos provides a [wallet adapter](../../sdks/wallet-adapter.mdx) that supports many ecosystem wallets to offering a common interface and UI package that can be used to add a wallet connect button and a wallet selector modal.
-
-1. Stop the local server if running.
-2. In the `client` folder, run:
-
-```bash filename="Terminal"
-npm i @aptos-labs/wallet-adapter-react
-```
-
-```bash filename="Terminal"
-npm i @aptos-labs/wallet-adapter-ant-design
-```
-
-This installs two packages:
-
-- the adapter React provider that holds the logic.
-- a wallet connect UI package.
-
-3. We now need to add wallets to our app. There is a list of [wallets the adapter supports](https://github.com/aptos-labs/aptos-wallet-adapter#supported-wallet-packages); but to keep this tutorial simple, we will use only one wallet.
- Still in the `client` folder, run
-
-```bash filename="Terminal"
-npm i petra-plugin-wallet-adapter
-```
-
-
-If you haven't installed the Petra wallet extension yet:
-
-1. [Install Petra Wallet](https://petra.app) and open the Chrome extension.
-2. Follow the [user instructions](https://petra.app/docs/use) on petra.app for help.
-3. Switch to the Devnet network by clicking **Settings** > **Network** and selecting **devnet**.
-4. Click the **Faucet** button to ensure you can receive test tokens.
-
-
-
-4. Open `Index.tsx` file. At the top of the file, add the following:
-
-```tsx filename="Index.tsx"
-import { PetraWallet } from "petra-plugin-wallet-adapter";
-import { AptosWalletAdapterProvider } from "@aptos-labs/wallet-adapter-react";
-```
-
-5. Still in `Index.tsx`, add a constant that holds an array of wallets:
-
-```tsx filename="Index.tsx"
-...
-const wallets = [new PetraWallet()];
-...
-```
-
-6. Inside the `render` method, update the code with the following:
-
-```tsx filename="Index.tsx"
-...
-
-
-
-...
-```
-
-That wraps our app with the adapter provider and initializes it with our wallets. It also sets the provider to autoConnect a wallet.
-
-7. Open the `App.tsx` file and import the wallet connect UI package we installed in the previous step. At the top of the file add the following:
-
-```tsx filename="App.tsx"
-import { WalletSelector } from "@aptos-labs/wallet-adapter-ant-design";
-```
-
-8. The UI package uses a style `.css` file; let's import that one also at the bottom of the import statements.
-
-```tsx filename="App.tsx"
-...
-import "@aptos-labs/wallet-adapter-ant-design/dist/index.css";
-```
-
-9. In the `return` statement, remove the `
Connect Wallet
` text and add the `WalletSelector` component:
-
-```tsx filename="App.tsx"
-...
-
-
-
-...
-```
-
-10. Start the local server with `npm start` and open the app in the browser.
-
-We now have a working Wallet connect button and a wallet selector modal. Feel free to play with it and connect a wallet with it.
-
-Then learn how to [fetch data from chain](4-fetch-data-from-chain.mdx) in chapter 4.
diff --git a/apps/nextra/pages/zh/build/guides/build-e2e-dapp/4-fetch-data-from-chain.mdx b/apps/nextra/pages/zh/build/guides/build-e2e-dapp/4-fetch-data-from-chain.mdx
deleted file mode 100644
index 5fa424a3d..000000000
--- a/apps/nextra/pages/zh/build/guides/build-e2e-dapp/4-fetch-data-from-chain.mdx
+++ /dev/null
@@ -1,167 +0,0 @@
-import { Callout } from 'nextra/components';
-
-# 4. Fetch Data from Chain
-
-In the fourth chapter of the tutorial on [building an end-to-end dapp on Aptos](../build-e2e-dapp.mdx), you will be learning to fetch data from chain.
-
-Our UI logic relies on whether the connected account has created a todo list. If the account has created a todo list, our app should display that list; if not, the app should display a button offering the option to create a new list.
-
-For that, we first need to check if the connected account has a `TodoList` resource. In our smart contract, whenever someone creates a todo list we create and assign a `TodoList` resource to their account.
-
-To fetch data from chain, we can use the [Aptos TypeScript SDK](../../sdks/ts-sdk.mdx). The SDK provides classes and functions for us to easily interact and query the Aptos chain.
-
-To get started:
-
-1. Stop the local server if running.
-2. In the `client` directory, run: `npm i @aptos-labs/ts-sdk`
-3. In the `App.tsx` file, import the `Aptos` class like so:
-
-```tsx filename="App.tsx"
-import { Aptos } from "@aptos-labs/ts-sdk";
-```
-
-The TypeScript SDK provides us with a `Aptos` class which is the main entry point into Aptos's API. By initializing `Aptos` we can query the Aptos chain.
-
-
-Read more about the [`Aptos`](../../sdks/ts-sdk.mdx) class in the Aptos TypeScript SDK docs.
-
-
-1. In the `App.tsx` file, add:
-
-```tsx filename="App.tsx"
-const aptos = new Aptos();
-```
-
-This will initialize a `Aptos` instance for us.
-
-
-By default, `Aptos` will interact with the `devnet` network, to set up a [different network](../../../network/nodes/networks.mdx), we can use `AptosConfig` class.
-
-```tsx filename="App.tsx"
-import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";
-
-const aptosConfig = new AptosConfig({ network: Network.MAINNET });
-const aptos = new Aptos(aptosConfig);
-```
-
-
-
-Our app displays different UIs based on a user resource (i.e if a user has a list ⇒ if a user has a `TodoList` resource). For that, we need to know the current account connected to our app.
-
-1. Import wallet from the wallet adapter React provider:
-
-```tsx filename="App.tsx"
-import { useWallet } from "@aptos-labs/wallet-adapter-react";
-```
-
-2. Extract the account object from the wallet adapter:
-
-```tsx filename="App.tsx"
-function App (
- const { account } = useWallet();
- ...
-)
-```
-
-The `account` object is `null` if there is no account connected; when an account is connected, the `account` object holds the account information, including the account address.
-
-3. Next, we want to fetch the account’s TodoList resource.
- Begin by importing `useEffect` by using `jsx import useEffect from "react"; `
- Let’s add a `useEffect` hook to our file that would call a function to fetch the resource whenever our account address changes:
-
-```tsx filename="App.tsx"
-function App() {
- ...
- useEffect(() => {
- fetchList();
- }, [account?.address]);
- ...
-}
-```
-
-4. Before creating our `fetchList` function, let’s also create a local state to store whether the account has a list:
-
-```tsx filename="App.tsx"
-function App (
- ...
- const [accountHasList, setAccountHasList] = useState(false);
- ...
-)
-```
-
-also import `useEffect` using
-`import { useState, useEffect } from "react"; `
-
-5. Our `useEffect` hook is calling a `fetchList` function; let’s create it:
-
-```tsx filename="App.tsx"
-const fetchList = async () => {
- if (!account) return [];
- // change this to be your module account address
- const moduleAddress = "0xcbddf398841353776903dbab2fdaefc54f181d07e114ae818b1a67af28d1b018";
- try {
- const todoListResource = await aptos.getAccountResource(
- {
- accountAddress:account?.address,
- resourceType:`${moduleAddress}::todolist::TodoList`
- }
- );
- setAccountHasList(true);
- } catch (e: any) {
- setAccountHasList(false);
- }
-};
-```
-
-The `moduleAddress` is the address we publish the module under, i.e the account address you have in your `Move.toml` file (`myaddr`).
-
-The `provider.getAccountResource()`expects an _account address_ that holds the resource we are looking for and a string representation of an on-chain _Move struct type_.
-
-- account address - is the current connected account (we are getting it from the wallet account object)
-- Move struct type string syntax:
- - The account address who holds the move module = our profile account address (You might want to change the `moduleAddress` const to be your own account address)
- - The module name the resource lives in = `todolist`
- - The resource name = `TodoList`
-
-If the request succeeds and there is a resource for that account, we want to set our local state to `true`; otherwise, we would set it to `false`.
-
-6. Let’s update `import { Layout, Row, Col } from "antd"; ` to import Button:
- `import { Layout, Row, Col, Button } from "antd"; `
-
-7. Let’s update our UI based on the `accountHasList` state:
-
-```tsx filename="App.tsx"
-return (
- <>
-
-
-
-
Our todolist
-
-
-
-
-
-
- {!accountHasList && (
-
-
-
-
-
- )}
- >
-);
-```
-
-We now have an **Add new list** button that appears only if the account doesn’t have a list.
-
-Start the local server with `npm start`. You should see the **Add new list** button.
-
-Next, let’s understand how to create a new list by [submitting data to chain](5-submit-data-to-chain.mdx) in chapter 5.
diff --git a/apps/nextra/pages/zh/build/guides/build-e2e-dapp/5-submit-data-to-chain.mdx b/apps/nextra/pages/zh/build/guides/build-e2e-dapp/5-submit-data-to-chain.mdx
deleted file mode 100644
index 3458e4fec..000000000
--- a/apps/nextra/pages/zh/build/guides/build-e2e-dapp/5-submit-data-to-chain.mdx
+++ /dev/null
@@ -1,167 +0,0 @@
-# 5. Submit Data to Chain
-
-In the fifth chapter of the tutorial on [building an end-to-end dapp on Aptos](../build-e2e-dapp.mdx), you will be submitting data to the chain.
-
-So now we have an **Add new list** button that appears if the connected account hasn’t created a list yet. We still don't have a way for an account to create a list, so let’s add that functionality.
-
-1. First, our wallet adapter provider has a `signAndSubmitTransaction` function; let’s extract it by updating the following:
-
-```tsx filename="App.tsx"
-const { account, signAndSubmitTransaction } = useWallet();
-```
-
-2. Add an `onClick` event to the new list button:
-
-```tsx filename="App.tsx"
-
-```
-
-3. Update the import statement from `@aptos-labs/wallet-adapter-react` to also import the `InputTransactionData` type and
-
-```tsx filename="App.tsx"
-import {
- useWallet,
- InputTransactionData,
-} from "@aptos-labs/wallet-adapter-react";
-```
-
-4. Add the `addNewList` function:
-
-```tsx filename="App.tsx"
-
-const addNewList = async () => {
- if (!account) return [];
-
- const transaction:InputTransactionData = {
- data: {
- function:`${moduleAddress}::todolist::create_list`,
- functionArguments:[]
- }
- }
- try {
- // sign and submit transaction to chain
- const response = await signAndSubmitTransaction(transaction);
- // wait for transaction
- await aptos.waitForTransaction({transactionHash:response.hash});
- setAccountHasList(true);
- } catch (error: any) {
- setAccountHasList(false);
- }
-};
-```
-
-5. Since our new function also uses `moduleAddress`, let’s get it out of the `fetchList` function scope to the global scope so it can be used globally.
-
-In our `fetchList` function, find the line:
-
-```tsx filename="App.tsx"
-// replace with your own address
-const moduleAddress =
- "0xcbddf398841353776903dbab2fdaefc54f181d07e114ae818b1a67af28d1b018";
-```
-
-And move it to outside of the main `App` function, right beneath our const `provider` declarations.
-
-```tsx filename="App.tsx"
-export const aptos = new Aptos();
-// change this to be your module account address
-export const moduleAddress =
- "0xcbddf398841353776903dbab2fdaefc54f181d07e114ae818b1a67af28d1b018";
-```
-
-**Let’s go over the `addNewList` function code.**
-
-First, we use the `account` property from our wallet provider to make sure there is an account connected to our app.
-
-Then we build our transaction data to be submitted to chain:
-
-```tsx filename="App.tsx"
-const transaction:InputTransactionData = {
- data: {
- function:`${moduleAddress}::todolist::create_list`,
- functionArguments:[]
- }
- }
-```
-
-- `function`- is built from the module address, module name and the function name.
-- `functionArguments` - the arguments the function expects, in our case it doesn’t expect any arguments.
-
-Next, we submit the transaction payload and wait for its response. The response returned from the `signAndSubmitTransaction` function holds the transaction hash. Since it can take a bit for the transaction to be fully executed on chain and we also want to make sure it is executed successfully, we `waitForTransaction`. And only then we can set our local `accountHasList` state to `true`.
-
-6. Before testing our app, let’s tweak our UI a bit and add a Spinner component to show up while we are waiting for the transaction.
- Add a local state to keep track whether a transaction is in progress:
-
-```tsx filename="App.tsx"
-const [transactionInProgress, setTransactionInProgress] =
- useState(false);
-```
-
-7. Update our `addNewList` function to update the local state:
-
-```tsx filename="App.tsx"
-const addNewList = async () => {
- if (!account) return [];
- setTransactionInProgress(true);
- const transaction:InputTransactionData = {
- data: {
- function:`${moduleAddress}::todolist::create_list`,
- functionArguments:[]
- }
- }
- try {
- // sign and submit transaction to chain
- const response = await signAndSubmitTransaction(transaction);
- // wait for transaction
- await aptos.waitForTransaction({transactionHash:response.hash});
- setAccountHasList(true);
- } catch (error: any) {
- setAccountHasList(false);
- } finally {
- setTransactionInProgress(false);
- }
-};
-```
-
-8. Update the important statement from `antd` to also import `Spin`
-
-```tsx filename="App.tsx"
-import { Layout, Row, Col, Button, Spin } from "antd";
-```
-
-9. Update our UI with the following:
-
-```tsx filename="App.tsx"
-return (
- <>
- ...
-
- {!accountHasList && (
-
-
-
-
-
- )}
-
- >
-);
-```
-
-Now you can head over to our app, and add a new list!
-
-Since you haven’t made the user interface able to handle cases where an account has created a list, you will do so next [handling tasks](6-handle-tasks.mdx) in chapter 6.
diff --git a/apps/nextra/pages/zh/build/guides/build-e2e-dapp/6-handle-tasks.mdx b/apps/nextra/pages/zh/build/guides/build-e2e-dapp/6-handle-tasks.mdx
deleted file mode 100644
index eba4ece91..000000000
--- a/apps/nextra/pages/zh/build/guides/build-e2e-dapp/6-handle-tasks.mdx
+++ /dev/null
@@ -1,431 +0,0 @@
-# 6. Handle Tasks
-
-In the sixth and final chapter of the tutorial on [building an end-to-end dapp on Aptos](../build-e2e-dapp.mdx), you will add functionality to the app so the user interface is able to handle cases where an account has created a list.
-
-We have covered how to [fetch data](4-fetch-data-from-chain.mdx) (an account’s todo list) from chain and how to [submit a transaction](5-submit-data-to-chain.mdx) (new todo list) to chain using Wallet.
-
-Let’s finish building our app by implementing fetch tasks and adding a task function.
-
-## Fetch tasks
-
-1. Create a local state `tasks` that will hold our tasks. It will be a state of a Task type (that has the same properties we set on our smart contract):
-
-```ts filename="App.tsx"
-type Task = {
- address: string;
- completed: boolean;
- content: string;
- task_id: string;
-};
-
-function App() {
- const [tasks, setTasks] = useState([]);
- ...
-}
-```
-
-2. Update our `fetchList` function to fetch the tasks in the account’s `TodoList` resource:
-
-```ts filename="App.tsx"
-const fetchList = async () => {
- if (!account) return [];
- try {
- const todoListResource = await aptos.getAccountResource(
- {accountAddress:account?.address,
- resourceType:`${moduleAddress}::todolist::TodoList`}
- );
- setAccountHasList(true);
- // tasks table handle
- const tableHandle = (todoListResource as any).tasks.handle;
- // tasks table counter
- const taskCounter = (todoListResource as any).task_counter;
-
- let tasks = [];
- let counter = 1;
- while (counter <= taskCounter) {
- const tableItem = {
- key_type: "u64",
- value_type: `${moduleAddress}::todolist::Task`,
- key: `${counter}`,
- };
- const task = await aptos.getTableItem({handle:tableHandle, data:tableItem});
- tasks.push(task);
- counter++;
- }
- // set tasks in local state
- setTasks(tasks);
- } catch (e: any) {
- setAccountHasList(false);
- }
-};
-```
-
-**This part is a bit confusing, so stick with us!**
-
-Tasks are stored in a table (this is how we built our contract). To fetch a table item (i.e a task), we need that task's table handle. We also need the `task_counter` in that resource so we can loop over and fetch the task with the `task_id` that matches the `task_counter`.
-
-```ts filename="App.tsx"
-const tableHandle = (TodoListResource as any).data.tasks.handle;
-const taskCounter = (TodoListResource as any).data.task_counter;
-```
-
-Now that we have our tasks table handle and our `task_counter` variable, lets loop over the `taskCounter` . We define a `counter` and set it to 1 as the task_counter / task_id is never less than 1.
-
-We loop while the `counter` is less then the `taskCounter` and fetch the table item and push it to the tasks array:
-
-```ts filename="App.tsx"
-let tasks = [];
-let counter = 1;
-while (counter <= taskCounter) {
- const tableItem = {
- key_type: "u64",
- value_type: `${moduleAddress}::todolist::Task`,
- key: `${counter}`,
- };
- const task = await provider.getTableItem(tableHandle, tableItem);
- tasks.push(task);
- counter++;
-}
-```
-
-We build a `tableItem` object to fetch. If we take a look at our table structure from the contract:
-
-```ts filename="App.tsx"
-tasks: Table,
-```
-
-We see that it has a `key` type `u64` and a `value` of type `Task`. And whenever we create a new task, we assign the `key` to be the incremented task counter.
-
-```move filename="todolist.move"
-// adds the new task into the tasks table
-table::upsert(&mut todo_list.tasks, counter, new_task);
-```
-
-So the object we built is:
-
-```ts filename="App.tsx"
-{
- key_type: "u64",
- value_type:`${moduleAddress}::todolist::Task`,
- key: `${taskCounter}`,
-}
-```
-
-Where `key_type` is the table `key` type, `key` is the key value we are looking for, and the `value_type` is the table `value` which is a `Task` struct. The Task struct uses the same format from our previous resource query:
-
-- The account address who holds that module = our profile account address
-- The module name the resource lives in = `todolist`
-- The struct name = `Task`
-
-The last thing we want to do is display the tasks we just fetched.
-
-6. In our `App.tsx` file, update our UI with the following code:
-
-```tsx filename="App.tsx"
-{
- !accountHasList ? (
-
-
-
-
-
- ) : (
-
-
- {tasks && (
- (
- ]}>
- {`${task.address.slice(0, 6)}...${task.address.slice(-5)}`}
- }
- />
-
- )}
- />
- )}
-
-
- );
-}
-```
-
-That will display the **Add new list** button if account doesn’t have a list or instead the tasks if the account has a list.
-
-Go ahead and refresh your browser - see the magic!
-
-We haven’t added any tasks yet, so we simply see a box of empty data. Let’s add some tasks!
-
-## Add task
-
-1. Update our UI with an _add task_ input:
-
-Import `Input` from `antd` - `import { Input } from "antd";`
-
-```tsx filename="App.tsx"
-{!accountHasList ? (
- ...
-) : (
-
- // Add this!
-